Skip to content

Commit 3ee0c22

Browse files
authored
Merge pull request rust-lang#112 from wasmerio/atomic
Add `atomicrmw` and `cmpxchg` instructions to the builder.
2 parents 92814f2 + 2607b5a commit 3ee0c22

File tree

4 files changed

+295
-17
lines changed

4 files changed

+295
-17
lines changed

src/builder.rs

+104-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
//! A `Builder` enables you to build instructions.
22
33
use either::{Either, Left, Right};
4-
use llvm_sys::core::{LLVMBuildAdd, LLVMBuildAlloca, LLVMBuildAnd, LLVMBuildArrayAlloca, LLVMBuildArrayMalloc, LLVMBuildBr, LLVMBuildCall, LLVMBuildCast, LLVMBuildCondBr, LLVMBuildExtractValue, LLVMBuildFAdd, LLVMBuildFCmp, LLVMBuildFDiv, LLVMBuildFence, LLVMBuildFMul, LLVMBuildFNeg, LLVMBuildFree, LLVMBuildFSub, LLVMBuildGEP, LLVMBuildICmp, LLVMBuildInsertValue, LLVMBuildIsNotNull, LLVMBuildIsNull, LLVMBuildLoad, LLVMBuildMalloc, LLVMBuildMul, LLVMBuildNeg, LLVMBuildNot, LLVMBuildOr, LLVMBuildPhi, LLVMBuildPointerCast, LLVMBuildRet, LLVMBuildRetVoid, LLVMBuildStore, LLVMBuildSub, LLVMBuildUDiv, LLVMBuildUnreachable, LLVMBuildXor, LLVMDisposeBuilder, LLVMGetElementType, LLVMGetInsertBlock, LLVMGetReturnType, LLVMGetTypeKind, LLVMInsertIntoBuilder, LLVMPositionBuilderAtEnd, LLVMTypeOf, LLVMBuildExtractElement, LLVMBuildInsertElement, LLVMBuildIntToPtr, LLVMBuildPtrToInt, LLVMInsertIntoBuilderWithName, LLVMClearInsertionPosition, LLVMCreateBuilder, LLVMPositionBuilder, LLVMPositionBuilderBefore, LLVMBuildAggregateRet, LLVMBuildStructGEP, LLVMBuildInBoundsGEP, LLVMBuildPtrDiff, LLVMBuildNSWAdd, LLVMBuildNUWAdd, LLVMBuildNSWSub, LLVMBuildNUWSub, LLVMBuildNSWMul, LLVMBuildNUWMul, LLVMBuildSDiv, LLVMBuildSRem, LLVMBuildURem, LLVMBuildFRem, LLVMBuildNSWNeg, LLVMBuildNUWNeg, LLVMBuildFPToUI, LLVMBuildFPToSI, LLVMBuildSIToFP, LLVMBuildUIToFP, LLVMBuildFPTrunc, LLVMBuildFPExt, LLVMBuildIntCast, LLVMBuildFPCast, LLVMBuildSExtOrBitCast, LLVMBuildZExtOrBitCast, LLVMBuildTruncOrBitCast, LLVMBuildSwitch, LLVMAddCase, LLVMBuildShl, LLVMBuildAShr, LLVMBuildLShr, LLVMBuildGlobalString, LLVMBuildGlobalStringPtr, LLVMBuildExactSDiv, LLVMBuildTrunc, LLVMBuildSExt, LLVMBuildZExt, LLVMBuildSelect, LLVMBuildAddrSpaceCast, LLVMBuildBitCast, LLVMBuildShuffleVector, LLVMBuildVAArg, LLVMBuildIndirectBr, LLVMAddDestination};
4+
use llvm_sys::core::{LLVMBuildAdd, LLVMBuildAlloca, LLVMBuildAnd, LLVMBuildArrayAlloca, LLVMBuildArrayMalloc, LLVMBuildAtomicRMW, LLVMBuildBr, LLVMBuildCall, LLVMBuildCast, LLVMBuildCondBr, LLVMBuildExtractValue, LLVMBuildFAdd, LLVMBuildFCmp, LLVMBuildFDiv, LLVMBuildFence, LLVMBuildFMul, LLVMBuildFNeg, LLVMBuildFree, LLVMBuildFSub, LLVMBuildGEP, LLVMBuildICmp, LLVMBuildInsertValue, LLVMBuildIsNotNull, LLVMBuildIsNull, LLVMBuildLoad, LLVMBuildMalloc, LLVMBuildMul, LLVMBuildNeg, LLVMBuildNot, LLVMBuildOr, LLVMBuildPhi, LLVMBuildPointerCast, LLVMBuildRet, LLVMBuildRetVoid, LLVMBuildStore, LLVMBuildSub, LLVMBuildUDiv, LLVMBuildUnreachable, LLVMBuildXor, LLVMDisposeBuilder, LLVMGetElementType, LLVMGetInsertBlock, LLVMGetReturnType, LLVMGetTypeKind, LLVMInsertIntoBuilder, LLVMPositionBuilderAtEnd, LLVMTypeOf, LLVMBuildExtractElement, LLVMBuildInsertElement, LLVMBuildIntToPtr, LLVMBuildPtrToInt, LLVMInsertIntoBuilderWithName, LLVMClearInsertionPosition, LLVMCreateBuilder, LLVMPositionBuilder, LLVMPositionBuilderBefore, LLVMBuildAggregateRet, LLVMBuildStructGEP, LLVMBuildInBoundsGEP, LLVMBuildPtrDiff, LLVMBuildNSWAdd, LLVMBuildNUWAdd, LLVMBuildNSWSub, LLVMBuildNUWSub, LLVMBuildNSWMul, LLVMBuildNUWMul, LLVMBuildSDiv, LLVMBuildSRem, LLVMBuildURem, LLVMBuildFRem, LLVMBuildNSWNeg, LLVMBuildNUWNeg, LLVMBuildFPToUI, LLVMBuildFPToSI, LLVMBuildSIToFP, LLVMBuildUIToFP, LLVMBuildFPTrunc, LLVMBuildFPExt, LLVMBuildIntCast, LLVMBuildFPCast, LLVMBuildSExtOrBitCast, LLVMBuildZExtOrBitCast, LLVMBuildTruncOrBitCast, LLVMBuildSwitch, LLVMAddCase, LLVMBuildShl, LLVMBuildAShr, LLVMBuildLShr, LLVMBuildGlobalString, LLVMBuildGlobalStringPtr, LLVMBuildExactSDiv, LLVMBuildTrunc, LLVMBuildSExt, LLVMBuildZExt, LLVMBuildSelect, LLVMBuildAddrSpaceCast, LLVMBuildBitCast, LLVMBuildShuffleVector, LLVMBuildVAArg, LLVMBuildIndirectBr, LLVMAddDestination};
5+
#[llvm_versions(3.9..=latest)]
6+
use llvm_sys::core::LLVMBuildAtomicCmpXchg;
57
use llvm_sys::prelude::{LLVMBuilderRef, LLVMValueRef};
68
use llvm_sys::{LLVMTypeKind};
79

