Skip to content

Commit

Permalink
Merge pull request #86 from CodaFi/mushi-mushi
Browse files Browse the repository at this point in the history
Debug Info
  • Loading branch information
CodaFi authored Mar 14, 2018
2 parents c6de4c7 + 5980b19 commit 7ab7cdd
Show file tree
Hide file tree
Showing 5 changed files with 386 additions and 0 deletions.
259 changes: 259 additions & 0 deletions Sources/LLVM/DIBuilder.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
#if SWIFT_PACKAGE
import cllvm
#endif

/// Source languages known by DWARF.
public enum DWARFSourceLanguage {
case ada83

case ada95

case c

case c89

case c99

case c11

case cPlusPlus

case cPlusPlus03

case cPlusPlus11

case cPlusPlus14

case cobol74

case cobol85

case fortran77

case fortran90

case fortran03

case fortran08

case pascal83

case modula2

case java

case fortran95

case PLI

case objC

case objCPlusPlus

case UPC

case D

case python

case openCL

case go

case modula3

case haskell

case ocaml

case rust

case swift

case julia

case dylan

case renderScript

case BLISS

// MARK: Vendor Extensions

case mipsAssembler

case googleRenderScript

case borlandDelphi


private static let languageMapping: [DWARFSourceLanguage: LLVMDWARFSourceLanguage] = [
.c: LLVMDWARFSourceLanguageC, .c89: LLVMDWARFSourceLanguageC89,
.c99: LLVMDWARFSourceLanguageC99, .c11: LLVMDWARFSourceLanguageC11,
.ada83: LLVMDWARFSourceLanguageAda83,
.cPlusPlus: LLVMDWARFSourceLanguageC_plus_plus,
.cPlusPlus03: LLVMDWARFSourceLanguageC_plus_plus_03,
.cPlusPlus11: LLVMDWARFSourceLanguageC_plus_plus_11,
.cPlusPlus14: LLVMDWARFSourceLanguageC_plus_plus_14,
.cobol74: LLVMDWARFSourceLanguageCobol74,
.cobol85: LLVMDWARFSourceLanguageCobol85,
.fortran77: LLVMDWARFSourceLanguageFortran77,
.fortran90: LLVMDWARFSourceLanguageFortran90,
.pascal83: LLVMDWARFSourceLanguagePascal83,
.modula2: LLVMDWARFSourceLanguageModula2,
.java: LLVMDWARFSourceLanguageJava,
.ada95: LLVMDWARFSourceLanguageAda95,
.fortran95: LLVMDWARFSourceLanguageFortran95,
.PLI: LLVMDWARFSourceLanguagePLI,
.objC: LLVMDWARFSourceLanguageObjC,
.objCPlusPlus: LLVMDWARFSourceLanguageObjC_plus_plus,
.UPC: LLVMDWARFSourceLanguageUPC,
.D: LLVMDWARFSourceLanguageD,
.python: LLVMDWARFSourceLanguagePython,
.openCL: LLVMDWARFSourceLanguageOpenCL,
.go: LLVMDWARFSourceLanguageGo,
.modula3: LLVMDWARFSourceLanguageModula3,
.haskell: LLVMDWARFSourceLanguageHaskell,
.ocaml: LLVMDWARFSourceLanguageOCaml,
.rust: LLVMDWARFSourceLanguageRust,
.swift: LLVMDWARFSourceLanguageSwift,
.julia: LLVMDWARFSourceLanguageJulia,
.dylan: LLVMDWARFSourceLanguageDylan,
.fortran03: LLVMDWARFSourceLanguageFortran03,
.fortran08: LLVMDWARFSourceLanguageFortran08,
.renderScript: LLVMDWARFSourceLanguageRenderScript,
.BLISS: LLVMDWARFSourceLanguageBLISS,
.mipsAssembler: LLVMDWARFSourceLanguageMips_Assembler,
.googleRenderScript: LLVMDWARFSourceLanguageGOOGLE_RenderScript,
.borlandDelphi: LLVMDWARFSourceLanguageBORLAND_Delphi,
]

/// Retrieves the corresponding `LLVMDWARFSourceLanguage`.
public var llvm: LLVMDWARFSourceLanguage {
return DWARFSourceLanguage.languageMapping[self]!
}
}

