Skip to content

Commit 53da6f1

Browse files
committed
Improve IRBuilder docs
1 parent bf20f3a commit 53da6f1

File tree

2 files changed

+212
-89
lines changed

2 files changed

+212
-89
lines changed

Sources/LLVM/Global.swift

Lines changed: 86 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,77 @@
22
import cllvm
33
#endif
44

5-
/// Enumerates the supported models of reference of thread-local variables.
5+
/// A `Global` represents a region of memory allocated at compile time instead
6+
/// of at runtime. A global variable must either have an initializer, or make
7+
/// reference to an external definition that has an initializer.
8+
public final class Global: IRGlobal {
9+
internal let llvm: LLVMValueRef
10+
11+
internal init(llvm: LLVMValueRef) {
12+
self.llvm = llvm
13+
}
14+
15+
/// Returns whether this global variable has no initializer because it makes
16+
/// reference to an initialized value in another translation unit.
17+
public var isExternallyInitialized: Bool {
18+
get { return LLVMIsExternallyInitialized(llvm) != 0 }
19+
set { LLVMSetExternallyInitialized(llvm, newValue.llvm) }
20+
}
21+
22+
/// Retrieves the initializer for this global variable, if it exists.
23+
public var initializer: IRValue? {
24+
get { return LLVMGetInitializer(asLLVM()) }
25+
set { LLVMSetInitializer(asLLVM(), newValue!.asLLVM()) }
26+
}
27+
28+
/// Returns whether this global variable is a constant, whether or not the
29+
/// final definition of the global is not.
30+
public var isGlobalConstant: Bool {
31+
get { return LLVMIsGlobalConstant(asLLVM()) != 0 }
32+
set { LLVMSetGlobalConstant(asLLVM(), newValue.llvm) }
33+
}
34+
35+
/// Returns whether this global variable is thread-local. That is, returns
36+
/// if this variable is not shared by multiple threads.
37+
public var isThreadLocal: Bool {
38+
get { return LLVMIsThreadLocal(asLLVM()) != 0 }
39+
set { LLVMSetThreadLocal(asLLVM(), newValue.llvm) }
40+
}
41+
42+
/// Accesses the model of reference for this global variable if it is
43+
/// thread-local.
44+
public var threadLocalModel: ThreadLocalModel {
45+
get { return ThreadLocalModel(llvm: LLVMGetThreadLocalMode(asLLVM())) }
46+
set { LLVMSetThreadLocalMode(asLLVM(), newValue.llvm) }
47+
}
48+
49+
/// Retrieves the previous global in the module, if there is one.
50+
public func previous() -> Global? {
51+
guard let previous = LLVMGetPreviousGlobal(llvm) else { return nil }
52+
return Global(llvm: previous)
53+
}
54+
55+
/// Retrieves the next global in the module, if there is one.
56+
public func next() -> Global? {
57+
guard let next = LLVMGetNextGlobal(llvm) else { return nil }
58+
return Global(llvm: next)
59+
}
60+
61+
/// Deletes the global variable from its containing module.
62+
/// - note: This does not remove references to this global from the
63+
/// module. Ensure you have removed all instructions that reference
64+
/// this global before deleting it.
65+
public func delete() {
66+
LLVMDeleteGlobal(llvm)
67+
}
68+
69+
/// Retrieves the underlying LLVM value object.
70+
public func asLLVM() -> LLVMValueRef {
71+
return llvm
72+
}
73+
}
74+
75+
/// Enumerates the supported models of reference of thread-local variables.
676
///
777
/// These models are listed from the most general, but least optimized, to the
878
/// fastest, but most restrictive in general, as architectural differences
@@ -27,9 +97,9 @@ import cllvm
2797
public enum ThreadLocalModel {
2898
/// The variable is not thread local and hence has no associated model.
2999
case notThreadLocal
30-
/// Allows reference of all thread-local variables, from either a shared
31-
/// object or a dynamic executable. This model also supports the deferred
32-
/// allocation of a block of thread-local storage when the block is first
100+
/// Allows reference of all thread-local variables, from either a shared
101+
/// object or a dynamic executable. This model also supports the deferred
102+
/// allocation of a block of thread-local storage when the block is first
33103
/// referenced from a specific thread. Note that the linker is free to
34104
/// optimize accesses using this model to one of the more specific models
35105
/// below which may ultimately defeat lazy allocation of the TLS storagee
@@ -49,7 +119,7 @@ public enum ThreadLocalModel {
49119
/// object being built. In this case, the compiler instructs the linker
50120
/// to statically bind the dynamic offset of the variable and use this model.
51121
///
52-
/// This model provides a performance benefit over the General Dynamic model.
122+
/// This model provides a performance benefit over the General Dynamic model.
53123
/// Only one call to `__tls_get_addr` is required per function, to determine
54124
/// the starting address of the variable within the TLS block for its
55125
/// parent module. Additional accesses can add an offset to this address
@@ -63,29 +133,29 @@ public enum ThreadLocalModel {
63133
/// The linker cannot, in general, optimize from the general dynamic model
64134
/// to the local dynamic model.
65135
case localDynamic
66-
/// This model can only reference thread-local variables which are available
67-
/// as part of the initial static thread-local template. This template is
68-
/// composed of all thread-local storage blocks that are available at process
69-
/// startup, plus a small backup reservation.
136+
/// This model can only reference thread-local variables which are available
137+
/// as part of the initial static thread-local template. This template is
138+
/// composed of all thread-local storage blocks that are available at process
139+
/// startup, plus a small backup reservation.
70140
///
71141
/// In this model, the thread pointer-relative offset for a given variable `x`
72142
/// is stored in the GOT entry for x.
73143
///
74-
/// This model can reference a limited number of thread-local variables from
144+
/// This model can reference a limited number of thread-local variables from
75145
/// shared libraries loaded after initial process startup, such as by means of
76-
/// lazy loading, filters, or `dlopen()`. This access is satisfied from a
77-
/// fixed backup reservation. This reservation can only provide storage for
78-
/// uninitialized thread-local data items. For maximum flexibility, shared
146+
/// lazy loading, filters, or `dlopen()`. This access is satisfied from a
147+
/// fixed backup reservation. This reservation can only provide storage for
148+
/// uninitialized thread-local data items. For maximum flexibility, shared
79149
/// objects should reference thread-local variables using a dynamic model of
80150
/// thread-local storage.
81151
case initialExec
82152
/// This model is an optimization of the `localDynamic` model.
83153
///
84154
/// This model can only reference thread-local variables which are part of the
85155
/// thread-local storage block of the dynamic executable. The linker
86-
/// calculates the thread pointer-relative offsets statically, without the
87-
/// need for dynamic relocations, or the extra reference to the GOT. This
88-
/// model can not be used to reference variables outside of the dynamic
156+
/// calculates the thread pointer-relative offsets statically, without the
157+
/// need for dynamic relocations, or the extra reference to the GOT. This
158+
/// model can not be used to reference variables outside of the dynamic
89159
/// executable.
90160
case localExec
91161

@@ -113,73 +183,3 @@ public enum ThreadLocalModel {
113183
return ThreadLocalModel.modeMapping[self]!
114184
}
115185
}
116-
117-
/// A `Global` represents a region of memory allocated at compile time instead
118-
/// of at runtime. A global variable must either have an initializer, or make
119-
/// reference to an external definition that has an initializer.
120-
public final class Global: IRGlobal {
121-
internal let llvm: LLVMValueRef
122-
123-
internal init(llvm: LLVMValueRef) {
124-
self.llvm = llvm
125-
}
126-
127-
/// Returns whether this global variable has no initializer because it makes
128-
/// reference to an initialized value in another translation unit.
129-
public var isExternallyInitialized: Bool {
130-
get { return LLVMIsExternallyInitialized(llvm) != 0 }
131-
set { LLVMSetExternallyInitialized(llvm, newValue.llvm) }
132-
}
133-
134-
/// Retrieves the initializer for this global variable, if it exists.
135-
public var initializer: IRValue? {
136-
get { return LLVMGetInitializer(asLLVM()) }
137-
set { LLVMSetInitializer(asLLVM(), newValue!.asLLVM()) }
138-
}
139-
140-
/// Returns whether this global variable is a constant, whether or not the
141-
/// final definition of the global is not.
142-
public var isGlobalConstant: Bool {
143-
get { return LLVMIsGlobalConstant(asLLVM()) != 0 }
144-
set { LLVMSetGlobalConstant(asLLVM(), newValue.llvm) }
145-
}
146-
147-
/// Returns whether this global variable is thread-local. That is, returns
148-
/// if this variable is not shared by multiple threads.
149-
public var isThreadLocal: Bool {
150-
get { return LLVMIsThreadLocal(asLLVM()) != 0 }
151-
set { LLVMSetThreadLocal(asLLVM(), newValue.llvm) }
152-
}
153-
154-
/// Accesses the model of reference for this global variable if it is
155-
/// thread-local.
156-
public var threadLocalModel: ThreadLocalModel {
157-
get { return ThreadLocalModel(llvm: LLVMGetThreadLocalMode(asLLVM())) }
158-
set { LLVMSetThreadLocalMode(asLLVM(), newValue.llvm) }
159-
}
160-
161-
/// Retrieves the previous global in the module, if there is one.
162-
public func previous() -> Global? {
163-
guard let previous = LLVMGetPreviousGlobal(llvm) else { return nil }
164-
return Global(llvm: previous)
165-
}
166-
167-
/// Retrieves the next global in the module, if there is one.
168-
public func next() -> Global? {
169-
guard let next = LLVMGetNextGlobal(llvm) else { return nil }
170-
return Global(llvm: next)
171-
}
172-
173-
/// Deletes the global variable from its containing module.
174-
/// - note: This does not remove references to this global from the
175-
/// module. Ensure you have removed all instructions that reference
176-
/// this global before deleting it.
177-
public func delete() {
178-
LLVMDeleteGlobal(llvm)
179-
}
180-
181-
/// Retrieves the underlying LLVM value object.
182-
public func asLLVM() -> LLVMValueRef {
183-
return llvm
184-
}
185-
}

Sources/LLVM/IRBuilder.swift

Lines changed: 126 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,132 @@ import cllvm
33
import llvmshims
44
#endif
55

6-
/// An `IRBuilder` is a helper object that generates LLVM instructions. IR
7-
/// Builders keep track of a position within a function or basic block and has
8-
/// methods to insert instructions at that position.
6+
/// An `IRBuilder` is a helper object that generates LLVM instructions.
7+
///
8+
/// IR builders keep track of a position (the "insertion point") within a
9+
/// module, function, or basic block and has methods to insert instructions at
10+
/// that position. Other features include conveniences to insert calls to
11+
/// C standard library functions like `malloc` and `free`, the creation of
12+
/// global entities like (C) strings, and inline assembly.
13+
///
14+
/// Threading Considerations
15+
/// ========================
16+
///
17+
/// An `IRBuilder` object is not thread safe. It is associated with a single
18+
/// module which is, in turn, associated with a single LLVM context object.
19+
/// In concurrent environments, exactly one IRBuilder should be created per
20+
/// thread, and that thread should be the one that ultimately created its parent
21+
/// module and context. Inserting instructions into the same IR builder object
22+
/// in a concurrent manner will result in malformed IR being generated in
23+
/// non-deterministic ways. If concurrent codegen is needed, a separate LLVM
24+
/// context, module, and IRBuilder should be created on each thread.
25+
/// Once each thread has finished generating code, the resulting modules should
26+
/// be merged together. See `Module.link(_:)` for more information.
27+
///
28+
/// IR Navigation
29+
/// =============
30+
///
31+
/// By default, the insertion point of a builder is undefined. To move the
32+
/// IR builder's cursor, a basic block must be created, but not necessarily
33+
/// inserted into a function.
34+
///
35+
/// let module = Module(name: "Example")
36+
/// let builder = IRBuilder(module: module)
37+
/// // Create a freestanding basic block and insert an `ret`
38+
/// // instruction into it.
39+
/// let freestanding = BasicBlock(name: "freestanding")
40+
/// // Move the IR builder to the end of the block's instruction list.
41+
/// builder.positionAtEnd(of: freestanding)
42+
/// let ret = builder.buildRetVoid()
43+
///
44+
/// Instructions serve as a way to position the IR builder to a point before
45+
/// their creation. This allows for instructions to be inserted *before* a
46+
/// given instruction rather than at the very end of a basic block.
47+
///
48+
/// // Move before the `ret` instruction
49+
/// builder.positionBefore(ret)
50+
/// // Insert an `alloca` instruction before the `ret`.
51+
/// let intAlloca = builder.buildAlloca(type: IntType.int8)
52+
/// // Move before the `alloca`
53+
/// builder.positionBefore(intAlloca)
54+
/// // Insert an `malloc` call before the `alloca`.
55+
/// let intMalloc = builder.buildMalloc(IntType.int8)
56+
///
57+
/// To insert this block into a function, see `Function.append`.
58+
///
59+
/// Sometimes it is necessary to reset the insertion point. When the insertion
60+
/// point is reset, instructions built with the IR builder are still created,
61+
/// but are not inserted into a basic block. To clear the insertion point, call
62+
/// `IRBuilder.clearInsertionPosition()`.
63+
///
64+
/// Building LLVM IR
65+
/// ================
66+
///
67+
/// All functions that build instructions are prefixed with `build`. Invoking
68+
/// these functions inserts the appropriate LLVM instruction at the insertion
69+
/// point, assuming it points to a valid location.
70+
///
71+
/// let module = Module(name: "Example")
72+
/// let builder = IRBuilder(module: module)
73+
/// let fun = builder.addFunction("test",
74+
/// type: FunctionType(argTypes: [
75+
/// IntType.int8,
76+
/// IntType.int8,
77+
/// ], returnType: FloatType.float))
78+
/// let entry = fun.appendBasicBlock(named: "entry")
79+
/// // Set the insertion point to the entry block of this function
80+
/// builder.positionAtEnd(of: entry)
81+
/// // Build an `add` instruction at the insertion point
82+
/// let result = builder.buildAdd(fun.parameters[0], fun.parameters[1])
83+
///
84+
/// Customizing LLVM IR
85+
/// ===================
86+
///
87+
/// To be well-formed, certain instructions may involve more setup than just
88+
/// being built. In such cases, LLVMSwift will yield a specific instance of
89+
/// `IRInstruction` that will allow for this kind of configuration.
90+
///
91+
/// A prominent example of this is the PHI node. Building a PHI node produces
92+
/// an empty PHI node - this is not a well-formed instruction. A PHI node must
93+
/// have its incoming basic blocks attached. To do so, `PhiNode.addIncoming(_:)`
94+
/// is called with a list of pairs of incoming values and their enclosing
95+
/// basic blocks.
96+
///
97+
/// // Build a function that selects one of two floating parameters based
98+
/// // on a given boolean value.
99+
/// let module = Module(name: "Example")
100+
/// let builder = IRBuilder(module: module)
101+
/// let select = builder.addFunction("select",
102+
/// type: FunctionType(argTypes: [
103+
/// IntType.int1,
104+
/// FloatType.float,
105+
/// FloatType.float,
106+
/// ], returnType: FloatType.float))
107+
/// let entry = select.appendBasicBlock(named: "entry")
108+
/// builder.positionAtEnd(of: entry)
109+
///
110+
/// let thenBlock = select.appendBasicBlock(named: "then")
111+
/// let elseBlock = select.appendBasicBlock(named: "else")
112+
/// let mergeBB = select.appendBasicBlock(named: "merge")
113+
/// let branch = builder.buildCondBr(condition: select.parameters[0],
114+
/// then: thenBlock,
115+
/// else: elseBlock)
116+
/// builder.positionAtEnd(of: thenBlock)
117+
/// let opThen = builder.buildAdd(select.parameters[1], select.parameters[2])
118+
/// builder.buildBr(mergeBB)
119+
/// builder.positionAtEnd(of: elseBlock)
120+
/// let opElse = builder.buildSub(select.parameters[1], select.parameters[2])
121+
/// builder.buildBr(mergeBB)
122+
/// builder.positionAtEnd(of: mergeBB)
123+
///
124+
/// // Build the PHI node
125+
/// let phi = builder.buildPhi(FloatType.float)
126+
/// // Attach the incoming blocks.
127+
/// phi.addIncoming([
128+
/// (opThen, thenBlock),
129+
/// (opElse, elseBlock),
130+
/// ])
131+
/// builder.buildRet(phi)
9132
public class IRBuilder {
10133
internal let llvm: LLVMBuilderRef
11134

0 commit comments

Comments
 (0)