Skip to content

Commit 3204f19

Browse files
committed
XXX
1 parent a8eae3c commit 3204f19

File tree

3 files changed

+186
-36
lines changed

3 files changed

+186
-36
lines changed

Sources/LLVM/Intrinsics.swift

Lines changed: 101 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,49 +2,103 @@
22
import cllvm
33
#endif
44

5+
/// The `LLVMIntrinsic` protocol represents types that act as selectors for
6+
/// intrinsic functions.
7+
///
8+
/// LLVM supports the notion of an “intrinsic function”. These functions have
9+
/// well known names and semantics and are required to follow certain
10+
/// restrictions. Overall, these intrinsics represent an extension mechanism for
11+
/// the LLVM language that does not require changing all of the transformations
12+
/// in LLVM when adding to the language.
13+
///
14+
/// Intrinsic function names all start with an "llvm." prefix. This prefix is
15+
/// reserved in LLVM for intrinsic names; thus, function names may not begin
16+
/// with this prefix. Intrinsic functions must always be external functions:
17+
/// you cannot define the body of intrinsic functions. Intrinsic functions may
18+
/// only be used in call or invoke instructions: it is illegal to take the
19+
/// address of an intrinsic function.
520
public protocol LLVMIntrinsic {
621
var signature: FunctionType { get }
722
var llvmSelector: String { get }
823
}
924

25+
/// The `LLVMOverloadedIntrinsic` protocol represents types that act as
26+
/// selectors for a family of overloaded intrinsic functions.
27+
///
28+
/// Some intrinsic functions can be overloaded, i.e., the intrinsic represents a
29+
/// family of functions that perform the same operation but on different data
30+
/// types. Because LLVM can represent over 8 million different integer types,
31+
/// overloading is used commonly to allow an intrinsic function to operate on
32+
/// any integer type. One or more of the argument types or the result type can
33+
/// be overloaded to accept any integer type. Argument types may also be defined
34+
/// as exactly matching a previous argument’s type or the result type. This
35+
/// allows an intrinsic function which accepts multiple arguments, but needs all
36+
/// of them to be of the same type, to only be overloaded with respect to a
37+
/// single argument or the result.
38+
///
39+
/// Overloaded intrinsics will have the names of its overloaded argument types
40+
/// encoded into its function name, each preceded by a period. Only those types
41+
/// which are overloaded result in a name suffix. Arguments whose type is
42+
/// matched against another type do not. For example, the llvm.ctpop function
43+
/// can take an integer of any width and returns an integer of exactly the same
44+
/// integer width. This leads to a family of functions such as
45+
/// `i8 @llvm.ctpop.i8(i8 %val) and i29 @llvm.ctpop.i29(i29 %val)`. Only one
46+
/// type, the return type, is overloaded, and only one type suffix is required.
47+
/// Because the argument’s type is matched against the return type, it does not
48+
/// require its own name suffix.
1049
public protocol LLVMOverloadedIntrinsic: LLVMIntrinsic {
1150
static var overloadSet: [LLVMIntrinsic] { get }
1251
}
1352

