Skip to content

Commit

Permalink
Expose LLVM atomic operations to Crystal, and add Atomic(T) struct.
Browse files Browse the repository at this point in the history
We will need this for efficiently implementing multiple-thread support.
Ary Borenszweig committed Oct 5, 2016
1 parent a8eef9b commit 919818e
Showing 12 changed files with 550 additions and 14 deletions.
130 changes: 130 additions & 0 deletions spec/std/atomic_spec.cr
Original file line number Diff line number Diff line change
@@ -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
219 changes: 219 additions & 0 deletions src/atomic.cr
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion src/compiler/crystal/codegen/call.cr
Original file line number Diff line number Diff line change
@@ -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
112 changes: 111 additions & 1 deletion src/compiler/crystal/codegen/primitives.cr
Original file line number Diff line number Diff line change
@@ -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
10 changes: 10 additions & 0 deletions src/compiler/crystal/semantic/main_visitor.cr
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions src/llvm/builder.cr
Original file line number Diff line number Diff line change
@@ -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
24 changes: 24 additions & 0 deletions src/llvm/enums.cr
Original file line number Diff line number Diff line change
@@ -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
9 changes: 9 additions & 0 deletions src/llvm/ext/llvm_ext.cc
Original file line number Diff line number Diff line change
@@ -391,4 +391,13 @@ void LLVMSetCurrentDebugLocation2(LLVMBuilderRef Bref, unsigned Line,
DebugLoc::get(Line, Col, Scope ? unwrap<MDNode>(Scope) : nullptr,
InlinedAt ? unwrap<MDNode>(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));
}

}
5 changes: 5 additions & 0 deletions src/llvm/lib_llvm.cr
Original file line number Diff line number Diff line change
@@ -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
28 changes: 16 additions & 12 deletions src/llvm/lib_llvm_ext.cr
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions src/llvm/value_methods.cr
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions src/prelude.cr
Original file line number Diff line number Diff line change
@@ -20,6 +20,7 @@ require "string"

# Alpha-sorted list
require "array"
require "atomic"

This comment has been minimized.

Copy link
@luislavena

luislavena Oct 5, 2016

Contributor

The spec for Atomic has been commented out, but adding atomic to the prelude will make it available as part of 0.19.3, is this intended?

This comment has been minimized.

Copy link
@asterite

asterite Oct 5, 2016

Member

Yes. It's a bit hard to explain, but Atomic can't be used with 0.19.3 (compiler crash because of missing primitives) but it will work with the next compiler.

require "bool"
require "box"
require "char"

1 comment on commit 919818e

@ozra
Copy link
Contributor

@ozra ozra commented on 919818e Oct 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yippie-kie-yeay!

Please sign in to comment.