8-
use crate::{AtomicOrdering, IntPredicate, FloatPredicate};
10+
use crate::{AtomicOrdering, AtomicRMWBinOp, IntPredicate, FloatPredicate};
911
use crate::basic_block::BasicBlock;
10-
use crate::values::{AggregateValue, AggregateValueEnum, AsValueRef, BasicValue, BasicValueEnum, PhiValue, FunctionValue, IntValue, PointerValue, VectorValue, InstructionValue, GlobalValue, IntMathValue, FloatMathValue, PointerMathValue, InstructionOpcode, CallSiteValue};
12+
use crate::values::{AggregateValue, AggregateValueEnum, AsValueRef, BasicValue, BasicValueEnum, PhiValue, FunctionValue, IntValue, PointerValue, StructValue, VectorValue, InstructionValue, GlobalValue, IntMathValue, FloatMathValue, PointerMathValue, InstructionOpcode, CallSiteValue};
1113
use crate::types::{AsTypeRef, BasicType, IntMathType, FloatMathType, PointerType, PointerMathType};
1214

1315
use std::ffi::CString;
@@ -1338,7 +1340,7 @@ impl Builder {
13381340
let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");
13391341

13401342
let val = unsafe {
1341-
LLVMBuildFence(self.builder, atomic_ordering.as_llvm_enum(), num, c_string.as_ptr())
1343+
LLVMBuildFence(self.builder, atomic_ordering.into(), num, c_string.as_ptr())
13421344
};
13431345

13441346
InstructionValue::new(val)
@@ -1464,6 +1466,104 @@ impl Builder {
14641466

14651467
BasicValueEnum::new(value)
14661468
}
1469+
1470+
/// Builds an atomicrmw instruction. It allows you to atomically modify memory.
1471+
///
1472+
/// # Example
1473+
///
1474+
/// ```
1475+
/// use inkwell::context::Context;
1476+
/// use inkwell::{AddressSpace, AtomicOrdering, AtomicRMWBinOp};
1477+
/// let context = Context::create();
1478+
/// let module = context.create_module("rmw");
1479+
/// let void_type = context.void_type();
1480+
/// let i32_type = context.i32_type();
1481+
/// let i32_seven = i32_type.const_int(7, false);
1482+
/// let i32_ptr_type = i32_type.ptr_type(AddressSpace::Generic);
1483+
/// let fn_type = void_type.fn_type(&[i32_ptr_type.into()], false);
1484+
/// let fn_value = module.add_function("rmw", fn_type, None);
1485+
/// let entry = fn_value.append_basic_block("entry");
1486+
/// let i32_ptr_param = fn_value.get_first_param().unwrap().into_pointer_value();
1487+
/// let builder = context.create_builder();
1488+
/// builder.position_at_end(&entry);
1489+
/// builder.build_atomicrmw(AtomicRMWBinOp::Add, i32_ptr_param, i32_seven, AtomicOrdering::Unordered);
1490+
/// builder.build_return(None);
1491+
/// ```
1492+
// https://llvm.org/docs/LangRef.html#atomicrmw-instruction
1493+
pub fn build_atomicrmw(&self, op: AtomicRMWBinOp, ptr: PointerValue, value: IntValue, ordering: AtomicOrdering) -> Result<IntValue, &'static str> {
1494+
// TODO: add support for fadd, fsub and xchg on floating point types in LLVM 9+.
1495+
1496+
// "The type of ‘<value>’ must be an integer type whose bit width is a power of two greater than or equal to eight and less than or equal to a target-specific size limit. The type of the ‘<pointer>’ operand must be a pointer to that type." -- https://releases.llvm.org/3.6.2/docs/LangRef.html#atomicrmw-instruction
1497+
if value.get_type().get_bit_width() < 8 ||
1498+
!value.get_type().get_bit_width().is_power_of_two() {
1499+
return Err("The bitwidth of value must be a power of 2 and greater than 8.");
1500+
}
1501+
if ptr.get_type().get_element_type() != value.get_type().into() {
1502+
return Err("Pointer's pointee type must match the value's type.");
1503+
}
1504+
1505+
let val = unsafe {
1506+
LLVMBuildAtomicRMW(self.builder, op.into(), ptr.as_value_ref(), value.as_value_ref(), ordering.into(), false as i32)
1507+
};
1508+
1509+
Ok(IntValue::new(val))
1510+
}
1511+
1512+
/// Builds a cmpxchg instruction. It allows you to atomically compare and replace memory.
1513+
///
1514+
/// # Example
1515+
///
1516+
/// ```
1517+
/// use inkwell::context::Context;
1518+
/// use inkwell::{AddressSpace, AtomicOrdering};
1519+
/// let context = Context::create();
1520+
/// let module = context.create_module("cmpxchg");
1521+
/// let void_type = context.void_type();
1522+
/// let i32_type = context.i32_type();
1523+
/// let i32_ptr_type = i32_type.ptr_type(AddressSpace::Generic);
1524+
/// let fn_type = void_type.fn_type(&[i32_ptr_type.into()], false);
1525+
/// let fn_value = module.add_function("", fn_type, None);
1526+
/// let i32_ptr_param = fn_value.get_first_param().unwrap().into_pointer_value();
1527+
/// let i32_seven = i32_type.const_int(7, false);
1528+
/// let i32_eight = i32_type.const_int(8, false);
1529+
/// let entry = fn_value.append_basic_block("entry");
1530+
/// let builder = context.create_builder();
1531+
/// builder.position_at_end(&entry);
1532+
/// builder.build_cmpxchg(i32_ptr_param, i32_seven, i32_eight, AtomicOrdering::AcquireRelease, AtomicOrdering::Monotonic);
1533+
/// builder.build_return(None);
1534+
/// ```
1535+
// https://llvm.org/docs/LangRef.html#cmpxchg-instruction
1536+
#[llvm_versions(3.9..=latest)]
1537+
pub fn build_cmpxchg<V: BasicValue>(&self, ptr: PointerValue, cmp: V, new: V, success: AtomicOrdering, failure: AtomicOrdering) -> Result<StructValue, &'static str> {
1538+
let cmp = cmp.as_basic_value_enum();
1539+
let new = new.as_basic_value_enum();
1540+
if cmp.get_type() != new.get_type() {
1541+
return Err("The value to compare against and the value to replace with must have the same type.");
1542+
}
1543+
if !cmp.is_int_value() && !cmp.is_pointer_value() {
1544+
return Err("The values must have pointer or integer type.");
1545+
}
1546+
if ptr.get_type().get_element_type().to_basic_type_enum() != cmp.get_type() {
1547+
return Err("The pointer does not point to an element of the value type.");
1548+
}
1549+
1550+
// "Both ordering parameters must be at least monotonic, the ordering constraint on failure must be no stronger than that on success, and the failure ordering cannot be either release or acq_rel." -- https://llvm.org/docs/LangRef.html#cmpxchg-instruction
1551+
if success < AtomicOrdering::Monotonic || failure < AtomicOrdering::Monotonic {
1552+
return Err("Both success and failure orderings must be Monotonic or stronger.");
1553+
}
1554+
if failure > success {
1555+
return Err("The failure ordering may not be stronger than the success ordering.");
1556+
}
1557+
if failure == AtomicOrdering::Release || failure == AtomicOrdering::AcquireRelease {
1558+
return Err("The failure ordering may not be release or acquire release.");
1559+
}
1560+
1561+
let val = unsafe {
1562+
LLVMBuildAtomicCmpXchg(self.builder, ptr.as_value_ref(), cmp.as_value_ref(), new.as_value_ref(), success.into(), failure.into(), false as i32)
1563+
};
1564+
1565+
Ok(StructValue::new(val).into())
1566+
}
14671567
}
14681568