14-
internal struct IntrinsicSubstitutionMarker: IRType {
15-
func asLLVM() -> LLVMTypeRef {
16-
fatalError("Unhandled substitution marker in type")
17-
}
18-
}
53+
extension IRBuilder {
54+
/// Builds a call to an intrinsic function.
55+
///
56+
/// - note: In debug builds this function type checks its arguments to ensure
57+
/// calls are well-formed. In release builds no such checking will
58+
/// occur and all calls to intrinsics with mismatched arguments will
59+
/// result in undefined behavior.
60+
///
61+
/// - Parameters:
62+
/// - intr: The selector for the intrinsic to be invoked.
63+
/// - returnType: A suggested return type for overloaded intrinsics. By
64+
/// default, the framework will infer a type and attempt to match the
65+
/// right intrinsic function.
66+
/// - args: The arguments to the intrinsic function.
67+
/// - Returns: A value representing the result of calling the intrinsic
68+
/// function.
69+
public func buildIntrinsicCall<I: LLVMIntrinsic>(to intr: I, returnType: IRType? = nil, args: IRValue...) -> IRValue {
70+
assert(typeCheckArguments(to: intr, args: args))
1971

20-
internal struct ResolvedIntrinsic: LLVMIntrinsic {
21-
let signature: FunctionType
22-
let llvmSelector: String
23-
}
72+
return self.buildCall(self.buildIntrinsic(self.resolveIntrinsic(intr, args, returnType)), args: args)
73+
}
2474

25-
extension IRBuilder {
75+
2676
/// Builds a call to one of a family of overloaded intrinsic functions.
2777
///
28-
/// The type of the arguments ultimately determines the selector for the
29-
/// intrinsic that is called. Failure of the arguments to correspond to any
30-
/// of the overloads is a fatal condition.
31-
public func buildIntrinsicCall<I: LLVMOverloadedIntrinsic>(family i: I.Type, returnType: IRType? = nil, args: IRValue...) -> IRValue {
32-
guard let intr = resolveOverloadedArguments(to: i, args: args) else {
78+
/// - note: The type of the arguments ultimately determines the selector for
79+
/// the intrinsic that is called. Failure of the arguments to
80+
/// correspond to any of the overloads is a fatal condition.
81+
///
82+
/// - Parameters:
83+
/// - intr: The selector for the overloaded intrinsic to be resolved and
84+
/// invoked.
85+
/// - returnType: A suggested return type for overloaded intrinsics. By
86+
/// default, the framework will infer a type and attempt to match the
87+
/// right intrinsic function.
88+
/// - args: The arguments to the intrinsic function.
89+
/// - Returns: A value representing the result of calling the intrinsic
90+
/// function.
91+
public func buildIntrinsicCall<I: LLVMOverloadedIntrinsic>(to intr: I.Type, returnType: IRType? = nil, args: IRValue...) -> IRValue {
92+
guard let intr = resolveOverloadedArguments(to: intr, args: args) else {
3393
fatalError("Unable to resolve overload among \(I.overloadSet.map{$0.llvmSelector})")
3494
}
3595
return self.buildCall(self.buildIntrinsic(self.resolveIntrinsic(intr, args, returnType)), args: args)
3696
}
3797

38-
public func buildIntrinsicCall<I: LLVMIntrinsic>(to intr: I, returnType: IRType? = nil, args: IRValue...) -> IRValue {
39-
assert(typeCheckArguments(to: intr, args: args))
40-
41-
return self.buildCall(self.buildIntrinsic(self.resolveIntrinsic(intr, args, returnType)), args: args)
42-
}
43-
4498
private func resolveIntrinsic(_ i: LLVMIntrinsic, _ args: [IRValue], _ returnTy: IRType?) -> LLVMIntrinsic {
4599
let fnArgTypes = i.signature.argTypes
46100

47-
func markerForType(_ type: IRType) -> String {
101+
func typeNameForType(_ type: IRType) -> String {
48102
if let iTy = type as? IntType {
49103
return "i\(iTy.width)"
50104
} else if let fTy = type as? FloatType {
@@ -63,9 +117,9 @@ extension IRBuilder {
63117
return "ppcf128"
64118
}
65119
} else if let pTy = type as? PointerType {
66-
return "p0" + markerForType(pTy.pointee)
120+
return "p0" + typeNameForType(pTy.pointee)
67121
} else if let vTy = type as? VectorType {
68-
return "v\(vTy.count)" + markerForType(vTy.elementType)
122+
return "v\(vTy.count)" + typeNameForType(vTy.elementType)
69123
}
70124
fatalError()
71125
}
@@ -78,7 +132,7 @@ extension IRBuilder {
78132
continue
79133
}
80134
argTypes.append(t.1.type)
81-
sigName += "." + markerForType(t.1.type)
135+
sigName += "." + typeNameForType(t.1.type)
82136
}
83137

84138
let retTy: IRType
@@ -114,11 +168,17 @@ extension IRBuilder {
114168
if t.0 is IntrinsicSubstitutionMarker {
115169
continue
116170
} else if let vecTy = t.0 as? VectorType, vecTy.elementType is IntrinsicSubstitutionMarker {
117-
// Vector<<SUB>> => Vector<T>
171+
// Vector<n, <SUB>> => Vector<n, T>
118172
guard t.1 is VectorType else {
119173
return false
120174
}
121175
continue
176+
} else if let vecTy = t.0 as? VectorType, vecTy.count == -1 {
177+
// Vector<-1, T> => Vector<n, T>
178+
guard let vecTy2 = t.1 as? VectorType, vecTy.elementType.asLLVM() == vecTy2.elementType.asLLVM() else {
179+
return false
180+
}
181+
continue
122182
}
123183

124184
// Fall back to pointer equality
@@ -138,3 +198,18 @@ extension IRBuilder {
138198
}).first
139199
}
140200
}
201+
202+
// A marker type generated by the 'intrinsics-gen' tool to indicate that a
203+
// substitution should be performed on an argument. They cannot be reified into
204+
// an LLVM Type and will trap if an attempt is made to do so.
205+
internal struct IntrinsicSubstitutionMarker: IRType {
206+
func asLLVM() -> LLVMTypeRef {
207+
fatalError("Unhandled substitution marker in type")
208+
}
209+
}
210+
211+
/// A marker intrinsic for the candidate selected during overload resolution.
212+
internal struct ResolvedIntrinsic: LLVMIntrinsic {
213+
let signature: FunctionType
214+
let llvmSelector: String
215+
}

Tests/LLVMTests/IRIntrinsicSpec.swift

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,62 @@ class IRIntrinsicSpec : XCTestCase {
8888
// VIRTUALOVERLOAD-IRINTRINSIC-NEXT: }
8989
module.dump()
9090
})
91+
92+
XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["INTRINSIC-FAMILY-RESOLVE"]) {
93+
// INTRINSIC-FAMILY-RESOLVE: ; ModuleID = '[[ModuleName:IRIntrinsicTest]]'
94+
// INTRINSIC-FAMILY-RESOLVE-NEXT: source_filename = "[[ModuleName]]"
95+
let module = Module(name: "IRIntrinsicTest")
96+
let builder = IRBuilder(module: module)
97+
// INTRINSIC-FAMILY-RESOLVE: define i32 @main() {
98+
let main = builder.addFunction("main",
99+
type: FunctionType(argTypes: [],
100+
returnType: IntType.int32))
101+
// INTRINSIC-FAMILY-RESOLVE-NEXT: entry:
102+
let entry = main.appendBasicBlock(named: "entry")
103+
builder.positionAtEnd(of: entry)
104+
105+
let doubleAlloca = builder.buildAlloca(type: FloatType.double)
106+
builder.buildStore(FloatType.double.constant(1.0), to: doubleAlloca)
107+
let double = builder.buildLoad(doubleAlloca)
108+
109+
let floatAlloca = builder.buildAlloca(type: FloatType.float)
110+
builder.buildStore(FloatType.float.constant(1.0), to: floatAlloca)
111+
let float = builder.buildLoad(floatAlloca)
112+
113+
let halfAlloca = builder.buildAlloca(type: FloatType.half)
114+
builder.buildStore(FloatType.half.constant(1.0), to: halfAlloca)
115+
let half = builder.buildLoad(halfAlloca)
116+
117+
// INTRINSIC-FAMILY-RESOLVE: call double @llvm.sin.f64(double
118+
_ = builder.buildIntrinsicCall(to: GlobalIntrinsics.int_sin.self, args: double)
119+
// INTRINSIC-FAMILY-RESOLVE-NEXT: call double @llvm.cos.f64(double
120+
_ = builder.buildIntrinsicCall(to: GlobalIntrinsics.int_cos.self, args: double)
121+
// INTRINSIC-FAMILY-RESOLVE-NEXT: call double @llvm.sqrt.f64(double
122+
_ = builder.buildIntrinsicCall(to: GlobalIntrinsics.int_sqrt.self, args: double)
123+
// INTRINSIC-FAMILY-RESOLVE-NEXT: call double @llvm.log.f64(double
124+
_ = builder.buildIntrinsicCall(to: GlobalIntrinsics.int_log.self, args: double)
125+
126+
// INTRINSIC-FAMILY-RESOLVE-NEXT: call float @llvm.sin.f32(float
127+
_ = builder.buildIntrinsicCall(to: GlobalIntrinsics.int_sin.self, args: float)
128+
// INTRINSIC-FAMILY-RESOLVE-NEXT: call float @llvm.cos.f32(float
129+
_ = builder.buildIntrinsicCall(to: GlobalIntrinsics.int_cos.self, args: float)
130+
// INTRINSIC-FAMILY-RESOLVE-NEXT: call float @llvm.sqrt.f32(float
131+
_ = builder.buildIntrinsicCall(to: GlobalIntrinsics.int_sqrt.self, args: float)
132+
// INTRINSIC-FAMILY-RESOLVE-NEXT: call float @llvm.log.f32(float
133+
_ = builder.buildIntrinsicCall(to: GlobalIntrinsics.int_log.self, args: float)
134+
135+
// INTRINSIC-FAMILY-RESOLVE-NEXT: call half @llvm.sin.f16(half
136+
_ = builder.buildIntrinsicCall(to: GlobalIntrinsics.int_sin.self, args: half)
137+
// INTRINSIC-FAMILY-RESOLVE-NEXT: call half @llvm.cos.f16(half
138+
_ = builder.buildIntrinsicCall(to: GlobalIntrinsics.int_cos.self, args: half)
139+
// INTRINSIC-FAMILY-RESOLVE-NEXT: call half @llvm.sqrt.f16(half
140+
_ = builder.buildIntrinsicCall(to: GlobalIntrinsics.int_sqrt.self, args: half)
141+
// INTRINSIC-FAMILY-RESOLVE-NEXT: call half @llvm.log.f16(half
142+
_ = builder.buildIntrinsicCall(to: GlobalIntrinsics.int_log.self, args: half)
143+
144+
builder.buildRet(IntType.int32.zero())
145+
module.dump()
146+
})
91147
}
92148

