Skip to content

[WIP]: Metadata generation #34

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions Sources/LLVM/IRBuilder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1349,6 +1349,29 @@ public class IRBuilder {
return Alias(llvm: LLVMAddAlias(module.llvm, type.asLLVM(), aliasee.asLLVM(), name))
}

/// Adds the provided metadata value to the operands of a named metadata node
/// at module level. If the named metadata node does not exist, then it will
/// create a named metadata node with the provided value.
///
/// - Parameters:
/// - name: The name for the named metadata node.
/// - value: The value to add to the named metata.
public func addNamedMetadataOperand(name: String, value: IRMetadata) {
LLVMAddNamedMetadataOperand(module.llvm, name, value.asLLVM())
}

public func namedMetadataOperands(name: String) -> [IRMetadata] {
let count = Int(LLVMGetNamedMetadataNumOperands(module.llvm, name))
let ptr = UnsafeMutablePointer<LLVMValueRef?>.allocate(capacity: count)
defer { free(ptr) }
LLVMGetNamedMetadataOperands(module.llvm, name, ptr)
let buf = UnsafeMutableBufferPointer(start: ptr, count: count)
return buf.flatMap {
guard let llvm = $0 else { return nil }
return MetadataNode.fromLLVM(llvm)
}
}

deinit {
LLVMDisposeBuilder(llvm)
}
Expand Down
11 changes: 10 additions & 1 deletion Sources/LLVM/IRValue+Kinds.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import cllvm
// Automatically generated from the macros in llvm/Core.h