14691569
impl Drop for Builder {

src/lib.rs

+66-11
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ pub mod targets;
4343
pub mod types;
4444
pub mod values;
4545

46-
use llvm_sys::{LLVMIntPredicate, LLVMRealPredicate, LLVMVisibility, LLVMThreadLocalMode, LLVMDLLStorageClass, LLVMAtomicOrdering};
46+
use llvm_sys::{LLVMIntPredicate, LLVMRealPredicate, LLVMVisibility, LLVMThreadLocalMode, LLVMDLLStorageClass, LLVMAtomicOrdering, LLVMAtomicRMWBinOp};
4747

4848
use std::convert::TryFrom;
4949

@@ -173,16 +173,71 @@ enum_rename!{
173173
}
174174

175175
// REVIEW: Maybe this belongs in some sort of prelude?
176-
enum_rename!{
177-
AtomicOrdering <=> LLVMAtomicOrdering {
178-
NotAtomic <=> LLVMAtomicOrderingNotAtomic,
179-
Unordered <=> LLVMAtomicOrderingUnordered,
180-
Monotonic <=> LLVMAtomicOrderingMonotonic,
181-
Acquire <=> LLVMAtomicOrderingAcquire,
182-
Release <=> LLVMAtomicOrderingRelease,
183-
AcquireRelease <=> LLVMAtomicOrderingAcquireRelease,
184-
SequentiallyConsistent <=> LLVMAtomicOrderingSequentiallyConsistent,
185-
}
176+
#[llvm_enum(LLVMAtomicOrdering)]
177+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
178+
pub enum AtomicOrdering {
179+
#[llvm_variant(LLVMAtomicOrderingNotAtomic)]
180+
NotAtomic,
181+
#[llvm_variant(LLVMAtomicOrderingUnordered)]
182+
Unordered,
183+
#[llvm_variant(LLVMAtomicOrderingMonotonic)]
184+
Monotonic,
185+
#[llvm_variant(LLVMAtomicOrderingAcquire)]
186+
Acquire,
187+
#[llvm_variant(LLVMAtomicOrderingRelease)]
188+
Release,
189+
#[llvm_variant(LLVMAtomicOrderingAcquireRelease)]
190+
AcquireRelease,
191+
#[llvm_variant(LLVMAtomicOrderingSequentiallyConsistent)]
192+
SequentiallyConsistent,
193+
}
194+
195+
#[llvm_enum(LLVMAtomicRMWBinOp)]
196+
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
197+
pub enum AtomicRMWBinOp {
198+
/// Stores to memory and returns the prior value.
199+
#[llvm_variant(LLVMAtomicRMWBinOpXchg)]
200+
Xchg,
201+
202+
/// Adds to the value in memory and returns the prior value.
203+
#[llvm_variant(LLVMAtomicRMWBinOpAdd)]
204+
Add,
205+
206+
/// Subtract a value off the value in memory and returns the prior value.
207+
#[llvm_variant(LLVMAtomicRMWBinOpSub)]
208+
Sub,
209+
210+
/// Bitwise and into memory and returns the prior value.
211+
#[llvm_variant(LLVMAtomicRMWBinOpAnd)]
212+
And,
213+
214+
/// Bitwise nands into memory and returns the prior value.
215+
#[llvm_variant(LLVMAtomicRMWBinOpNand)]
216+
Nand,
217+
218+
/// Bitwise ors into memory and returns the prior value.
219+
#[llvm_variant(LLVMAtomicRMWBinOpOr)]
220+
Or,
221+
222+
/// Bitwise xors into memory and returns the prior value.
223+
#[llvm_variant(LLVMAtomicRMWBinOpXor)]
224+
Xor,
225+
226+
/// Sets memory to the signed-greater of the value provided and the value in memory. Returns the value that was in memory.
227+
#[llvm_variant(LLVMAtomicRMWBinOpMax)]
228+
Max,
229+
230+
/// Sets memory to the signed-lesser of the value provided and the value in memory. Returns the value that was in memory.
231+
#[llvm_variant(LLVMAtomicRMWBinOpMin)]
232+
Min,
233+
234+
/// Sets memory to the unsigned-greater of the value provided and the value in memory. Returns the value that was in memory.
235+
#[llvm_variant(LLVMAtomicRMWBinOpUMax)]
236+
UMax,
237+
238+
/// Sets memory to the unsigned-lesser of the value provided and the value in memory. Returns the value that was in memory.
239+
#[llvm_variant(LLVMAtomicRMWBinOpUMin)]
240+
UMin,
186241
}
187242

188243
/// Defines the optimization level used to compile a `Module`.

src/support/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@ macro_rules! enum_rename {
158158
($(#[$enum_attrs:meta])* $enum_name:ident <=> $llvm_enum_name:ident {
159159
$($(#[$variant_attrs:meta])* $args:ident <=> $llvm_args:ident,)+
160160
}) => (
161-
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
161+
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
162162
$(#[$enum_attrs])*
163163
pub enum $enum_name {
164164
$(

0 commit comments

Comments
 (0)