93149
#if !os(macOS)

utils/intrinsics-gen.swift

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ struct Parser<S: IteratorProtocol> where S.Element == Token {
639639

640640
indirect enum LLVMType : Equatable {
641641
enum MatchStyle {
642-
case direct, extend, truncate
642+
case direct, extend, truncate, sameWidth, pointersToElem, pointerToElt
643643
}
644644

645645
case void
@@ -672,7 +672,7 @@ indirect enum LLVMType : Equatable {
672672
return "f\(w)"
673673
case let .pointer(.some(w)):
674674
return "p0\(w.llvmName)"
675-
case let .vector(.some(w, ty)):
675+
case let .vector(.some(w, ty)) where w != -1:
676676
return "v\(w)\(ty.llvmName)"
677677
case .matchedType(_, _),
678678
.vector(_),
@@ -704,7 +704,7 @@ indirect enum LLVMType : Equatable {
704704
case (.descriptor, .descriptor): return true
705705
case (.x86MMX, .x86MMX): return true
706706
case let (.mips(l), .mips(r)): return l == r
707-
case let (.matchType(l, _), .matchType(r, _)): return l == r
707+
case let (.matchType(l, s1), .matchType(r, s2)): return l == r && s1 == s2
708708
case let (.matchedType(l, _), .matchedType(r, _)): return l == r
709709
default: return false
710710
}
@@ -789,6 +789,14 @@ extension LLVMType {
789789
return
790790
}
791791

792+
if t.name == "LLVMVectorSameWidth", case let TDValue.type(t) = t.args[1] {
793+
guard let sameVecTy = LLVMType(t).map({LLVMType.vector(.some((-1, $0)))}) else {
794+
return nil
795+
}
796+
self = sameVecTy
797+
return
798+
}
799+
792800
let styleo = { () -> MatchStyle? in
793801
switch t.name {
794802
case "LLVMMatchType":
@@ -797,8 +805,10 @@ extension LLVMType {
797805
return MatchStyle.extend
798806
case "LLVMTruncatedType":
799807
return MatchStyle.truncate
800-
case "LLVMAnyPointerType":
801-
return nil
808+
case "LLVMVectorOfPointersToElt", "LLVMVectorOfAnyPointersToElt":
809+
return MatchStyle.pointersToElem
810+
case "LLVMPointerToElt":
811+
return MatchStyle.pointerToElt
802812
default:
803813
return nil
804814
}
@@ -900,6 +910,7 @@ struct Intrinsic {
900910
switch v {
901911
case TDValue.type(let t):
902912
guard let lt = LLVMType(t) else {
913+
_ = LLVMType(t)
903914
throw Stop.iter
904915
}
905916
return lt
@@ -967,12 +978,16 @@ extension Intrinsic {
967978
return [.descriptor]
968979
case .any:
969980
return [.any]
981+
case let .matchType(n, .pointerToElt):
982+
return [.matchedType(n, LLVMType.anyPtr(tys[n]))]
983+
case let .matchType(n, .pointersToElem):
984+
return [.matchedType(n, LLVMType.vector(.some((-1, LLVMType.anyPtr(tys[n])))))]
970985
case let .matchType(n, _):
971986
return [.matchedType(n, tys[n])]
972987
// N.B. Do not expand the overload set here. There are simply too many
973988
// permutations to cover. Dynamically generate these instead.
974-
case .vector(.none):
975-
return [.vector(.none)]
989+
case .vector(_):
990+
return [ty]
976991
default:
977992
fatalError()
978993
}
@@ -1103,8 +1118,12 @@ func formFunctionTypeString(_ params: [LLVMType], _ retTy: LLVMType) -> String {
11031118
return translateAType(ty)
11041119
case .pointer(.some(let ty)):
11051120
return "PointerType(pointee: \(translateAType(ty)))"
1121+
case .vector(.none):
1122+
return "VectorType(elementType: IntrinsicSubstitutionMarker(), count: -1)"
1123+
case .vector(.some(let size, let ty)) where size == -1:
1124+
return "VectorType(elementType: \(translateAType(ty)), count: -1)"
11061125
case .vector(.some(let size, let ty)):
1107-
return "VectorType(elementType: \(translateAType(ty)), count: \(size)"
1126+
return "VectorType(elementType: \(translateAType(ty)), count: \(size))"
11081127
case .metadata:
11091128
return "MetadataType()"
11101129
case .token:
@@ -1115,8 +1134,6 @@ func formFunctionTypeString(_ params: [LLVMType], _ retTy: LLVMType) -> String {
11151134
return "PointerType(pointee: StructType(elementTypes: []))"
11161135
case .any:
11171136
return "IntrinsicSubstitutionMarker()"
1118-
case .vector(.none):
1119-
return "VectorType(elementType: IntrinsicSubstitutionMarker(), count: -1)"
11201137
case .vararg:
11211138
return ""
11221139
default:
@@ -1266,3 +1283,5 @@ func run() {
12661283
}
12671284

12681285
run()
1286+
1287+

0 commit comments

Comments
 (0)