Skip to content

Commit

Permalink
Merge pull request #2 from mrLSD/feat/extend-module
Browse files Browse the repository at this point in the history
Feat: Exnted Module with Flag and Metadata funcitons
  • Loading branch information
mrLSD authored Jul 23, 2024
2 parents 7b72ed6 + 0157b3d commit 647e0c7
Show file tree
Hide file tree
Showing 4 changed files with 260 additions and 20 deletions.
61 changes: 58 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,68 @@
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Swift CI](https://github.com/mrLSD/llvm-api-swift/actions/workflows/swift.yaml/badge.svg)](https://github.com/mrLSD/llvm-api-swift/actions/workflows/swift.yaml)

# `llvm-api-swift`
<center>
<h1>mrLSD<code>/llvm-api-swift</code></h1>
</center>

`llvm-api-swift` is a library representing Swift LLVM API, a pure Swift interface to the [LLVM API](https://llvm.org/docs/) and its associated libraries.
It provides native, easy-to-use components to create compilers codegen backend. It contains LLVM bindings,
components and toolset for efficiently use LLVM as compilers backend implementation on Swift.

## Overview

The `llvm-api-swift` provides a robust and comprehensive interface to the LLVM Compiler Infrastructure,
leveraging the `LLVM-C API` to offer a blend of safety, flexibility, and extendability. This library
is designed to serve as a powerful tool for developers looking to create backends for compilers, enabling
them to harness the full potential of **LLVM** in a secure and user-friendly manner.

## Safety

Safety is a paramount concern in the design of this library. By building on the `LLVM-C API`, we ensure that
interactions
with the **LLVM** infrastructure are conducted in a type-safe and memory-safe manner. The library employs Swift’s
stringent
safety guarantees to prevent common issues such as null pointer dereferencing, buffer overflows, and memory leaks. This
commitment to safety allows developers to focus on the functionality of their compiler backends without worrying about
underlying security vulnerabilities.

## Flexibility

Flexibility is another core attribute of the `llvm-api-swift`. The library provides a rich set of APIs that cover a wide
range of LLVM’s capabilities, from module management and inline assembly to debugging metadata and function iteration.
Developers can easily access and manipulate **LLVM** constructs, enabling the creation of highly customized and
optimized
compiler backends. The library’s design ensures that it can adapt to various use cases and requirements, making it an
ideal choice for a diverse set of compiler development projects based on Swift.

## Extendability

The 'llvm-api-swift' is built with extendability in mind. It is designed to be easily extendable, allowing developers to
add
new functionalities and support for additional **LLVM** features as needed. The modular structure of the library
facilitates
the incorporation of new components, ensuring that it can grow and evolve alongside the **LLVM** ecosystem. This
extendability ensures that the library remains relevant and useful as **LLVM** continues to advance and expand its
capabilities.

## Why LLVM?

**LLVM** (Low-Level Virtual Machine) is a powerful and versatile compiler infrastructure that provides a collection of
modular and reusable compiler and toolchain technologies. It is widely used in the development of modern compilers,
providing a framework for optimizing intermediate representations and generating machine code for various target
architectures. LLVM’s ability to support multiple languages and platforms, coupled with its extensive optimization
capabilities, makes it an essential tool for compiler developers. By leveraging **LLVM**, developers can create highly
efficient and portable compilers that meet the demands of today’s diverse computing environments.

## Design

The `llvm-api-awift` library adheres to the structure of the `LLVM C API`, ensuring easy navigation through the extensive LLVM
functions. Logical elements are grouped into modules, providing a clear organizational structure. Within these modules,
Rust structures are introduced to wrap LLVM types, implementing corresponding functions for the wrapped LLVM types. This
approach enhances flexibility and usability while maintaining the original LLVM code structure. The design avoids
unnecessary complexity in the code and documentation, remaining fully aligned with the `LLVM API`. This alignment allows
developers to easily navigate the `llvm-api-swift` library using existing LLVM-C documentation.

### Compatibility with LLVM-C API

When creating the library, we were guided by **full compatibility** with [LLVM-C API](https://llvm.org/doxygen/group__LLVMC.html).
Expand All @@ -15,8 +71,7 @@ And filling the components also with the appropriate `LLVM-C API`.
When implementing Swift types, we were guided by the approach of abstracting away from C types, completely transforming them into Swift types.
At the same time adhering to the principles of a safety and reliability implementation - without explicit memory management, means of safe techniques, functions provided by Swift.


### Requirements
## Requirements

- Supported OS: MacOS 12.0 or above

Expand Down
9 changes: 8 additions & 1 deletion llvm-api/LLVM/Core/Context.swift
Original file line number Diff line number Diff line change
Expand Up @@ -222,12 +222,19 @@ public final class Context: ContextRef {
}
}

///Destroy a context instance.
///
/// This should be called for every call to LLVMContextCreate() or memory will be leaked.
public func dispose() {
LLVMContextDispose(llvm)
}

/// Deinitialize this value and dispose of its resources.
///
/// Destroy a context instance.
/// This should be called for every call to LLVMContextCreate() or memory
/// will be leaked.
deinit {
LLVMContextDispose(llvm)
self.dispose()
}
}
204 changes: 191 additions & 13 deletions llvm-api/LLVM/Core/Modules.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,152 @@ public final class Module: ModuleRef {
/// Retrieves the underlying LLVM value object.
public var moduleRef: LLVMModuleRef { llvm }

public enum InlineAsmDialect {
case att
case intel
}

/// Named Metadata Node
public class NamedMetadataNode: NamedMetadataNodeRef {
private var llvm: LLVMNamedMDNodeRef

/// Retrieves the underlying LLVM value object.
public var namedMetadataNodeRef: LLVMNamedMDNodeRef { llvm }

init(llvm: LLVMNamedMDNodeRef) {
self.llvm = llvm
}

/// Advance a `NamedMetaDataNode` iterator to the next `NamedMetaDataNode`.
///
/// Returns NULL if the iterator was already at the end and there are no more
/// named metadata nodes.
public func getNext() -> NamedMetadataNode? {
guard let nextRef = LLVMGetNextNamedMetadata(llvm) else {
return nil
}
return NamedMetadataNode(llvm: nextRef)
}

/// Decrement a `NamedMetaDataNode` iterator to the previous `NamedMetaDataNode`.
///
/// Returns NULL if the iterator was already at the beginning and there are
/// no previously named metadata nodes.
public func getPrevious() -> NamedMetadataNode? {
guard let prevRef = LLVMGetPreviousNamedMetadata(llvm) else {
return nil
}
return NamedMetadataNode(llvm: prevRef)
}

/// Retrieve the name of a `NamedMetadataNode`.
public func getName() -> String? {
var length: size_t = 0
guard let cStr = LLVMGetNamedMetadataName(llvm, &length) else {
return nil
}
return String(cString: cStr)
}
}

/// Enumerates the supported behaviors for resolving collisions when two
/// module flags share the same key. These collisions can occur when the
/// different flags are inserted under the same key, or when modules
/// containing flags under the same key are merged.
public enum ModuleFlagBehavior {
/// Emits an error if two values disagree, otherwise the resulting value
/// is that of the operands.
case error
/// Emits a warning if two values disagree. The result value will be the
/// operand for the flag from the first module being linked.
case warning
/// Adds a requirement that another module flag be present and have a
/// specified value after linking is performed. The value must be a
/// metadata pair, where the first element of the pair is the ID of the
/// module flag to be restricted, and the second element of the pair is
/// the value the module flag should be restricted to. This behavior can
/// be used to restrict the allowable results (via triggering of an error)
/// of linking IDs with the **Override** behavior.
case require
/// Uses the specified value, regardless of the behavior or value of the
/// other module. If both modules specify **Override**, but the values
/// differ, an error will be emitted.
case override
/// Appends the two values, which are required to be metadata nodes.
case append
/// Appends the two values, which are required to be metadata
/// nodes. However, duplicate entries in the second list are dropped
/// during the append operation.
case appendUnique

init(raw: LLVMModuleFlagBehavior) {
switch raw {
case LLVMModuleFlagBehaviorError:
self = .error
case LLVMModuleFlagBehaviorWarning:
self = .warning
case LLVMModuleFlagBehaviorRequire:
self = .require
case LLVMModuleFlagBehaviorOverride:
self = .override
case LLVMModuleFlagBehaviorAppend:
self = .append
case LLVMModuleFlagBehaviorAppendUnique:
self = .appendUnique
default:
fatalError("Unknown behavior kind")
}
}
}

class Metadata: MetadataRef {
private let llvm:LLVMMetadataRef
public var metadataRef: LLVMMetadataRef {
llvm }
public init(llvm: LLVMMetadataRef ) {
self.llvm = llvm
}
}

public class ModuleFlagEntry {
private let llvm: OpaquePointer?
private let bounds: Int

public init(llvm:OpaquePointer?, bounds: Int) {
self.llvm = llvm
self.bounds = bounds
}

/// Get Metadata flags etries count
public var count: Int { self.bounds }

/// Returns the flag behavior for a module flag entry at a specific index.
public func getFlagBehavior(at index: UInt32) -> ModuleFlagBehavior {
let bh = LLVMModuleFlagEntriesGetFlagBehavior(llvm, index)
return ModuleFlagBehavior(raw: bh)
}

/// Returns the key for a module flag entry at a specific index.
public func getKey(at index: UInt32) -> String {
var length: Int = 0
let keyPointer = LLVMModuleFlagEntriesGetKey(llvm, index, &length)
return String(cString: keyPointer!)

}

/// Returns the metadata for a module flag entry at a specific index.
public func getMetadata(at index: UInt32) -> MetadataRef {
let metadata = LLVMModuleFlagEntriesGetMetadata(llvm, index)!
return Metadata(llvm: metadata)
}

/// Deinitialize this value and dispose of its resources.
deinit {
guard let ptr = llvm else { return }
LLVMDisposeModuleFlagsMetadata(ptr)
}
}

/// Init function by LLVM Value
public init(llvm: LLVMModuleRef) {
self.llvm = llvm
Expand Down Expand Up @@ -36,33 +182,46 @@ public final class Module: ModuleRef {
}

/// Return an exact copy of the specified module.
public func clone_nodule() -> ModuleRef {
public func cloneModule() -> Self {
let new_module = LLVMCloneModule(llvm)!
return Self(llvm: new_module)
}

/// Get and Set the identifier of a module.
public var moduleIdentifier: String {
get {
self.getModuleIdentifier
}
set {
self.setModuleIdentifier(identifier: newValue)
}
}

/// Obtain the identifier of a module.
public var getLLVMModuleIdentifier: String {
public var getModuleIdentifier: String {
var length: UInt = 0
guard let cString = LLVMGetModuleIdentifier(llvm, &length) else { return "" }
return String(cString: cString)
}

public func setLLVMModuleIdentifier(module: LLVMModuleRef, identifier: String) {
/// Set the identifier of a module to a string Ident with length Len.
public func setModuleIdentifier(identifier: String) {
identifier.withCString { cString in
LLVMSetModuleIdentifier(module, cString, identifier.count)
LLVMSetModuleIdentifier(llvm, cString, identifier.count)
}
}

public var getModuleIdentifier: String? {
var length: UInt = 0
guard let cString = LLVMGetModuleIdentifier(llvm, &length) else {
return nil
/// Get and Set the original source file name of a module to a string Name
public var sourceFileName: String {
get {
self.getSourceFileName!
}
set {
self.setSourceFileName(fileName: newValue)
}
return String(cString: cString)
}

/// Set the identifier of a module to a string Ident with length Len.
///Set the original source file name of a module to a string Name
public func setSourceFileName(fileName: String) {
fileName.withCString { cString in
LLVMSetSourceFileName(llvm, cString, fileName.utf8.count)
Expand All @@ -71,17 +230,17 @@ public final class Module: ModuleRef {

/// Obtain the module's original source file name.
public var getSourceFileName: String? {
var length: size_t = 0
var length: Int = 0
guard let cString = LLVMGetSourceFileName(llvm, &length) else {
return nil
}
return String(cString: cString)
}

/// Set the data layout for a module.
public func setDataLayout(module: LLVMModuleRef, dataLayout: String) {
public func setDataLayout(dataLayout: String) {
dataLayout.withCString { cString in
LLVMSetDataLayout(module, cString)
LLVMSetDataLayout(llvm, cString)
}
}

Expand All @@ -93,13 +252,32 @@ public final class Module: ModuleRef {
return String(cString: cString)
}


/// Obtain the target triple for a module.
func getTargetTriple() -> String {
guard let targetTriplePointer = LLVMGetTarget(llvm) else {
return ""
}
return String(cString: targetTriplePointer)
}

/// Set the target triple for a module.
public func setTarget(triple: String) {
triple.withCString { cString in
LLVMSetTarget(llvm, cString)
}
}

/// Returns the module flags as an array of flag-key-value triples. The caller
/// is responsible for freeing this array by calling
/// `LLVMDisposeModuleFlagsMetadata`.
public func copyModuleFlagsMetadata() -> ModuleFlagEntry? {
var length: Int = 0
guard let flagsPointer = LLVMCopyModuleFlagsMetadata(llvm, &length) else { return nil }

return ModuleFlagEntry(llvm: flagsPointer, bounds: length)
}

/// Destroy a module instance.
///
/// This must be called for every created module or memory will be leaked.
Expand Down
6 changes: 3 additions & 3 deletions llvm-api/LLVM/Core/Types/Types.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ public protocol ModuleRef {
var moduleRef: LLVMModuleRef { get }
}

/// Represents an LLVM Named Metadata Node.
public protocol NamedMDNodeRef {
var namedMDNodeRef: LLVMNamedMDNodeRef { get }
/// Represents an LLVM Named Metadata Node (NamedMDNodeRef).
public protocol NamedMetadataNodeRef {
var namedMetadataNodeRef: LLVMNamedMDNodeRef { get }
}

public protocol OperandBundleRef {
Expand Down

0 comments on commit 647e0c7

Please sign in to comment.