Using Builtin from packages

Prompted by the review result of SE-0282:

I imagine it would be helpful in exploring use-cases like atomics (maybe async constructs as well?) if it were possible to prototype APIs that require LLVM intrinsics in SwiftPM packages.

Would it be feasible (and sensible) to allow SwiftPM packages access to the Builtin module (gated by some flag in the package manifest I imagine) to allow more community-driven experimentation in these areas?

I've played around a little with -parse-stdlib, and it seems to work like I expected, at least at the top-level of a single swift file.

Running the following code with swift -parse-stdlib test.swift shows that the atomic increment I implemented here works as it should:

import Swift
import Dispatch


let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)


p.pointee = 42
let l = Builtin.atomicload_monotonic_Int64(p._rawValue)
print(Int(l)) // => 42


@_transparent @_alwaysEmitIntoClient func addOne(_ x: UnsafeMutablePointer<Int>) {
	_ = Builtin.atomicrmw_add_monotonic_Int64(x._rawValue, 1._value)
}

@_transparent @_alwaysEmitIntoClient func nonAtomicAddOne(_ x: UnsafeMutablePointer<Int>) {
	x.pointee += 1
}


addOne(p)
print(p.pointee) // => 43


print("testing non-atomic")
p.pointee = 0
DispatchQueue.concurrentPerform(iterations: 10) { _ in
  for _ in 0 ..< 1_000_000 {
    nonAtomicAddOne(p)
  }
}
print(p.pointee) // => > 0, < 10_000_000, as expected


print("testing atomic")
p.pointee = 0
DispatchQueue.concurrentPerform(iterations: 10) { _ in
  for _ in 0 ..< 1_000_000 {
    addOne(p)
  }
}
print(p.pointee) // => 10_000_000

For atomics, you could develop a package that exports atomic operations via static inline C functions imported into Swift without using builtins.

2 Likes

That sounds reasonable for atomics.

Maybe I'm overestimating the number of things that one could do with access to the intrinsics. Do you think there are use-cases that would benefit from access to intrinsics which would not be well served by wrapping C?

E.g. might it be possible to use the LLVM coroutine stuff to prototype some form of coroutine support in a package, or anything else where this would be useful?

Tbh, the review rationale just reminded me of a quote (by Chris iirc, can't quite remember) that Swift is just syntax sugar over LLVM, and I thought it might be good to allow non-stdlib developers to do the same thing, but I might well be wrong there :D

Chris has an interesting understanding of what "syntactic sugar" is.

3 Likes

LLVM isn't really designed to be a stable API; it can and regularly does change its semantics in breaking ways, so some degree of insulation is necessary for libraries like the Swift standard library that want to provide a long-term stable API. I don't think the coroutine primitives would be of much use without support from the rest of the compiler.

3 Likes

There is a stable interface to llvm builtins--the __builtin_xxx operations defined in clang via C header shims (Swift Numerics uses this, for example). See Clang Language Extensions — Clang 16.0.0git documentation. It's not possible to get at everything this way, but it is possible to get at most things (in particular, they cover most "interesting semantics that are relevant to C programmers, but not easily accessible from standard C"), and for the reasons that Joe sketched out, we really shouldn't try to expose the Swift Builtins module more broadly unless we have to.

2 Likes

The tblgen language is just syntactic sugar over writing huge tables by hand...

3 Likes