|
| 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 | +} |
0 commit comments