Skip to content

Commit 749b257

Browse files
authored
Merge pull request #180 from CodaFi/intr'act
Another try at intrinsics
2 parents 68de103 + e6e8dfb commit 749b257

File tree

8 files changed

+504
-4
lines changed

8 files changed

+504
-4
lines changed

Package.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,14 @@ let package = Package(
2020
.brew(["llvm"]),
2121
]),
2222
.target(
23-
name: "LLVM",
23+
name: "llvmshims",
2424
dependencies: ["cllvm"]),
25+
.target(
26+
name: "LLVM",
27+
dependencies: ["cllvm", "llvmshims"]),
2528
.testTarget(
2629
name: "LLVMTests",
2730
dependencies: ["LLVM", "FileCheck"]),
28-
]
31+
],
32+
cxxLanguageStandard: .cxx14
2933
)

Sources/LLVM/Intrinsic.swift

Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
#if SWIFT_PACKAGE
2+
import cllvm
3+
import llvmshims
4+
#endif
5+
import Foundation
6+
7+
/// An `Intrinsic` represents an intrinsic known to LLVM.
8+
///
9+
/// Intrinsic functions have well known names and semantics and are required to
10+
/// follow certain restrictions. Overall, these intrinsics represent an
11+
/// extension mechanism for the LLVM language that does not require changing all
12+
/// of the transformations in LLVM when adding to the language (or the bitcode
13+
/// reader/writer, the parser, etc…).
14+
///
15+
/// Intrinsic function names must all start with an `llvm.` prefix. This prefix
16+
/// is reserved in LLVM for intrinsic names; thus, function names may not begin
17+
/// with this prefix. Intrinsic functions must always be external functions: you
18+
/// cannot define the body of intrinsic functions. Intrinsic functions may only
19+
/// be used in call or invoke instructions: it is illegal to take the address of
20+
/// an intrinsic function.
21+
///
22+
/// Some intrinsic functions can be overloaded, i.e., the intrinsic represents a
23+
/// family of functions that perform the same operation but on different data
24+
/// types. Because LLVM can represent over 8 million different integer types,
25+
/// overloading is used commonly to allow an intrinsic function to operate on
26+
/// any integer type. One or more of the argument types or the result type can
27+
/// be overloaded to accept any integer type. Argument types may also be defined
28+
/// as exactly matching a previous argument’s type or the result type. This
29+
/// allows an intrinsic function which accepts multiple arguments, but needs all
30+
/// of them to be of the same type, to only be overloaded with respect to a
31+
/// single argument or the result.
32+
///
33+
/// Overloaded intrinsics will have the names of its overloaded argument types
34+
/// encoded into its function name, each preceded by a period. Only those types
35+
/// which are overloaded result in a name suffix. Arguments whose type is
36+
/// matched against another type do not. For example, the llvm.ctpop function
37+
/// can take an integer of any width and returns an integer of exactly the same
38+
/// integer width. This leads to a family of functions such as
39+
/// `i8 @llvm.ctpop.i8(i8 %val)` and `i29 @llvm.ctpop.i29(i29 %val)`. Only one
40+
/// type, the return type, is overloaded, and only one type suffix is required.
41+
/// Because the argument’s type is matched against the return type, it does not
42+
/// require its own name suffix.
43+
///
44+
/// Dynamic Member Lookup For Intrinsics
45+
/// ====================================
46+
///
47+
/// This library provides a dynamic member lookup facility for retrieving
48+
/// intrinsic selectors. For any LLVM intrinsic selector of the form
49+
/// `llvm.foo.bar.baz`, the name of the corresponding dynamic member is that
50+
/// name with any dots replaced by underscores.
51+
///
52+
/// For example:
53+
///
54+
/// llvm.foo.bar.baz -> Intrinsic.ID.llvm_foo_bar_baz
55+
/// llvm.stacksave -> Intrinsic.ID.llvm_stacksave
56+
/// llvm.x86.xsave64 -> Intrinsic.ID.llvm_x86_xsave64
57+
///
58+
/// Any existing underscores do not need to be replaced, e.g.
59+
/// `llvm.va_copy` becomes `Intrinsic.ID.llvm_va_copy`.
60+
///
61+
/// For overloaded intrinsics, the non-overloaded prefix excluding the explicit
62+
/// type parameters is used and normalized according to the convention above.
63+
///
64+
/// For example:
65+
///
66+
/// llvm.sinf64 -> Intrinsic.ID.llvm_sin
67+
/// llvm.memcpy.p0i8.p0i8.i32 -> Intrinsic.ID.llvm_memcpy
68+
/// llvm.bswap.v4i32 -> Intrinsic.ID.llvm_bswap
69+
public class Intrinsic: Function {
70+
public struct Selector {
71+
let index: UInt32
72+
fileprivate init(_ index: UInt32) { self.index = index }
73+
}
74+
75+
/// This type provides a dynamic member lookup facility for LLVM intrinsics.
76+
///
77+
/// It is not possible to create a `DynamicIntrinsicResolver` in user code.
78+
/// One is provided by `Intrinsic.ID`.
79+
@dynamicMemberLookup public struct DynamicIntrinsicResolver {
80+
private let exceptionalSet: Set<String>
81+
82+
fileprivate init() {
83+
// LLVM naming conventions for intrinsics is really not consistent at all
84+
// (see llvm.ssa_copy). We need to gather a table of all the intrinsics
85+
// that have underscores in their names so we don't naively replace them
86+
// with dots later.
87+
//
88+
// Luckily, no intrinsics that use underscores in their name also use
89+
// dots. If this changes in the future, we need to be smarter here.
90+
var table = Set<String>()
91+
table.reserveCapacity(16)
92+
for idx in 1..<LLVMSwiftCountIntrinsics() {
93+
guard let value = LLVMSwiftGetIntrinsicAtIndex(idx) else { fatalError() }
94+
guard strchr(value, Int32(UInt8(ascii: "_"))) != nil else {
95+
continue
96+
}
97+
table.insert(String(cString: value))
98+
}
99+
self.exceptionalSet = table
100+
}
101+
102+
/// Performs a dynamic lookup for the normalized form of an intrinsic
103+
/// selector.
104+
///
105+
/// For any LLVM intrinsic selector of the form `llvm.foo.bar.baz`, the name
106+
/// of the corresponding dynamic member is that name with any dots replaced
107+
/// by underscores.
108+
///
109+
/// For example:
110+
///
111+
/// llvm.foo.bar.baz -> Intrinsic.ID.llvm_foo_bar_baz
112+
/// llvm.stacksave -> Intrinsic.ID.llvm_stacksave
113+
/// llvm.x86.xsave64 -> Intrinsic.ID.llvm_x86_xsave64
114+
///
115+
/// Any existing underscores do not need to be replaced, e.g.
116+
/// `llvm.va_copy` becomes `Intrinsic.ID.llvm_va_copy`.
117+
///
118+
/// For overloaded intrinsics, the non-overloaded prefix excluding the
119+
/// explicit type parameters is used and normalized according to the
120+
/// convention above.
121+
///
122+
/// For example:
123+
///
124+
/// llvm.sinf64 -> Intrinsic.ID.llvm_sin
125+
/// llvm.memcpy.p0i8.p0i8.i32 -> Intrinsic.ID.llvm_memcpy
126+
/// llvm.bswap.v4i32 -> Intrinsic.ID.llvm_bswap
127+
///
128+
/// - Parameter selector: The LLVMSwift form of an intrinsic selector.
129+
public subscript(dynamicMember selector: String) -> Selector {
130+
precondition(selector.starts(with: "llvm_"))
131+
132+
let prefix = "llvm."
133+
let suffix = selector.dropFirst("llvm_".count)
134+
let finalSelector: String
135+
if self.exceptionalSet.contains(prefix + suffix) {
136+
// If the exceptions set contains an entry for this selector, then it
137+
// has an underscore and no dots so we can just concatenate prefix and
138+
// suffix.
139+
finalSelector = prefix + suffix
140+
} else {
141+
// Else, naively replace all the underscores with dots.
142+
finalSelector = selector.replacingOccurrences(of: "_", with: ".")
143+
}
144+
145+
let index = LLVMLookupIntrinsicID(finalSelector, finalSelector.count)
146+
assert(index > 0, "Member does not correspond to an LLVM intrinsic")
147+
return Selector(index)
148+
}
149+
}
150+
151+
/// The default dynamic intrinsic resolver.
152+
public static let ID = DynamicIntrinsicResolver()
153+
154+
/// Retrieves the name of this intrinsic if it is not overloaded.
155+
public var name: String {
156+
precondition(!self.isOverloaded,
157+
"Cannot retrieve the full name of an overloaded intrinsic")
158+
var count = 0
159+
guard let ptr = LLVMIntrinsicGetName(self.identifier, &count) else {
160+
return ""
161+
}
162+
return String(cString: ptr)
163+
}
164+
165+
/// Retrieves the name of this intrinsic if it is overloaded, resolving it
166+
/// with the given parameter types.
167+
public func overloadedName(for parameterTypes: [IRType]) -> String {
168+
precondition(self.isOverloaded,
169+
"Cannot retrieve the overloaded name of a non-overloaded intrinsic")
170+
var argIRTypes = parameterTypes.map { $0.asLLVM() as Optional }
171+
return argIRTypes.withUnsafeMutableBufferPointer { buf -> String in
172+
var count = 0
173+
guard let ptr = LLVMIntrinsicCopyOverloadedName(self.identifier, buf.baseAddress, parameterTypes.count, &count) else {
174+
return ""
175+
}
176+
defer { free(UnsafeMutableRawPointer(mutating: ptr)) }
177+
return String(cString: ptr)
178+
}
179+
}
180+
181+
/// Retrieves the type of this intrinsic if it is not overloaded.
182+
public var type: IRType {
183+
precondition(!self.isOverloaded,
184+
"Cannot retrieve type of overloaded intrinsic")
185+
return convertType(LLVMTypeOf(asLLVM()))
186+
}
187+
188+
/// Resolves the type of an overloaded intrinsic using the given parameter
189+
/// types.
190+
///
191+
/// - Parameters:
192+
/// - context: The context in which to create the type.
193+
/// - parameterTypes: The type of the parameters to the intrinsic.
194+
/// - Returns: The type of the intrinsic function with its overloaded
195+
/// parameter types resolved.
196+
public func overloadedType(in context: Context = .global, with parameterTypes: [IRType]) -> IRType {
197+
var argIRTypes = parameterTypes.map { $0.asLLVM() as Optional }
198+
return argIRTypes.withUnsafeMutableBufferPointer { buf -> IRType in
199+
guard let ty = LLVMIntrinsicGetType(context.llvm, self.identifier, buf.baseAddress, parameterTypes.count) else {
200+
fatalError("Could not retrieve type of intrinsic")
201+
}
202+
return convertType(ty)
203+
}
204+
}
205+
206+
/// Retrieves if this intrinsic is overloaded.
207+
public var isOverloaded: Bool {
208+
return LLVMIntrinsicIsOverloaded(self.identifier) != 0
209+
}
210+
211+
/// Retrieves the ID number of this intrinsic function.
212+
public var identifier: UInt32 {
213+
return LLVMGetIntrinsicID(self.llvm)
214+
}
215+
}

