From 5a4d51ca906e7587fee46a4140044bcc0b0746c5 Mon Sep 17 00:00:00 2001 From: Dimitri Racordon Date: Sat, 10 Mar 2018 02:28:04 +0100 Subject: [PATCH 01/10] Wrap LLVMAttributeRef API --- Sources/LLVM/Function+Attributes.swift | 262 +++++++++++++++++++++++++ 1 file changed, 262 insertions(+) create mode 100644 Sources/LLVM/Function+Attributes.swift diff --git a/Sources/LLVM/Function+Attributes.swift b/Sources/LLVM/Function+Attributes.swift new file mode 100644 index 00000000..254e2d0b --- /dev/null +++ b/Sources/LLVM/Function+Attributes.swift @@ -0,0 +1,262 @@ +#if SWIFT_PACKAGE +import cllvm +#endif + +/// Enumerates the attributes of LLVM functions. +public enum FunctionAttribute: String { + /// This attribute indicates that, when emitting the prologue and epilogue, + /// the backend should forcibly align the stack pointer. + case alignstack + /// This attribute indicates that the annotated function will always return + /// at least a given number of bytes (or null). + case allocsize + /// This attribute indicates that the inliner should attempt to inline this + /// function into callers whenever possible, ignoring any active inlining + /// size threshold for this caller. + case alwaysinline + /// This indicates that the callee function at a call site should be + /// recognized as a built-in function, even though the function’s declaration + /// uses the nobuiltin attribute + case builtin + /// This attribute indicates that this function is rarely called. + case cold + /// In some parallel execution models, there exist operations that cannot be + /// made control-dependent on any additional values. + case convergent + /// This attribute indicates that the function may only access memory that is + /// not accessible by the module being compiled. + case inaccessiblememonly + /// This attribute indicates that the function may only access memory that is + /// either not accessible by the module being compiled, or is pointed to by + /// its pointer arguments. + case inaccessiblemem_or_argmemonly + /// This attribute indicates that the source code contained a hint that inlin + /// inlining this function is desirable (such as the “inline” keyword in + /// C/C++). + case inlinehint + /// This attribute indicates that the function should be added to a + /// jump-instruction table at code-generation time, and that all + /// address-taken references to this function should be replaced with a + /// reference to the appropriate jump-instruction-table function pointer. + case jumptable + /// This attribute suggests that optimization passes and code generator + /// passes make choices that keep the code size of this function as small as + /// possible and perform optimizations that may sacrifice runtime performance + /// in order to minimize the size of the generated code. + case minsize + /// This attribute disables prologue / epilogue emission for the function. + case naked + /// When this attribute is set to true, the jump tables and lookup tables + /// that can be generated from a switch case lowering are disabled. + case no_jump_tables = "no-jump-tables" + /// This indicates that the callee function at a call site is not recognized + /// as a built-in function. + case nobuiltin + /// This attribute indicates that calls to the function cannot be duplicated. + case noduplicate + /// This attributes disables implicit floating point instructions. + case noimplicitfloat + /// This attribute indicates that the inliner should never inline this + /// function in any situation. + case noinline + /// This attribute suppresses lazy symbol binding for the function. + case nonlazybind + /// This attribute indicates that the code generator should not use a red + /// zone, even if the target-specific ABI normally permits it. + case noredzone + /// This function attribute indicates that the function never returns + /// normally. + case noreturn + /// This function attribute indicates that the function does not call itself + /// either directly or indirectly down any possible call path. + case norecurse + /// This function attribute indicates that the function never raises an + /// exception. + case nounwind + /// This function attribute indicates that most optimization passes will skip + /// this function, with the exception of interprocedural optimization passes. + case optnone + /// This attribute suggests that optimization passes and code generator + /// passes make choices that keep the code size of this function low, and + /// otherwise do optimizations specifically to reduce code size as long as + /// they do not significantly impact runtime performance. + case optsize + /// This attribute tells the code generator that the code generated for this + /// function needs to follow certain conventions that make it possible for a + /// runtime function to patch over it later. + case patchable_function = "patchable-function" + /// This attribute indicates that the function will trigger a guard region in + /// the end of the stack. + case probe_stack = "probe-stack" + /// This attribute indicates that the function computes its result (or + /// decides to unwind an exception) based strictly on its arguments, without + /// dereferencing any pointer arguments or otherwise accessing any mutable + /// state (e.g. memory, control registers, etc) visible to caller functions. + case readnone + /// This attribute indicates that the function does not write through any + /// pointer arguments (including byval arguments) or otherwise modify any + /// state (e.g. memory, control registers, etc) visible to caller functions. + case readonly + /// This attribute controls the behavior of stack probes: either the + /// "probe-stack" attribute, or ABI-required stack probes, if any. + case stack_proble_size = "stack-probe-size" + /// This attribute disables ABI-required stack probes, if any. + case no_stack_arg_probe = "no-stack-arg-probe" + /// This attribute indicates that the function may write to but does not read + /// read from memory. + case writeonly + /// This attribute indicates that the only memory accesses inside function + /// are loads and stores from objects pointed to by its pointer-typed + /// arguments, with arbitrary offsets. + case argmemonly + /// This attribute indicates that this function can return twice. + case returns_twice + /// This attribute indicates that SafeStack protection is enabled for this + /// function. + case safestack + /// This attribute indicates that AddressSanitizer checks (dynamic address + /// safety analysis) are enabled for this function. + case sanitize_address + /// This attribute indicates that MemorySanitizer checks (dynamic detection + /// of accesses to uninitialized memory) are enabled for this function. + case sanitize_memory + /// This attribute indicates that ThreadSanitizer checks (dynamic thread + /// safety analysis) are enabled for this function. + case sanitize_thread + /// This attribute indicates that HWAddressSanitizer checks (dynamic address + /// safety analysis based on tagged pointers) are enabled for this function. + case sanitize_hwaddress + /// This function attribute indicates that the function does not have any + /// effects besides calculating its result and does not have undefined + /// behavior. + case speculatable + /// This attribute indicates that the function should emit a stack smashing + /// protector. + case ssp + /// This attribute indicates that the function should always emit a stack + /// smashing protector + case sspreq + /// This attribute indicates that the function should emit a stack smashing + /// protector. + case sspstrong + /// This attribute indicates that the function was called from a scope that + /// requires strict floating point semantics. + case strictfp + /// This attribute indicates that the function will delegate to some other + /// function with a tail call. + case thunk + /// This attribute indicates that the ABI being targeted requires that an + /// unwind table entry be produced for this function even if we can show + /// that no exceptions passes by it. + case uwtable + + /// ID of the attribute. + internal var kindID: UInt32 { + return LLVMGetEnumAttributeKindForName(rawValue, rawValue.count) + } +} + +/// Enumerates the parameter attributes of LLVM functions. +public enum ParameterAttribute: String { + /// This indicates to the code generator that the parameter or return value + /// should be zero-extended to the extent required by the target’s ABI by the + /// caller (for a parameter) or the callee (for a return value). + case zeroext + /// This indicates to the code generator that the parameter or return value + /// should be sign-extended to the extent required by the target’s ABI (which + /// is usually 32-bits) by the caller (for a parameter) or the callee (for a + /// return value). + case signext + /// This indicates that this parameter or return value should be treated in a + /// special target-dependent fashion while emitting code for a function call + /// or return. + case inreg + /// This indicates that the pointer parameter should really be passed by + /// value to the function. + case byval + /// The inalloca argument attribute allows the caller to take the address of + /// outgoing stack arguments + case inalloca + /// This indicates that the pointer parameter specifies the address of a + /// structure that is the return value of the function in the source program. + case sret + /// This indicates that the pointer value may be assumed by the optimizer to + /// have the specified alignment. + case align + /// This indicates that objects accessed via pointer values based on the + /// argument or return value are not also accessed, during the execution of + /// the function, via pointer values not based on the argument or return + /// value. + case noalias + /// This indicates that the callee does not make any copies of the pointer + /// that outlive the callee itself. + case nocapture + /// This indicates that the pointer parameter can be excised using the + /// trampoline intrinsics. + case nest + /// This indicates that the function always returns the argument as its + /// return value. + case returned + /// This indicates that the parameter or return pointer is not null. + case nonnull + /// This indicates that the parameter or return pointer is dereferenceable. + case dereferenceable + /// This indicates that the parameter or return value isn’t both non-null and + /// non-dereferenceable (up to `n` bytes) at the same time. + case dereferenceable_or_null + /// This indicates that the parameter is the self/context parameter. + case swiftself + /// This attribute is motivated to model and optimize Swift error handling. + case swifterror + + /// ID of the attribute. + internal var kindID: UInt32 { + return LLVMGetEnumAttributeKindForName(rawValue, rawValue.count) + } +} + +extension Function { + /// Adds an attribute to the function. + /// + /// - parameter attr: The attribute to add. + /// - parameter value: The optional value of the attribute. + public func addAttribute(_ attr: FunctionAttribute, value: UInt64 = 0) { + let ctx = LLVMGetModuleContext(LLVMGetGlobalParent(llvm)) + let attrRef = LLVMCreateEnumAttribute(ctx, attr.kindID, value) + LLVMAddAttributeAtIndex(llvm, 0, attrRef) + } + + /// Adds an attribute to the given function's parameter. + /// + /// - parameter attr: The attribute to add. + /// - parameter value: The optional value of the attribute. + /// - parameterIndex: The index of the parameter to which add the + /// attribute, starting from 0. + public func addParameterAttribute(_ attr: ParameterAttribute, value: UInt64 = 0, + for parameterIndex: LLVMAttributeIndex) { + let ctx = LLVMGetModuleContext(LLVMGetGlobalParent(llvm)) + let attrRef = LLVMCreateEnumAttribute(ctx, attr.kindID, value) + LLVMAddAttributeAtIndex(llvm, parameterIndex + 1, attrRef) + } + + /// Removes an attribute from the function. + /// + /// - parameter attr: The attribute to remove. + public func removeAttribute(_ attr: FunctionAttribute, value: UInt64 = 0) { + LLVMRemoveEnumAttributeAtIndex(llvm, 0, attr.kindID) + } + + /// Removes an attribute from the function. + /// + /// - parameter attr: The attribute to add. + + /// Removes an attribute from the given function's parameter. + /// + /// - parameter attr: The attribute to remove. + /// - parameter parameterIndex: The index of the parameter to which add the + /// attribute, starting from 0. + public func removeParameterAttribute(_ attr: ParameterAttribute, + for parameterIndex: LLVMAttributeIndex) { + LLVMRemoveEnumAttributeAtIndex(llvm, parameterIndex + 1, attr.kindID) + } +} From 6ffe444162988d9d61fba1698219cb5290c54f3a Mon Sep 17 00:00:00 2001 From: Dimitri Racordon Date: Sat, 10 Mar 2018 14:13:38 +0100 Subject: [PATCH 02/10] Lift LLVMAttributeIndex with a raw representable enum --- Sources/LLVM/Function+Attributes.swift | 80 +++++++++++++------------- 1 file changed, 39 insertions(+), 41 deletions(-) diff --git a/Sources/LLVM/Function+Attributes.swift b/Sources/LLVM/Function+Attributes.swift index 254e2d0b..6d49f120 100644 --- a/Sources/LLVM/Function+Attributes.swift +++ b/Sources/LLVM/Function+Attributes.swift @@ -2,7 +2,7 @@ import cllvm #endif -/// Enumerates the attributes of LLVM functions. +/// Enumerates the attributes of LLVM functions and function parameters. public enum FunctionAttribute: String { /// This attribute indicates that, when emitting the prologue and epilogue, /// the backend should forcibly align the stack pointer. @@ -149,15 +149,6 @@ public enum FunctionAttribute: String { /// unwind table entry be produced for this function even if we can show /// that no exceptions passes by it. case uwtable - - /// ID of the attribute. - internal var kindID: UInt32 { - return LLVMGetEnumAttributeKindForName(rawValue, rawValue.count) - } -} - -/// Enumerates the parameter attributes of LLVM functions. -public enum ParameterAttribute: String { /// This indicates to the code generator that the parameter or return value /// should be zero-extended to the extent required by the target’s ABI by the /// caller (for a parameter) or the callee (for a return value). @@ -215,48 +206,55 @@ public enum ParameterAttribute: String { } } -extension Function { - /// Adds an attribute to the function. - /// - /// - parameter attr: The attribute to add. - /// - parameter value: The optional value of the attribute. - public func addAttribute(_ attr: FunctionAttribute, value: UInt64 = 0) { - let ctx = LLVMGetModuleContext(LLVMGetGlobalParent(llvm)) - let attrRef = LLVMCreateEnumAttribute(ctx, attr.kindID, value) - LLVMAddAttributeAtIndex(llvm, 0, attrRef) +/// Represents the possible indices of function attributes. +public enum AttributeIndex: ExpressibleByIntegerLiteral, RawRepresentable { + /// Represents the function itself. + case function + /// Represents the function's return value. + case returnValue + /// Represents the function's i-th argument. + case argument(Int) + + public init(integerLiteral value: UInt32) { + switch value { + case ~0: self = .function + case 0: self = .returnValue + default: self = .argument(Int(value) - 1) + } } - /// Adds an attribute to the given function's parameter. + public init?(rawValue: RawValue) { + self.init(integerLiteral: rawValue) + } + + public var rawValue: UInt32 { + switch self { + case .function: return ~0 + case .returnValue: return 0 + case .argument(let i): return UInt32(i) + 1 + } + } +} + +extension Function { + /// Adds an attribute to the function, its return value or its parameters. /// /// - parameter attr: The attribute to add. /// - parameter value: The optional value of the attribute. - /// - parameterIndex: The index of the parameter to which add the - /// attribute, starting from 0. - public func addParameterAttribute(_ attr: ParameterAttribute, value: UInt64 = 0, - for parameterIndex: LLVMAttributeIndex) { + /// - parameter index: The index representing the function, its return value + /// or one of its parameters. + public func addAttribute(_ attr: FunctionAttribute, value: UInt64 = 0, to index: AttributeIndex) { let ctx = LLVMGetModuleContext(LLVMGetGlobalParent(llvm)) let attrRef = LLVMCreateEnumAttribute(ctx, attr.kindID, value) - LLVMAddAttributeAtIndex(llvm, parameterIndex + 1, attrRef) - } - - /// Removes an attribute from the function. - /// - /// - parameter attr: The attribute to remove. - public func removeAttribute(_ attr: FunctionAttribute, value: UInt64 = 0) { - LLVMRemoveEnumAttributeAtIndex(llvm, 0, attr.kindID) + LLVMAddAttributeAtIndex(llvm, index.rawValue, attrRef) } /// Removes an attribute from the function. /// - /// - parameter attr: The attribute to add. - - /// Removes an attribute from the given function's parameter. - /// /// - parameter attr: The attribute to remove. - /// - parameter parameterIndex: The index of the parameter to which add the - /// attribute, starting from 0. - public func removeParameterAttribute(_ attr: ParameterAttribute, - for parameterIndex: LLVMAttributeIndex) { - LLVMRemoveEnumAttributeAtIndex(llvm, parameterIndex + 1, attr.kindID) + /// - parameter index: The index representing the function, its return value + /// or one of its parameters. + public func removeAttribute(_ attr: FunctionAttribute, value: UInt64 = 0, from index: AttributeIndex) { + LLVMRemoveEnumAttributeAtIndex(llvm, index.rawValue, attr.kindID) } } From 0b8e9bddb72a0b530ced09d9c0446d038d7570fc Mon Sep 17 00:00:00 2001 From: Dimitri Racordon Date: Sat, 10 Mar 2018 14:17:25 +0100 Subject: [PATCH 03/10] Rewrite snake_case to camelCase --- Sources/LLVM/Function+Attributes.swift | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/Sources/LLVM/Function+Attributes.swift b/Sources/LLVM/Function+Attributes.swift index 6d49f120..6df9fb28 100644 --- a/Sources/LLVM/Function+Attributes.swift +++ b/Sources/LLVM/Function+Attributes.swift @@ -29,7 +29,7 @@ public enum FunctionAttribute: String { /// This attribute indicates that the function may only access memory that is /// either not accessible by the module being compiled, or is pointed to by /// its pointer arguments. - case inaccessiblemem_or_argmemonly + case inaccessiblememOrArgmemonly = "inaccessiblemem_or_argmemonly" /// This attribute indicates that the source code contained a hint that inlin /// inlining this function is desirable (such as the “inline” keyword in /// C/C++). @@ -48,7 +48,7 @@ public enum FunctionAttribute: String { case naked /// When this attribute is set to true, the jump tables and lookup tables /// that can be generated from a switch case lowering are disabled. - case no_jump_tables = "no-jump-tables" + case noJumpTables = "no-jump-tables" /// This indicates that the callee function at a call site is not recognized /// as a built-in function. case nobuiltin @@ -84,10 +84,10 @@ public enum FunctionAttribute: String { /// This attribute tells the code generator that the code generated for this /// function needs to follow certain conventions that make it possible for a /// runtime function to patch over it later. - case patchable_function = "patchable-function" + case patchableFunction = "patchable-function" /// This attribute indicates that the function will trigger a guard region in /// the end of the stack. - case probe_stack = "probe-stack" + case probeStack = "probe-stack" /// This attribute indicates that the function computes its result (or /// decides to unwind an exception) based strictly on its arguments, without /// dereferencing any pointer arguments or otherwise accessing any mutable @@ -99,9 +99,9 @@ public enum FunctionAttribute: String { case readonly /// This attribute controls the behavior of stack probes: either the /// "probe-stack" attribute, or ABI-required stack probes, if any. - case stack_proble_size = "stack-probe-size" + case stackProbleSize = "stack-probe-size" /// This attribute disables ABI-required stack probes, if any. - case no_stack_arg_probe = "no-stack-arg-probe" + case noStackArgProbe = "no-stack-arg-probe" /// This attribute indicates that the function may write to but does not read /// read from memory. case writeonly @@ -110,22 +110,22 @@ public enum FunctionAttribute: String { /// arguments, with arbitrary offsets. case argmemonly /// This attribute indicates that this function can return twice. - case returns_twice + case returnsTwice = "returns_twice" /// This attribute indicates that SafeStack protection is enabled for this /// function. case safestack /// This attribute indicates that AddressSanitizer checks (dynamic address /// safety analysis) are enabled for this function. - case sanitize_address + case sanitizeAddress = "sanitize_address" /// This attribute indicates that MemorySanitizer checks (dynamic detection /// of accesses to uninitialized memory) are enabled for this function. - case sanitize_memory + case sanitizeMemory = "sanitize_memory" /// This attribute indicates that ThreadSanitizer checks (dynamic thread /// safety analysis) are enabled for this function. - case sanitize_thread + case sanitizeThread = "sanitize_thread" /// This attribute indicates that HWAddressSanitizer checks (dynamic address /// safety analysis based on tagged pointers) are enabled for this function. - case sanitize_hwaddress + case sanitizeHWAddress = "sanitize_hwaddress" /// This function attribute indicates that the function does not have any /// effects besides calculating its result and does not have undefined /// behavior. @@ -194,7 +194,7 @@ public enum FunctionAttribute: String { case dereferenceable /// This indicates that the parameter or return value isn’t both non-null and /// non-dereferenceable (up to `n` bytes) at the same time. - case dereferenceable_or_null + case dereferenceableOrNull = "dereferenceable_or_null" /// This indicates that the parameter is the self/context parameter. case swiftself /// This attribute is motivated to model and optimize Swift error handling. From f7116abbb14f9d93999fa6dfff99abc528db0bf7 Mon Sep 17 00:00:00 2001 From: Dimitri Racordon Date: Sat, 10 Mar 2018 21:27:45 +0100 Subject: [PATCH 04/10] Test `addAttribute` --- Tests/LLVMTests/IRAttributesSpec.swift | 80 ++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 Tests/LLVMTests/IRAttributesSpec.swift diff --git a/Tests/LLVMTests/IRAttributesSpec.swift b/Tests/LLVMTests/IRAttributesSpec.swift new file mode 100644 index 00000000..06b842bd --- /dev/null +++ b/Tests/LLVMTests/IRAttributesSpec.swift @@ -0,0 +1,80 @@ +import LLVM +import XCTest +import FileCheck +import Foundation + +class IRAttributesSpec : XCTestCase { + func testIRAttributes() { + XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["FNATTR"]) { + // FNATTR: ; ModuleID = '[[ModuleName:IRBuilderTest]]' + // FNATTR-NEXT: source_filename = "[[ModuleName]]" + let module = Module(name: "IRBuilderTest") + let builder = IRBuilder(module: module) + let fn = builder.addFunction("fn", + type: FunctionType(argTypes: [IntType.int32, IntType.int32], + returnType: IntType.int32)) + + // FNATTR: define i32 @fn(i32, i32) #0 { + fn.addAttribute(.nounwind, to: .function) + + // FNATTR-NEXT: entry: + let entry = fn.appendBasicBlock(named: "entry") + builder.positionAtEnd(of: entry) + // FNATTR-NEXT: ret i32 0 + builder.buildRet(IntType.int32.constant(0)) + // FNATTR-NEXT: } + // FNATTR: attributes #0 = { nounwind } + module.dump() + }) + + XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["RVATTR"]) { + // RVATTR: ; ModuleID = '[[ModuleName:IRBuilderTest]]' + // RVATTR-NEXT: source_filename = "[[ModuleName]]" + let module = Module(name: "IRBuilderTest") + let builder = IRBuilder(module: module) + let fn = builder.addFunction("fn", + type: FunctionType(argTypes: [IntType.int32, IntType.int32], + returnType: IntType.int32)) + + // RVATTR: define signext i32 @fn(i32, i32) { + fn.addAttribute(.signext, to: .returnValue) + + // RVATTR-NEXT: entry: + let entry = fn.appendBasicBlock(named: "entry") + builder.positionAtEnd(of: entry) + // RVATTR-NEXT: ret i32 0 + builder.buildRet(IntType.int32.constant(0)) + // RVATTR-NEXT: } + module.dump() + }) + + XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["ARGATTR"]) { + // ARGATTR: ; ModuleID = '[[ModuleName:IRBuilderTest]]' + // ARGATTR-NEXT: source_filename = "[[ModuleName]]" + let module = Module(name: "IRBuilderTest") + let builder = IRBuilder(module: module) + let fn = builder.addFunction("fn", + type: FunctionType(argTypes: [IntType.int32, IntType.int32], + returnType: IntType.int32)) + + // ARGATTR: define i32 @fn(i32 zeroext, i32 signext) { + fn.addAttribute(.zeroext, to: .argument(0)) + fn.addAttribute(.signext, to: .argument(1)) + + // ARGATTR-NEXT: entry: + let entry = fn.appendBasicBlock(named: "entry") + builder.positionAtEnd(of: entry) + // ARGATTR-NEXT: ret i32 0 + builder.buildRet(IntType.int32.constant(0)) + // ARGATTR-NEXT: } + module.dump() + }) + } + + #if !os(macOS) + static var allTests = testCase([ + ("testIRAttributes", testIRAttributes), + ]) + #endif +} + From b35f5bc8d2b3e3b2d76246b985257f2b33a09b72 Mon Sep 17 00:00:00 2001 From: Dimitri Racordon Date: Sun, 11 Mar 2018 00:21:30 +0100 Subject: [PATCH 05/10] Distinguish between enum and string attributes --- Sources/LLVM/Function+Attributes.swift | 164 +++++++++++++++++++++---- 1 file changed, 139 insertions(+), 25 deletions(-) diff --git a/Sources/LLVM/Function+Attributes.swift b/Sources/LLVM/Function+Attributes.swift index 6df9fb28..f4a24b91 100644 --- a/Sources/LLVM/Function+Attributes.swift +++ b/Sources/LLVM/Function+Attributes.swift @@ -2,8 +2,8 @@ import cllvm #endif -/// Enumerates the attributes of LLVM functions and function parameters. -public enum FunctionAttribute: String { +/// Enumerates the kinds of attributes of LLVM functions and function parameters. +public enum AttributeKind: String { /// This attribute indicates that, when emitting the prologue and epilogue, /// the backend should forcibly align the stack pointer. case alignstack @@ -81,13 +81,6 @@ public enum FunctionAttribute: String { /// otherwise do optimizations specifically to reduce code size as long as /// they do not significantly impact runtime performance. case optsize - /// This attribute tells the code generator that the code generated for this - /// function needs to follow certain conventions that make it possible for a - /// runtime function to patch over it later. - case patchableFunction = "patchable-function" - /// This attribute indicates that the function will trigger a guard region in - /// the end of the stack. - case probeStack = "probe-stack" /// This attribute indicates that the function computes its result (or /// decides to unwind an exception) based strictly on its arguments, without /// dereferencing any pointer arguments or otherwise accessing any mutable @@ -97,11 +90,6 @@ public enum FunctionAttribute: String { /// pointer arguments (including byval arguments) or otherwise modify any /// state (e.g. memory, control registers, etc) visible to caller functions. case readonly - /// This attribute controls the behavior of stack probes: either the - /// "probe-stack" attribute, or ABI-required stack probes, if any. - case stackProbleSize = "stack-probe-size" - /// This attribute disables ABI-required stack probes, if any. - case noStackArgProbe = "no-stack-arg-probe" /// This attribute indicates that the function may write to but does not read /// read from memory. case writeonly @@ -142,9 +130,6 @@ public enum FunctionAttribute: String { /// This attribute indicates that the function was called from a scope that /// requires strict floating point semantics. case strictfp - /// This attribute indicates that the function will delegate to some other - /// function with a tail call. - case thunk /// This attribute indicates that the ABI being targeted requires that an /// unwind table entry be produced for this function even if we can show /// that no exceptions passes by it. @@ -201,7 +186,7 @@ public enum FunctionAttribute: String { case swifterror /// ID of the attribute. - internal var kindID: UInt32 { + internal var id: UInt32 { return LLVMGetEnumAttributeKindForName(rawValue, rawValue.count) } } @@ -236,25 +221,154 @@ public enum AttributeIndex: ExpressibleByIntegerLiteral, RawRepresentable { } } +/// An LLVM attribute. +public protocol Attribute { + var name: String { get } + func asLLVM() -> LLVMAttributeRef +} + +/// An "enum" (a.k.a. target-independent) attribute. +public struct EnumAttribute: Attribute { + internal let llvm: LLVMAttributeRef + internal init(llvm: LLVMAttributeRef) { + self.llvm = llvm + } + + /// The kind ID of the attribute. + internal var kindID: UInt32 { + return LLVMGetEnumAttributeKind(llvm) + } + + /// The name of the attribute's kind. + public var name: String { + return "" + } + + /// The value of the attribute. + public var value: UInt64 { + return LLVMGetEnumAttributeValue(llvm) + } + + /// Retrieves the underlying LLVM attribute object. + public func asLLVM() -> LLVMAttributeRef { + return llvm + } +} + +/// A "string" (a.k.a. target-dependent) attribute. +public struct StringAttribute: Attribute { + internal let llvm: LLVMAttributeRef + internal init(llvm: LLVMAttributeRef) { + self.llvm = llvm + } + + /// The name of the attribute. + public var name: String { + var length: UInt32 = 0 + let cstring = LLVMGetStringAttributeKind(llvm, &length) + return String.init(cString: cstring!) + } + + /// The value of the attribute. + public var value: String { + var length: UInt32 = 0 + let cstring = LLVMGetStringAttributeValue(llvm, &length) + return String.init(cString: cstring!) + } + + /// Retrieves the underlying LLVM attribute object. + public func asLLVM() -> LLVMAttributeRef { + return llvm + } +} + extension Function { - /// Adds an attribute to the function, its return value or its parameters. + /// Adds an enum attribute to the function, its return value or one of its + /// parameters. /// - /// - parameter attr: The attribute to add. + /// - parameter attrKind: The kind of the attribute to add. /// - parameter value: The optional value of the attribute. /// - parameter index: The index representing the function, its return value /// or one of its parameters. - public func addAttribute(_ attr: FunctionAttribute, value: UInt64 = 0, to index: AttributeIndex) { + @discardableResult + public func addAttribute(_ attrKind: AttributeKind, value: UInt64 = 0, to index: AttributeIndex) -> EnumAttribute { let ctx = LLVMGetModuleContext(LLVMGetGlobalParent(llvm)) - let attrRef = LLVMCreateEnumAttribute(ctx, attr.kindID, value) + let attrRef = LLVMCreateEnumAttribute(ctx, attrKind.id, value) LLVMAddAttributeAtIndex(llvm, index.rawValue, attrRef) + return EnumAttribute(llvm: attrRef!) } - /// Removes an attribute from the function. + /// Adds a string attribute to the function, its return value or one of its + /// parameters. + /// + /// - parameter name: The name of the attribute to add. + /// - parameter value: The optional value of the attribute. + /// - parameter index: The index representing the function, its return value + /// or one of its parameters. + @discardableResult + public func addAttribute(_ name: String, value: String = "", to index: AttributeIndex) -> StringAttribute { + let ctx = LLVMGetModuleContext(LLVMGetGlobalParent(llvm)) + let attrRef = name.withCString { cname -> LLVMAttributeRef! in + return value.withCString { cvalue in + return LLVMCreateStringAttribute(ctx, cname, UInt32(name.count), cvalue, UInt32(value.count)) + } + } + LLVMAddAttributeAtIndex(llvm, index.rawValue, attrRef) + return StringAttribute(llvm: attrRef!) + } + + /// Removes an attribute from the function, its return value or one of its + /// parameters. /// /// - parameter attr: The attribute to remove. /// - parameter index: The index representing the function, its return value /// or one of its parameters. - public func removeAttribute(_ attr: FunctionAttribute, value: UInt64 = 0, from index: AttributeIndex) { - LLVMRemoveEnumAttributeAtIndex(llvm, index.rawValue, attr.kindID) + public func removeAttribute(_ attr: Attribute, from index: AttributeIndex) { + switch attr { + case let enumAttr as EnumAttribute: + LLVMRemoveEnumAttributeAtIndex(llvm, index.rawValue, enumAttr.kindID) + case let stringAttr as StringAttribute: + var length: UInt32 = 0 + let cstring = LLVMGetStringAttributeKind(stringAttr.llvm, &length) + LLVMRemoveStringAttributeAtIndex(llvm, index.rawValue, cstring, length) + default: + fatalError() + } + } + + /// Removes an enum attribute from the function, its return value or one of + /// its parameters. + /// + /// - parameter attr: The kind of the attribute to remove. + /// - parameter index: The index representing the function, its return value + /// or one of its parameters. + public func removeAttribute(_ attrKind: AttributeKind, from index: AttributeIndex) { + LLVMRemoveEnumAttributeAtIndex(llvm, index.rawValue, attrKind.id) + } + + /// Removes a string attribute from the function, its return value or one of + /// its parameters. + /// + /// - parameter name: The name of the attribute to remove. + /// - parameter index: The index representing the function, its return value + /// or one of its parameters. + public func removeAttribute(_ name: String, from index: AttributeIndex) { + name.withCString { + LLVMRemoveStringAttributeAtIndex(llvm, index.rawValue, $0, UInt32(name.count)) + } + } + + /// Gets the attributes of the function, its return value or its parameters. + public func attributes(at index: AttributeIndex) -> [Attribute] { + let attrCount = LLVMGetAttributeCountAtIndex(llvm, index.rawValue) + var attrRefs: [LLVMAttributeRef?] = Array(repeating: nil, count: Int(attrCount)) + LLVMGetAttributesAtIndex(llvm, index.rawValue, &attrRefs) + return attrRefs.map { attrRef -> Attribute in + if LLVMIsEnumAttribute(attrRef) != 0 { + return EnumAttribute(llvm: attrRef!) + } else { + return StringAttribute(llvm: attrRef!) + } + } } } From 880fbbe63ce66118b081196dcb8e62e89aae1a43 Mon Sep 17 00:00:00 2001 From: Dimitri Racordon Date: Sun, 11 Mar 2018 01:30:10 +0100 Subject: [PATCH 06/10] Test set/unset attributes --- Tests/LLVMTests/IRAttributesSpec.swift | 329 +++++++++++++++++++------ 1 file changed, 258 insertions(+), 71 deletions(-) diff --git a/Tests/LLVMTests/IRAttributesSpec.swift b/Tests/LLVMTests/IRAttributesSpec.swift index 06b842bd..0bcc01ce 100644 --- a/Tests/LLVMTests/IRAttributesSpec.swift +++ b/Tests/LLVMTests/IRAttributesSpec.swift @@ -4,77 +4,264 @@ import FileCheck import Foundation class IRAttributesSpec : XCTestCase { - func testIRAttributes() { - XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["FNATTR"]) { - // FNATTR: ; ModuleID = '[[ModuleName:IRBuilderTest]]' - // FNATTR-NEXT: source_filename = "[[ModuleName]]" - let module = Module(name: "IRBuilderTest") - let builder = IRBuilder(module: module) - let fn = builder.addFunction("fn", - type: FunctionType(argTypes: [IntType.int32, IntType.int32], - returnType: IntType.int32)) - - // FNATTR: define i32 @fn(i32, i32) #0 { - fn.addAttribute(.nounwind, to: .function) - - // FNATTR-NEXT: entry: - let entry = fn.appendBasicBlock(named: "entry") - builder.positionAtEnd(of: entry) - // FNATTR-NEXT: ret i32 0 - builder.buildRet(IntType.int32.constant(0)) - // FNATTR-NEXT: } - // FNATTR: attributes #0 = { nounwind } - module.dump() - }) - - XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["RVATTR"]) { - // RVATTR: ; ModuleID = '[[ModuleName:IRBuilderTest]]' - // RVATTR-NEXT: source_filename = "[[ModuleName]]" - let module = Module(name: "IRBuilderTest") - let builder = IRBuilder(module: module) - let fn = builder.addFunction("fn", - type: FunctionType(argTypes: [IntType.int32, IntType.int32], - returnType: IntType.int32)) - - // RVATTR: define signext i32 @fn(i32, i32) { - fn.addAttribute(.signext, to: .returnValue) - - // RVATTR-NEXT: entry: - let entry = fn.appendBasicBlock(named: "entry") - builder.positionAtEnd(of: entry) - // RVATTR-NEXT: ret i32 0 - builder.buildRet(IntType.int32.constant(0)) - // RVATTR-NEXT: } - module.dump() - }) - - XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["ARGATTR"]) { - // ARGATTR: ; ModuleID = '[[ModuleName:IRBuilderTest]]' - // ARGATTR-NEXT: source_filename = "[[ModuleName]]" - let module = Module(name: "IRBuilderTest") - let builder = IRBuilder(module: module) - let fn = builder.addFunction("fn", - type: FunctionType(argTypes: [IntType.int32, IntType.int32], - returnType: IntType.int32)) - - // ARGATTR: define i32 @fn(i32 zeroext, i32 signext) { - fn.addAttribute(.zeroext, to: .argument(0)) - fn.addAttribute(.signext, to: .argument(1)) - - // ARGATTR-NEXT: entry: - let entry = fn.appendBasicBlock(named: "entry") - builder.positionAtEnd(of: entry) - // ARGATTR-NEXT: ret i32 0 - builder.buildRet(IntType.int32.constant(0)) - // ARGATTR-NEXT: } - module.dump() - }) + func testIRAttributes() { + XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["FNATTR"]) { + // FNATTR: ; ModuleID = '[[ModuleName:IRBuilderTest]]' + // FNATTR-NEXT: source_filename = "[[ModuleName]]" + let module = Module(name: "IRBuilderTest") + let builder = IRBuilder(module: module) + let fn = builder.addFunction("fn", + type: FunctionType(argTypes: [IntType.int32, IntType.int32], + returnType: IntType.int32)) + + // FNATTR: define i32 @fn(i32, i32) #0 { + fn.addAttribute(.nounwind, to: .function) + + // FNATTR-NEXT: entry: + let entry = fn.appendBasicBlock(named: "entry") + builder.positionAtEnd(of: entry) + // FNATTR-NEXT: ret i32 0 + builder.buildRet(IntType.int32.constant(0)) + // FNATTR-NEXT: } + // FNATTR: attributes #0 = { nounwind } + module.dump() + }) + + XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["RVATTR"]) { + // RVATTR: ; ModuleID = '[[ModuleName:IRBuilderTest]]' + // RVATTR-NEXT: source_filename = "[[ModuleName]]" + let module = Module(name: "IRBuilderTest") + let builder = IRBuilder(module: module) + let fn = builder.addFunction("fn", + type: FunctionType(argTypes: [IntType.int32, IntType.int32], + returnType: IntType.int32)) + + // RVATTR: define signext i32 @fn(i32, i32) { + fn.addAttribute(.signext, to: .returnValue) + + // RVATTR-NEXT: entry: + let entry = fn.appendBasicBlock(named: "entry") + builder.positionAtEnd(of: entry) + // RVATTR-NEXT: ret i32 0 + builder.buildRet(IntType.int32.constant(0)) + // RVATTR-NEXT: } + module.dump() + }) + + XCTAssert(fileCheckOutput(of: .stderr, withPrefixes: ["ARGATTR"]) { + // ARGATTR: ; ModuleID = '[[ModuleName:IRBuilderTest]]' + // ARGATTR-NEXT: source_filename = "[[ModuleName]]" + let module = Module(name: "IRBuilderTest") + let builder = IRBuilder(module: module) + let i8ptr = PointerType(pointee: IntType.int8) + let fn = builder.addFunction("fn", + type: FunctionType(argTypes: [IntType.int32, i8ptr], + returnType: IntType.int32)) + + // ARGATTR: define i32 @fn(i32 zeroext, i8* align 8) { + fn.addAttribute(.zeroext, to: .argument(0)) + fn.addAttribute(.align, value: 8, to: .argument(1)) + + // ARGATTR-NEXT: entry: + let entry = fn.appendBasicBlock(named: "entry") + builder.positionAtEnd(of: entry) + // ARGATTR-NEXT: ret i32 0 + builder.buildRet(IntType.int32.constant(0)) + // ARGATTR-NEXT: } + module.dump() + }) + } + + func testSetUnsetFunctionAttributes() { + let module = Module(name: "FNATTR") + let builder = IRBuilder(module: module) + + let i8ptr = PointerType(pointee: IntType.int8) + let fn = builder.addFunction("fn", + type: FunctionType(argTypes: [i8ptr], returnType: i8ptr)) + + // MARK: Enum attributes + + var enumAttr: EnumAttribute + let fnAttrs: [AttributeKind: UInt64] = [ + .alignstack: 8, + .allocsize: 8, + .alwaysinline: 0, + .builtin: 0, + .cold: 0, + .convergent: 0, + .inaccessiblememonly: 0, + .inaccessiblememOrArgmemonly: 0, + .inlinehint: 0, + .jumptable: 0, + .minsize: 0, + .naked: 0, + .noJumpTables: 0, + .nobuiltin: 0, + .noduplicate: 0, + .noimplicitfloat: 0, + .noinline: 0, + .nonlazybind: 0, + .noredzone: 0, + .noreturn: 0, + .norecurse: 0, + .nounwind: 0, + .optnone: 0, + .optsize: 0, + .readnone: 0, + .readonly: 0, + .writeonly: 0, + .argmemonly: 0, + .returnsTwice: 0, + .safestack: 0, + .sanitizeAddress: 0, + .sanitizeMemory: 0, + .sanitizeThread: 0, + .sanitizeHWAddress: 0, + .speculatable: 0, + .ssp: 0, + .sspreq: 0, + .sspstrong: 0, + .strictfp: 0, + .uwtable: 0, + ] + + for (attrKind, value) in fnAttrs { + enumAttr = fn.addAttribute(attrKind, value: value, to: .function) + XCTAssertEqual(enumAttr.value, value) + XCTAssert(fn.attributes(at: .function).contains { $0.asLLVM() == enumAttr.asLLVM() }) + + // LLVM issue? + // Removing an integer attribute seems to always fail, because + // `AttributeList::removeAttribute` (Attributes.cpp:1096) attempts to add the + // attribute about to be removed without preserving its value. + guard value == 0 else { continue } + + fn.removeAttribute(enumAttr, from: .function) + XCTAssertFalse(fn.attributes(at: .function).contains { $0.asLLVM() == enumAttr.asLLVM() }) + + enumAttr = fn.addAttribute(attrKind, value: value, to: .function) + fn.removeAttribute(attrKind, from: .function) + XCTAssertFalse(fn.attributes(at: .function).contains { $0.asLLVM() == enumAttr.asLLVM() }) } - #if !os(macOS) - static var allTests = testCase([ - ("testIRAttributes", testIRAttributes), - ]) - #endif -} + // MARK: String attributes + + var stringAttr: StringAttribute + for (name, value) in [("foo", ""), ("foo", "bar")] { + stringAttr = fn.addAttribute(name, value: value, to: .function) + XCTAssertEqual(stringAttr.name, name) + XCTAssertEqual(stringAttr.value, value) + XCTAssert(fn.attributes(at: .function).contains { $0.asLLVM() == stringAttr.asLLVM() }) + + fn.removeAttribute(stringAttr, from: .function) + XCTAssertFalse(fn.attributes(at: .function).contains { $0.asLLVM() == stringAttr.asLLVM() }) + + stringAttr = fn.addAttribute(name, to: .function) + fn.removeAttribute(name, from: .function) + XCTAssertFalse(fn.attributes(at: .function).contains { $0.asLLVM() == stringAttr.asLLVM() }) + } + } + + func testSetUnsetArgumentAttributes() { + let module = Module(name: "ARGATTR") + let builder = IRBuilder(module: module) + + let i8ptr = PointerType(pointee: IntType.int8) + let fn = builder.addFunction("fn", + type: FunctionType(argTypes: [i8ptr], returnType: i8ptr)) + // MARK: Enum attributes + + var enumAttr: EnumAttribute + let argAttrs: [AttributeKind: UInt64] = [ + .zeroext: 0, + .signext: 0, + .inreg: 0, + .byval: 0, + .inalloca: 0, + .sret: 0, + .align: 8, + .noalias: 0, + .nocapture: 0, + .nest: 0, + .returned: 0, + .nonnull: 0, + .dereferenceable: 8, + .dereferenceableOrNull: 8, + .swiftself: 0, + .swifterror: 0, + ] + + for (attrKind, value) in argAttrs { + enumAttr = fn.addAttribute(attrKind, value: value, to: .returnValue) + XCTAssertEqual(enumAttr.value, value) + XCTAssert(fn.attributes(at: .returnValue).contains { $0.asLLVM() == enumAttr.asLLVM() }) + + // LLVM issue? + guard value == 0 else { continue } + + fn.removeAttribute(enumAttr, from: .returnValue) + XCTAssertFalse(fn.attributes(at: .returnValue).contains { $0.asLLVM() == enumAttr.asLLVM() }) + + enumAttr = fn.addAttribute(attrKind, value: value, to: .returnValue) + fn.removeAttribute(attrKind, from: .returnValue) + XCTAssertFalse(fn.attributes(at: .returnValue).contains { $0.asLLVM() == enumAttr.asLLVM() }) + } + + for (attrKind, value) in argAttrs { + enumAttr = fn.addAttribute(attrKind, value: value, to: .argument(0)) + XCTAssertEqual(enumAttr.value, value) + XCTAssert(fn.attributes(at: .argument(0)).contains { $0.asLLVM() == enumAttr.asLLVM() }) + + // LLVM issue? + guard value == 0 else { continue } + + fn.removeAttribute(enumAttr, from: .argument(0)) + XCTAssertFalse(fn.attributes(at: .argument(0)).contains { $0.asLLVM() == enumAttr.asLLVM() }) + + enumAttr = fn.addAttribute(attrKind, value: value, to: .argument(0)) + fn.removeAttribute(attrKind, from: .argument(0)) + XCTAssertFalse(fn.attributes(at: .argument(0)).contains { $0.asLLVM() == enumAttr.asLLVM() }) + } + + // MARK: String attributes + + var stringAttr: StringAttribute + for (name, value) in [("foo", ""), ("foo", "bar")] { + stringAttr = fn.addAttribute(name, value: value, to: .returnValue) + XCTAssertEqual(stringAttr.name, name) + XCTAssertEqual(stringAttr.value, value) + XCTAssert(fn.attributes(at: .returnValue).contains { $0.asLLVM() == stringAttr.asLLVM() }) + + fn.removeAttribute(stringAttr, from: .returnValue) + XCTAssertFalse(fn.attributes(at: .returnValue).contains { $0.asLLVM() == stringAttr.asLLVM() }) + + stringAttr = fn.addAttribute(name, to: .returnValue) + fn.removeAttribute(name, from: .returnValue) + XCTAssertFalse(fn.attributes(at: .returnValue).contains { $0.asLLVM() == stringAttr.asLLVM() }) + } + + for (name, value) in [("foo", ""), ("foo", "bar")] { + stringAttr = fn.addAttribute(name, value: value, to: .argument(0)) + XCTAssertEqual(stringAttr.name, name) + XCTAssertEqual(stringAttr.value, value) + XCTAssert(fn.attributes(at: .argument(0)).contains { $0.asLLVM() == stringAttr.asLLVM() }) + + fn.removeAttribute(stringAttr, from: .argument(0)) + XCTAssertFalse(fn.attributes(at: .argument(0)).contains { $0.asLLVM() == stringAttr.asLLVM() }) + + stringAttr = fn.addAttribute(name, to: .argument(0)) + fn.removeAttribute(name, from: .argument(0)) + XCTAssertFalse(fn.attributes(at: .argument(0)).contains { $0.asLLVM() == stringAttr.asLLVM() }) + } + } + + #if !os(macOS) + static var allTests = testCase([ + ("testIRAttributes", testIRAttributes), + ("testSetUnsetFunctionAttributes", testSetUnsetFunctionAttributes), + ("testSetUnsetArgumentAttributes", testSetUnsetArgumentAttributes), + ]) + #endif +} From f0485c6fac680dd2838e83e5ca6e190f850ca5db Mon Sep 17 00:00:00 2001 From: Dimitri Racordon Date: Sun, 11 Mar 2018 23:30:31 +0100 Subject: [PATCH 07/10] Remove `name` property requirements from `Attribute` --- Sources/LLVM/Function+Attributes.swift | 6 ------ 1 file changed, 6 deletions(-) diff --git a/Sources/LLVM/Function+Attributes.swift b/Sources/LLVM/Function+Attributes.swift index f4a24b91..8d43ae87 100644 --- a/Sources/LLVM/Function+Attributes.swift +++ b/Sources/LLVM/Function+Attributes.swift @@ -223,7 +223,6 @@ public enum AttributeIndex: ExpressibleByIntegerLiteral, RawRepresentable { /// An LLVM attribute. public protocol Attribute { - var name: String { get } func asLLVM() -> LLVMAttributeRef } @@ -239,11 +238,6 @@ public struct EnumAttribute: Attribute { return LLVMGetEnumAttributeKind(llvm) } - /// The name of the attribute's kind. - public var name: String { - return "" - } - /// The value of the attribute. public var value: UInt64 { return LLVMGetEnumAttributeValue(llvm) From 0741b134e039d569eb4ac3d90baeb06d5910d725 Mon Sep 17 00:00:00 2001 From: Dimitri Racordon Date: Sun, 11 Mar 2018 23:31:09 +0100 Subject: [PATCH 08/10] Provide an explanation for failure to cast attribute --- Sources/LLVM/Function+Attributes.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/LLVM/Function+Attributes.swift b/Sources/LLVM/Function+Attributes.swift index 8d43ae87..7b4eb246 100644 --- a/Sources/LLVM/Function+Attributes.swift +++ b/Sources/LLVM/Function+Attributes.swift @@ -326,7 +326,7 @@ extension Function { let cstring = LLVMGetStringAttributeKind(stringAttr.llvm, &length) LLVMRemoveStringAttributeAtIndex(llvm, index.rawValue, cstring, length) default: - fatalError() + fatalError("unexpected attribute type") } } From 71095452fdfa5358bf19b686d3a465b8760da2d0 Mon Sep 17 00:00:00 2001 From: Dimitri Racordon Date: Sun, 11 Mar 2018 23:40:05 +0100 Subject: [PATCH 09/10] Add some documentation --- Sources/LLVM/Function+Attributes.swift | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Sources/LLVM/Function+Attributes.swift b/Sources/LLVM/Function+Attributes.swift index 7b4eb246..bb4b8193 100644 --- a/Sources/LLVM/Function+Attributes.swift +++ b/Sources/LLVM/Function+Attributes.swift @@ -222,7 +222,13 @@ public enum AttributeIndex: ExpressibleByIntegerLiteral, RawRepresentable { } /// An LLVM attribute. +/// +/// Functions, return types and each parameter may have a set of attributes +/// associated with them. Such attributes are used to communicate additional +/// information about a function. Attributes are considered to be part of the +/// function, but not of the function type. public protocol Attribute { + /// Retrieves the underlying LLVM attribute reference object. func asLLVM() -> LLVMAttributeRef } From ee76920f370270688747ef4ee4647ac7a271f9c1 Mon Sep 17 00:00:00 2001 From: Dimitri Racordon Date: Mon, 12 Mar 2018 00:04:47 +0100 Subject: [PATCH 10/10] Add an error message when trying to remove valued enum attributes The current version of LLVM crashes when attempting to remove valued enum attributes. The bug was discovered and fixed in LLVM 7 (see https://github.com/llvm-mirror/llvm/commit/9bc0b10). The guard placed in `removeAttribute` should be removed once this version is released. --- Sources/LLVM/Function+Attributes.swift | 11 +++++++++++ Tests/LLVMTests/IRAttributesSpec.swift | 9 +++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/Sources/LLVM/Function+Attributes.swift b/Sources/LLVM/Function+Attributes.swift index bb4b8193..47e74f0f 100644 --- a/Sources/LLVM/Function+Attributes.swift +++ b/Sources/LLVM/Function+Attributes.swift @@ -343,6 +343,17 @@ extension Function { /// - parameter index: The index representing the function, its return value /// or one of its parameters. public func removeAttribute(_ attrKind: AttributeKind, from index: AttributeIndex) { + // FIXME: Remove when LLVM 7.0.0 is released. + guard attrKind != .align + && attrKind != .alignstack + && attrKind != .allocsize + && attrKind != .dereferenceable + && attrKind != .dereferenceableOrNull else { + fatalError( + "Removing valued enum attributes crashes in LLVM <7.0.0 " + + "and is currently disabled in LLVMSwift.") + } + LLVMRemoveEnumAttributeAtIndex(llvm, index.rawValue, attrKind.id) } diff --git a/Tests/LLVMTests/IRAttributesSpec.swift b/Tests/LLVMTests/IRAttributesSpec.swift index 0bcc01ce..4aa79f55 100644 --- a/Tests/LLVMTests/IRAttributesSpec.swift +++ b/Tests/LLVMTests/IRAttributesSpec.swift @@ -131,10 +131,7 @@ class IRAttributesSpec : XCTestCase { XCTAssertEqual(enumAttr.value, value) XCTAssert(fn.attributes(at: .function).contains { $0.asLLVM() == enumAttr.asLLVM() }) - // LLVM issue? - // Removing an integer attribute seems to always fail, because - // `AttributeList::removeAttribute` (Attributes.cpp:1096) attempts to add the - // attribute about to be removed without preserving its value. + // FIXME: Remove when LLVM 7.0.0 is released. guard value == 0 else { continue } fn.removeAttribute(enumAttr, from: .function) @@ -198,7 +195,7 @@ class IRAttributesSpec : XCTestCase { XCTAssertEqual(enumAttr.value, value) XCTAssert(fn.attributes(at: .returnValue).contains { $0.asLLVM() == enumAttr.asLLVM() }) - // LLVM issue? + // FIXME: Remove when LLVM 7.0.0 is released. guard value == 0 else { continue } fn.removeAttribute(enumAttr, from: .returnValue) @@ -214,7 +211,7 @@ class IRAttributesSpec : XCTestCase { XCTAssertEqual(enumAttr.value, value) XCTAssert(fn.attributes(at: .argument(0)).contains { $0.asLLVM() == enumAttr.asLLVM() }) - // LLVM issue? + // FIXME: Remove when LLVM 7.0.0 is released. guard value == 0 else { continue } fn.removeAttribute(enumAttr, from: .argument(0))