Skip to content

Commit 7ab7cdd

Browse files
authored
Merge pull request #86 from CodaFi/mushi-mushi
Debug Info
2 parents c6de4c7 + 5980b19 commit 7ab7cdd

File tree

5 files changed

+386
-0
lines changed

5 files changed

+386
-0
lines changed

Sources/LLVM/DIBuilder.swift

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
#if SWIFT_PACKAGE
2+
import cllvm
3+
#endif
4+
5+
/// Source languages known by DWARF.
6+
public enum DWARFSourceLanguage {
7+
case ada83
8+
9+
case ada95
10+
11+
case c
12+
13+
case c89
14+
15+
case c99
16+
17+
case c11
18+
19+
case cPlusPlus
20+
21+
case cPlusPlus03
22+
23+
case cPlusPlus11
24+
25+
case cPlusPlus14
26+
27+
case cobol74
28+
29+
case cobol85
30+
31+
case fortran77
32+
33+
case fortran90
34+
35+
case fortran03
36+
37+
case fortran08
38+
39+
case pascal83
40+
41+
case modula2
42+
43+
case java
44+
45+
case fortran95
46+
47+
case PLI
48+
49+
case objC
50+
51+
case objCPlusPlus
52+
53+
case UPC
54+
55+
case D
56+
57+
case python
58+
59+
case openCL
60+
61+
case go
62+
63+
case modula3
64+
65+
case haskell
66+
67+
case ocaml
68+
69+
case rust
70+
71+
case swift
72+
73+
case julia
74+
75+
case dylan
76+
77+
case renderScript
78+
79+
case BLISS
80+
81+
// MARK: Vendor Extensions
82+
83+
case mipsAssembler
84+
85+
case googleRenderScript
86+
87+
case borlandDelphi
88+
89+
90+
private static let languageMapping: [DWARFSourceLanguage: LLVMDWARFSourceLanguage] = [
91+
.c: LLVMDWARFSourceLanguageC, .c89: LLVMDWARFSourceLanguageC89,
92+
.c99: LLVMDWARFSourceLanguageC99, .c11: LLVMDWARFSourceLanguageC11,
93+
.ada83: LLVMDWARFSourceLanguageAda83,
94+
.cPlusPlus: LLVMDWARFSourceLanguageC_plus_plus,
95+
.cPlusPlus03: LLVMDWARFSourceLanguageC_plus_plus_03,
96+
.cPlusPlus11: LLVMDWARFSourceLanguageC_plus_plus_11,
97+
.cPlusPlus14: LLVMDWARFSourceLanguageC_plus_plus_14,
98+
.cobol74: LLVMDWARFSourceLanguageCobol74,
99+
.cobol85: LLVMDWARFSourceLanguageCobol85,
100+
.fortran77: LLVMDWARFSourceLanguageFortran77,
101+
.fortran90: LLVMDWARFSourceLanguageFortran90,
102+
.pascal83: LLVMDWARFSourceLanguagePascal83,
103+
.modula2: LLVMDWARFSourceLanguageModula2,
104+
.java: LLVMDWARFSourceLanguageJava,
105+
.ada95: LLVMDWARFSourceLanguageAda95,
106+
.fortran95: LLVMDWARFSourceLanguageFortran95,
107+
.PLI: LLVMDWARFSourceLanguagePLI,
108+
.objC: LLVMDWARFSourceLanguageObjC,
109+
.objCPlusPlus: LLVMDWARFSourceLanguageObjC_plus_plus,
110+
.UPC: LLVMDWARFSourceLanguageUPC,
111+
.D: LLVMDWARFSourceLanguageD,
112+
.python: LLVMDWARFSourceLanguagePython,
113+
.openCL: LLVMDWARFSourceLanguageOpenCL,
114+
.go: LLVMDWARFSourceLanguageGo,
115+
.modula3: LLVMDWARFSourceLanguageModula3,
116+
.haskell: LLVMDWARFSourceLanguageHaskell,
117+
.ocaml: LLVMDWARFSourceLanguageOCaml,
118+
.rust: LLVMDWARFSourceLanguageRust,
119+
.swift: LLVMDWARFSourceLanguageSwift,
120+
.julia: LLVMDWARFSourceLanguageJulia,
121+
.dylan: LLVMDWARFSourceLanguageDylan,
122+
.fortran03: LLVMDWARFSourceLanguageFortran03,
123+
.fortran08: LLVMDWARFSourceLanguageFortran08,
124+
.renderScript: LLVMDWARFSourceLanguageRenderScript,
125+
.BLISS: LLVMDWARFSourceLanguageBLISS,
126+
.mipsAssembler: LLVMDWARFSourceLanguageMips_Assembler,
127+
.googleRenderScript: LLVMDWARFSourceLanguageGOOGLE_RenderScript,
128+
.borlandDelphi: LLVMDWARFSourceLanguageBORLAND_Delphi,
129+
]
130+
131+
/// Retrieves the corresponding `LLVMDWARFSourceLanguage`.
132+
public var llvm: LLVMDWARFSourceLanguage {
133+
return DWARFSourceLanguage.languageMapping[self]!
134+
}
135+
}
136+
137+
/// The amount of debug information to emit.
138+
public enum DWARFEmissionKind {
139+
case none
140+
case full
141+
case lineTablesOnly
142+
143+
private static let emissionMapping: [DWARFEmissionKind: LLVMDWARFEmissionKind] = [
144+
.none: LLVMDWARFEmissionNone, .full: LLVMDWARFEmissionFull,
145+
.lineTablesOnly: LLVMDWARFEmissionLineTablesOnly,
146+
]
147+
148+
/// Retrieves the corresponding `LLVMDWARFEmissionKind`.
149+
public var llvm: LLVMDWARFEmissionKind {
150+
return DWARFEmissionKind.emissionMapping[self]!
151+
}
152+
}
153+
154+
public final class DIBuilder {
155+
internal let llvm: LLVMDIBuilderRef
156+
157+
/// The module this `IRBuilder` is associated with.
158+
public let module: Module
159+
160+
public init(module: Module, allowUnresolved: Bool = false) {
161+
self.module = module
162+
if allowUnresolved {
163+
self.llvm = LLVMCreateDIBuilder(module.llvm)
164+
} else {
165+
self.llvm = LLVMCreateDIBuilderDisallowUnresolved(module.llvm)
166+
}
167+
}
168+
169+
/// A CompileUnit provides an anchor for all debugging information generated
170+
/// during this instance of compilation.
171+
///
172+
/// - Parameters:
173+
/// - language: The source programming language.
174+
/// - file: The file descriptor for the source file.
175+
/// - kind: The kind of debug info to generate.
176+
/// - optimized: A flag that indicates whether optimization is enabled or
177+
/// not when compiling the source file. Defaults to `false`.
178+
/// - splitDebugInlining: A flag that indicates whether to emit inline debug
179+
/// information. Defaults to `false`.
180+
/// - debugInfoForProfiling: A flag that indicates whether to emit extra
181+
/// debug information for profile collection.
182+
/// - flags: Command line options that are embedded in debug info for use
183+
/// by third-party tools.
184+
/// - identity: The identity of the tool that is compiling this source file.
185+
/// - Returns: A value representing a compilation-unit level scope.
186+
public func createCompileUnit(
187+
for language: DWARFSourceLanguage,
188+
in file: FileMetadata,
189+
kind: DWARFEmissionKind,
190+
optimized: Bool = false,
191+
splitDebugInlining: Bool = false,
192+
debugInfoForProfiling: Bool = false,
193+
flags: [String] = [],
194+
identity: String = "",
195+
splitName: String = ""
196+
) -> Scope {
197+
let allFlags = flags.joined(separator: " ")
198+
guard let cu = LLVMDIBuilderCreateCompileUnit(
199+
self.llvm, language.llvm, file.llvm, identity, identity.count,
200+
optimized.llvm,
201+
allFlags, allFlags.count,
202+
/*Runtime Version*/0,
203+
splitName, splitName.count,
204+
kind.llvm,
205+
/*DWOId*/0,
206+
splitDebugInlining.llvm,
207+
debugInfoForProfiling.llvm
208+
) else {
209+
fatalError()
210+
}
211+
return Scope(llvm: cu)
212+
}
213+
214+
/// Create a file descriptor to hold debugging information for a file.
215+
///
216+
/// - Parameters:
217+
/// - name: The name of the file.
218+
/// - directory: The directory the file resides in.
219+
/// - Returns: A value represending metadata about a given file.
220+
public func createFile(named name: String, in directory: String) -> FileMetadata {
221+
guard let file = LLVMDIBuilderCreateFile(self.llvm, name, name.count, directory, directory.count) else {
222+
fatalError("Failed to allocate metadata for a file")
223+
}
224+
return FileMetadata(llvm: file)
225+
}
226+
227+
/// Creates a new debug location that describes a source location.
228+
///
229+
/// - Parameters:
230+
/// - location: The location of the line and column for this information.
231+
/// If the location of the value is unknown, pass
232+
/// `(line: 0, column: 0)`.
233+
/// - scope: The scope this debug location resides in.
234+
/// - inlinedAt: If this location has been inlined somewhere, the scope in
235+
/// which it was inlined. Defaults to `nil`.
236+
/// - Returns: A value representing a debug location.
237+
public func createDebugLocation(
238+
at location : (line: Int, column: Int),
239+
in scope: Scope,
240+
inlinedAt: Scope? = nil
241+
) -> DebugLocation {
242+
guard let loc = LLVMDIBuilderCreateDebugLocation(
243+
self.module.context.llvm, UInt32(location.line), UInt32(location.column),
244+
scope.llvm, inlinedAt?.llvm
245+
) else {
246+
fatalError("Failed to allocate metadata for a debug location")
247+
}
248+
return DebugLocation(llvm: loc)
249+
}
250+
251+
/// Construct any deferred debug info descriptors.
252+
public func finalize() {
253+
LLVMDIBuilderFinalize(self.llvm)
254+
}
255+
256+
deinit {
257+
LLVMDisposeDIBuilder(self.llvm)
258+
}
259+
}

