|
1 | 1 | //! A `Builder` enables you to build instructions.
|
2 | 2 |
|
3 | 3 | 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; |
5 | 7 | use llvm_sys::prelude::{LLVMBuilderRef, LLVMValueRef};
|
6 | 8 | use llvm_sys::{LLVMTypeKind};
|
7 | 9 |
|
8 |
| -use crate::{AtomicOrdering, IntPredicate, FloatPredicate}; |
| 10 | +use crate::{AtomicOrdering, AtomicRMWBinOp, IntPredicate, FloatPredicate}; |
9 | 11 | 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}; |
11 | 13 | use crate::types::{AsTypeRef, BasicType, IntMathType, FloatMathType, PointerType, PointerMathType};
|
12 | 14 |
|
13 | 15 | use std::ffi::CString;
|
@@ -1338,7 +1340,7 @@ impl Builder {
|
1338 | 1340 | let c_string = CString::new(name).expect("Conversion to CString failed unexpectedly");
|
1339 | 1341 |
|
1340 | 1342 | 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()) |
1342 | 1344 | };
|
1343 | 1345 |
|
1344 | 1346 | InstructionValue::new(val)
|
@@ -1464,6 +1466,104 @@ impl Builder {
|
1464 | 1466 |
|
1465 | 1467 | BasicValueEnum::new(value)
|
1466 | 1468 | }
|
| 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 | + } |
1467 | 1567 | }
|
1468 | 1568 |
|
1469 | 1569 | impl Drop for Builder {
|
|
0 commit comments