Skip to content

Commit 66f7026

Browse files
authored
Merge pull request rust-lang#254 from folkertdev/callable-value
Add CallableValue
2 parents db59ef3 + e895503 commit 66f7026

File tree

6 files changed

+155
-49
lines changed

6 files changed

+155
-49
lines changed

src/builder.rs

Lines changed: 15 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
//! A `Builder` enables you to build instructions.
22
3-
use either::{Either, Left, Right};
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, 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};
3+
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, LLVMGetInsertBlock, LLVMInsertIntoBuilder, LLVMPositionBuilderAtEnd, LLVMBuildExtractElement, LLVMBuildInsertElement, LLVMBuildIntToPtr, LLVMBuildPtrToInt, LLVMInsertIntoBuilderWithName, LLVMClearInsertionPosition, 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};
54
#[llvm_versions(3.9..=latest)]
65
use llvm_sys::core::LLVMBuildAtomicCmpXchg;
76
#[llvm_versions(8.0..=latest)]
87
use llvm_sys::core::{LLVMBuildMemCpy, LLVMBuildMemMove};
98
use llvm_sys::prelude::{LLVMBuilderRef, LLVMValueRef};
10-
use llvm_sys::{LLVMTypeKind};
119

1210
use crate::{AtomicOrdering, AtomicRMWBinOp, IntPredicate, FloatPredicate};
1311
use crate::basic_block::BasicBlock;
1412
use crate::support::to_c_str;
15-
use crate::values::{AggregateValue, AggregateValueEnum, AsValueRef, BasicValue, BasicValueEnum, PhiValue, FunctionValue, IntValue, PointerValue, VectorValue, InstructionValue, GlobalValue, IntMathValue, FloatMathValue, PointerMathValue, InstructionOpcode, CallSiteValue};
13+
use crate::values::{AggregateValue, AggregateValueEnum, AsValueRef, BasicValue, BasicValueEnum, PhiValue, IntValue, PointerValue, VectorValue, InstructionValue, GlobalValue, IntMathValue, FloatMathValue, PointerMathValue, InstructionOpcode, CallSiteValue};
1614
#[llvm_versions(7.0..=latest)]
1715
use crate::debug_info::DILocation;
1816
#[llvm_versions(3.9..=latest)]
1917
use crate::values::StructValue;
18+
use crate::values::CallableValue;
2019
use crate::types::{AsTypeRef, BasicType, IntMathType, FloatMathType, PointerType, PointerMathType};
2120

2221
use std::marker::PhantomData;
@@ -107,9 +106,11 @@ impl<'ctx> Builder<'ctx> {
107106
}
108107
}
109108

110-
/// Builds a function call instruction. It can take either a `FunctionValue` or a `PointerValue`
111-
/// which is a function pointer. It will panic if the `PointerValue` is not a function pointer.
112-
/// This may be turned into a Result in the future, however.
109+
/// Builds a function call instruction.
110+
/// [`FunctionValue`]s can be implicitly converted into a [`CallableValue`].
111+
/// See [`CallableValue`] for details on calling a [`PointerValue`] that points to a function.
112+
///
113+
/// [`FunctionValue`]: crate::values::FunctionValue
113114
///
114115
/// # Example
115116
///
@@ -137,32 +138,16 @@ impl<'ctx> Builder<'ctx> {
137138
/// ```
138139
pub fn build_call<F>(&self, function: F, args: &[BasicValueEnum<'ctx>], name: &str) -> CallSiteValue<'ctx>
139140
where
140-
F: Into<FunctionOrPointerValue<'ctx>>,
141+
F: Into<CallableValue<'ctx>>,
141142
{
142-
let fn_val_ref = match function.into() {
143-
Left(val) => val.as_value_ref(),
144-
Right(val) => {
145-
// If using a pointer value, we must validate it's a valid function ptr
146-
let value_ref = val.as_value_ref();
147-
let ty_kind = unsafe { LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(value_ref))) };
148-
let is_a_fn_ptr = match ty_kind {
149-
LLVMTypeKind::LLVMFunctionTypeKind => true,
150-
_ => false,
151-
};
152-
153-
// REVIEW: We should probably turn this into a Result?
154-
assert!(is_a_fn_ptr, "build_call called with a pointer which is not a function pointer");
155-
156-
value_ref
157-
},
158-
};
143+
let callable_value = function.into();
144+
let fn_val_ref = callable_value.as_value_ref();
159145

160146
// LLVM gets upset when void return calls are named because they don't return anything
161-
let name = unsafe {
162-
match LLVMGetTypeKind(LLVMGetReturnType(LLVMGetElementType(LLVMTypeOf(fn_val_ref)))) {
163-
LLVMTypeKind::LLVMVoidTypeKind => "",
164-
_ => name,
165-
}
147+
let name = if callable_value.returns_void() {
148+
""
149+
} else {
150+
name
166151
};
167152

168153
let c_string = to_c_str(name);
@@ -1892,17 +1877,3 @@ impl Drop for Builder<'_> {
18921877
}
18931878
}
18941879
}
1895-
1896-
type FunctionOrPointerValue<'ctx> = Either<FunctionValue<'ctx>, PointerValue<'ctx>>;
1897-
1898-
impl<'ctx> Into<FunctionOrPointerValue<'ctx>> for FunctionValue<'ctx> {
1899-
fn into(self) -> FunctionOrPointerValue<'ctx> {
1900-
Left(self)
1901-
}
1902-
}
1903-
1904-
impl<'ctx> Into<FunctionOrPointerValue<'ctx>> for PointerValue<'ctx> {
1905-
fn into(self) -> FunctionOrPointerValue<'ctx> {
1906-
Right(self)
1907-
}
1908-
}

