Skip to content

Commit 099077e

Browse files
authored
Merge pull request rust-lang#252 from folkertdev/invoke-experiments
Invoke & some friends
2 parents 66f7026 + 39976ab commit 099077e

File tree

2 files changed

+587
-2
lines changed

2 files changed

+587
-2
lines changed

src/builder.rs

Lines changed: 373 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! A `Builder` enables you to build instructions.
22
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};
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, LLVMBuildInvoke, LLVMBuildResume, LLVMBuildLandingPad, LLVMSetCleanup, LLVMAddClause};
44
#[llvm_versions(3.9..=latest)]
55
use llvm_sys::core::LLVMBuildAtomicCmpXchg;
66
#[llvm_versions(8.0..=latest)]
@@ -10,7 +10,7 @@ use llvm_sys::prelude::{LLVMBuilderRef, LLVMValueRef};
1010
use crate::{AtomicOrdering, AtomicRMWBinOp, IntPredicate, FloatPredicate};
1111
use crate::basic_block::BasicBlock;
1212
use crate::support::to_c_str;
13-
use crate::values::{AggregateValue, AggregateValueEnum, AsValueRef, BasicValue, BasicValueEnum, PhiValue, IntValue, PointerValue, VectorValue, InstructionValue, GlobalValue, IntMathValue, FloatMathValue, PointerMathValue, InstructionOpcode, CallSiteValue};
13+
use crate::values::{AggregateValue, AggregateValueEnum, AsValueRef, FunctionValue, BasicValue, BasicValueEnum, PhiValue, IntValue, PointerValue, VectorValue, InstructionValue, GlobalValue, IntMathValue, FloatMathValue, PointerMathValue, InstructionOpcode, CallSiteValue};
1414
#[llvm_versions(7.0..=latest)]
1515
use crate::debug_info::DILocation;
1616
#[llvm_versions(3.9..=latest)]
@@ -163,6 +163,377 @@ impl<'ctx> Builder<'ctx> {
163163
}
164164
}
165165

