Skip to content

Commit 1b146b0

Browse files
committed
documentation
1 parent 0d1c929 commit 1b146b0

File tree

2 files changed

+281
-117
lines changed

2 files changed

+281
-117
lines changed

src/builder.rs

Lines changed: 200 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -166,59 +166,11 @@ impl<'ctx> Builder<'ctx> {
166166
///
167167
/// When the called function returns normally, the `then` block is evaluated next. If instead
168168
/// the function threw an exception, the `catch` block is intered. The first non-phi
169-
/// instruction of the catch block must be a `landingpad` instruction.
170-
///
171-
/// This function can take either a `FunctionValue` or a `PointerValue`
172-
/// which is a function pointer. It will panic if the `PointerValue` is not a function pointer.
173-
/// This may be turned into a Result in the future, however.
174-
pub fn build_invoke<F>(
175-
&self,
176-
function: F,
177-
args: &[BasicValueEnum<'ctx>],
178-
then_block: BasicBlock<'ctx>,
179-
catch_block: BasicBlock<'ctx>,
180-
name: &str,
181-
) -> CallSiteValue<'ctx>
182-
where
183-
F: Into<CallableValue<'ctx>>,
184-
{
185-
let callable_value = function.into();
186-
let fn_val_ref = callable_value.as_value_ref();
187-
188-
// LLVM gets upset when void return calls are named because they don't return anything
189-
let name = if callable_value.returns_void() {
190-
""
191-
} else {
192-
name
193-
};
194-
195-
let c_string = to_c_str(name);
196-
let mut args: Vec<LLVMValueRef> = args.iter().map(|val| val.as_value_ref()).collect();
197-
let value = unsafe {
198-
LLVMBuildInvoke(
199-
self.builder,
200-
fn_val_ref,
201-
args.as_mut_ptr(),
202-
args.len() as u32,
203-
then_block.basic_block,
204-
catch_block.basic_block,
205-
c_string.as_ptr(),
206-
)
207-
};
208-
209-
unsafe {
210-
CallSiteValue::new(value)
211-
}
212-
}
213-
214-
/// Build a cleanup landing pad. This landing pad is always visited when unwinding the stack.
215-
/// A cleanup is extra code that needs to be run when unwinding a scope. C++ destructors are a
216-
/// typical example. In a language with reference counting, the cleanup block can decrement the
217-
/// refcount of values in scope.
218-
///
219-
/// The personality function determines how an exception is handled.
220-
/// This example shows how to use the C++ personality function:
169+
/// instruction of the catch block must be a `landingpad` instruction. See also
170+
/// [`Builder::build_landing_pad`].
221171
///
172+
/// This example catches C++ exceptions of type `int`, and returns `0` if an exceptions is thrown.
173+
/// For usage of a cleanup landing pad and the `resume` instruction, see [`Builder::build_resume`]
222174
/// ```no_run
223175
/// use inkwell::context::Context;
224176
/// use inkwell::AddressSpace;
@@ -274,24 +226,197 @@ impl<'ctx> Builder<'ctx> {
274226
/// let i32_type = context.i32_type();
275227
/// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false);
276228
///
277-
/// let res = builder.build_cleanup_landing_pad( exception_type, personality_function, "res");
229+
/// let null = i8_ptr_type.const_zero();
230+
/// let res = builder.build_landing_pad(exception_type, personality_function, &[null], false, "res");
278231
///
279-
/// // do cleanup ...
280-
///
281-
/// builder.build_resume(res);
232+
/// // we handle the exception by returning a default value
233+
/// builder.build_return(Some(&f32_type.const_zero()));
282234
/// }
283235
/// ```
284-
pub fn build_cleanup_landing_pad<T>(
236+
pub fn build_invoke<F>(
237+
&self,
238+
function: F,
239+
args: &[BasicValueEnum<'ctx>],
240+
then_block: BasicBlock<'ctx>,
241+
catch_block: BasicBlock<'ctx>,
242+
name: &str,
243+
) -> CallSiteValue<'ctx>
244+
where
245+
F: Into<CallableValue<'ctx>>,
246+
{
247+
let callable_value = function.into();
248+
let fn_val_ref = callable_value.as_value_ref();
249+
250+
// LLVM gets upset when void return calls are named because they don't return anything
251+
let name = if callable_value.returns_void() {
252+
""
253+
} else {
254+
name
255+
};
256+
257+
let c_string = to_c_str(name);
258+
let mut args: Vec<LLVMValueRef> = args.iter().map(|val| val.as_value_ref()).collect();
259+
let value = unsafe {
260+
LLVMBuildInvoke(
261+
self.builder,
262+
fn_val_ref,
263+
args.as_mut_ptr(),
264+
args.len() as u32,
265+
then_block.basic_block,
266+
catch_block.basic_block,
267+
c_string.as_ptr(),
268+
)
269+
};
270+
271+
unsafe {
272+
CallSiteValue::new(value)
273+
}
274+
}
275+
276+
/// Landing pads are places where control flow jumps to if a [`Builder::build_invoke`] triggered an exception.
277+
/// The landing pad will inspect the exception, and either handle the exception, or after some optional cleanup,
278+
/// will resume the exception handling, causing the exception to bubble up.
279+
///
280+
/// Exceptions in LLVM are designed based on the needs of a C++ compiler, but can be used more generally.
281+
/// Here are some specific examples of landing pads. For a full example, see [`Builder::build_invoke`].
282+
///
283+
/// * **cleanup**: a cleanup landing pad is always visited when unwinding the stack.
284+
/// A cleanup is extra code that needs to be run when unwinding a scope. C++ destructors are a typical example.
285+
/// In a language with reference counting, the cleanup block can decrement the refcount of values in scope.
286+
///
287+
/// ```no_run
288+
/// use inkwell::context::Context;
289+
/// use inkwell::AddressSpace;
290+
///
291+
/// let context = Context::create();
292+
/// let module = context.create_module("sum");
293+
/// let builder = context.create_builder();
294+
///
295+
/// // type of an exception in C++
296+
/// let i8_ptr_type = context.i8_type().ptr_type(AddressSpace::Generic);
297+
/// let i32_type = context.i32_type();
298+
/// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false);
299+
///
300+
/// // the personality function used by C++
301+
/// let personality_function = {
302+
/// let name = "__gxx_personality_v0";
303+
///
304+
/// module.add_function(name, context.i64_type().fn_type(&[], false), None)
305+
/// };
306+
///
307+
/// // make the cleanup landing pad
308+
/// let clauses: &[inkwell::values::PointerValue] = &[];
309+
/// let res = builder.build_landing_pad( exception_type, personality_function, clauses, true, "res");
310+
/// ```
311+
///
312+
/// * **catch all**: An implementation of the C++ `catch(...)`, which catches all exceptions.
313+
/// A catch clause with a NULL pointer value will match anything.
314+
///
315+
/// ```no_run
316+
/// use inkwell::context::Context;
317+
/// use inkwell::AddressSpace;
318+
///
319+
/// let context = Context::create();
320+
/// let module = context.create_module("sum");
321+
/// let builder = context.create_builder();
322+
///
323+
/// // type of an exception in C++
324+
/// let i8_ptr_type = context.i8_type().ptr_type(AddressSpace::Generic);
325+
/// let i32_type = context.i32_type();
326+
/// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false);
327+
///
328+
/// // the personality function used by C++
329+
/// let personality_function = {
330+
/// let name = "__gxx_personality_v0";
331+
///
332+
/// module.add_function(name, context.i64_type().fn_type(&[], false), None)
333+
/// };
334+
///
335+
/// // make a null pointer of type i8
336+
/// let null = i8_ptr_type.const_zero();
337+
///
338+
/// // make the catch all landing pad
339+
/// let res = builder.build_landing_pad(exception_type, personality_function, &[null], false, "res");
340+
/// ```
341+
///
342+
/// * **catch a typeinfo**: Catch a specific type of exception. The example uses C++'s type info.
343+
///
344+
/// ```no_run
345+
/// use inkwell::context::Context;
346+
/// use inkwell::module::Linkage;
347+
/// use inkwell::AddressSpace;
348+
///
349+
/// let context = Context::create();
350+
/// let module = context.create_module("sum");
351+
/// let builder = context.create_builder();
352+
///
353+
/// // type of an exception in C++
354+
/// let i8_ptr_type = context.i8_type().ptr_type(AddressSpace::Generic);
355+
/// let i32_type = context.i32_type();
356+
/// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false);
357+
///
358+
/// // the personality function used by C++
359+
/// let personality_function = {
360+
/// let name = "__gxx_personality_v0";
361+
///
362+
/// module.add_function(name, context.i64_type().fn_type(&[], false), None)
363+
/// };
364+
///
365+
/// // link in the C++ type info for the `int` type
366+
/// let type_info_int = module.add_global(i8_ptr_type, Some(AddressSpace::Generic), "_ZTIi");
367+
/// type_info_int.set_linkage(Linkage::External);
368+
///
369+
/// // make the catch landing pad
370+
/// let res = builder.build_landing_pad(exception_type, personality_function, &[type_info_int], false, "res");
371+
/// ```
372+
///
373+
/// * **filter**: A filter clause encodes that only some types of exceptions are valid at this
374+
/// point. A filter clause is made by constructing a clause from a constant array.
375+
///
376+
/// ```no_run
377+
/// use inkwell::context::Context;
378+
/// use inkwell::module::Linkage;
379+
/// use inkwell::values::AnyValue;
380+
/// use inkwell::AddressSpace;
381+
///
382+
/// let context = Context::create();
383+
/// let module = context.create_module("sum");
384+
/// let builder = context.create_builder();
385+
///
386+
/// // type of an exception in C++
387+
/// let i8_ptr_type = context.i8_type().ptr_type(AddressSpace::Generic);
388+
/// let i32_type = context.i32_type();
389+
/// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false);
390+
///
391+
/// // the personality function used by C++
392+
/// let personality_function = {
393+
/// let name = "__gxx_personality_v0";
394+
///
395+
/// module.add_function(name, context.i64_type().fn_type(&[], false), None)
396+
/// };
397+
///
398+
/// // link in the C++ type info for the `int` type
399+
/// let type_info_int = module.add_global(i8_ptr_type, Some(AddressSpace::Generic), "_ZTIi");
400+
/// type_info_int.set_linkage(Linkage::External);
401+
///
402+
/// // make the filter landing pad
403+
/// let filter_pattern = i8_ptr_type.const_array(&[type_info_int.as_any_value_enum().into_pointer_value()]);
404+
/// let res = builder.build_landing_pad(exception_type, personality_function, &[filter_pattern], false, "res");
405+
/// ```
406+
pub fn build_landing_pad<T, V>(
285407
&self,
286408
exception_type: T,
287409
personality_function: FunctionValue<'ctx>,
410+
clauses: &[V],
411+
is_cleanup: bool,
288412
name: &str
289413
) -> BasicValueEnum<'ctx>
290414
where
291415
T: BasicType<'ctx>,
416+
V: BasicValue<'ctx>,
292417
{
293418
let c_string = to_c_str(name);
294-
let num_clauses = 0u32;
419+
let num_clauses = clauses.len() as u32;
295420

296421
let value = unsafe {
297422
LLVMBuildLandingPad(
@@ -303,19 +428,27 @@ impl<'ctx> Builder<'ctx> {
303428
)
304429
};
305430

431+
432+
for clause in clauses {
433+
unsafe {
434+
LLVMAddClause(value, clause.as_value_ref());
435+
}
436+
}
437+
306438
unsafe {
307-
LLVMSetCleanup(value, 1);
439+
LLVMSetCleanup(value, is_cleanup as _);
308440
};
309441

310442
unsafe {
311443
BasicValueEnum::new(value)
312444
}
313445
}
314446

315-
/// Build a landing pad that matches all exceptions. This corresponds to a `catch(...)` in C++.
447+
/// Resume propagation of an existing (in-flight) exception whose unwinding was interrupted with a landingpad instruction.
316448
///
317-
/// The personality function determines how an exception is handled.
318-
/// This example shows how to use the C++ personality function:
449+
/// This example uses a cleanup landing pad. A cleanup is extra code that needs to be run when
450+
/// unwinding a scope. C++ destructors are a typical example. In a language with reference counting,
451+
/// the cleanup block can decrement the refcount of values in scope.
319452
///
320453
/// ```no_run
321454
/// use inkwell::context::Context;
@@ -372,63 +505,15 @@ impl<'ctx> Builder<'ctx> {
372505
/// let i32_type = context.i32_type();
373506
/// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false);
374507
///
375-
/// let res = builder.build_catch_all_landing_pad(exception_type, personality_function, "res");
508+
/// // make the landing pad; must give a concrete type to the slice
509+
/// let clauses: &[inkwell::values::PointerValue] = &[];
510+
/// let res = builder.build_landing_pad( exception_type, personality_function, clauses, true, "res");
376511
///
377-
/// // we handle the exception by returning a default value
512+
/// // do cleanup ...
378513
///
379-
/// builder.build_return(Some(&f32_type.const_zero()));
514+
/// builder.build_resume(res);
380515
/// }
381516
/// ```
382-
pub fn build_catch_all_landing_pad<T>(
383-
&self,
384-
exception_type: T,
385-
personality_function: FunctionValue<'ctx>,
386-
name: &str,
387-
) -> BasicValueEnum<'ctx>
388-
where
389-
T: BasicType<'ctx>,
390-
{
391-
let c_string = to_c_str(name);
392-
let num_clauses = 1u32;
393-
394-
let value = unsafe {
395-
LLVMBuildLandingPad(
396-
self.builder,
397-
exception_type.as_type_ref(),
398-
personality_function.as_value_ref(),
399-
num_clauses,
400-
c_string.as_ptr(),
401-
)
402-
};
403-
404-
// in landingpad clauses, a null pointer functions as a wildcard pattern:
405-
// it matches any exception.
406-
//
407-
// We follow how c++ encodes the `catch(...)` case, which is by
408-
// using an i8 pointer. This will generate roughly this LLVM IR
409-
//
410-
// ```ll
411-
// %7 = landingpad { i8*, i32 }
412-
// catch i8* null, !dbg !933
413-
// ```
414-
//
415-
let context = personality_function.get_type().get_context();
416-
let i8_type = context.i8_type();
417-
let i8_ptr_type = i8_type.ptr_type(crate::AddressSpace::Generic);
418-
let null = i8_ptr_type.const_zero();
419-
420-
unsafe {
421-
LLVMAddClause(value, null.as_value_ref());
422-
};
423-
424-
unsafe {
425-
BasicValueEnum::new(value)
426-
}
427-
}
428-
429-
/// Resume propagation of an existing (in-flight) exception whose unwinding was interrupted with a landingpad instruction.
430-
///
431-
/// See [`Builder::build_cleanup_landing_pad`] for example usage.
432517
pub fn build_resume<V : BasicValue<'ctx>>(&self, value: V) -> InstructionValue<'ctx> {
433518
let val = unsafe { LLVMBuildResume(self.builder, value.as_value_ref()) };
434519

0 commit comments

Comments
 (0)