src/context.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,9 @@ impl Context {
195195
///
196196
/// # Example
197197
/// ```no_run
198+
/// use std::convert::TryFrom;
198199
/// use inkwell::context::Context;
200+
/// use inkwell::values::CallableValue;
199201
///
200202
/// let context = Context::create();
201203
/// let module = context.create_module("my_module");
@@ -209,7 +211,8 @@ impl Context {
209211
/// let asm_fn = context.i64_type().fn_type(&[context.i64_type().into(), context.i64_type().into()], false);
210212
/// let asm = context.create_inline_asm(asm_fn, "syscall".to_string(), "=r,{rax},{rdi}".to_string(), true, false, None);
211213
/// let params = &[context.i64_type().const_int(60, false).into(), context.i64_type().const_int(1, false).into()];
212-
/// builder.build_call(asm, params, "exit");
214+
/// let callable_value = CallableValue::try_from(asm).unwrap();
215+
/// builder.build_call(callable_value, params, "exit");
213216
/// builder.build_return(None);
214217
#[llvm_versions(7.0..=latest)]
215218
pub fn create_inline_asm(&self, ty: FunctionType, mut assembly: String, mut constraints: String, sideeffects: bool, alignstack: bool, dialect: Option<InlineAsmDialect>) -> PointerValue {
@@ -234,7 +237,9 @@ impl Context {
234237
///
235238
/// # Example
236239
/// ```no_run
240+
/// use std::convert::TryFrom;
237241
/// use inkwell::context::Context;
242+
/// use inkwell::values::CallableValue;
238243
///
239244
/// let context = Context::create();
240245
/// let module = context.create_module("my_module");
@@ -248,7 +253,8 @@ impl Context {
248253
/// let asm_fn = context.i64_type().fn_type(&[context.i64_type().into(), context.i64_type().into()], false);
249254
/// let asm = context.create_inline_asm(asm_fn, "syscall".to_string(), "=r,{rax},{rdi}".to_string(), true, false);
250255
/// let params = &[context.i64_type().const_int(60, false).into(), context.i64_type().const_int(1, false).into()];
251-
/// builder.build_call(asm, params, "exit");
256+
/// let callable_value = CallableValue::try_from(asm).unwrap();
257+
/// builder.build_call(callable_value, params, "exit");
252258
/// builder.build_return(None);
253259
#[llvm_versions(3.6..7.0)]
254260
pub fn create_inline_asm(&self, ty: FunctionType, assembly: String, constraints: String, sideeffects: bool, alignstack: bool) -> PointerValue {

src/values/callable_value.rs

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
use std::convert::TryFrom;
2+
use either::Either;
3+
4+
use crate::values::AsValueRef;
5+
use crate::values::{FunctionValue, PointerValue, AnyValue};
6+
7+
use llvm_sys::prelude::LLVMValueRef;
8+
use llvm_sys::core::{LLVMGetTypeKind, LLVMGetElementType, LLVMTypeOf, LLVMGetReturnType};
9+
use llvm_sys::LLVMTypeKind;
10+
11+
/// A value that can be called with the [`build_call`] instruction.
12+
///
13+
/// In practice, the `F : Into<CallableValue<'ctx>>` bound of [`build_call`] means it is
14+
/// possible to pass a [`FunctionValue`] to [`build_call`] directly. It will be implicitly converted
15+
/// into a `CallableValue`.
16+
///
17+
/// [`build_call`]: crate::builder::Builder::build_call
18+
///
19+
/// ```no_run
20+
/// use inkwell::context::Context;
21+
///
22+
/// // A simple function which calls itself:
23+
/// let context = Context::create();
24+
/// let module = context.create_module("ret");
25+
/// let builder = context.create_builder();
26+
/// let i32_type = context.i32_type();
27+
/// let fn_type = i32_type.fn_type(&[i32_type.into()], false);
28+
/// let fn_value = module.add_function("ret", fn_type, None);
29+
/// let entry = context.append_basic_block(fn_value, "entry");
30+
/// let i32_arg = fn_value.get_first_param().unwrap();
31+
///
32+
/// builder.position_at_end(entry);
33+
///
34+
/// let ret_val = builder.build_call(fn_value, &[i32_arg], "call")
35+
/// .try_as_basic_value()
36+
/// .left()
37+
/// .unwrap();
38+
///
39+
/// builder.build_return(Some(&ret_val));
40+
/// ```
41+
///
42+
/// A [`PointerValue`] cannot be implicitly converted to a `CallableValue` because the pointer may
43+
/// point to a non-function value. Instead we can use [`TryFrom`] to handle this failure case explicitly.
44+
///
45+
/// ```no_run
46+
/// use std::convert::TryFrom;
47+
/// use inkwell::context::Context;
48+
/// use inkwell::values::CallableValue;
49+
///
50+
/// // A simple function which calls itself:
51+
/// let context = Context::create();
52+
/// let module = context.create_module("ret");
53+
/// let builder = context.create_builder();
54+
/// let i32_type = context.i32_type();
55+
/// let fn_type = i32_type.fn_type(&[i32_type.into()], false);
56+
/// let fn_value = module.add_function("ret", fn_type, None);
57+
/// let entry = context.append_basic_block(fn_value, "entry");
58+
/// let i32_arg = fn_value.get_first_param().unwrap();
59+
///
60+
/// builder.position_at_end(entry);
61+
///
62+
/// // take a pointer to the function value
63+
/// let fn_pointer_value = fn_value.as_global_value().as_pointer_value();
64+
///
65+
/// // convert that pointer value into a callable value
66+
/// // explicitly handling the failure case (here with `unwrap`)
67+
/// let callable_value = CallableValue::try_from(fn_pointer_value).unwrap();
68+
///
69+
/// let ret_val = builder.build_call(callable_value, &[i32_arg], "call")
70+
/// .try_as_basic_value()
71+
/// .left()
72+
/// .unwrap();
73+
///
74+
/// builder.build_return(Some(&ret_val));
75+
/// ```
76+
#[derive(Debug)]
77+
pub struct CallableValue<'ctx>(Either<FunctionValue<'ctx>, PointerValue<'ctx>>);
78+
79+
impl<'ctx> AsValueRef for CallableValue<'ctx> {
80+
fn as_value_ref(&self) -> LLVMValueRef {
81+
use either::Either::*;
82+
83+
match self.0 {
84+
Left(function) => function.as_value_ref(),
85+
Right(pointer) => pointer.as_value_ref(),
86+
}
87+
}
88+
}
89+
90+
impl<'ctx> AnyValue<'ctx> for CallableValue<'ctx> {}
91+
92+
impl<'ctx> CallableValue<'ctx> {
93+
pub(crate) fn returns_void(&self) -> bool {
94+
let return_type = unsafe {
95+
LLVMGetTypeKind(LLVMGetReturnType(LLVMGetElementType(LLVMTypeOf(self.as_value_ref()))))
96+
};
97+
98+
matches!(return_type, LLVMTypeKind::LLVMVoidTypeKind)
99+
}
100+
}
101+
102+
impl<'ctx> From<FunctionValue<'ctx>> for CallableValue<'ctx> {
103+
fn from(value: FunctionValue<'ctx>) -> Self {
104+
Self(Either::Left(value))
105+
}
106+
}
107+
108+
impl<'ctx> TryFrom<PointerValue<'ctx>> for CallableValue<'ctx> {
109+
type Error = ();
110+
111+
fn try_from(value: PointerValue<'ctx>) -> Result<Self, Self::Error> {
112+
// If using a pointer value, we must validate it's a valid function ptr
113+
let value_ref = value.as_value_ref();
114+
let ty_kind = unsafe { LLVMGetTypeKind(LLVMGetElementType(LLVMTypeOf(value_ref))) };
115+
let is_a_fn_ptr = matches!(ty_kind, LLVMTypeKind::LLVMFunctionTypeKind);
116+
117+
if is_a_fn_ptr {
118+
Ok(Self(Either::Right(value)))
119+
} else {
120+
Err(())
121+
}
122+
}
123+
}

src/values/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ mod ptr_value;
1919
mod struct_value;
2020
mod traits;
2121
mod vec_value;
22+
mod callable_value;
2223

2324
use crate::support::LLVMString;
2425
pub use crate::values::array_value::ArrayValue;
@@ -37,6 +38,7 @@ pub use crate::values::metadata_value::{MetadataValue, FIRST_CUSTOM_METADATA_KIN
3738
pub use crate::values::phi_value::PhiValue;
3839
pub use crate::values::ptr_value::PointerValue;
3940
pub use crate::values::struct_value::StructValue;
41+
pub use crate::values::callable_value::CallableValue;
4042
pub use crate::values::traits::{AnyValue, AggregateValue, BasicValue, IntMathValue, FloatMathValue, PointerMathValue};
4143
pub use crate::values::vec_value::VectorValue;
4244
pub(crate) use crate::values::traits::AsValueRef;

tests/all/test_builder.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
use inkwell::{AddressSpace, AtomicOrdering, AtomicRMWBinOp, OptimizationLevel};
22
use inkwell::context::Context;
33
use inkwell::values::BasicValue;
4+
use inkwell::values::CallableValue;
45

56
use std::ptr::null;
7+
use std::convert::TryFrom;
68

79
#[test]
810
fn test_build_call() {
@@ -57,7 +59,8 @@ fn test_build_call() {
5759

5860
let load = builder.build_load(alloca, "load").into_pointer_value();
5961

60-
builder.build_call(load, &[], "call");
62+
let callable_value = CallableValue::try_from(load).unwrap();
63+
builder.build_call(callable_value, &[], "call");
6164
builder.build_return(None);
6265

6366
assert!(module.verify().is_ok());

tests/all/test_values.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use inkwell::attributes::AttributeLoc;
33
use inkwell::context::Context;
44
use inkwell::module::Linkage::*;
55
use inkwell::types::{AnyType, StringRadix, VectorType};
6-
use inkwell::values::{AnyValue, BasicValue, InstructionOpcode::*, FIRST_CUSTOM_METADATA_KIND_ID};
6+
use inkwell::values::{AnyValue, BasicValue, CallableValue, InstructionOpcode::*, FIRST_CUSTOM_METADATA_KIND_ID};
77
#[llvm_versions(7.0..=latest)]
88
use inkwell::comdat::ComdatSelectionKind;
99

@@ -1134,7 +1134,8 @@ fn test_non_fn_ptr_called() {
11341134
let i8_ptr_param = fn_value.get_first_param().unwrap().into_pointer_value();
11351135

11361136
builder.position_at_end(bb);
1137-
builder.build_call(i8_ptr_param, &[], "call");
1137+
let callable_value = CallableValue::try_from(i8_ptr_param).unwrap();
1138+
builder.build_call(callable_value, &[], "call");
11381139
builder.build_return(None);
11391140

11401141
assert!(module.verify().is_ok());

0 commit comments

Comments
 (0)