/// The amount of debug information to emit.
public enum DWARFEmissionKind {
case none
case full
case lineTablesOnly

private static let emissionMapping: [DWARFEmissionKind: LLVMDWARFEmissionKind] = [
.none: LLVMDWARFEmissionNone, .full: LLVMDWARFEmissionFull,
.lineTablesOnly: LLVMDWARFEmissionLineTablesOnly,
]

/// Retrieves the corresponding `LLVMDWARFEmissionKind`.
public var llvm: LLVMDWARFEmissionKind {
return DWARFEmissionKind.emissionMapping[self]!
}
}

public final class DIBuilder {
internal let llvm: LLVMDIBuilderRef

/// The module this `IRBuilder` is associated with.
public let module: Module

public init(module: Module, allowUnresolved: Bool = false) {
self.module = module
if allowUnresolved {
self.llvm = LLVMCreateDIBuilder(module.llvm)
} else {
self.llvm = LLVMCreateDIBuilderDisallowUnresolved(module.llvm)
}
}

/// A CompileUnit provides an anchor for all debugging information generated
/// during this instance of compilation.
///
/// - Parameters:
/// - language: The source programming language.
/// - file: The file descriptor for the source file.
/// - kind: The kind of debug info to generate.
/// - optimized: A flag that indicates whether optimization is enabled or
/// not when compiling the source file. Defaults to `false`.
/// - splitDebugInlining: A flag that indicates whether to emit inline debug
/// information. Defaults to `false`.
/// - debugInfoForProfiling: A flag that indicates whether to emit extra
/// debug information for profile collection.
/// - flags: Command line options that are embedded in debug info for use
/// by third-party tools.
/// - identity: The identity of the tool that is compiling this source file.
/// - Returns: A value representing a compilation-unit level scope.
public func createCompileUnit(
for language: DWARFSourceLanguage,
in file: FileMetadata,
kind: DWARFEmissionKind,
optimized: Bool = false,
splitDebugInlining: Bool = false,
debugInfoForProfiling: Bool = false,
flags: [String] = [],
identity: String = "",
splitName: String = ""
) -> Scope {
let allFlags = flags.joined(separator: " ")
guard let cu = LLVMDIBuilderCreateCompileUnit(
self.llvm, language.llvm, file.llvm, identity, identity.count,
optimized.llvm,
allFlags, allFlags.count,
/*Runtime Version*/0,
splitName, splitName.count,
kind.llvm,
/*DWOId*/0,
splitDebugInlining.llvm,
debugInfoForProfiling.llvm
) else {
fatalError()
}
return Scope(llvm: cu)
}

/// Create a file descriptor to hold debugging information for a file.
///
/// - Parameters:
/// - name: The name of the file.
/// - directory: The directory the file resides in.
/// - Returns: A value represending metadata about a given file.
public func createFile(named name: String, in directory: String) -> FileMetadata {
guard let file = LLVMDIBuilderCreateFile(self.llvm, name, name.count, directory, directory.count) else {
fatalError("Failed to allocate metadata for a file")
}
return FileMetadata(llvm: file)
}

/// Creates a new debug location that describes a source location.
///
/// - Parameters:
/// - location: The location of the line and column for this information.
/// If the location of the value is unknown, pass
/// `(line: 0, column: 0)`.
/// - scope: The scope this debug location resides in.
/// - inlinedAt: If this location has been inlined somewhere, the scope in
/// which it was inlined. Defaults to `nil`.
/// - Returns: A value representing a debug location.
public func createDebugLocation(
at location : (line: Int, column: Int),
in scope: Scope,
inlinedAt: Scope? = nil
) -> DebugLocation {
guard let loc = LLVMDIBuilderCreateDebugLocation(
self.module.context.llvm, UInt32(location.line), UInt32(location.column),
scope.llvm, inlinedAt?.llvm
) else {
fatalError("Failed to allocate metadata for a debug location")
}
return DebugLocation(llvm: loc)
}

/// Construct any deferred debug info descriptors.
public func finalize() {
LLVMDIBuilderFinalize(self.llvm)
}

deinit {
LLVMDisposeDIBuilder(self.llvm)
}
}
72 changes: 72 additions & 0 deletions Sources/LLVM/Metadata.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#if SWIFT_PACKAGE
import cllvm
#endif

