Skip to content

Commit d8cc316

Browse files
committed
Add an example API
1 parent 59418df commit d8cc316

File tree

3 files changed

+227
-21
lines changed

3 files changed

+227
-21
lines changed

Sources/LLVM/Intrinsics.swift

Lines changed: 131 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ public enum LLVMIntrinsic: String {
1616
case prefetch = "llvm.prefetch"
1717
case pcmarker = "llvm.pcmarker"
1818
case readcyclecounter = "llvm.readcyclecounter"
19+
case assume = "llvm.assume"
20+
case stackprotector = "llvm.stackprotector"
21+
case stackprotectorcheck = "llvm.stackprotectorcheck"
1922
case memcpy_i8 = "llvm.memcpy.i8"
2023
case memcpy_i16 = "llvm.memcpy.i16"
2124
case memcpy_i32 = "llvm.memcpy.i32"
@@ -146,7 +149,6 @@ public enum LLVMIntrinsic: String {
146149
case annotation_i64 = "llvm.annotation.i64"
147150
case annotation_i256 = "llvm.annotation.i256"
148151
case trap = "llvm.trap"
149-
case stackprotector = "llvm.stackprotector"
150152

151153
var signature: FunctionType {
152154
switch self {
@@ -215,6 +217,21 @@ public enum LLVMIntrinsic: String {
215217
argTypes: [],
216218
returnType: IntType.int64
217219
)
220+
case .assume:
221+
return FunctionType(
222+
argTypes: [IntType.int1],
223+
returnType: VoidType()
224+
)
225+
case .stackprotector:
226+
return FunctionType(
227+
argTypes: [PointerType(pointee: IntType.int8), PointerType(pointee: PointerType(pointee: IntType.int8))],
228+
returnType: VoidType()
229+
)
230+
case .stackprotectorcheck:
231+
return FunctionType(
232+
argTypes: [PointerType(pointee: PointerType(pointee: IntType.int8))],
233+
returnType: VoidType()
234+
)
218235
case .memcpy_i8:
219236
return FunctionType(
220237
argTypes: [PointerType(pointee: IntType.int8), PointerType(pointee: IntType.int8)],
@@ -865,38 +882,131 @@ public enum LLVMIntrinsic: String {
865882
argTypes: [],
866883
returnType: VoidType()
867884
)
868-
case .stackprotector:
869-
return FunctionType(
870-
argTypes: [PointerType(pointee: IntType.int8), PointerType(pointee: PointerType(pointee: IntType.int8))],
871-
returnType: VoidType()
872-
)
873885
}
874886
}
875887
}
876888

877889
extension IRBuilder {
878-
public func buildIntrinsic(_ i : LLVMIntrinsic) -> Function {
890+
public func buildIntrinsic(_ i: LLVMIntrinsic) -> Function {
879891
if let f = self.module.function(named: i.rawValue) {
880892
return f
881893
}
882894
var f = self.addFunction(i.rawValue, type: i.signature)
883895
f.linkage = .external
884896
return f
885897
}
886-
//
887-
// func buildva_start(PointerType(pointee: IntType.int8))
888-
// func buildva_copy(PointerType(pointee: IntType.int8), PointerType(pointee: IntType.int8))
889-
// func buildva_end(PointerType(pointee: IntType.int8))
890-
// func buildgcroot(PointerType(pointee: PointerType(pointee: IntType.int8)), PointerType(pointee: IntType.int8))
891-
// func buildgcread(PointerType(pointee: IntType.int8), PointerType(pointee: PointerType(pointee: IntType.int8)))
892-
// func buildgcwrite(PointerType(pointee: IntType.int8), PointerType(pointee: IntType.int8), PointerType(pointee: PointerType(pointee: IntType.int8)))
893-
// func buildreturnaddress(IntType.int32)
894-
// func buildframeaddress(IntType.int32)
895-
// func buildstacksave()
896-
// func buildstackrestore(PointerType(pointee: IntType.int8))
897-
// func buildprefetch(PointerType(pointee: IntType.int8), IntType.int32, IntType.int32)
898-
// func buildpcmarker(IntType.int32)
899-
// func buildreadcyclecounter()
898+
899+
private func typeCheckArguments(to fn: LLVMIntrinsic, args: IRValue...) -> Bool {
900+
let fnArgTypes = fn.signature.argTypes
901+
guard fnArgTypes.count == args.count else { return false }
902+
for t in zip(fnArgTypes, args) {
903+
guard t.0.asLLVM() == t.1.type.asLLVM() else { return false }
904+
}
905+
return true
906+
}
907+
908+
private func resolveOverloadedArguments(to fns: [LLVMIntrinsic], args: IRValue...) -> LLVMIntrinsic? {
909+
return fns.filter({ (fn) -> Bool in
910+
let fnArgTypes = fn.signature.argTypes
911+
guard fnArgTypes.count == args.count else { return false }
912+
for t in zip(fnArgTypes, args) {
913+
guard t.0.asLLVM() == t.1.type.asLLVM() else { return false }
914+
}
915+
return true
916+
}).first
917+
}
918+
919+
@discardableResult
920+
public func buildVaStart(_ x: IRValue, name: String = "") -> IRValue {
921+
precondition(typeCheckArguments(to: .va_start, args: x))
922+
return buildCall(buildIntrinsic(.va_start), args: [x], name: name)
923+
}
924+
925+
@discardableResult
926+
public func buildVaCopy(from src: IRValue, to dest: IRValue, name: String = "") -> IRValue {
927+
precondition(typeCheckArguments(to: .va_copy, args: dest, src))
928+
return buildCall(buildIntrinsic(.va_copy), args: [dest, src], name: name)
929+
}
930+
931+
@discardableResult
932+
public func buildVaEnd(_ x: IRValue, name: String = "") -> IRValue {
933+
precondition(typeCheckArguments(to: .va_end, args: x))
934+
return buildCall(buildIntrinsic(.va_end), args: [x], name: name)
935+
}
936+
937+
func buildGCRoot(_ x: IRValue, _ y: IRValue, name: String = "") -> IRValue {
938+
precondition(typeCheckArguments(to: .gcroot, args: x, y))
939+
return buildCall(buildIntrinsic(.gcroot), args: [x, y], name: name)
940+
}
941+
942+
func buildGCRead(_ x: IRValue, _ y: IRValue, _ z: IRValue, name: String = "") -> IRValue {
943+
precondition(typeCheckArguments(to: .gcread, args: x, y, z))
944+
return buildCall(buildIntrinsic(.gcread), args: [x, y, z], name: name)
945+
}
946+
947+
func buildGCWrite(_ x: IRValue, _ y: IRValue, _ z: IRValue, name: String = "") -> IRValue {
948+
precondition(typeCheckArguments(to: .gcwrite, args: x, y, z))
949+
return buildCall(buildIntrinsic(.gcwrite), args: [x, y, z], name: name)
950+
}
951+
952+
func buildReturnAddress(_ x: IRValue, name: String = "") -> IRValue {
953+
precondition(typeCheckArguments(to: .returnaddress, args: x))
954+
return buildCall(buildIntrinsic(.returnaddress), args: [x], name: name)
955+
}
956+
957+
func buildFrameAddress(_ x: IRValue, name: String = "") -> IRValue {
958+
precondition(typeCheckArguments(to: .frameaddress, args: x))
959+
return buildCall(buildIntrinsic(.frameaddress), args: [x], name: name)
960+
}
961+
962+
func buildStackSave(name: String = "") -> IRValue {
963+
precondition(typeCheckArguments(to: .stacksave))
964+
return buildCall(buildIntrinsic(.stacksave), args: [], name: name)
965+
}
966+
967+
func buildStackRestore(_ x: IRValue, name: String = "") -> IRValue {
968+
precondition(typeCheckArguments(to: .stackrestore, args: x))
969+
return buildCall(buildIntrinsic(.stackrestore), args: [x], name: name)
970+
}
971+
972+
func buildPrefetch(_ x: IRValue, _ y: IRValue, _ z: IRValue, name: String = "") -> IRValue {
973+
precondition(typeCheckArguments(to: .prefetch, args: x, y, z))
974+
return buildCall(buildIntrinsic(.prefetch), args: [x, y, z], name: name)
975+
}
976+
977+
func buildPCMarker(_ x: IRValue, name: String = "") -> IRValue {
978+
precondition(typeCheckArguments(to: .pcmarker, args: x))
979+
return buildCall(buildIntrinsic(.pcmarker), args: [x], name: name)
980+
}
981+
982+
func buildReadyCycleCounter(name: String = "") -> IRValue {
983+
precondition(typeCheckArguments(to: .readcyclecounter))
984+
return buildCall(buildIntrinsic(.readcyclecounter), args: [], name: name)
985+
}
986+
987+
func buildAssume(_ x: IRValue, name: String = "") -> IRValue {
988+
precondition(typeCheckArguments(to: .assume, args: x))
989+
return buildCall(buildIntrinsic(.assume), args: [x], name: name)
990+
}
991+
992+
func buildStackProtector(_ x: IRValue, _ y: IRValue, name: String = "") -> IRValue {
993+
precondition(typeCheckArguments(to: .stackprotector, args: x, y))
994+
return buildCall(buildIntrinsic(.stackprotector), args: [x, y], name: name)
995+
}
996+
997+
func buildStackProtectorCheck(_ x: IRValue, name: String = "") -> IRValue {
998+
precondition(typeCheckArguments(to: .stackprotectorcheck, args: x))
999+
return buildCall(buildIntrinsic(.stackprotectorcheck), args: [x], name: name)
1000+
}
1001+
1002+
func buildMemcpy(dest: IRValue, src: IRValue, len: IRValue, align: IRValue = IntType.int32.constant(0), name: String = "") -> IRValue {
1003+
guard let overload = resolveOverloadedArguments(to: [.memcpy_i8, .memcpy_i16, .memcpy_i32, .memcpy_i64], args: dest, src, len, align) else {
1004+
fatalError("Could not resolve overload of 'memcpy'")
1005+
}
1006+
precondition(typeCheckArguments(to: overload, args: dest, src, len, align))
1007+
return buildCall(buildIntrinsic(overload), args: [dest, src, len, align], name: name)
1008+
}
1009+
9001010
// func buildmemcpy(PointerType(pointee: IntType.int8), PointerType(pointee: IntType.int8))
9011011
// func buildmemmove(PointerType(pointee: IntType.int8), PointerType(pointee: IntType.int8))
9021012
// func buildmemset(PointerType(pointee: IntType.int8), IntType.int8)

Tests/LLVMTests/IRIntrinsicSpec.swift

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import LLVM
2+
import XCTest
3+
import Foundation
4+
5+
class IRIntrinsicSpec : XCTestCase {
6+
func testIRIntrinsics() {
7+
XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["IRINTRINSIC"]) {
8+
// IRINTRINSIC: ; ModuleID = '[[ModuleName:IRIntrinsicTest]]'
9+
// IRINTRINSIC-NEXT: source_filename = "[[ModuleName]]"
10+
let module = Module(name: "IRIntrinsicTest")
11+
let builder = IRBuilder(module: module)
12+
// IRINTRINSIC: define i32 @readOneArg(i32, ...) {
13+
let main = builder.addFunction("readOneArg",
14+
type: FunctionType(argTypes: [IntType.int32],
15+
returnType: IntType.int32,
16+
isVarArg: true))
17+
// IRINTRINSIC-NEXT: entry:
18+
let entry = main.appendBasicBlock(named: "entry")
19+
builder.positionAtEnd(of: entry)
20+
21+
// IRINTRINSIC-NEXT: [[VA_BUF:%[1-9]+]] = alloca i8*
22+
let ap = builder.buildAlloca(type: PointerType(pointee: IntType.int8))
23+
// IRINTRINSIC-NEXT: [[CAST_VA_BUF:%[1-9]+]] = bitcast i8** [[VA_BUF]] to i8*
24+
let ap2 = builder.buildBitCast(ap, type: PointerType(pointee: IntType.int8))
25+
26+
// IRINTRINSIC-NEXT: call void @llvm.va_start(i8* [[CAST_VA_BUF]])
27+
builder.buildVaStart(ap2)
28+
29+
// IRINTRINSIC-NEXT: [[RET_VAL:%[1-9]+]] = va_arg i8** [[VA_BUF]], i32
30+
let tmp = builder.buildVAArg(ap, type: IntType.int32)
31+
32+
// IRINTRINSIC-NEXT: [[VA_COPY_BUF:%[1-9]+]] = alloca i8*
33+
let aq = builder.buildAlloca(type: PointerType(pointee: IntType.int8))
34+
// IRINTRINSIC-NEXT: [[CAST_VA_COPY_BUF:%[1-9]+]] = bitcast i8** [[VA_COPY_BUF]] to i8*
35+
let aq2 = builder.buildBitCast(aq, type: PointerType(pointee: IntType.int8))
36+
37+
// IRINTRINSIC-NEXT: call void @llvm.va_copy(i8* [[CAST_VA_COPY_BUF]], i8* [[CAST_VA_BUF]])
38+
builder.buildVaCopy(from: ap2, to: aq2)
39+
// IRINTRINSIC-NEXT: call void @llvm.va_end(i8* [[CAST_VA_COPY_BUF]])
40+
builder.buildVaEnd(aq2)
41+
42+
// IRINTRINSIC-NEXT: call void @llvm.va_end(i8* [[CAST_VA_BUF]])
43+
builder.buildVaEnd(ap2)
44+
45+
// IRINTRINSIC-NEXT: i32 [[RET_VAL]]
46+
builder.buildRet(tmp)
47+
// IRINTRINSIC-NEXT: }
48+
module.dump()
49+
50+
// IRINTRINSIC: ; Function Attrs: nounwind
51+
// IRINTRINSIC-NEXT: declare void @llvm.va_start(i8*) #0
52+
53+
// IRINTRINSIC: ; Function Attrs: nounwind
54+
// IRINTRINSIC-NEXT: declare void @llvm.va_copy(i8*, i8*) #0
55+
56+
// IRINTRINSIC: ; Function Attrs: nounwind
57+
// IRINTRINSIC-NEXT: declare void @llvm.va_end(i8*) #0
58+
})
59+
}
60+
61+
#if !os(macOS)
62+
static var allTests = testCase([
63+
("testIRIntrinsics", testIRIntrinsics),
64+
])
65+
#endif
66+
}
67+

utils/instrinsics-gen.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
/// Runs the specified program at the provided path.
2+
/// - parameter path: The full path of the executable you
3+
/// wish to run.
4+
/// - parameter args: The arguments you wish to pass to the
5+
/// process.
6+
/// - returns: The standard output of the process, or nil if it was empty.
7+
func run(_ path: String, args: [String] = []) -> String? {
8+
print("Running \(path) \(args.joined(separator: " "))...")
9+
let pipe = Pipe()
10+
let process = Process()
11+
process.launchPath = path
12+
process.arguments = args
13+
process.standardOutput = pipe
14+
process.launch()
15+
process.waitUntilExit()
16+
17+
let data = pipe.fileHandleForReading.readDataToEndOfFile()
18+
guard let result = String(data: data, encoding: .utf8)?
19+
.trimmingCharacters(in: .whitespacesAndNewlines),
20+
!result.isEmpty else { return nil }
21+
return result
22+
}
23+
24+
/// Finds the location of the provided binary on your system.
25+
func which(_ name: String) -> String? {
26+
return run("/usr/bin/which", args: [name])
27+
}
28+
29+
run()

0 commit comments

Comments
 (0)