166+
/// An invoke is similar to a normal function call, but used to
167+
/// call functions that may throw an exception, and then respond to the exception.
168+
///
169+
/// When the called function returns normally, the `then` block is evaluated next. If instead
170+
/// the function threw an exception, the `catch` block is entered. The first non-phi
171+
/// instruction of the catch block must be a `landingpad` instruction. See also
172+
/// [`Builder::build_landing_pad`].
173+
///
174+
/// The [`add_prune_eh_pass`] turns an invoke into a call when the called function is
175+
/// guaranteed to never throw an exception.
176+
///
177+
/// [`add_prune_eh_pass`]: crate::passes::PassManager::add_prune_eh_pass
178+
///
179+
/// This example catches C++ exceptions of type `int`, and returns `0` if an exceptions is thrown.
180+
/// For usage of a cleanup landing pad and the `resume` instruction, see [`Builder::build_resume`]
181+
/// ```no_run
182+
/// use inkwell::context::Context;
183+
/// use inkwell::AddressSpace;
184+
///
185+
/// let context = Context::create();
186+
/// let module = context.create_module("sum");
187+
/// let builder = context.create_builder();
188+
///
189+
/// let f32_type = context.f32_type();
190+
/// let fn_type = f32_type.fn_type(&[], false);
191+
///
192+
/// // we will pretend this function can throw an exception
193+
/// let function = module.add_function("bomb", fn_type, None);
194+
/// let basic_block = context.append_basic_block(function, "entry");
195+
///
196+
/// builder.position_at_end(basic_block);
197+
///
198+
/// let pi = f32_type.const_float(::std::f64::consts::PI);
199+
///
200+
/// builder.build_return(Some(&pi));
201+
///
202+
/// let function2 = module.add_function("wrapper", fn_type, None);
203+
/// let basic_block2 = context.append_basic_block(function2, "entry");
204+
///
205+
/// builder.position_at_end(basic_block2);
206+
///
207+
/// let then_block = context.append_basic_block(function2, "then_block");
208+
/// let catch_block = context.append_basic_block(function2, "catch_block");
209+
///
210+
/// let call_site = builder.build_invoke(function, &[], then_block, catch_block, "get_pi");
211+
///
212+
/// {
213+
/// builder.position_at_end(then_block);
214+
///
215+
/// // in the then_block, the `call_site` value is defined and can be used
216+
/// let result = call_site.try_as_basic_value().left().unwrap();
217+
///
218+
/// builder.build_return(Some(&result));
219+
/// }
220+
///
221+
/// {
222+
/// builder.position_at_end(catch_block);
223+
///
224+
/// // the personality function used by C++
225+
/// let personality_function = {
226+
/// let name = "__gxx_personality_v0";
227+
/// let linkage = Some(Linkage::External);
228+
///
229+
/// module.add_function(name, context.i64_type().fn_type(&[], false), linkage)
230+
/// };
231+
///
232+
/// // type of an exception in C++
233+
/// let i8_ptr_type = context.i32_type().ptr_type(AddressSpace::Generic);
234+
/// let i32_type = context.i32_type();
235+
/// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false);
236+
///
237+
/// let null = i8_ptr_type.const_zero();
238+
/// let res = builder.build_landing_pad(exception_type, personality_function, &[null], false, "res");
239+
///
240+
/// // we handle the exception by returning a default value
241+
/// builder.build_return(Some(&f32_type.const_zero()));
242+
/// }
243+
/// ```
244+
pub fn build_invoke<F>(
245+
&self,
246+
function: F,
247+
args: &[BasicValueEnum<'ctx>],
248+
then_block: BasicBlock<'ctx>,
249+
catch_block: BasicBlock<'ctx>,
250+
name: &str,
251+
) -> CallSiteValue<'ctx>
252+
where
253+
F: Into<CallableValue<'ctx>>,
254+
{
255+
let callable_value = function.into();
256+
let fn_val_ref = callable_value.as_value_ref();
257+
258+
// LLVM gets upset when void return calls are named because they don't return anything
259+
let name = if callable_value.returns_void() {
260+
""
261+
} else {
262+
name
263+
};
264+
265+
let c_string = to_c_str(name);
266+
let mut args: Vec<LLVMValueRef> = args.iter().map(|val| val.as_value_ref()).collect();
267+
let value = unsafe {
268+
LLVMBuildInvoke(
269+
self.builder,
270+
fn_val_ref,
271+
args.as_mut_ptr(),
272+
args.len() as u32,
273+
then_block.basic_block,
274+
catch_block.basic_block,
275+
c_string.as_ptr(),
276+
)
277+
};
278+
279+
unsafe {
280+
CallSiteValue::new(value)
281+
}
282+
}
283+
284+
/// Landing pads are places where control flow jumps to if a [`Builder::build_invoke`] triggered an exception.
285+
/// The landing pad will match the exception against its *clauses*. Depending on the clause
286+
/// that is matched, the exception can then be handled, or resumed after some optional cleanup,
287+
/// causing the exception to bubble up.
288+
///
289+
/// Exceptions in LLVM are designed based on the needs of a C++ compiler, but can be used more generally.
290+
/// Here are some specific examples of landing pads. For a full example of handling an exception, see [`Builder::build_invoke`].
291+
///
292+
/// * **cleanup**: a cleanup landing pad is always visited when unwinding the stack.
293+
/// A cleanup is extra code that needs to be run when unwinding a scope. C++ destructors are a typical example.
294+
/// In a language with reference counting, the cleanup block can decrement the refcount of values in scope.
295+
/// The [`Builder::build_resume`] function has a full example using a cleanup lading pad.
296+
///
297+
/// ```no_run
298+
/// use inkwell::context::Context;
299+
/// use inkwell::AddressSpace;
300+
///
301+
/// let context = Context::create();
302+
/// let module = context.create_module("sum");
303+
/// let builder = context.create_builder();
304+
///
305+
/// // type of an exception in C++
306+
/// let i8_ptr_type = context.i8_type().ptr_type(AddressSpace::Generic);
307+
/// let i32_type = context.i32_type();
308+
/// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false);
309+
///
310+
/// // the personality function used by C++
311+
/// let personality_function = {
312+
/// let name = "__gxx_personality_v0";
313+
/// let linkage = Some(Linkage::External);
314+
///
315+
/// module.add_function(name, context.i64_type().fn_type(&[], false), linkage)
316+
/// };
317+
///
318+
/// // make the cleanup landing pad
319+
/// let res = builder.build_landing_pad( exception_type, personality_function, &[], true, "res");
320+
/// ```
321+
///
322+
/// * **catch all**: An implementation of the C++ `catch(...)`, which catches all exceptions.
323+
/// A catch clause with a NULL pointer value will match anything.
324+
///
325+
/// ```no_run
326+
/// use inkwell::context::Context;
327+
/// use inkwell::AddressSpace;
328+
///
329+
/// let context = Context::create();
330+
/// let module = context.create_module("sum");
331+
/// let builder = context.create_builder();
332+
///
333+
/// // type of an exception in C++
334+
/// let i8_ptr_type = context.i8_type().ptr_type(AddressSpace::Generic);
335+
/// let i32_type = context.i32_type();
336+
/// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false);
337+
///
338+
/// // the personality function used by C++
339+
/// let personality_function = {
340+
/// let name = "__gxx_personality_v0";
341+
/// let linkage = Some(Linkage::External);
342+
///
343+
/// module.add_function(name, context.i64_type().fn_type(&[], false), linkage)
344+
/// };
345+
///
346+
/// // make a null pointer of type i8
347+
/// let null = i8_ptr_type.const_zero();
348+
///
349+
/// // make the catch all landing pad
350+
/// let res = builder.build_landing_pad(exception_type, personality_function, &[null], false, "res");
351+
/// ```
352+
///
353+
/// * **catch a type of exception**: Catch a specific type of exception. The example uses C++'s type info.
354+
///
355+
/// ```no_run
356+
/// use inkwell::context::Context;
357+
/// use inkwell::module::Linkage;
358+
/// use inkwell::AddressSpace;
359+
///
360+
/// let context = Context::create();
361+
/// let module = context.create_module("sum");
362+
/// let builder = context.create_builder();
363+
///
364+
/// // type of an exception in C++
365+
/// let i8_ptr_type = context.i8_type().ptr_type(AddressSpace::Generic);
366+
/// let i32_type = context.i32_type();
367+
/// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false);
368+
///
369+
/// // the personality function used by C++
370+
/// let personality_function = {
371+
/// let name = "__gxx_personality_v0";
372+
/// let linkage = Some(Linkage::External);
373+
///
374+
/// module.add_function(name, context.i64_type().fn_type(&[], false), linkage)
375+
/// };
376+
///
377+
/// // link in the C++ type info for the `int` type
378+
/// let type_info_int = module.add_global(i8_ptr_type, Some(AddressSpace::Generic), "_ZTIi");
379+
/// type_info_int.set_linkage(Linkage::External);
380+
///
381+
/// // make the catch landing pad
382+
/// let res = builder.build_landing_pad(exception_type, personality_function, &[type_info_int], false, "res");
383+
/// ```
384+
///
385+
/// * **filter**: A filter clause encodes that only some types of exceptions are valid at this
386+
/// point. A filter clause is made by constructing a clause from a constant array.
387+
///
388+
/// ```no_run
389+
/// use inkwell::context::Context;
390+
/// use inkwell::module::Linkage;
391+
/// use inkwell::values::AnyValue;
392+
/// use inkwell::AddressSpace;
393+
///
394+
/// let context = Context::create();
395+
/// let module = context.create_module("sum");
396+
/// let builder = context.create_builder();
397+
///
398+
/// // type of an exception in C++
399+
/// let i8_ptr_type = context.i8_type().ptr_type(AddressSpace::Generic);
400+
/// let i32_type = context.i32_type();
401+
/// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false);
402+
///
403+
/// // the personality function used by C++
404+
/// let personality_function = {
405+
/// let name = "__gxx_personality_v0";
406+
/// let linkage = Some(Linkage::External);
407+
///
408+
/// module.add_function(name, context.i64_type().fn_type(&[], false), linkage)
409+
/// };
410+
///
411+
/// // link in the C++ type info for the `int` type
412+
/// let type_info_int = module.add_global(i8_ptr_type, Some(AddressSpace::Generic), "_ZTIi");
413+
/// type_info_int.set_linkage(Linkage::External);
414+
///
415+
/// // make the filter landing pad
416+
/// let filter_pattern = i8_ptr_type.const_array(&[type_info_int.as_any_value_enum().into_pointer_value()]);
417+
/// let res = builder.build_landing_pad(exception_type, personality_function, &[filter_pattern], false, "res");
418+
/// ```
419+
pub fn build_landing_pad<T>(
420+
&self,
421+
exception_type: T,
422+
personality_function: FunctionValue<'ctx>,
423+
clauses: &[BasicValueEnum<'ctx>],
424+
is_cleanup: bool,
425+
name: &str
426+
) -> BasicValueEnum<'ctx>
427+
where
428+
T: BasicType<'ctx>,
429+
{
430+
let c_string = to_c_str(name);
431+
let num_clauses = clauses.len() as u32;
432+
433+
let value = unsafe {
434+
LLVMBuildLandingPad(
435+
self.builder,
436+
exception_type.as_type_ref(),
437+
personality_function.as_value_ref(),
438+
num_clauses,
439+
c_string.as_ptr(),
440+
)
441+
};
442+
443+
444+
for clause in clauses {
445+
unsafe {
446+
LLVMAddClause(value, clause.as_value_ref());
447+
}
448+
}
449+
450+
unsafe {
451+
LLVMSetCleanup(value, is_cleanup as _);
452+
};
453+
454+
unsafe {
455+
BasicValueEnum::new(value)
456+
}
457+
}
458+
459+
/// Resume propagation of an existing (in-flight) exception whose unwinding was interrupted with a landingpad instruction.
460+
///
461+
/// This example uses a cleanup landing pad. A cleanup is extra code that needs to be run when
462+
/// unwinding a scope. C++ destructors are a typical example. In a language with reference counting,
463+
/// the cleanup block can decrement the refcount of values in scope.
464+
///
465+
/// ```no_run
466+
/// use inkwell::context::Context;
467+
/// use inkwell::AddressSpace;
468+
///
469+
/// let context = Context::create();
470+
/// let module = context.create_module("sum");
471+
/// let builder = context.create_builder();
472+
///
473+
/// let f32_type = context.f32_type();
474+
/// let fn_type = f32_type.fn_type(&[], false);
475+
///
476+
/// // we will pretend this function can throw an exception
477+
/// let function = module.add_function("bomb", fn_type, None);
478+
/// let basic_block = context.append_basic_block(function, "entry");
479+
///
480+
/// builder.position_at_end(basic_block);
481+
///
482+
/// let pi = f32_type.const_float(::std::f64::consts::PI);
483+
///
484+
/// builder.build_return(Some(&pi));
485+
///
486+
/// let function2 = module.add_function("wrapper", fn_type, None);
487+
/// let basic_block2 = context.append_basic_block(function2, "entry");
488+
///
489+
/// builder.position_at_end(basic_block2);
490+
///
491+
/// let then_block = context.append_basic_block(function2, "then_block");
492+
/// let catch_block = context.append_basic_block(function2, "catch_block");
493+
///
494+
/// let call_site = builder.build_invoke(function, &[], then_block, catch_block, "get_pi");
495+
///
496+
/// {
497+
/// builder.position_at_end(then_block);
498+
///
499+
/// // in the then_block, the `call_site` value is defined and can be used
500+
/// let result = call_site.try_as_basic_value().left().unwrap();
501+
///
502+
/// builder.build_return(Some(&result));
503+
/// }
504+
///
505+
/// {
506+
/// builder.position_at_end(catch_block);
507+
///
508+
/// // the personality function used by C++
509+
/// let personality_function = {
510+
/// let name = "__gxx_personality_v0";
511+
/// let linkage = Some(Linkage::External);
512+
///
513+
/// module.add_function(name, context.i64_type().fn_type(&[], false), linkage)
514+
/// };
515+
///
516+
/// // type of an exception in C++
517+
/// let i8_ptr_type = context.i32_type().ptr_type(AddressSpace::Generic);
518+
/// let i32_type = context.i32_type();
519+
/// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false);
520+
///
521+
/// // make the landing pad; must give a concrete type to the slice
522+
/// let res = builder.build_landing_pad( exception_type, personality_function, &[], true, "res");
523+
///
524+
/// // do cleanup ...
525+
///
526+
/// builder.build_resume(res);
527+
/// }
528+
/// ```
529+
pub fn build_resume<V : BasicValue<'ctx>>(&self, value: V) -> InstructionValue<'ctx> {
530+
let val = unsafe { LLVMBuildResume(self.builder, value.as_value_ref()) };
531+
532+
unsafe {
533+
InstructionValue::new(val)
534+
}
535+
}
536+
166537
// REVIEW: Doesn't GEP work on array too?
167538
/// GEP is very likely to segfault if indexes are used incorrectly, and is therefore an unsafe function. Maybe we can change this in the future.
168539
pub unsafe fn build_gep(&self, ptr: PointerValue<'ctx>, ordered_indexes: &[IntValue<'ctx>], name: &str) -> PointerValue<'ctx> {

0 commit comments

Comments
 (0)