public extension IRValue {

/// Whether or not the underlying LLVM value is an `Argument`
public var isAArgument: Bool {
return LLVMIsAArgument(asLLVM()) != nil
Expand Down Expand Up @@ -154,6 +153,16 @@ public extension IRValue {
return LLVMIsADbgDeclareInst(asLLVM()) != nil
}

/// Whether or not the underlying LLVM value is an `MDString`
public var isAnMDString: Bool {
return LLVMIsAMDString(asLLVM()) != nil
}

/// Whether or not the underlying LLVM value is an `MDNode`
public var isAnMDNode: Bool {
return LLVMIsAMDNode(asLLVM()) != nil
}

/// Whether or not the underlying LLVM value is a `MemIntrinsic`
public var isAMemIntrinsic: Bool {
return LLVMIsAMemIntrinsic(asLLVM()) != nil
Expand Down
26 changes: 26 additions & 0 deletions Sources/LLVM/IRValue.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,32 @@ public extension IRValue {
return LLVMIsConstant(asLLVM()) != 0
}

/// Returns whether this IRValue has any metadata attached.
public var hasMetadata: Bool {
return LLVMHasMetadata(asLLVM()) != 0
}

/// Returns the metadata attached to this value for the specified kind.
///
/// - parameter kind: The kind of metadata you're looking for
/// - returns: The metadata attached to this node for the specified kind,
/// or `nil` if no metadata is attached.
public func metadata(kind: MetadataKind) -> IRMetadata? {
guard let meta = LLVMGetMetadata(asLLVM(), kind.rawValue) else {
return nil
}
return MetadataNode.fromLLVM(meta)
}

/// Sets the metadata of the provided kind for this value.
///
/// - parameters:
/// - value: The metadata value you wish to set.
/// - kind: The kind of metadata you wish to set.
public func setMetadata(_ value: IRMetadata, kind: MetadataKind) {
LLVMSetMetadata(asLLVM(), kind.rawValue, value.asLLVM())
}

/// Returns whether this value has been initialized with the special `undef`
/// value.
///
Expand Down
182 changes: 182 additions & 0 deletions Sources/LLVM/MetadataType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,185 @@ public struct MetadataType: IRType {
return llvm
}
}

/// Represents the debugInfofferent kinds of metadata available
public protocol IRMetadata: IRValue {}

extension IRMetadata {
/// Retrieves the operands to which this metadata is attached
public var operands: [IRValue] {
let count = Int(LLVMGetMDNodeNumOperands(asLLVM()))
let ptr = UnsafeMutablePointer<LLVMValueRef?>.allocate(capacity: count)
LLVMGetMDNodeOperands(asLLVM(), ptr)

var operands = [IRValue]()
for i in 0..<count {
operands.append(ptr[i]!)
}
return operands
}
}

/// A `MetadataNode` is a bit of arbitrary metadata attached to an instruction
/// or value. It can be initialized with `IRValues` and attached to any kind of
/// value.
public struct MetadataNode: IRMetadata {
let llvm: LLVMValueRef

/// Creates a `MetadataNode` from an underlying LLVM value
///
/// - parameter llvm: The LLVM value for this `MDNode`
internal init(llvm: LLVMValueRef) {
self.llvm = llvm
}

/// Creates the appropriate MDNode or MDString from the provided
/// `LLVMValueRef`.
/// - parameter llvm: The LLVMValueRef representing the desired metadata.
/// - returns: Either a `MetadataString` or `MetadataNode` depending on the
/// underlying value.
internal static func fromLLVM(_ llvm: LLVMValueRef) -> IRMetadata {
if llvm.isAnMDString { return MetadataString(llvm: llvm) }
return MetadataNode(llvm: llvm)
}

/// Creates a `MetadataNode` with the provided values, in the provided
/// context. If no context is provided, it will be created in the global
/// context.
///
/// - parameters:
/// - values: The values to add to the metadata
/// - context: The context in which to create the metadata. Defaults to
/// `nil`, which means the global context.
public init(values: [IRValue], in context: Context? = nil) {
var vals = values.map { $0.asLLVM() as Optional }
self.llvm = vals.withUnsafeMutableBufferPointer { buf in
if let context = context {
return LLVMMDNodeInContext(context.llvm, buf.baseAddress,
UInt32(buf.count))
} else {
return LLVMMDNode(buf.baseAddress, UInt32(buf.count))
}
}
}

/// Returns the underlying `LLVMValueRef` backing this node.
public func asLLVM() -> LLVMValueRef {
return llvm
}
}

/// A `MetadataString` is an arbitrary string of metadata attached to an
/// instruction or value.
public struct MetadataString: IRMetadata, ExpressibleByStringLiteral {
let llvm: LLVMValueRef

/// Creates a `MetadataString` from an underlying LLVM value
///
/// - parameter llvm: The LLVM value for this `MDString`
internal init(llvm: LLVMValueRef) {
self.llvm = llvm
}

/// Creates a `MetadataString` with the provided string as a value, in the
/// provided context. If no context is provided, it will be created in the
/// global context.
///
/// - parameters:
/// - string: The string with which to create this node.
/// - context: The context in which to create this node.
public init(_ string: String, in context: Context? = nil) {
if let context = context {
self.llvm = LLVMMDStringInContext(context.llvm, string,
UInt32(string.utf8.count))
} else {
self.llvm = LLVMMDString(string, UInt32(string.utf8.count))
}
}

/// Creates a `MetadataString` from a `String` literal.
///
/// - parameter value: The string with which to create this node.
public init(stringLiteral value: String) {
self.init(value)
}

/// Creates a `MetadataString` from a `UnicodeScalar` literal.
///
/// - parameter value: The string with which to create this node.
public init(unicodeScalarLiteral value: String) {
self.init(value)
}

/// Creates a `MetadataString` from a `ExtendedGraphemeClusterLiteral`
/// literal.
///
/// - parameter value: The string with which to create this node.
public init(extendedGraphemeClusterLiteral value: String) {
self.init(value)
}

/// Returns the underlying `LLVMValueRef` backing this string.
public func asLLVM() -> LLVMValueRef {
return llvm
}
}

/// Enumerates the possible kinds of metadata that LLVM supports.
/// - note: These must match, exactly, the enum in `llvm/Metadata.h`
public enum MetadataKind: UInt32 {
/// A simple metadata node with a `tuple` of operands.
case tuple = 0

/// A debug location in source code, used for debug info and otherwise.
case debugInfoLocation = 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should debug information be a sub-enum?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These aren't correct anyway -- it's a completely different enum.

I think there just isn't a way to generate DIFile/DICompileUnit/etc. from the C API. I'm gonna remove that enum and replace it with a matching one.


/// An un-specialized DWARF-like metadata node. The first operand is a
/// (possibly empty) null-separated `MetadataString` header that contains
/// arbitrary fields. The remaining operands are references to other metadata.
case genericDebugInfoNode = 2

/// An array subrange.
case debugInfoSubrange = 3

/// A wrapper for an enumerator (e.g. `x` and `y` in `enum { case x, y }`)
case debugInfoEnumerator = 4

/// A basic type, like 'Int' or 'Double'.
case debugInfoBasicType = 5

/// A derived type. This includes qualified types, pointers, references,
/// friends, typedefs, and class members.
case debugInfoDerivedType = 6

/// A composite type, like a struct or vector.
case debugInfoCompositeType = 7

/// A type corresponding to a function. It has one child node, `types`,
/// a tuple of type metadata.
/// The first element is the return type of the function, or `null` if
/// the function returns void. The rest are the parameter types of the
/// function, in order.
case debugInfoSubroutineType = 8

/// A file node.
case debugInfoFile = 9
case debugInfoCompileUnit = 10
case debugInfoSubprogram = 11
case debugInfoLexicalBlock = 12
case debugInfoLexicalBlockFile = 13
case debugInfoNamespace = 14
case debugInfoModule = 15
case debugInfoTemplateTypeParameter = 16
case debugInfoTemplateValueParameter = 17
case debugInfoGlobalVariable = 18
case debugInfoLocalVariable = 19
case debugInfoExpression = 20
case debugInfoObjCProperty = 21
case debugInfoImportedEntity = 22
case constantAsMetadata = 23
case localAsMetadata = 24
case string = 25
case debugInfoMacro = 26
case debugInfoMacroFile = 27
}
19 changes: 19 additions & 0 deletions Sources/LLVM/Utilities.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
//
// Utilities.swift
// LLVM
//
// Created by Harlan Haskins on 1/15/17.
//
//

import Foundation

extension UnsafeMutablePointer {
internal func asArray(count: Int) -> [Pointee] {
var vals = [Pointee]()
for i in 0..<count {
vals.append(self[i])
}
return vals
}
}