@@ -166,59 +166,11 @@ impl<'ctx> Builder<'ctx> {
166
166
///
167
167
/// When the called function returns normally, the `then` block is evaluated next. If instead
168
168
/// 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`].
221
171
///
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`]
222
174
/// ```no_run
223
175
/// use inkwell::context::Context;
224
176
/// use inkwell::AddressSpace;
@@ -274,24 +226,197 @@ impl<'ctx> Builder<'ctx> {
274
226
/// let i32_type = context.i32_type();
275
227
/// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false);
276
228
///
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");
278
231
///
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()));
282
234
/// }
283
235
/// ```
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 > (
285
407
& self ,
286
408
exception_type : T ,
287
409
personality_function : FunctionValue < ' ctx > ,
410
+ clauses : & [ V ] ,
411
+ is_cleanup : bool ,
288
412
name : & str
289
413
) -> BasicValueEnum < ' ctx >
290
414
where
291
415
T : BasicType < ' ctx > ,
416
+ V : BasicValue < ' ctx > ,
292
417
{
293
418
let c_string = to_c_str ( name) ;
294
- let num_clauses = 0u32 ;
419
+ let num_clauses = clauses . len ( ) as u32 ;
295
420
296
421
let value = unsafe {
297
422
LLVMBuildLandingPad (
@@ -303,19 +428,27 @@ impl<'ctx> Builder<'ctx> {
303
428
)
304
429
} ;
305
430
431
+
432
+ for clause in clauses {
433
+ unsafe {
434
+ LLVMAddClause ( value, clause. as_value_ref ( ) ) ;
435
+ }
436
+ }
437
+
306
438
unsafe {
307
- LLVMSetCleanup ( value, 1 ) ;
439
+ LLVMSetCleanup ( value, is_cleanup as _ ) ;
308
440
} ;
309
441
310
442
unsafe {
311
443
BasicValueEnum :: new ( value)
312
444
}
313
445
}
314
446
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 .
316
448
///
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.
319
452
///
320
453
/// ```no_run
321
454
/// use inkwell::context::Context;
@@ -372,63 +505,15 @@ impl<'ctx> Builder<'ctx> {
372
505
/// let i32_type = context.i32_type();
373
506
/// let exception_type = context.struct_type(&[i8_ptr_type.into(), i32_type.into()], false);
374
507
///
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");
376
511
///
377
- /// // we handle the exception by returning a default value
512
+ /// // do cleanup ...
378
513
///
379
- /// builder.build_return(Some(&f32_type.const_zero()) );
514
+ /// builder.build_resume(res );
380
515
/// }
381
516
/// ```
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.
432
517
pub fn build_resume < V : BasicValue < ' ctx > > ( & self , value : V ) -> InstructionValue < ' ctx > {
433
518
let val = unsafe { LLVMBuildResume ( self . builder , value. as_value_ref ( ) ) } ;
434
519
0 commit comments