Skip to content

Commit 46df4d3

Browse files
authored
Merge pull request #149 from CodaFi/aligned-again-naturally
Introduce the Size and Alignment Types
2 parents 2df0a57 + a8d28e2 commit 46df4d3

File tree

5 files changed

+609
-13
lines changed

5 files changed

+609
-13
lines changed

Sources/LLVM/Module.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,14 @@ public final class Module: CustomStringConvertible {
7777
llvm = LLVMModuleCreateWithName(name)
7878
self.context = Context(llvm: LLVMGetModuleContext(llvm)!)
7979
}
80+
self.dataLayout = TargetData(llvm: LLVMGetModuleDataLayout(llvm))
8081
}
8182

8283
/// Returns the context associated with this module.
8384
public let context: Context
8485

8586
/// Obtain the data layout for this module.
86-
public var dataLayout: TargetData {
87-
return TargetData(llvm: LLVMGetModuleDataLayout(llvm))
88-
}
87+
public var dataLayout: TargetData
8988

9089
/// The identifier of this module.
9190
public var name: String {

Sources/LLVM/StructType.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,22 @@ public struct StructType: IRType {
115115
public func asLLVM() -> LLVMTypeRef {
116116
return llvm
117117
}
118+
119+
/// Return true if this is a struct type with an identity that has an
120+
/// unspecified body.
121+
///
122+
/// Opaque struct types print as `opaque` in serialized .ll files.
123+
public var isOpaque: Bool {
124+
return LLVMIsOpaqueStruct(self.llvm) != 0
125+
}
126+
127+
/// Returns true if this is a packed struct type.
128+
///
129+
/// A packed struct type includes no padding between fields and is thus
130+
/// laid out in one contiguous region of memory with its elements laid out
131+
/// one after the other. A non-packed struct type includes padding according
132+
/// to the data layout of the target.
133+
public var isPacked: Bool {
134+
return LLVMIsPackedStruct(self.llvm) != 0
135+
}
118136
}

Sources/LLVM/TargetData.swift

Lines changed: 206 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import cllvm
77
/// sizes and offsets of types with respect to this target.
88
public class TargetData {
99
internal let llvm: LLVMTargetDataRef
10+
private var structLayoutCache = [LLVMTypeRef: StructLayout]()
1011

1112
/// Creates a Target Data object from an `LLVMTargetDataRef` object.
1213
public init(llvm: LLVMTargetDataRef) {
@@ -46,16 +47,6 @@ public class TargetData {
4647
return Int(LLVMSizeOfTypeInBits(llvm, type.asLLVM()))
4748
}
4849

49-
/// Computes the minimum ABI-required number of bits necessary to hold a value
50-
/// of the given type for this target environment.
51-
///
52-
/// - parameter type: The type to compute the size of.
53-
///
54-
/// - returns: The minimum ABI-required size of the type in bytes.
55-
public func abiSizeOfType(_ type: IRType) -> Int {
56-
return Int(LLVMABISizeOfType(llvm, type.asLLVM()))
57-
}
58-
5950
/// The current platform byte order, either big or little endian.
6051
public var byteOrder: ByteOrder {
6152
return ByteOrder(llvm: LLVMByteOrder(llvm))
@@ -94,6 +85,124 @@ public class TargetData {
9485
///
9586
/// - parameter global: The global variable
9687
/// - returns: The variable's preferred alignment in this target
88+
public func preferredAlignment(of global: Global) -> Alignment {
89+
return Alignment(LLVMPreferredAlignmentOfGlobal(llvm, global.asLLVM()))
90+
}
91+
92+
/// Computes the preferred alignment of the given type for this target
93+
///
94+
/// - parameter type: The type for which you're computing the alignment
95+
/// - returns: The type's preferred alignment in this target
96+
public func preferredAlignment(of type: IRType) -> Alignment {
97+
return Alignment(LLVMPreferredAlignmentOfType(llvm, type.asLLVM()))
98+
}
99+
100+
/// Computes the minimum ABI-required alignment for the specified type.
101+
///
102+
/// - parameter type: The type to whose ABI alignment you wish to compute.
103+
/// - returns: The minimum ABI-required alignment for the specified type.
104+
public func abiAlignment(of type: IRType) -> Alignment {
105+
return Alignment(LLVMABIAlignmentOfType(llvm, type.asLLVM()))
106+
}
107+
108+
/// Computes the minimum ABI-required alignment for the specified type.
109+
///
110+
/// This function is equivalent to `TargetData.abiAlignment(of:)`.
111+
///
112+
/// - parameter type: The type to whose ABI alignment you wish to compute.
113+
/// - returns: The minimum ABI-required alignment for the specified type.
114+
public func callFrameAlignment(of type: IRType) -> Alignment {
115+
return Alignment(LLVMCallFrameAlignmentOfType(llvm, type.asLLVM()))
116+
}
117+
118+
/// Computes the ABI size of a type in bytes for a target.
119+
///
120+
/// - parameter type: The type to whose ABI size you wish to compute.
121+
/// - returns: The ABI size for the specified type.
122+
public func abiSize(of type: IRType) -> Size {
123+
return Size(LLVMABISizeOfType(llvm, type.asLLVM()))
124+
}
125+
/// Computes the maximum number of bytes that may be overwritten by
126+
/// storing the specified type.
127+
///
128+
/// - parameter type: The type to whose store size you wish to compute.
129+
/// - returns: The store size of the type in the given target.
130+
public func storeSize(of type: IRType) -> Size {
131+
return Size(LLVMStoreSizeOfType(llvm, type.asLLVM()))
132+
}
133+
134+
/// Computes the pointer size for the platform, optionally in a given
135+
/// address space.
136+
///
137+
/// - parameter addressSpace: The address space in which to compute
138+
/// pointer size.
139+
/// - returns: The size of a pointer in the target address space.
140+
public func pointerSize(addressSpace: Int? = nil) -> Size {
141+
if let addressSpace = addressSpace {
142+
return Size(UInt64(LLVMPointerSizeForAS(llvm, UInt32(addressSpace))))
143+
} else {
144+
return Size(UInt64(LLVMPointerSize(llvm)))
145+
}
146+
}
147+
148+
/// Returns the offset in bytes between successive objects of the
149+
/// specified type, including alignment padding.
150+
///
151+
/// This is the amount that alloca reserves for this type. For example,
152+
/// returns 12 or 16 for x86_fp80, depending on alignment.
153+
///
154+
/// - parameter type: The type whose allocation size you wish to compute.
155+
/// - returns: The size an alloca would reserve for the given type.
156+
public func allocationSize(of type: IRType) -> Size {
157+
// Round up to the next alignment boundary.
158+
return TargetData.align(self.storeSize(of: type), to: self.abiAlignment(of: type))
159+
}
160+
161+
/// Returns a `StructLayout` object containing the alignment of the
162+
/// struct, its size, and the offsets of its fields with respect to this
163+
/// data layout.
164+
///
165+
/// - parameter struct: The struct type whose layout you wish to retrieve.
166+
/// - returns: A `StructLayout` describing the layout of the given type.
167+
public func layout(of struct: StructType) -> StructLayout {
168+
guard let cachedLayout = self.structLayoutCache[`struct`.asLLVM()] else {
169+
let layout = StructLayout(`struct`, self)
170+
self.structLayoutCache[`struct`.asLLVM()] = layout
171+
return layout
172+
}
173+
return cachedLayout
174+
}
175+
176+
/// Returns the next integer (mod 2**64) that is greater than or equal to
177+
/// \p Value and is a multiple of \p Align. \p Align must be non-zero.
178+
///
179+
/// If non-zero \p Skew is specified, the return value will be a minimal
180+
/// integer that is greater than or equal to \p Value and equal to
181+
/// \p Align * N + \p Skew for some integer N. If \p Skew is larger than
182+
/// \p Align, its value is adjusted to '\p Skew mod \p Align'.
183+
///
184+
/// Computes the next size value that is greater than or equal to the given
185+
/// value and is a multiple of the given alignment.
186+
///
187+
/// If the skew value is non-zero, the return value will be the next size
188+
/// value that is greater than or equal to the given value multipled by the
189+
/// provided alignment with a skew value added
190+
public static func align(_ value: Size, to align: Alignment, skew: Size = Size.zero) -> Size {
191+
precondition(!align.isZero, "Align can't be 0.")
192+
193+
let skewValue = skew.rawValue % UInt64(align.rawValue)
194+
let alignValue = UInt64(align.rawValue)
195+
let retVal = (value.rawValue + alignValue - 1 - skewValue) / alignValue * alignValue + skewValue
196+
return Size(retVal)
197+
}
198+
}
199+
200+
extension TargetData {
201+
/// Computes the preferred alignment of the given global for this target
202+
///
203+
/// - parameter global: The global variable
204+
/// - returns: The variable's preferred alignment in this target
205+
@available(*, message: "Prefer the overload of prefferedAlignment(of:) that returns an Alignment")
97206
public func preferredAlignment(of global: Global) -> Int {
98207
return Int(LLVMPreferredAlignmentOfGlobal(llvm, global.asLLVM()))
99208
}
@@ -102,6 +211,7 @@ public class TargetData {
102211
///
103212
/// - parameter type: The type for which you're computing the alignment
104213
/// - returns: The type's preferred alignment in this target
214+
@available(*, message: "Prefer the overload of prefferedAlignment(of:) that returns an Alignment")
105215
public func preferredAlignment(of type: IRType) -> Int {
106216
return Int(LLVMPreferredAlignmentOfType(llvm, type.asLLVM()))
107217
}
@@ -110,6 +220,7 @@ public class TargetData {
110220
///
111221
/// - parameter type: The type to whose ABI alignment you wish to compute.
112222
/// - returns: The minimum ABI-required alignment for the specified type.
223+
@available(*, message: "Prefer the overload of abiAlignment(of:) that returns an Alignment")
113224
public func abiAlignment(of type: IRType) -> Int {
114225
return Int(LLVMABIAlignmentOfType(llvm, type.asLLVM()))
115226
}
@@ -120,6 +231,7 @@ public class TargetData {
120231
///
121232
/// - parameter type: The type to whose ABI alignment you wish to compute.
122233
/// - returns: The minimum ABI-required alignment for the specified type.
234+
@available(*, message: "Prefer the overload of callFrameAlignment(of:) that returns an Alignment")
123235
public func callFrameAlignment(of type: IRType) -> Int {
124236
return Int(LLVMCallFrameAlignmentOfType(llvm, type.asLLVM()))
125237
}
@@ -128,6 +240,7 @@ public class TargetData {
128240
///
129241
/// - parameter type: The type to whose ABI size you wish to compute.
130242
/// - returns: The ABI size for the specified type.
243+
@available(*, message: "Prefer the overload of abiSize(of:) that returns a Size")
131244
public func abiSize(of type: IRType) -> Int {
132245
return Int(LLVMABISizeOfType(llvm, type.asLLVM()))
133246
}
@@ -136,6 +249,7 @@ public class TargetData {
136249
///
137250
/// - parameter type: The type to whose store size you wish to compute.
138251
/// - returns: The store size of the type in the given target.
252+
@available(*, message: "Prefer the overload of storeSize(of:) that returns a Size")
139253
public func storeSize(of type: IRType) -> Int {
140254
return Int(LLVMStoreSizeOfType(llvm, type.asLLVM()))
141255
}
@@ -146,6 +260,7 @@ public class TargetData {
146260
/// - parameter addressSpace: The address space in which to compute
147261
/// pointer size.
148262
/// - returns: The size of a pointer in the target address space.
263+
@available(*, message: "Prefer the overload of pointerSize(addressSpace:) that returns a Size")
149264
public func pointerSize(addressSpace: Int? = nil) -> Int {
150265
if let addressSpace = addressSpace {
151266
return Int(LLVMPointerSizeForAS(llvm, UInt32(addressSpace)))
@@ -155,6 +270,87 @@ public class TargetData {
155270
}
156271
}
157272

273+
/// A `StructLayout` encapsulates information about the layout of a `StructType`.
274+
public struct StructLayout {
275+
/// Returns the total size of the struct in bytes.
276+
///
277+
/// If the structure is packed, this returns a value that is effectively
278+
/// the sum of the sizes of its fields. If the structure is not packed, this
279+
/// returns the value of the sum of the sizes of its fields plus any
280+
/// platform-dictated padding.
281+
public let size: Size
282+
/// Returns the alignment of the struct in bytes.
283+
///
284+
/// The alignment value is effectively the maximum of the alignment of each of
285+
/// its member values. This value will never be zero, as even an empty
286+
/// structure has an alignment of one byte.
287+
public let alignment: Alignment
288+
/// Returns true if the structure type includes padding between elements.
289+
public let isPadded: Bool
290+
/// Returns the number of elements of this structure type.
291+
public let elementCount: Int
292+
/// Returns the offsets of each member from the start of the of the struct in
293+
/// bytes.
294+
public let memberOffsets: [Size]
295+
296+
fileprivate init(_ st: StructType, _ dl: TargetData) {
297+
assert(!st.isOpaque, "Cannot get layout of opaque structs")
298+
var structAlignment = Alignment.zero
299+
var structSize = Size.zero
300+
var structIsPadded = false
301+
var structMemberOffsets = [Size]()
302+
structMemberOffsets.reserveCapacity(st.elementTypes.count)
303+
self.elementCount = st.elementTypes.count
304+
305+
// Loop over each of the elements, placing them in memory.
306+
for ty in st.elementTypes {
307+
let tyAlign = Alignment(UInt32(st.isPacked ? 1 : dl.abiAlignment(of: ty)))
308+
309+
// Add padding if necessary to align the data element properly.
310+
if (structSize.rawValue & UInt64(tyAlign.rawValue-1)) != 0 {
311+
structIsPadded = true
312+
structSize = TargetData.align(structSize, to: tyAlign)
313+
}
314+
315+
// Keep track of maximum alignment constraint.
316+
structAlignment = max(tyAlign, structAlignment)
317+
318+
structMemberOffsets.append(structSize)
319+
// Consume space for this data item
320+
structSize = structSize + dl.allocationSize(of: ty)
321+
}
322+
323+
// Empty structures have alignment of 1 byte.
324+
if structAlignment == Alignment.zero {
325+
structAlignment = Alignment.one
326+
}
327+
328+
// Add padding to the end of the struct so that it could be put in an array
329+
// and all array elements would be aligned correctly.
330+
if (structSize.rawValue & UInt64(structAlignment.rawValue-1)) != 0 {
331+
structIsPadded = true
332+
structSize = TargetData.align(structSize, to: structAlignment)
333+
}
334+
self.size = structSize
335+
self.alignment = structAlignment
336+
self.isPadded = structIsPadded
337+
self.memberOffsets = structMemberOffsets
338+
}
339+
340+
/// Given a valid byte offset into the structure, returns the structure
341+
/// index that contains it.
342+
public func index(of offset: Size) -> Int {
343+
precondition(!self.memberOffsets.isEmpty,
344+
"Cannot compute index member offset of an empty struct type!")
345+
// Multiple fields can have the same offset if any of them are zero sized.
346+
// For example, in { i32, [0 x i32], i32 }, searching for offset 4 will stop
347+
// at the i32 element, because it is the last element at that offset. This is
348+
// the right one to return, because anything after it will have a higher
349+
// offset, implying that this element is non-empty.
350+
return self.memberOffsets.firstIndex(where: { $0 > offset }) ?? self.memberOffsets.endIndex
351+
}
352+
}
353+
158354
/// `ByteOrder` enumerates the ordering semantics of sequences of bytes on a
159355
/// particular target architecture.
160356
public enum ByteOrder {

0 commit comments

Comments
 (0)