public protocol Metadata {
func asMetadata() -> LLVMMetadataRef
}

struct AnyMetadata: Metadata {
let llvm: LLVMMetadataRef

func asMetadata() -> LLVMMetadataRef {
return llvm
}
}

public struct VariableMetadata: Metadata {
internal let llvm: LLVMMetadataRef

public func asMetadata() -> LLVMMetadataRef {
return llvm
}
}

public struct FileMetadata: Metadata {
internal let llvm: LLVMMetadataRef

public func asMetadata() -> LLVMMetadataRef {
return llvm
}
}

public struct Scope: Metadata {
internal let llvm: LLVMMetadataRef

public func asMetadata() -> LLVMMetadataRef {
return llvm
}
}

public struct Macro: Metadata {
internal let llvm: LLVMMetadataRef

public func asMetadata() -> LLVMMetadataRef {
return llvm
}
}

public struct DIModule: Metadata {
internal let llvm: LLVMMetadataRef

public func asMetadata() -> LLVMMetadataRef {
return llvm
}
}

public struct DIExpression: Metadata {
internal let llvm: LLVMMetadataRef

public func asMetadata() -> LLVMMetadataRef {
return llvm
}
}


public struct DebugLocation: Metadata {
internal let llvm: LLVMMetadataRef

public func asMetadata() -> LLVMMetadataRef {
return llvm
}
}
19 changes: 19 additions & 0 deletions Sources/LLVM/Module.swift
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,25 @@ public final class Module: CustomStringConvertible {
}
}

/// The current debug metadata version number.
public static var debugMetadataVersion: UInt32 {
return LLVMDebugMetadataVersion();
}

/// The version of debug metadata that's present in this module.
public var debugMetadataVersion: UInt32 {
return LLVMGetModuleDebugMetadataVersion(self.llvm)
}

/// Strip debug info in the module if it exists.
///
/// To do this, we remove all calls to the debugger intrinsics and any named
/// metadata for debugging. We also remove debug locations for instructions.
/// Return true if module is modified.
public func stripDebugInfo() -> Bool {
return LLVMStripModuleDebugInfo(self.llvm) != 0
}

/// Dump a representation of this module to stderr.
public func dump() {
LLVMDumpModule(llvm)
Expand Down
35 changes: 35 additions & 0 deletions Tests/LLVMTests/DIBuilderSpec.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import LLVM
import XCTest
import FileCheck
import Foundation

class DIBuilderSpec : XCTestCase {
func testDIBuilder() {
XCTAssertTrue(fileCheckOutput(of: .stderr, withPrefixes: ["DIBUILDER"]) {
// DIBUILDER: ; ModuleID = 'DIBuilderTest'
let module = Module(name: "DIBuilderTest")
// DIBUILDER: source_filename = "DIBuilderTest"
let builder = IRBuilder(module: module)
let debugBuilder = DIBuilder(module: module)

let f = builder.addFunction("foo", type: FunctionType(argTypes: [], returnType: VoidType()))
let bb = f.appendBasicBlock(named: "entry")
builder.positionAtEnd(of: bb)
_ = builder.buildAlloca(type: IntType.int8)

// DIBUILDER-DAG: !{{[0-9]+}} = !DIFile(filename: "test.trill", directory: "/")
let file = debugBuilder.createFile(named: "test.trill", in: "/")
// DIBUILDER-DAG: !{{[0-9]+}} = distinct !DICompileUnit(language: DW_LANG_Swift, file: !{{[0-9]+}}, isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !{{[0-9]+}}, splitDebugInlining: false)
_ = debugBuilder.createCompileUnit(for: .swift, in: file, kind: .full)

debugBuilder.finalize()
module.dump()
})
}

#if !os(macOS)
static var allTests = testCase([
("testDIBuilder", testDIBuilder),
])
#endif
}
Loading

0 comments on commit 7ab7cdd

Please sign in to comment.