Sources/LLVM/Metadata.swift

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#if SWIFT_PACKAGE
2+
import cllvm
3+
#endif
4+
5+
public protocol Metadata {
6+
func asMetadata() -> LLVMMetadataRef
7+
}
8+
9+
struct AnyMetadata: Metadata {
10+
let llvm: LLVMMetadataRef
11+
12+
func asMetadata() -> LLVMMetadataRef {
13+
return llvm
14+
}
15+
}
16+
17+
public struct VariableMetadata: Metadata {
18+
internal let llvm: LLVMMetadataRef
19+
20+
public func asMetadata() -> LLVMMetadataRef {
21+
return llvm
22+
}
23+
}
24+
25+
public struct FileMetadata: Metadata {
26+
internal let llvm: LLVMMetadataRef
27+
28+
public func asMetadata() -> LLVMMetadataRef {
29+
return llvm
30+
}
31+
}
32+
33+
public struct Scope: Metadata {
34+
internal let llvm: LLVMMetadataRef
35+
36+
public func asMetadata() -> LLVMMetadataRef {
37+
return llvm
38+
}
39+
}
40+
41+
public struct Macro: Metadata {
42+
internal let llvm: LLVMMetadataRef
43+
44+
public func asMetadata() -> LLVMMetadataRef {
45+
return llvm
46+
}
47+
}
48+
49+
public struct DIModule: Metadata {
50+
internal let llvm: LLVMMetadataRef
51+
52+
public func asMetadata() -> LLVMMetadataRef {
53+
return llvm
54+
}
55+
}
56+
57+
public struct DIExpression: Metadata {
58+
internal let llvm: LLVMMetadataRef
59+
60+
public func asMetadata() -> LLVMMetadataRef {
61+
return llvm
62+
}
63+
}
64+
65+
66+
public struct DebugLocation: Metadata {
67+
internal let llvm: LLVMMetadataRef
68+
69+
public func asMetadata() -> LLVMMetadataRef {
70+
return llvm
71+
}
72+
}

