diff --git a/spec/std/atomic_spec.cr b/spec/std/atomic_spec.cr new file mode 100644 index 000000000000..2b0d6d18746a --- /dev/null +++ b/spec/std/atomic_spec.cr @@ -0,0 +1,130 @@ +# TODO: enable after 0.19.3 +# require "spec" + +# describe Atomic do +# it "compares and sets with integer" do +# atomic = Atomic.new(1) + +# atomic.compare_and_set(2, 3).should eq({1, false}) +# atomic.get.should eq(1) + +# atomic.compare_and_set(1, 3).should eq({1, true}) +# atomic.get.should eq(3) +# end + +# it "compares and sets with nilable type" do +# atomic = Atomic(String?).new(nil) +# string = "hello" + +# atomic.compare_and_set(string, "foo").should eq({nil, false}) +# atomic.get.should be_nil + +# atomic.compare_and_set(nil, string).should eq({nil, true}) +# atomic.get.should be(string) + +# atomic.compare_and_set(string, nil).should eq({string, true}) +# atomic.get.should be_nil +# end + +# it "compares and sets with reference type" do +# str1 = "hello" +# str2 = "bye" + +# atomic = Atomic(String).new(str1) + +# atomic.compare_and_set(str2, "foo").should eq({str1, false}) +# atomic.get.should eq(str1) + +# atomic.compare_and_set(str1, str2).should eq({str1, true}) +# atomic.get.should be(str2) + +# atomic.compare_and_set(str2, str1).should eq({str2, true}) +# atomic.get.should be(str1) +# end + +# it "#adds" do +# atomic = Atomic.new(1) +# atomic.add(2).should eq(1) +# atomic.get.should eq(3) +# end + +# it "#sub" do +# atomic = Atomic.new(1) +# atomic.sub(2).should eq(1) +# atomic.get.should eq(-1) +# end + +# it "#and" do +# atomic = Atomic.new(5) +# atomic.and(3).should eq(5) +# atomic.get.should eq(1) +# end + +# it "#nand" do +# atomic = Atomic.new(5) +# atomic.nand(3).should eq(5) +# atomic.get.should eq(-2) +# end + +# it "#or" do +# atomic = Atomic.new(5) +# atomic.or(2).should eq(5) +# atomic.get.should eq(7) +# end + +# it "#xor" do +# atomic = Atomic.new(5) +# atomic.xor(3).should eq(5) +# atomic.get.should eq(6) +# end + +# it "#max with signed" do +# atomic = Atomic.new(5) +# atomic.max(2).should eq(5) +# atomic.get.should eq(5) +# atomic.max(10).should eq(5) +# atomic.get.should eq(10) +# end + +# it "#max with unsigned" do +# atomic = Atomic.new(5_u32) +# atomic.max(2_u32).should eq(5_u32) +# atomic.get.should eq(5_u32) +# atomic.max(UInt32::MAX).should eq(5_u32) +# atomic.get.should eq(UInt32::MAX) +# end + +# it "#min with signed" do +# atomic = Atomic.new(5) +# atomic.min(10).should eq(5) +# atomic.get.should eq(5) +# atomic.min(2).should eq(5) +# atomic.get.should eq(2) +# end + +# it "#min with unsigned" do +# atomic = Atomic.new(UInt32::MAX) +# atomic.min(10_u32).should eq(UInt32::MAX) +# atomic.get.should eq(10_u32) +# atomic.min(15_u32).should eq(10_u32) +# atomic.get.should eq(10_u32) +# end + +# it "#set" do +# atomic = Atomic.new(1) +# atomic.set(2).should eq(2) +# atomic.get.should eq(2) +# end + +# it "#lazy_set" do +# atomic = Atomic.new(1) +# atomic.lazy_set(2).should eq(2) +# atomic.get.should eq(2) +# end + +# it "#swap" do +# atomic = Atomic.new(1) +# atomic.swap(2).should eq(1) +# atomic.get.should eq(2) +# end +# end diff --git a/src/atomic.cr b/src/atomic.cr new file mode 100644 index 000000000000..44cbeab2c7fe --- /dev/null +++ b/src/atomic.cr @@ -0,0 +1,219 @@ +# A value that may be updated atomically. +# +# Only primitive integer types, reference types or nilable reference types +# can be used with an Atomic type. +struct Atomic(T) + # Creates an Atomic with the given initial value. + def initialize(@value : T) + {% if !T.union? && (T == Char || T < Int::Primitive) %} + # Support integer types or char (because it's represented as an integer) + {% elsif T < Reference || (T.union? && T.union_types.all? { |t| t == Nil || t < Reference }) %} + # Support reference types, or union types with only nil or reference types + {% else %} + {{ raise "Can only create Atomic with primitive integer types, reference types or nilable reference types, not #{T}" }} + {% end %} + end + + # Compares this atomic's value with *cmp*: + # + # * if they are equal, sets the value to *new*, and returns `{old_value, true}` + # * if they are not equal the value remains the same, and returns `{old_value, false}` + # + # ``` + # atomic = Atomic.new(1) + # + # atomic.compare_and_set(2, 3) # => {1, false} + # atomic.value # => 1 + # + # atomic.compare_and_set(1, 3) # => {1, true} + # atomic.value # => 3 + # ``` + def compare_and_set(cmp : T, new : T) : {T, Bool} + # Check if it's a nilable reference type + {% if T.union? && T.union_types.all? { |t| t == Nil || t < Reference } %} + # If so, use addresses because LLVM < 3.9 doesn't support cmpxchg with pointers + address, success = Ops.cmpxchg(pointerof(@value).as(LibC::SizeT*), LibC::SizeT.new(cmp.as(T).object_id), LibC::SizeT.new(new.as(T).object_id), :sequentially_consistent, :sequentially_consistent) + {address == 0 ? nil : Pointer(T).new(address).as(T), success} + # Check if it's a reference type + {% elsif T < Reference %} + # Use addresses again (but this can't return nil) + address, success = Ops.cmpxchg(pointerof(@value).as(LibC::SizeT*), LibC::SizeT.new(cmp.as(T).object_id), LibC::SizeT.new(new.as(T).object_id), :sequentially_consistent, :sequentially_consistent) + {Pointer(T).new(address).as(T), success} + {% else %} + # Otherwise, this is an integer type + Ops.cmpxchg(pointerof(@value), cmp, new, :sequentially_consistent, :sequentially_consistent) + {% end %} + end + + # Performs `atomic_value += value`. Returns the old value. + # + # ``` + # atomic = Atomic.new(1) + # atomic.add(2) # => 2 + # atomic.value # => 3 + # ``` + def add(value : T) + Ops.atomicrmw(:add, pointerof(@value), value, :sequentially_consistent, false) + end + + # Performs `atomic_value -= value`. Returns the old value. + # + # ``` + # atomic = Atomic.new(9) + # atomic.sub(2) # => 9 + # atomic.value # => 7 + # ``` + def sub(value : T) + Ops.atomicrmw(:sub, pointerof(@value), value, :sequentially_consistent, false) + end + + # Performs `atomic_value &= value`. Returns the old value. + # + # ``` + # atomic = Atomic.new(5) + # atomic.and(3) # => 5 + # atomic.value # => 1 + # ``` + def and(value : T) + Ops.atomicrmw(:and, pointerof(@value), value, :sequentially_consistent, false) + end + + # Performs `atomic_value = ~(atomic_value & value)`. Returns the old value. + # + # ``` + # atomic = Atomic.new(5) + # atomic.nand(3) # => 5 + # atomic.value # => -2 + # ``` + def nand(value : T) + Ops.atomicrmw(:nand, pointerof(@value), value, :sequentially_consistent, false) + end + + # Performs `atomic_value |= value`. Returns the old value. + # + # ``` + # atomic = Atomic.new(5) + # atomic.or(2) # => 5 + # atomic.value # => 7 + # ``` + def or(value : T) + Ops.atomicrmw(:or, pointerof(@value), value, :sequentially_consistent, false) + end + + # Performs `atomic_value ^= value`. Returns the old value. + # + # ``` + # atomic = Atomic.new(5) + # atomic.or(3) # => 5 + # atomic.value # => 6 + # ``` + def xor(value : T) + Ops.atomicrmw(:xor, pointerof(@value), value, :sequentially_consistent, false) + end + + # Performs `atomic_value = max(atomic_value, value)`. Returns the old value. + # + # ``` + # atomic = Atomic.new(5) + # + # atomic.max(3) # => 5 + # atomic.value # => 5 + # + # atomic.max(10) # => 5 + # atomic.value # => 10 + # ``` + def max(value : T) + {% if T < Int::Signed %} + Ops.atomicrmw(:max, pointerof(@value), value, :sequentially_consistent, false) + {% else %} + Ops.atomicrmw(:umax, pointerof(@value), value, :sequentially_consistent, false) + {% end %} + end + + # Performs `atomic_value = min(atomic_value, value)`. Returns the old value. + # + # ``` + # atomic = Atomic.new(5) + # + # atomic.min(10) # => 5 + # atomic.value # => 5 + # + # atomic.min(3) # => 5 + # atomic.value # => 3 + # ``` + def min(value : T) + {% if T < Int::Signed %} + Ops.atomicrmw(:min, pointerof(@value), value, :sequentially_consistent, false) + {% else %} + Ops.atomicrmw(:umin, pointerof(@value), value, :sequentially_consistent, false) + {% end %} + end + + # Atomically sets this atomic's value to *value*. Returns the **old** value. + # + # ``` + # atomic = Atomic.new(5) + # atomic.set(10) # => 5 + # atomic.value # => 10 + # ``` + def swap(value : T) + Ops.atomicrmw(:xchg, pointerof(@value), value, :sequentially_consistent, false) + end + + # Atomically sets this atomic's value to *value*. Returns the **new** value. + # + # ``` + # atomic = Atomic.new(5) + # atomic.set(10) # => 10 + # atomic.value # => 10 + # ``` + def set(value : T) + Ops.store(pointerof(@value), value, :sequentially_consistent, true) + value + end + + # **Non-atomically** sets this atomic's value to *value*. Returns the **new** value. + # + # ``` + # atomic = Atomic.new(5) + # atomic.lazy_set(10) # => 10 + # atomic.value # => 10 + # ``` + def lazy_set(@value : T) + end + + # Atomically returns this atomic's value. + def get + Ops.load(pointerof(@value), :sequentially_consistent, true) + end + + # **Non-atomically* returns this atomic's value. + def lazy_get + @value + end + + # :nodoc: + module Ops + # Defines methods that directly map to LLVM instructions related to atomic operations. + + @[Primitive(:cmpxchg)] + def self.cmpxchg(ptr : T*, cmp : T, new : T, success_ordering : Symbol, failure_ordering : Symbol) : {T, Bool} forall T + end + + @[Primitive(:atomicrmw)] + def self.atomicrmw(op : Symbol, ptr : T*, val : T, ordering : Symbol, singlethread : Bool) : T forall T + end + + @[Primitive(:fence)] + def self.fence(ordering : Symbol, singlethread : Bool) : Nil + end + + @[Primitive(:load_atomic)] + def self.load(ptr : T*, ordering : Symbol, volatile : Bool) : T forall T + end + + @[Primitive(:store_atomic)] + def self.store(ptr : T*, value : T, ordering : Symbol, volatile : Bool) : Nil forall T + end + end +end diff --git a/src/compiler/crystal/codegen/call.cr b/src/compiler/crystal/codegen/call.cr index 33b8c9998131..2a35ab64d782 100644 --- a/src/compiler/crystal/codegen/call.cr +++ b/src/compiler/crystal/codegen/call.cr @@ -378,7 +378,7 @@ class Crystal::CodeGenVisitor # Change context type: faster then creating a new context old_type = context.type context.type = self_type - codegen_primitive(body, target_def, call_args) + codegen_primitive(node, body, target_def, call_args) context.type = old_type return true end diff --git a/src/compiler/crystal/codegen/primitives.cr b/src/compiler/crystal/codegen/primitives.cr index fede422bccc7..eee38d663b32 100644 --- a/src/compiler/crystal/codegen/primitives.cr +++ b/src/compiler/crystal/codegen/primitives.cr @@ -13,7 +13,7 @@ class Crystal::CodeGenVisitor end end - def codegen_primitive(node, target_def, call_args) + def codegen_primitive(call, node, target_def, call_args) @last = case node.name when "binary" codegen_primitive_binary node, target_def, call_args @@ -57,6 +57,16 @@ class Crystal::CodeGenVisitor codegen_primitive_tuple_indexer_known_index node, target_def, call_args when "enum_value", "enum_new" call_args[0] + when "cmpxchg" + codegen_primitive_cmpxchg call, node, target_def, call_args + when "atomicrmw" + codegen_primitive_atomicrmw call, node, target_def, call_args + when "fence" + codegen_primitive_fence call, node, target_def, call_args + when "load_atomic" + codegen_primitive_load_atomic call, node, target_def, call_args + when "store_atomic" + codegen_primitive_store_atomic call, node, target_def, call_args else raise "Bug: unhandled primitive in codegen: #{node.name}" end @@ -745,4 +755,104 @@ class Crystal::CodeGenVisitor value end end + + def codegen_primitive_cmpxchg(call, node, target_def, call_args) + success_ordering = atomic_ordering_from_symbol_literal(call.args[-2]) + failure_ordering = atomic_ordering_from_symbol_literal(call.args[-1]) + + pointer, cmp, new = call_args + value = builder.cmpxchg(pointer, cmp, new, success_ordering, failure_ordering) + value_ptr = alloca llvm_type(node.type) + store extract_value(value, 0), gep(value_ptr, 0, 0) + store extract_value(value, 1), gep(value_ptr, 0, 1) + value_ptr + end + + def codegen_primitive_atomicrmw(call, node, target_def, call_args) + op = atomicrwm_bin_op_from_symbol_literal(call.args[0]) + ordering = atomic_ordering_from_symbol_literal(call.args[-2]) + singlethread = bool_from_bool_literal(call.args[-1]) + + _, pointer, val = call_args + builder.atomicrmw(op, pointer, val, ordering, singlethread) + end + + def codegen_primitive_fence(call, node, target_def, call_args) + ordering = atomic_ordering_from_symbol_literal(call.args[0]) + singlethread = bool_from_bool_literal(call.args[1]) + + builder.fence(ordering, singlethread) + llvm_nil + end + + def codegen_primitive_load_atomic(call, node, target_def, call_args) + ordering = atomic_ordering_from_symbol_literal(call.args[-2]) + volatile = bool_from_bool_literal(call.args[-1]) + + ptr = call_args.first + + inst = builder.load(ptr) + inst.ordering = ordering + inst.volatile = true if volatile + set_alignment inst, node.type + inst + end + + def codegen_primitive_store_atomic(call, node, target_def, call_args) + ordering = atomic_ordering_from_symbol_literal(call.args[-2]) + volatile = bool_from_bool_literal(call.args[-1]) + + ptr, value = call_args + + inst = builder.store(value, ptr) + inst.ordering = ordering + inst.volatile = true if volatile + set_alignment inst, node.type + inst + end + + def set_alignment(inst, type) + case type + when IntegerType, FloatType + inst.alignment = type.bytes + when CharType + inst.alignment = 4 + else + inst.alignment = @program.has_flag?("x86_64") ? 8 : 4 + end + end + + def atomic_ordering_from_symbol_literal(node) + unless node.is_a?(SymbolLiteral) + node.raise "Bug: expected symbol literal" + end + + ordering = LLVM::AtomicOrdering.parse?(node.value) + unless ordering + node.raise "unknown atomic ordering: #{node.value}" + end + + ordering + end + + def atomicrwm_bin_op_from_symbol_literal(node) + unless node.is_a?(SymbolLiteral) + node.raise "Bug: expected symbol literal" + end + + op = LLVM::AtomicRMWBinOp.parse?(node.value) + unless op + node.raise "unknown atomic rwm bin op: #{node.value}" + end + + op + end + + def bool_from_bool_literal(node) + unless node.is_a?(BoolLiteral) + node.raise "Bug: expected bool literal" + end + + node.value + end end diff --git a/src/compiler/crystal/semantic/main_visitor.cr b/src/compiler/crystal/semantic/main_visitor.cr index bde67955ee04..af4cbe5f3020 100644 --- a/src/compiler/crystal/semantic/main_visitor.cr +++ b/src/compiler/crystal/semantic/main_visitor.cr @@ -2113,6 +2113,16 @@ module Crystal # Nothing to do when "enum_new" # Nothing to do + when "cmpxchg" + node.type = program.tuple_of([typed_def.args[1].type, program.bool]) + when "atomicrmw" + node.type = typed_def.args[2].type + when "fence" + node.type = program.nil_type + when "load_atomic" + node.type = typed_def.args.first.type.as(PointerInstanceType).element_type + when "store_atomic" + node.type = program.nil_type else node.raise "Bug: unhandled primitive in MainVisitor: #{node.name}" end diff --git a/src/llvm/builder.cr b/src/llvm/builder.cr index 53318d522ec7..3bfc5b8232e2 100644 --- a/src/llvm/builder.cr +++ b/src/llvm/builder.cr @@ -150,6 +150,18 @@ class LLVM::Builder switch end + def atomicrmw(op, ptr, val, ordering, singlethread) + Value.new LibLLVM.build_atomicrmw(self, op, ptr, val, ordering, singlethread ? 1 : 0) + end + + def cmpxchg(pointer, cmp, new, success_ordering, failure_ordering) + Value.new LibLLVMExt.build_cmpxchg(self, pointer, cmp, new, success_ordering, failure_ordering) + end + + def fence(ordering, singlethread, name = "") + Value.new LibLLVM.build_fence(self, ordering, singlethread ? 1 : 0, name) + end + def set_current_debug_location(line, column, scope, inlined_at = nil) LibLLVMExt.set_current_debug_location(self, line, column, scope, inlined_at) end diff --git a/src/llvm/enums.cr b/src/llvm/enums.cr index 74822ab27a34..a5aace372d3c 100644 --- a/src/llvm/enums.cr +++ b/src/llvm/enums.cr @@ -172,4 +172,28 @@ module LLVM LoUser = 0x80 HiUser = 0xff end + + enum AtomicOrdering + NotAtomic = 0 + Unordered = 1 + Monotonic = 2 + Acquire = 4 + Release = 5 + AcquireRelease = 6 + SequentiallyConsistent = 7 + end + + enum AtomicRMWBinOp + Xchg + Add + Sub + And + Nand + Or + Xor + Max + Min + UMax + UMin + end end diff --git a/src/llvm/ext/llvm_ext.cc b/src/llvm/ext/llvm_ext.cc index 082ab2714a3a..8eadb3c1ab12 100644 --- a/src/llvm/ext/llvm_ext.cc +++ b/src/llvm/ext/llvm_ext.cc @@ -391,4 +391,13 @@ void LLVMSetCurrentDebugLocation2(LLVMBuilderRef Bref, unsigned Line, DebugLoc::get(Line, Col, Scope ? unwrap(Scope) : nullptr, InlinedAt ? unwrap(InlinedAt) : nullptr)); } + +LLVMValueRef LLVMBuildCmpxchg(LLVMBuilderRef B, + LLVMValueRef PTR, LLVMValueRef Cmp, LLVMValueRef New, + LLVMAtomicOrdering SuccessOrdering, + LLVMAtomicOrdering FailureOrdering) { + return wrap(unwrap(B)->CreateAtomicCmpXchg(unwrap(PTR), unwrap(Cmp), unwrap(New), + (llvm::AtomicOrdering)SuccessOrdering, (llvm::AtomicOrdering)FailureOrdering)); +} + } diff --git a/src/llvm/lib_llvm.cr b/src/llvm/lib_llvm.cr index ff5cf4873175..2941748a08e5 100644 --- a/src/llvm/lib_llvm.cr +++ b/src/llvm/lib_llvm.cr @@ -64,6 +64,7 @@ lib LibLLVM fun build_and = LLVMBuildAnd(builder : BuilderRef, lhs : ValueRef, rhs : ValueRef, name : UInt8*) : ValueRef fun build_array_malloc = LLVMBuildArrayMalloc(builder : BuilderRef, type : TypeRef, val : ValueRef, name : UInt8*) : ValueRef fun build_ashr = LLVMBuildAShr(builder : BuilderRef, lhs : ValueRef, rhs : ValueRef, name : UInt8*) : ValueRef + fun build_atomicrmw = LLVMBuildAtomicRMW(builder : BuilderRef, op : LLVM::AtomicRMWBinOp, ptr : ValueRef, val : ValueRef, ordering : LLVM::AtomicOrdering, singlethread : Int32) : ValueRef fun build_bit_cast = LLVMBuildBitCast(builder : BuilderRef, value : ValueRef, type : TypeRef, name : UInt8*) : ValueRef fun build_br = LLVMBuildBr(builder : BuilderRef, block : BasicBlockRef) : ValueRef fun build_call = LLVMBuildCall(builder : BuilderRef, fn : ValueRef, args : ValueRef*, num_args : Int32, name : UInt8*) : ValueRef @@ -73,6 +74,7 @@ lib LibLLVM fun build_fadd = LLVMBuildFAdd(builder : BuilderRef, lhs : ValueRef, rhs : ValueRef, name : UInt8*) : ValueRef fun build_fcmp = LLVMBuildFCmp(builder : BuilderRef, op : LLVM::RealPredicate, lhs : ValueRef, rhs : ValueRef, name : UInt8*) : ValueRef fun build_fdiv = LLVMBuildFDiv(builder : BuilderRef, lhs : ValueRef, rhs : ValueRef, name : UInt8*) : ValueRef + fun build_fence = LLVMBuildFence(builder : BuilderRef, ordering : LLVM::AtomicOrdering, singlethread : UInt32, name : UInt8*) : ValueRef fun build_fmul = LLVMBuildFMul(builder : BuilderRef, lhs : ValueRef, rhs : ValueRef, name : UInt8*) : ValueRef fun build_fp2si = LLVMBuildFPToSI(builder : BuilderRef, val : ValueRef, dest_ty : TypeRef, name : UInt8*) : ValueRef fun build_fp2ui = LLVMBuildFPToUI(builder : BuilderRef, val : ValueRef, dest_ty : TypeRef, name : UInt8*) : ValueRef @@ -272,4 +274,7 @@ lib LibLLVM fun dispose_pass_manager = LLVMDisposePassManager(PassManagerRef) fun dispose_target_data = LLVMDisposeTargetData(TargetDataRef) fun dispose_pass_manager_builder = LLVMPassManagerBuilderDispose(PassManagerBuilderRef) + fun set_volatile = LLVMSetVolatile(value : ValueRef, volatile : UInt32) + fun set_ordering = LLVMSetOrdering(value : ValueRef, ordering : LLVM::AtomicOrdering) + fun set_alignment = LLVMSetAlignment(value : ValueRef, bytes : UInt32) end diff --git a/src/llvm/lib_llvm_ext.cr b/src/llvm/lib_llvm_ext.cr index 716d3a3aa3f6..8c13063b6433 100644 --- a/src/llvm/lib_llvm_ext.cr +++ b/src/llvm/lib_llvm_ext.cr @@ -6,19 +6,21 @@ lib LibLLVMExt fun create_di_builder = LLVMNewDIBuilder(LibLLVM::ModuleRef) : DIBuilder fun di_builder_finalize = LLVMDIBuilderFinalize(DIBuilder) + {% if LibLLVM::IS_36 || LibLLVM::IS_35 %} - fun di_builder_create_function = LLVMDIBuilderCreateFunction( - builder : DIBuilder, scope : Metadata, name : LibC::Char*, - linkage_name : LibC::Char*, file : Metadata, line : LibC::UInt, - composite_type : Metadata, is_local_to_unit : LibC::Int, is_definition : LibC::Int, - scope_line : LibC::UInt, flags : LibC::UInt, is_optimized : LibC::Int, func : LibLLVM::ValueRef) : Metadata -{% else %} - fun di_builder_create_function = LLVMDIBuilderCreateFunction( - builder : DIBuilder, scope : Metadata, name : LibC::Char*, - linkage_name : LibC::Char*, file : Metadata, line : LibC::UInt, - composite_type : Metadata, is_local_to_unit : Bool, is_definition : Bool, - scope_line : LibC::UInt, flags : LibC::UInt, is_optimized : Bool, func : LibLLVM::ValueRef) : Metadata -{% end %} + fun di_builder_create_function = LLVMDIBuilderCreateFunction( + builder : DIBuilder, scope : Metadata, name : LibC::Char*, + linkage_name : LibC::Char*, file : Metadata, line : LibC::UInt, + composite_type : Metadata, is_local_to_unit : LibC::Int, is_definition : LibC::Int, + scope_line : LibC::UInt, flags : LibC::UInt, is_optimized : LibC::Int, func : LibLLVM::ValueRef) : Metadata + {% else %} + fun di_builder_create_function = LLVMDIBuilderCreateFunction( + builder : DIBuilder, scope : Metadata, name : LibC::Char*, + linkage_name : LibC::Char*, file : Metadata, line : LibC::UInt, + composite_type : Metadata, is_local_to_unit : Bool, is_definition : Bool, + scope_line : LibC::UInt, flags : LibC::UInt, is_optimized : Bool, func : LibLLVM::ValueRef) : Metadata + {% end %} + fun di_builder_create_file = LLVMDIBuilderCreateFile(builder : DIBuilder, file : LibC::Char*, dir : LibC::Char*) : Metadata fun di_builder_create_compile_unit = LLVMDIBuilderCreateCompileUnit(builder : DIBuilder, lang : LibC::UInt, file : LibC::Char*, @@ -88,4 +90,6 @@ lib LibLLVMExt fun metadata_replace_all_uses_with = LLVMMetadataReplaceAllUsesWith(Metadata, Metadata) fun set_current_debug_location = LLVMSetCurrentDebugLocation2(LibLLVM::BuilderRef, LibC::Int, LibC::Int, Metadata, Metadata) + + fun build_cmpxchg = LLVMBuildCmpxchg(builder : LibLLVM::BuilderRef, pointer : LibLLVM::ValueRef, cmp : LibLLVM::ValueRef, new : LibLLVM::ValueRef, success_ordering : LLVM::AtomicOrdering, failure_ordering : LLVM::AtomicOrdering) : LibLLVM::ValueRef end diff --git a/src/llvm/value_methods.cr b/src/llvm/value_methods.cr index 7b5a23047589..87768bf53b46 100644 --- a/src/llvm/value_methods.cr +++ b/src/llvm/value_methods.cr @@ -71,6 +71,18 @@ module LLVM::ValueMethods init ? LLVM::Value.new(init) : nil end + def volatile=(volatile) + LibLLVM.set_volatile(self, volatile ? 1 : 0) + end + + def ordering=(ordering) + LibLLVM.set_ordering(self, ordering) + end + + def alignment=(bytes) + LibLLVM.set_alignment(self, bytes) + end + def to_value LLVM::Value.new unwrap end diff --git a/src/prelude.cr b/src/prelude.cr index 183c875493fd..4904c1b0cbad 100644 --- a/src/prelude.cr +++ b/src/prelude.cr @@ -20,6 +20,7 @@ require "string" # Alpha-sorted list require "array" +require "atomic" require "bool" require "box" require "char"