Files
Hiroshi Yamauchi 60e60cd01a [Embedded] Fix platform allocation function arguments (#93003)
_swift_typedAllocate and _swift_typedDeallocate took alignment masks
while EmbeddedPlatform.h documents them as taking alignments, and
swift_slowDealloc passed _swift_deallocate the alignment mask and the
size in the wrong order. Fix these by passing alignments and correcting
the argument order in swift_slowDealloc.
2026-10-07 09:26:53 -07:00

267 lines
8.3 KiB
Swift

import Builtin
// POSIX dependencies
@_extern(c, "posix_memalign")
func posix_memalign(_: UnsafeMutablePointer<UnsafeMutableRawPointer?>, _: Int, _: Int) -> CInt
@_extern(c, "free")
func free(_ p: UnsafeMutableRawPointer?)
#if os(Linux) && !SWIFT_STDLIB_HAS_ARC4RANDOM
// See EmbeddedRuntime.swift: glibc only gained arc4random_buf in 2.36.
@_extern(c, "getrandom")
func getrandom(
_ buf: UnsafeMutableRawPointer, _ nbytes: Int, _ flags: CUnsignedInt
) -> Int
@_extern(c, "__errno_location")
func __errno_location() -> UnsafeMutablePointer<CInt>
#else
@_extern(c, "arc4random_buf")
func arc4random_buf(_ buf: UnsafeMutableRawPointer, _ nbytes: Int)
#endif
@_extern(c, "putchar")
func putchar(_: CInt) -> CInt
@_extern(c, "exit")
func exit(_: CInt) /* -> Never */
@_extern(c, "malloc_type_malloc")
func malloc_type_malloc(_ : Int, _ : UInt64) -> UnsafeMutableRawPointer
@_extern(c, "malloc_type_posix_memalign")
func malloc_type_posix_memalign(_: UnsafeMutablePointer<UnsafeMutableRawPointer?>, _: Int, _: Int, _:UInt64) -> CInt
@_extern(c, "malloc_type_free")
func malloc_type_free(_ : UnsafeMutableRawPointer, _ : UInt64)
@inline(never)
private func clearMemory(pointer: UnsafeMutableRawPointer, numBytes: Int) {
let bytePtr = unsafe pointer.assumingMemoryBound(to: UInt8.self)
for i in 0..<numBytes {
unsafe bytePtr[i] = 0
}
}
// Implementations of the Embedded Swift Platform layer on top of the POSIX
// dependencies.
@export(interface)
@implementation @c
public func _swift_allocate(_ alignment: Int, _ size: Int, _ flags: SwiftAllocateFlags) -> UnsafeMutableRawPointer? {
var pointer: UnsafeMutableRawPointer? = nil
guard posix_memalign(&pointer, alignment, size) == 0 else {
return nil
}
// Clear the memory if requested.
if flags.contains(.zeroMemory), let pointer {
clearMemory(pointer: pointer, numBytes: size)
}
return pointer
}
@export(interface)
@implementation @c
public func _swift_deallocate(_ pointer: UnsafeMutableRawPointer, _ alignment: Int, _ size: Int, _ flags: SwiftDeallocFlags) {
free(pointer)
}
private func generateRandom(_ buf: UnsafeMutableRawPointer, _ nbytes: Int) {
#if os(Linux) && !SWIFT_STDLIB_HAS_ARC4RANDOM
let EINTR: CInt = 4
var buf = buf
var remaining = nbytes
while remaining > 0 {
let count = getrandom(buf, remaining, 0)
if count <= 0 {
// A signal can interrupt the call while it waits for the entropy pool to
// be seeded. Every other failure means there is no entropy source, and
// handing back a buffer that was never filled would silently produce
// predictable values.
if count < 0, unsafe __errno_location().pointee == EINTR { continue }
_writeStaticString(
"Fatal error: unable to obtain entropy from getrandom\n")
_swift_exit(1)
}
buf += count
remaining -= count
}
#else
arc4random_buf(buf, nbytes)
#endif
}
@export(interface)
@implementation @c
public func _swift_generateRandom(_ buf: UnsafeMutableRawPointer, _ nbytes: Int) {
generateRandom(buf, nbytes)
}
@export(interface)
@implementation @c
public func _swift_generateRandomHashSeed(_ buf: UnsafeMutableRawPointer, _ nbytes: Int) {
generateRandom(buf, nbytes)
}
@export(interface)
@implementation @c
public func _swift_writeToStandardOutput(
_ pointer: UnsafePointer<UInt8>?,
_ count: Int
) -> Int {
for unsafe char in unsafe UnsafeBufferPointer(start: pointer, count: count) {
_ = putchar(CInt(char))
}
return count
}
@export(interface)
@implementation @c
public func _swift_exit(_ code: CInt) -> Never {
exit(code)
Builtin.unreachable()
}
/// The human-readable prefix that precedes an error message, chosen by the
/// error kind held in the low 8 bits of `flags` (a `swift_error_kind_t` /
/// `SwiftErrorKind` value).
private func _reportErrorPrefix(_ flags: UInt64) -> StaticString {
switch flags & 0xff {
case 1: return "Precondition failed" // SwiftErrorKind.precondition
case 2: return "Assertion failed" // SwiftErrorKind.assertion
default: return "Fatal error" // SwiftErrorKind.fatal
}
}
/// Writes a NUL-terminated static string to standard output.
private func _writeStaticString(_ string: StaticString) {
string.withUTF8Buffer { buffer in
_ = unsafe _swift_writeToStandardOutput(buffer.baseAddress, buffer.count)
}
}
/// Writes a non-negative integer to standard output in decimal.
private func _writeInt(_ value: Int) {
if value >= 10 { _writeInt(value / 10) }
_ = putchar(CInt(UInt8(ascii: "0") + UInt8(value % 10)))
}
/// Prints a fatal error report to standard output, optionally prefixed by a
/// source location, e.g. "file:line: Fatal error: message". Output goes through
/// `putchar` (via `_swift_writeToStandardOutput`) so it doesn't allocate and
/// honors any platform-provided unbuffered `putchar`.
private func _reportError(
prefix: StaticString,
fileName: UnsafePointer<UInt8>?, fileNameCount: Int, line: Int,
message: UnsafePointer<UInt8>?, messageCount: Int
) {
// Optional "<file>:<line>: " source-location prefix.
if let fileName, fileNameCount > 0 {
_ = unsafe _swift_writeToStandardOutput(fileName, fileNameCount)
_writeStaticString(":")
_writeInt(line)
_writeStaticString(": ")
}
// The error-kind prefix, followed by ": <message>" when a message is given.
_writeStaticString(prefix)
if let message, messageCount > 0 {
_writeStaticString(": ")
_ = unsafe _swift_writeToStandardOutput(message, messageCount)
}
_writeStaticString("\n")
}
@export(interface)
@implementation @c
public func _swift_reportError(
_ message: UnsafePointer<UInt8>?,
_ messageCount: Int,
_ flags: UInt64
) {
unsafe _reportError(
prefix: _reportErrorPrefix(flags),
fileName: nil, fileNameCount: 0, line: 0,
message: message, messageCount: messageCount)
}
@export(interface)
@implementation @c
public func _swift_reportErrorAt(
_ message: UnsafePointer<UInt8>?,
_ messageCount: Int,
_ fileName: UnsafePointer<UInt8>?,
_ fileNameCount: Int,
_ line: Int,
_ flags: UInt64
) {
unsafe _reportError(
prefix: _reportErrorPrefix(flags),
fileName: fileName, fileNameCount: fileNameCount, line: line,
message: message, messageCount: messageCount)
}
#if SWIFT_STDLIB_HAS_MALLOC_TYPE
// `#available` calls the non-inlinable `_stdlib_isOSVersionAtLeast`, which
// has no body in the embedded stdlib; call the inlinable `_AEIC` entry
// point directly instead.
@inline(__always)
private func _isMallocTypeOSVersionAtLeast() -> Bool {
#if os(macOS)
return Bool(_stdlib_isOSVersionAtLeast_AEIC(15._builtinWordValue, 0._builtinWordValue, 0._builtinWordValue))
#elseif os(iOS)
return Bool(_stdlib_isOSVersionAtLeast_AEIC(17._builtinWordValue, 0._builtinWordValue, 0._builtinWordValue))
#elseif os(tvOS)
return Bool(_stdlib_isOSVersionAtLeast_AEIC(17._builtinWordValue, 0._builtinWordValue, 0._builtinWordValue))
#elseif os(watchOS)
return Bool(_stdlib_isOSVersionAtLeast_AEIC(10._builtinWordValue, 0._builtinWordValue, 0._builtinWordValue))
#else
return false
#endif
}
#endif
@export(interface)
@implementation @c
public func _swift_typedAllocate(_ size: Int, _ alignment: Int, _ flags: SwiftAllocateFlags, _ typeId: UInt64) -> UnsafeMutableRawPointer? {
if (size == 0) {
return unsafe _swift_typedAllocate(1, alignment, flags, typeId)
}
#if SWIFT_STDLIB_HAS_MALLOC_TYPE
if _isMallocTypeOSVersionAtLeast() {
// malloc() is always 16-byte aligned on Apple platforms. This also covers
// the default alignment, 0.
let MALLOC_ALIGNMENT = 16
if alignment <= MALLOC_ALIGNMENT {
return unsafe malloc_type_malloc(size, typeId);
} else {
// Do not use malloc_type_aligned_alloc() here, because we want this
// to work if `size` is not an integer multiple of `alignment`, which
// was a requirement of the latter in C11 (but not C17 and later).
var pointer: UnsafeMutableRawPointer? = nil
guard unsafe malloc_type_posix_memalign(&pointer, alignment, size, typeId) == 0 else {
return nil
}
return pointer
}
}
#endif
return unsafe swift_slowAlloc(size, alignment &- 1)
}
@export(interface)
@implementation @c
public func _swift_typedDeallocate(_ pointer: UnsafeMutableRawPointer, _ size: Int, _ alignment: Int, _ flags: SwiftDeallocFlags, _ typeId: UInt64) {
#if SWIFT_STDLIB_HAS_MALLOC_TYPE
if _isMallocTypeOSVersionAtLeast() {
unsafe malloc_type_free(pointer, typeId);
return
}
#endif
swift_slowDealloc(pointer, size, alignment &- 1);
}