Sources/LLVM/Module.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,25 @@ public final class Module: CustomStringConvertible {
209209
}
210210
}
211211

212+
/// The current debug metadata version number.
213+
public static var debugMetadataVersion: UInt32 {
214+
return LLVMDebugMetadataVersion();
215+
}
216+
217+
/// The version of debug metadata that's present in this module.
218+
public var debugMetadataVersion: UInt32 {
219+
return LLVMGetModuleDebugMetadataVersion(self.llvm)
220+
}
221+
222+
/// Strip debug info in the module if it exists.
223+
///
224+
/// To do this, we remove all calls to the debugger intrinsics and any named
225+
/// metadata for debugging. We also remove debug locations for instructions.
226+
/// Return true if module is modified.
227+
public func stripDebugInfo() -> Bool {
228+
return LLVMStripModuleDebugInfo(self.llvm) != 0
229+
}
230+
212231
/// Dump a representation of this module to stderr.
213232
public func dump() {
214233
LLVMDumpModule(llvm)

Tests/LLVMTests/DIBuilderSpec.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import LLVM
2+
import XCTest
3+
import FileCheck
4+
import Foundation
5+
6+
class DIBuilderSpec : XCTestCase {
7+
func testDIBuilder() {
8+
XCTAssertTrue(fileCheckOutput(of: .stderr, withPrefixes: ["DIBUILDER"]) {
9+
// DIBUILDER: ; ModuleID = 'DIBuilderTest'
10+
let module = Module(name: "DIBuilderTest")
11+
// DIBUILDER: source_filename = "DIBuilderTest"
12+
let builder = IRBuilder(module: module)
13+
let debugBuilder = DIBuilder(module: module)
14+
15+
let f = builder.addFunction("foo", type: FunctionType(argTypes: [], returnType: VoidType()))
16+
let bb = f.appendBasicBlock(named: "entry")
17+
builder.positionAtEnd(of: bb)
18+
_ = builder.buildAlloca(type: IntType.int8)
19+
20+
// DIBUILDER-DAG: !{{[0-9]+}} = !DIFile(filename: "test.trill", directory: "/")
21+
let file = debugBuilder.createFile(named: "test.trill", in: "/")
22+
// DIBUILDER-DAG: !{{[0-9]+}} = distinct !DICompileUnit(language: DW_LANG_Swift, file: !{{[0-9]+}}, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !{{[0-9]+}}, splitDebugInlining: false)
23+
_ = debugBuilder.createCompileUnit(for: .swift, in: file, kind: .full)
24+
25+
debugBuilder.finalize()
26+
module.dump()
27+
})
28+
}
29+
30+
#if !os(macOS)
31+
static var allTests = testCase([
32+
("testDIBuilder", testDIBuilder),
33+
])
34+
#endif
35+
}

0 commit comments

Comments
 (0)