mirror of
https://github.com/superseriousbusiness/gotosocial
synced 2025-06-05 21:59:39 +02:00
[experiment] add alternative wasm sqlite3 implementation available via build-tag (#2863)
This allows for building GoToSocial with [SQLite transpiled to WASM](https://github.com/ncruces/go-sqlite3) and accessed through [Wazero](https://wazero.io/).
This commit is contained in:
25
vendor/github.com/tetratelabs/wazero/internal/platform/cpuid.go
generated
vendored
Normal file
25
vendor/github.com/tetratelabs/wazero/internal/platform/cpuid.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package platform
|
||||
|
||||
// CpuFeatureFlags exposes methods for querying CPU capabilities
|
||||
type CpuFeatureFlags interface {
|
||||
// Has returns true when the specified flag (represented as uint64) is supported
|
||||
Has(cpuFeature CpuFeature) bool
|
||||
// HasExtra returns true when the specified extraFlag (represented as uint64) is supported
|
||||
HasExtra(cpuFeature CpuFeature) bool
|
||||
}
|
||||
|
||||
type CpuFeature uint64
|
||||
|
||||
const (
|
||||
// CpuFeatureAmd64SSE3 is the flag to query CpuFeatureFlags.Has for SSEv3 capabilities on amd64
|
||||
CpuFeatureAmd64SSE3 CpuFeature = 1
|
||||
// CpuFeatureAmd64SSE4_1 is the flag to query CpuFeatureFlags.Has for SSEv4.1 capabilities on amd64
|
||||
CpuFeatureAmd64SSE4_1 CpuFeature = 1 << 19
|
||||
// CpuFeatureAmd64SSE4_2 is the flag to query CpuFeatureFlags.Has for SSEv4.2 capabilities on amd64
|
||||
CpuFeatureAmd64SSE4_2 CpuFeature = 1 << 20
|
||||
)
|
||||
|
||||
const (
|
||||
// CpuExtraFeatureAmd64ABM is the flag to query CpuFeatureFlags.HasExtra for Advanced Bit Manipulation capabilities (e.g. LZCNT) on amd64
|
||||
CpuExtraFeatureAmd64ABM CpuFeature = 1 << 5
|
||||
)
|
59
vendor/github.com/tetratelabs/wazero/internal/platform/cpuid_amd64.go
generated
vendored
Normal file
59
vendor/github.com/tetratelabs/wazero/internal/platform/cpuid_amd64.go
generated
vendored
Normal file
@ -0,0 +1,59 @@
|
||||
//go:build amd64 && !tinygo
|
||||
|
||||
package platform
|
||||
|
||||
// CpuFeatures exposes the capabilities for this CPU, queried via the Has, HasExtra methods
|
||||
var CpuFeatures CpuFeatureFlags = loadCpuFeatureFlags()
|
||||
|
||||
// cpuFeatureFlags implements CpuFeatureFlags interface
|
||||
type cpuFeatureFlags struct {
|
||||
flags uint64
|
||||
extraFlags uint64
|
||||
}
|
||||
|
||||
// cpuid exposes the CPUID instruction to the Go layer (https://www.amd.com/system/files/TechDocs/25481.pdf)
|
||||
// implemented in impl_amd64.s
|
||||
func cpuid(arg1, arg2 uint32) (eax, ebx, ecx, edx uint32)
|
||||
|
||||
// cpuidAsBitmap combines the result of invoking cpuid to uint64 bitmap
|
||||
func cpuidAsBitmap(arg1, arg2 uint32) uint64 {
|
||||
_ /* eax */, _ /* ebx */, ecx, edx := cpuid(arg1, arg2)
|
||||
return (uint64(edx) << 32) | uint64(ecx)
|
||||
}
|
||||
|
||||
// loadStandardRange load flags from the standard range, panics otherwise
|
||||
func loadStandardRange(id uint32) uint64 {
|
||||
// ensure that the id is in the valid range, returned by cpuid(0,0)
|
||||
maxRange, _, _, _ := cpuid(0, 0)
|
||||
if id > maxRange {
|
||||
panic("cannot query standard CPU flags")
|
||||
}
|
||||
return cpuidAsBitmap(id, 0)
|
||||
}
|
||||
|
||||
// loadStandardRange load flags from the extended range, panics otherwise
|
||||
func loadExtendedRange(id uint32) uint64 {
|
||||
// ensure that the id is in the valid range, returned by cpuid(0x80000000,0)
|
||||
maxRange, _, _, _ := cpuid(0x80000000, 0)
|
||||
if id > maxRange {
|
||||
panic("cannot query extended CPU flags")
|
||||
}
|
||||
return cpuidAsBitmap(id, 0)
|
||||
}
|
||||
|
||||
func loadCpuFeatureFlags() CpuFeatureFlags {
|
||||
return &cpuFeatureFlags{
|
||||
flags: loadStandardRange(1),
|
||||
extraFlags: loadExtendedRange(0x80000001),
|
||||
}
|
||||
}
|
||||
|
||||
// Has implements the same method on the CpuFeatureFlags interface
|
||||
func (f *cpuFeatureFlags) Has(cpuFeature CpuFeature) bool {
|
||||
return (f.flags & uint64(cpuFeature)) != 0
|
||||
}
|
||||
|
||||
// HasExtra implements the same method on the CpuFeatureFlags interface
|
||||
func (f *cpuFeatureFlags) HasExtra(cpuFeature CpuFeature) bool {
|
||||
return (f.extraFlags & uint64(cpuFeature)) != 0
|
||||
}
|
14
vendor/github.com/tetratelabs/wazero/internal/platform/cpuid_amd64.s
generated
vendored
Normal file
14
vendor/github.com/tetratelabs/wazero/internal/platform/cpuid_amd64.s
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
#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
|
||||
MOVL arg2+4(FP), CX
|
||||
CPUID
|
||||
MOVL AX, eax+8(FP)
|
||||
MOVL BX, ebx+12(FP)
|
||||
MOVL CX, ecx+16(FP)
|
||||
MOVL DX, edx+20(FP)
|
||||
RET
|
||||
|
14
vendor/github.com/tetratelabs/wazero/internal/platform/cpuid_unsupported.go
generated
vendored
Normal file
14
vendor/github.com/tetratelabs/wazero/internal/platform/cpuid_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
//go:build !amd64 || tinygo
|
||||
|
||||
package platform
|
||||
|
||||
var CpuFeatures CpuFeatureFlags = &cpuFeatureFlags{}
|
||||
|
||||
// cpuFeatureFlags implements CpuFeatureFlags for unsupported platforms
|
||||
type cpuFeatureFlags struct{}
|
||||
|
||||
// Has implements the same method on the CpuFeatureFlags interface
|
||||
func (c *cpuFeatureFlags) Has(cpuFeature CpuFeature) bool { return false }
|
||||
|
||||
// HasExtra implements the same method on the CpuFeatureFlags interface
|
||||
func (c *cpuFeatureFlags) HasExtra(cpuFeature CpuFeature) bool { return false }
|
17
vendor/github.com/tetratelabs/wazero/internal/platform/crypto.go
generated
vendored
Normal file
17
vendor/github.com/tetratelabs/wazero/internal/platform/crypto.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"io"
|
||||
"math/rand"
|
||||
)
|
||||
|
||||
// seed is a fixed seed value for NewFakeRandSource.
|
||||
//
|
||||
// Trivia: While arbitrary, 42 was chosen as it is the "Ultimate Answer" in
|
||||
// the Douglas Adams novel "The Hitchhiker's Guide to the Galaxy."
|
||||
const seed = int64(42)
|
||||
|
||||
// NewFakeRandSource returns a deterministic source of random values.
|
||||
func NewFakeRandSource() io.Reader {
|
||||
return rand.New(rand.NewSource(seed))
|
||||
}
|
76
vendor/github.com/tetratelabs/wazero/internal/platform/mmap_linux.go
generated
vendored
Normal file
76
vendor/github.com/tetratelabs/wazero/internal/platform/mmap_linux.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
const (
|
||||
// https://man7.org/linux/man-pages/man2/mmap.2.html
|
||||
__MAP_HUGE_SHIFT = 26
|
||||
__MAP_HUGETLB = 0x40000
|
||||
)
|
||||
|
||||
var hugePagesConfigs []hugePagesConfig
|
||||
|
||||
type hugePagesConfig struct {
|
||||
size int
|
||||
flag int
|
||||
}
|
||||
|
||||
func (hpc *hugePagesConfig) match(size int) bool {
|
||||
return (size & (hpc.size - 1)) == 0
|
||||
}
|
||||
|
||||
func init() {
|
||||
dirents, err := os.ReadDir("/sys/kernel/mm/hugepages/")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for _, dirent := range dirents {
|
||||
name := dirent.Name()
|
||||
if !strings.HasPrefix(name, "hugepages-") {
|
||||
continue
|
||||
}
|
||||
if !strings.HasSuffix(name, "kB") {
|
||||
continue
|
||||
}
|
||||
n, err := strconv.ParseUint(name[10:len(name)-2], 10, 64)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if bits.OnesCount64(n) != 1 {
|
||||
continue
|
||||
}
|
||||
n *= 1024
|
||||
hugePagesConfigs = append(hugePagesConfigs, hugePagesConfig{
|
||||
size: int(n),
|
||||
flag: int(bits.TrailingZeros64(n)<<__MAP_HUGE_SHIFT) | __MAP_HUGETLB,
|
||||
})
|
||||
}
|
||||
|
||||
sort.Slice(hugePagesConfigs, func(i, j int) bool {
|
||||
return hugePagesConfigs[i].size > hugePagesConfigs[j].size
|
||||
})
|
||||
}
|
||||
|
||||
func mmapCodeSegment(size, prot int) ([]byte, error) {
|
||||
flags := syscall.MAP_ANON | syscall.MAP_PRIVATE
|
||||
|
||||
for _, hugePagesConfig := range hugePagesConfigs {
|
||||
if hugePagesConfig.match(size) {
|
||||
b, err := syscall.Mmap(-1, 0, size, prot, flags|hugePagesConfig.flag)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
return b, nil
|
||||
}
|
||||
}
|
||||
|
||||
return syscall.Mmap(-1, 0, size, prot, flags)
|
||||
}
|
18
vendor/github.com/tetratelabs/wazero/internal/platform/mmap_other.go
generated
vendored
Normal file
18
vendor/github.com/tetratelabs/wazero/internal/platform/mmap_other.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
// Separated from linux which has support for huge pages.
|
||||
//go:build darwin || freebsd
|
||||
|
||||
package platform
|
||||
|
||||
import "syscall"
|
||||
|
||||
func mmapCodeSegment(size, prot int) ([]byte, error) {
|
||||
return syscall.Mmap(
|
||||
-1,
|
||||
0,
|
||||
size,
|
||||
prot,
|
||||
// Anonymous as this is not an actual file, but a memory,
|
||||
// Private as this is in-process memory region.
|
||||
syscall.MAP_ANON|syscall.MAP_PRIVATE,
|
||||
)
|
||||
}
|
49
vendor/github.com/tetratelabs/wazero/internal/platform/mmap_unix.go
generated
vendored
Normal file
49
vendor/github.com/tetratelabs/wazero/internal/platform/mmap_unix.go
generated
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
//go:build (darwin || linux || freebsd) && !tinygo
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const (
|
||||
mmapProtAMD64 = syscall.PROT_READ | syscall.PROT_WRITE | syscall.PROT_EXEC
|
||||
mmapProtARM64 = syscall.PROT_READ | syscall.PROT_WRITE
|
||||
)
|
||||
|
||||
const MmapSupported = true
|
||||
|
||||
func munmapCodeSegment(code []byte) error {
|
||||
return syscall.Munmap(code)
|
||||
}
|
||||
|
||||
// mmapCodeSegmentAMD64 gives all read-write-exec permission to the mmap region
|
||||
// to enter the function. Otherwise, segmentation fault exception is raised.
|
||||
func mmapCodeSegmentAMD64(size int) ([]byte, error) {
|
||||
// The region must be RWX: RW for writing native codes, X for executing the region.
|
||||
return mmapCodeSegment(size, mmapProtAMD64)
|
||||
}
|
||||
|
||||
// mmapCodeSegmentARM64 cannot give all read-write-exec permission to the mmap region.
|
||||
// Otherwise, the mmap systemcall would raise an error. Here we give read-write
|
||||
// to the region so that we can write contents at call-sites. Callers are responsible to
|
||||
// execute MprotectRX on the returned buffer.
|
||||
func mmapCodeSegmentARM64(size int) ([]byte, error) {
|
||||
// The region must be RW: RW for writing native codes.
|
||||
return mmapCodeSegment(size, mmapProtARM64)
|
||||
}
|
||||
|
||||
// MprotectRX is like syscall.Mprotect with RX permission, defined locally so that freebsd compiles.
|
||||
func MprotectRX(b []byte) (err error) {
|
||||
var _p0 unsafe.Pointer
|
||||
if len(b) > 0 {
|
||||
_p0 = unsafe.Pointer(&b[0])
|
||||
}
|
||||
const prot = syscall.PROT_READ | syscall.PROT_EXEC
|
||||
_, _, e1 := syscall.Syscall(syscall.SYS_MPROTECT, uintptr(_p0), uintptr(len(b)), uintptr(prot))
|
||||
if e1 != 0 {
|
||||
err = syscall.Errno(e1)
|
||||
}
|
||||
return
|
||||
}
|
28
vendor/github.com/tetratelabs/wazero/internal/platform/mmap_unsupported.go
generated
vendored
Normal file
28
vendor/github.com/tetratelabs/wazero/internal/platform/mmap_unsupported.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
//go:build !(darwin || linux || freebsd || windows) || tinygo
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
var errUnsupported = fmt.Errorf("mmap unsupported on GOOS=%s. Use interpreter instead.", runtime.GOOS)
|
||||
|
||||
const MmapSupported = false
|
||||
|
||||
func munmapCodeSegment(code []byte) error {
|
||||
panic(errUnsupported)
|
||||
}
|
||||
|
||||
func mmapCodeSegmentAMD64(size int) ([]byte, error) {
|
||||
panic(errUnsupported)
|
||||
}
|
||||
|
||||
func mmapCodeSegmentARM64(size int) ([]byte, error) {
|
||||
panic(errUnsupported)
|
||||
}
|
||||
|
||||
func MprotectRX(b []byte) (err error) {
|
||||
panic(errUnsupported)
|
||||
}
|
97
vendor/github.com/tetratelabs/wazero/internal/platform/mmap_windows.go
generated
vendored
Normal file
97
vendor/github.com/tetratelabs/wazero/internal/platform/mmap_windows.go
generated
vendored
Normal file
@ -0,0 +1,97 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procVirtualAlloc = kernel32.NewProc("VirtualAlloc")
|
||||
procVirtualProtect = kernel32.NewProc("VirtualProtect")
|
||||
procVirtualFree = kernel32.NewProc("VirtualFree")
|
||||
)
|
||||
|
||||
const (
|
||||
windows_MEM_COMMIT uintptr = 0x00001000
|
||||
windows_MEM_RELEASE uintptr = 0x00008000
|
||||
windows_PAGE_READWRITE uintptr = 0x00000004
|
||||
windows_PAGE_EXECUTE_READ uintptr = 0x00000020
|
||||
windows_PAGE_EXECUTE_READWRITE uintptr = 0x00000040
|
||||
)
|
||||
|
||||
const MmapSupported = true
|
||||
|
||||
func munmapCodeSegment(code []byte) error {
|
||||
return freeMemory(code)
|
||||
}
|
||||
|
||||
// allocateMemory commits the memory region via the "VirtualAlloc" function.
|
||||
// See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualalloc
|
||||
func allocateMemory(size uintptr, protect uintptr) (uintptr, error) {
|
||||
address := uintptr(0) // system determines where to allocate the region.
|
||||
alloctype := windows_MEM_COMMIT
|
||||
if r, _, err := procVirtualAlloc.Call(address, size, alloctype, protect); r == 0 {
|
||||
return 0, fmt.Errorf("compiler: VirtualAlloc error: %w", ensureErr(err))
|
||||
} else {
|
||||
return r, nil
|
||||
}
|
||||
}
|
||||
|
||||
// freeMemory releases the memory region via the "VirtualFree" function.
|
||||
// See https://docs.microsoft.com/en-us/windows/win32/api/memoryapi/nf-memoryapi-virtualfree
|
||||
func freeMemory(code []byte) error {
|
||||
address := unsafe.Pointer(&code[0])
|
||||
size := uintptr(0) // size must be 0 because we're using MEM_RELEASE.
|
||||
freetype := windows_MEM_RELEASE
|
||||
if r, _, err := procVirtualFree.Call(uintptr(address), size, freetype); r == 0 {
|
||||
return fmt.Errorf("compiler: VirtualFree error: %w", ensureErr(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func virtualProtect(address, size, newprotect uintptr, oldprotect *uint32) error {
|
||||
if r, _, err := procVirtualProtect.Call(address, size, newprotect, uintptr(unsafe.Pointer(oldprotect))); r == 0 {
|
||||
return fmt.Errorf("compiler: VirtualProtect error: %w", ensureErr(err))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func mmapCodeSegmentAMD64(size int) ([]byte, error) {
|
||||
p, err := allocateMemory(uintptr(size), windows_PAGE_EXECUTE_READWRITE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return unsafe.Slice((*byte)(unsafe.Pointer(p)), size), nil
|
||||
}
|
||||
|
||||
func mmapCodeSegmentARM64(size int) ([]byte, error) {
|
||||
p, err := allocateMemory(uintptr(size), windows_PAGE_READWRITE)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return unsafe.Slice((*byte)(unsafe.Pointer(p)), size), nil
|
||||
}
|
||||
|
||||
var old = uint32(windows_PAGE_READWRITE)
|
||||
|
||||
func MprotectRX(b []byte) (err error) {
|
||||
err = virtualProtect(uintptr(unsafe.Pointer(&b[0])), uintptr(len(b)), windows_PAGE_EXECUTE_READ, &old)
|
||||
return
|
||||
}
|
||||
|
||||
// ensureErr returns syscall.EINVAL when the input error is nil.
|
||||
//
|
||||
// We are supposed to use "GetLastError" which is more precise, but it is not safe to execute in goroutines. While
|
||||
// "GetLastError" is thread-local, goroutines are not pinned to threads.
|
||||
//
|
||||
// See https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-getlasterror
|
||||
func ensureErr(err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return syscall.EINVAL
|
||||
}
|
23
vendor/github.com/tetratelabs/wazero/internal/platform/mremap_other.go
generated
vendored
Normal file
23
vendor/github.com/tetratelabs/wazero/internal/platform/mremap_other.go
generated
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
//go:build !(darwin || linux || freebsd) || tinygo
|
||||
|
||||
package platform
|
||||
|
||||
func remapCodeSegmentAMD64(code []byte, size int) ([]byte, error) {
|
||||
b, err := mmapCodeSegmentAMD64(size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(b, code)
|
||||
mustMunmapCodeSegment(code)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
func remapCodeSegmentARM64(code []byte, size int) ([]byte, error) {
|
||||
b, err := mmapCodeSegmentARM64(size)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(b, code)
|
||||
mustMunmapCodeSegment(code)
|
||||
return b, nil
|
||||
}
|
21
vendor/github.com/tetratelabs/wazero/internal/platform/mremap_unix.go
generated
vendored
Normal file
21
vendor/github.com/tetratelabs/wazero/internal/platform/mremap_unix.go
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
//go:build (darwin || linux || freebsd) && !tinygo
|
||||
|
||||
package platform
|
||||
|
||||
func remapCodeSegmentAMD64(code []byte, size int) ([]byte, error) {
|
||||
return remapCodeSegment(code, size, mmapProtAMD64)
|
||||
}
|
||||
|
||||
func remapCodeSegmentARM64(code []byte, size int) ([]byte, error) {
|
||||
return remapCodeSegment(code, size, mmapProtARM64)
|
||||
}
|
||||
|
||||
func remapCodeSegment(code []byte, size, prot int) ([]byte, error) {
|
||||
b, err := mmapCodeSegment(size, prot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
copy(b, code)
|
||||
mustMunmapCodeSegment(code)
|
||||
return b, nil
|
||||
}
|
6
vendor/github.com/tetratelabs/wazero/internal/platform/path.go
generated
vendored
Normal file
6
vendor/github.com/tetratelabs/wazero/internal/platform/path.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
//go:build !windows
|
||||
|
||||
package platform
|
||||
|
||||
// ToPosixPath returns the input, as only windows might return backslashes.
|
||||
func ToPosixPath(in string) string { return in }
|
17
vendor/github.com/tetratelabs/wazero/internal/platform/path_windows.go
generated
vendored
Normal file
17
vendor/github.com/tetratelabs/wazero/internal/platform/path_windows.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
package platform
|
||||
|
||||
import "strings"
|
||||
|
||||
// ToPosixPath returns the input, converting any backslashes to forward ones.
|
||||
func ToPosixPath(in string) string {
|
||||
// strings.Map only allocates on change, which is good enough especially as
|
||||
// path.Join uses forward slash even on windows.
|
||||
return strings.Map(windowsToPosixSeparator, in)
|
||||
}
|
||||
|
||||
func windowsToPosixSeparator(r rune) rune {
|
||||
if r == '\\' {
|
||||
return '/'
|
||||
}
|
||||
return r
|
||||
}
|
81
vendor/github.com/tetratelabs/wazero/internal/platform/platform.go
generated
vendored
Normal file
81
vendor/github.com/tetratelabs/wazero/internal/platform/platform.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
// Package platform includes runtime-specific code needed for the compiler or otherwise.
|
||||
//
|
||||
// Note: This is a dependency-free alternative to depending on parts of Go's x/sys.
|
||||
// See /RATIONALE.md for more context.
|
||||
package platform
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// 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.
|
||||
func CompilerSupported() bool {
|
||||
switch runtime.GOOS {
|
||||
case "darwin", "windows", "linux", "freebsd":
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
return archRequirementsVerified
|
||||
}
|
||||
|
||||
// MmapCodeSegment copies the code into the executable region and returns the byte slice of the region.
|
||||
//
|
||||
// See https://man7.org/linux/man-pages/man2/mmap.2.html for mmap API and flags.
|
||||
func MmapCodeSegment(size int) ([]byte, error) {
|
||||
if size == 0 {
|
||||
panic("BUG: MmapCodeSegment with zero length")
|
||||
}
|
||||
if runtime.GOARCH == "amd64" {
|
||||
return mmapCodeSegmentAMD64(size)
|
||||
} else {
|
||||
return mmapCodeSegmentARM64(size)
|
||||
}
|
||||
}
|
||||
|
||||
// RemapCodeSegment reallocates the memory mapping of an existing code segment
|
||||
// to increase its size. The previous code mapping is unmapped and must not be
|
||||
// reused after the function returns.
|
||||
//
|
||||
// This is similar to mremap(2) on linux, and emulated on platforms which do not
|
||||
// have this syscall.
|
||||
//
|
||||
// See https://man7.org/linux/man-pages/man2/mremap.2.html
|
||||
func RemapCodeSegment(code []byte, size int) ([]byte, error) {
|
||||
if size < len(code) {
|
||||
panic("BUG: RemapCodeSegment with size less than code")
|
||||
}
|
||||
if code == nil {
|
||||
return MmapCodeSegment(size)
|
||||
}
|
||||
if runtime.GOARCH == "amd64" {
|
||||
return remapCodeSegmentAMD64(code, size)
|
||||
} else {
|
||||
return remapCodeSegmentARM64(code, size)
|
||||
}
|
||||
}
|
||||
|
||||
// MunmapCodeSegment unmaps the given memory region.
|
||||
func MunmapCodeSegment(code []byte) error {
|
||||
if len(code) == 0 {
|
||||
panic("BUG: MunmapCodeSegment with zero length")
|
||||
}
|
||||
return munmapCodeSegment(code)
|
||||
}
|
||||
|
||||
// mustMunmapCodeSegment panics instead of returning an error to the
|
||||
// application.
|
||||
//
|
||||
// # Why panic?
|
||||
//
|
||||
// It is less disruptive to the application to leak the previous block if it
|
||||
// could be unmapped than to leak the new block and return an error.
|
||||
// Realistically, either scenarios are pretty hard to debug, so we panic.
|
||||
func mustMunmapCodeSegment(code []byte) {
|
||||
if err := munmapCodeSegment(code); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
7
vendor/github.com/tetratelabs/wazero/internal/platform/platform_amd64.go
generated
vendored
Normal file
7
vendor/github.com/tetratelabs/wazero/internal/platform/platform_amd64.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
package platform
|
||||
|
||||
// init verifies that the current CPU supports the required AMD64 instructions
|
||||
func init() {
|
||||
// Ensure SSE4.1 is supported.
|
||||
archRequirementsVerified = CpuFeatures.Has(CpuFeatureAmd64SSE4_1)
|
||||
}
|
7
vendor/github.com/tetratelabs/wazero/internal/platform/platform_arm64.go
generated
vendored
Normal file
7
vendor/github.com/tetratelabs/wazero/internal/platform/platform_arm64.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
package platform
|
||||
|
||||
// init verifies that the current CPU supports the required ARM64 features
|
||||
func init() {
|
||||
// No further checks currently needed.
|
||||
archRequirementsVerified = true
|
||||
}
|
76
vendor/github.com/tetratelabs/wazero/internal/platform/time.go
generated
vendored
Normal file
76
vendor/github.com/tetratelabs/wazero/internal/platform/time.go
generated
vendored
Normal file
@ -0,0 +1,76 @@
|
||||
package platform
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/tetratelabs/wazero/sys"
|
||||
)
|
||||
|
||||
const (
|
||||
ms = int64(time.Millisecond)
|
||||
// FakeEpochNanos is midnight UTC 2022-01-01 and exposed for testing
|
||||
FakeEpochNanos = 1640995200000 * ms
|
||||
)
|
||||
|
||||
// NewFakeWalltime implements sys.Walltime with FakeEpochNanos that increases by 1ms each reading.
|
||||
// See /RATIONALE.md
|
||||
func NewFakeWalltime() sys.Walltime {
|
||||
// AddInt64 returns the new value. Adjust so the first reading will be FakeEpochNanos
|
||||
t := FakeEpochNanos - ms
|
||||
return func() (sec int64, nsec int32) {
|
||||
wt := atomic.AddInt64(&t, ms)
|
||||
return wt / 1e9, int32(wt % 1e9)
|
||||
}
|
||||
}
|
||||
|
||||
// NewFakeNanotime implements sys.Nanotime that increases by 1ms each reading.
|
||||
// See /RATIONALE.md
|
||||
func NewFakeNanotime() sys.Nanotime {
|
||||
// AddInt64 returns the new value. Adjust so the first reading will be zero.
|
||||
t := int64(0) - ms
|
||||
return func() int64 {
|
||||
return atomic.AddInt64(&t, ms)
|
||||
}
|
||||
}
|
||||
|
||||
// FakeNanosleep implements sys.Nanosleep by returning without sleeping.
|
||||
var FakeNanosleep = sys.Nanosleep(func(int64) {})
|
||||
|
||||
// FakeOsyield implements sys.Osyield by returning without yielding.
|
||||
var FakeOsyield = sys.Osyield(func() {})
|
||||
|
||||
// Walltime implements sys.Walltime with time.Now.
|
||||
//
|
||||
// Note: This is only notably less efficient than it could be is reading
|
||||
// runtime.walltime(). time.Now defensively reads nanotime also, just in case
|
||||
// time.Since is used. This doubles the performance impact. However, wall time
|
||||
// is likely to be read less frequently than Nanotime. Also, doubling the cost
|
||||
// matters less on fast platforms that can return both in <=100ns.
|
||||
func Walltime() (sec int64, nsec int32) {
|
||||
t := time.Now()
|
||||
return t.Unix(), int32(t.Nanosecond())
|
||||
}
|
||||
|
||||
// nanoBase uses time.Now to ensure a monotonic clock reading on all platforms
|
||||
// via time.Since.
|
||||
var nanoBase = time.Now()
|
||||
|
||||
// nanotimePortable implements sys.Nanotime with time.Since.
|
||||
//
|
||||
// Note: This is less efficient than it could be is reading runtime.nanotime(),
|
||||
// Just to do that requires CGO.
|
||||
func nanotimePortable() int64 {
|
||||
return time.Since(nanoBase).Nanoseconds()
|
||||
}
|
||||
|
||||
// Nanotime implements sys.Nanotime with runtime.nanotime() if CGO is available
|
||||
// and time.Since if not.
|
||||
func Nanotime() int64 {
|
||||
return nanotime()
|
||||
}
|
||||
|
||||
// Nanosleep implements sys.Nanosleep with time.Sleep.
|
||||
func Nanosleep(ns int64) {
|
||||
time.Sleep(time.Duration(ns))
|
||||
}
|
11
vendor/github.com/tetratelabs/wazero/internal/platform/time_cgo.go
generated
vendored
Normal file
11
vendor/github.com/tetratelabs/wazero/internal/platform/time_cgo.go
generated
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
//go:build cgo && !windows
|
||||
|
||||
package platform
|
||||
|
||||
import _ "unsafe" // for go:linkname
|
||||
|
||||
// nanotime uses runtime.nanotime as it is available on all platforms and
|
||||
// benchmarks faster than using time.Since.
|
||||
//
|
||||
//go:linkname nanotime runtime.nanotime
|
||||
func nanotime() int64
|
7
vendor/github.com/tetratelabs/wazero/internal/platform/time_notcgo.go
generated
vendored
Normal file
7
vendor/github.com/tetratelabs/wazero/internal/platform/time_notcgo.go
generated
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
//go:build !cgo && !windows
|
||||
|
||||
package platform
|
||||
|
||||
func nanotime() int64 {
|
||||
return nanotimePortable()
|
||||
}
|
40
vendor/github.com/tetratelabs/wazero/internal/platform/time_windows.go
generated
vendored
Normal file
40
vendor/github.com/tetratelabs/wazero/internal/platform/time_windows.go
generated
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
//go:build windows
|
||||
|
||||
package platform
|
||||
|
||||
import (
|
||||
"math/bits"
|
||||
"time"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var (
|
||||
_QueryPerformanceCounter = kernel32.NewProc("QueryPerformanceCounter")
|
||||
_QueryPerformanceFrequency = kernel32.NewProc("QueryPerformanceFrequency")
|
||||
)
|
||||
|
||||
var qpcfreq uint64
|
||||
|
||||
func init() {
|
||||
_, _, _ = _QueryPerformanceFrequency.Call(uintptr(unsafe.Pointer(&qpcfreq)))
|
||||
}
|
||||
|
||||
// On Windows, time.Time handled in time package cannot have the nanosecond precision.
|
||||
// The reason is that by default, it doesn't use QueryPerformanceCounter[1], but instead, use "interrupt time"
|
||||
// which doesn't support nanoseconds precision (though it is a monotonic) [2, 3, 4, 5].
|
||||
//
|
||||
// [1] https://learn.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter
|
||||
// [2] https://github.com/golang/go/blob/0cd309e12818f988693bf8e4d9f1453331dcf9f2/src/runtime/sys_windows_amd64.s#L297-L298
|
||||
// [3] https://github.com/golang/go/blob/0cd309e12818f988693bf8e4d9f1453331dcf9f2/src/runtime/os_windows.go#L549-L551
|
||||
// [4] https://github.com/golang/go/blob/master/src/runtime/time_windows.h#L7-L13
|
||||
// [5] http://web.archive.org/web/20210411000829/https://wrkhpi.wordpress.com/2007/08/09/getting-os-information-the-kuser_shared_data-structure/
|
||||
//
|
||||
// Therefore, on Windows, we directly invoke the syscall for QPC instead of time.Now or runtime.nanotime.
|
||||
// See https://github.com/golang/go/issues/31160 for example.
|
||||
func nanotime() int64 {
|
||||
var counter uint64
|
||||
_, _, _ = _QueryPerformanceCounter.Call(uintptr(unsafe.Pointer(&counter)))
|
||||
hi, lo := bits.Mul64(counter, uint64(time.Second))
|
||||
nanos, _ := bits.Div64(hi, lo, qpcfreq)
|
||||
return int64(nanos)
|
||||
}
|
Reference in New Issue
Block a user