Sources/LLVM/Module.swift

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -371,13 +371,79 @@ extension Module {
371371
///
372372
/// - parameter name: The name of the function to create.
373373
///
374-
/// - returns: A representation of the newly created function with the given
374+
/// - returns: A representation of a function with the given
375375
/// name or nil if such a representation could not be created.
376376
public func function(named name: String) -> Function? {
377377
guard let fn = LLVMGetNamedFunction(llvm, name) else { return nil }
378378
return Function(llvm: fn)
379379
}
380380

381+
/// Searches for and retrieves an intrinsic with the given name in this module
382+
/// if that name references an intrinsic.
383+
///
384+
/// - Parameters:
385+
/// - name: The name of the intrinsic to search for.
386+
/// - parameters: The type of the parameters of the intrinsic to resolve any
387+
/// ambiguity caused by overloads.
388+
/// - returns: A representation of an intrinsic with the given name or nil
389+
/// if such an intrinsic could not be found.
390+
public func intrinsic(named name: String, parameters: [IRType] = []) -> Intrinsic? {
391+
guard let intrinsic = self.function(named: name) else {
392+
return nil
393+
}
394+
395+
let index = LLVMGetIntrinsicID(intrinsic.asLLVM())
396+
guard index != 0 else { return nil }
397+
398+
guard LLVMIntrinsicIsOverloaded(index) != 0 else {
399+
return Intrinsic(llvm: intrinsic.asLLVM())
400+
}
401+
402+
var argIRTypes = parameters.map { $0.asLLVM() as Optional }
403+
return argIRTypes.withUnsafeMutableBufferPointer { buf -> Intrinsic in
404+
guard let value = LLVMGetIntrinsicDeclaration(self.llvm, index, buf.baseAddress, parameters.count) else {
405+
fatalError("Could not retrieve type of intrinsic")
406+
}
407+
return Intrinsic(llvm: value)
408+
}
409+
}
410+
411+
/// Searches for and retrieves an intrinsic with the given selector in this
412+
/// module if that selector references an intrinsic.
413+
///
414+
/// Unlike `Module.intrinsic(named:parameters:)`, the intrinsic function need
415+
/// not be declared. If the module contains a declaration of the intrinsic
416+
/// function, it will be returned. Else, a declaration for the intrinsic
417+
/// will be created and appended to this module.
418+
///
419+
/// LLVMSwift intrinsic selector syntax differs from the LLVM naming
420+
/// conventions for intrinsics in one crucial way: all dots are replaced by
421+
/// underscores.
422+
///
423+
/// For example:
424+
///
425+
/// llvm.foo.bar.baz -> Intrinsic.ID.llvm_foo_bar_baz
426+
/// llvm.sin -> Intrinsic.ID.llvm_sin
427+
/// llvm.stacksave -> Intrinsic.ID.llvm_stacksave
428+
/// llvm.x86.xsave64 -> Intrinsic.ID.llvm_x86_xsave64
429+
///
430+
/// - Parameters:
431+
/// - selector: The selector of the intrinsic to search for.
432+
/// - parameters: The type of the parameters of the intrinsic to resolve any
433+
/// ambiguity caused by overloads.
434+
/// - returns: A representation of an intrinsic with the given name or nil
435+
/// if such an intrinsic could not be found.
436+
public func intrinsic(_ selector: Intrinsic.Selector, parameters: [IRType] = []) -> Intrinsic? {
437+
var argIRTypes = parameters.map { $0.asLLVM() as Optional }
438+
return argIRTypes.withUnsafeMutableBufferPointer { buf -> Intrinsic in
439+
guard let value = LLVMGetIntrinsicDeclaration(self.llvm, selector.index, buf.baseAddress, parameters.count) else {
440+
fatalError("Could not retrieve type of intrinsic")
441+
}
442+
return Intrinsic(llvm: value)
443+
}
444+
}
445+
446+
381447
/// Searches for and retrieves an alias with the given name in this module
382448
/// if that name references an existing alias.
383449
///
@@ -414,6 +480,16 @@ extension Module {
414480
return NamedMetadata(module: self, llvm: LLVMGetOrInsertNamedMetadata(self.llvm, name, name.count))
415481
}
416482

483+
/// Build a named function body with the given type.
484+
///
485+
/// - parameter name: The name of the newly defined function.
486+
/// - parameter type: The type of the newly defined function.
487+
///
488+
/// - returns: A value representing the newly inserted function definition.
489+
public func addFunction(_ name: String, type: FunctionType) -> Function {
490+
return Function(llvm: LLVMAddFunction(self.llvm, name, type.asLLVM()))
491+
}
492+
417493
/// Build a named global of the given type.
418494
///
419495
/// - parameter name: The name of the newly inserted global value.

Sources/cllvm/shim.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,3 @@
2929
#include <llvm-c/Transforms/Utils.h>
3030
#include <llvm-c/Transforms/Vectorize.h>
3131
#include <llvm-c/Types.h>
32-

Sources/llvmshims/include/shim.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#include <stddef.h>
2+
3+
#ifndef LLVMSWIFT_LLVM_SHIM_H
4+
#define LLVMSWIFT_LLVM_SHIM_H
5+
6+
size_t LLVMSwiftCountIntrinsics(void);
7+
const char *LLVMSwiftGetIntrinsicAtIndex(size_t index);
8+
unsigned LLVMLookupIntrinsicID(const char *Name, size_t NameLen);
9+
10+
11+
#endif /* LLVMSWIFT_LLVM_SHIM_H */

Sources/llvmshims/src/shim.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include "llvm/IR/Intrinsics.h"
2+
#include "llvm/IR/Function.h"
3+
4+
extern "C" {
5+
size_t LLVMSwiftCountIntrinsics(void);
6+
const char *LLVMSwiftGetIntrinsicAtIndex(size_t index);
7+
unsigned LLVMLookupIntrinsicID(const char *Name, size_t NameLen);
8+
}
9+
10+
size_t LLVMSwiftCountIntrinsics(void) {
11+
return llvm::Intrinsic::num_intrinsics;
12+
}
13+
14+
const char *LLVMSwiftGetIntrinsicAtIndex(size_t index) {
15+
return llvm::Intrinsic::getName(static_cast<llvm::Intrinsic::ID>(index)).data();
16+
}
17+
18+
unsigned LLVMLookupIntrinsicID(const char *Name, size_t NameLen) {
19+
return llvm::Function::lookupIntrinsicID({Name, NameLen});
20+
}

0 commit comments

Comments
 (0)