diff --git a/gcc/rust/backend/rust-compile-intrinsic.cc b/gcc/rust/backend/rust-compile-intrinsic.cc index 08b64439dab6..3d624a0d6739 100644 --- a/gcc/rust/backend/rust-compile-intrinsic.cc +++ b/gcc/rust/backend/rust-compile-intrinsic.cc @@ -23,6 +23,7 @@ #include "rust-diagnostics.h" #include "rust-location.h" #include "rust-constexpr.h" +#include "rust-session-manager.h" #include "rust-tree.h" #include "tree-core.h" #include "rust-gcc.h" @@ -194,6 +195,17 @@ expect_handler (bool likely) }; } +static tree +try_handler_inner (Context *ctx, TyTy::FnType *fntype, bool is_new_api); + +const static std::function +try_handler (bool is_new_api) +{ + return [is_new_api] (Context *ctx, TyTy::FnType *fntype) { + return try_handler_inner (ctx, fntype, is_new_api); + }; +} + inline tree sorry_handler (Context *ctx, TyTy::FnType *fntype) { @@ -241,6 +253,8 @@ static const std::mapget_params ().size () == 3); + + tree lookup = NULL_TREE; + if (check_for_cached_intrinsic (ctx, fntype, &lookup)) + return lookup; + auto fndecl = compile_intrinsic_function (ctx, fntype); + + enter_intrinsic_block (ctx, fndecl); + + // The following tricks are needed to make sure the try-catch blocks are not + // optimized away + TREE_READONLY (fndecl) = 0; + DECL_DISREGARD_INLINE_LIMITS (fndecl) = 1; + DECL_ATTRIBUTES (fndecl) = tree_cons (get_identifier ("always_inline"), + NULL_TREE, DECL_ATTRIBUTES (fndecl)); + + // BUILTIN try_handler FN BODY BEGIN + // setup the params + std::vector param_vars; + compile_fn_params (ctx, fntype, fndecl, ¶m_vars); + if (!Backend::function_set_parameters (fndecl, param_vars)) + return error_mark_node; + tree enclosing_scope = NULL_TREE; + + bool panic_is_abort = Session::get_instance ().options.get_panic_strategy () + == CompileOptions::PanicStrategy::Abort; + tree try_fn = Backend::var_expression (param_vars[0], UNDEF_LOCATION); + tree user_data = Backend::var_expression (param_vars[1], UNDEF_LOCATION); + tree catch_fn = Backend::var_expression (param_vars[2], UNDEF_LOCATION); + tree normal_return_stmt = NULL_TREE; + tree error_return_stmt = NULL_TREE; + tree try_call = Backend::call_expression (try_fn, {user_data}, nullptr, + BUILTINS_LOCATION); + tree catch_call = NULL_TREE; + tree try_block = Backend::block (fndecl, enclosing_scope, {}, UNDEF_LOCATION, + UNDEF_LOCATION); + + if (is_new_api) + { + auto ret_type = TyTyResolveCompile::get_unit_type (); + auto ret_expr = Backend::constructor_expression (ret_type, false, {}, -1, + UNDEF_LOCATION); + normal_return_stmt + = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION); + error_return_stmt + = Backend::return_statement (fndecl, ret_expr, BUILTINS_LOCATION); + } + else + { + normal_return_stmt = Backend::return_statement (fndecl, integer_zero_node, + BUILTINS_LOCATION); + error_return_stmt = Backend::return_statement (fndecl, integer_one_node, + BUILTINS_LOCATION); + } + Backend::block_add_statements (try_block, + std::vector{try_call, + normal_return_stmt}); + if (panic_is_abort) + { + // skip building the try-catch construct + ctx->add_statement (try_block); + finalize_intrinsic_block (ctx, fndecl); + return fndecl; + } + + tree eh_pointer + = build_call_expr (builtin_decl_explicit (BUILT_IN_EH_POINTER), 1, + integer_zero_node); + catch_call = Backend::call_expression (catch_fn, {user_data, eh_pointer}, + NULL_TREE, BUILTINS_LOCATION); + + tree catch_block = Backend::block (fndecl, enclosing_scope, {}, + UNDEF_LOCATION, UNDEF_LOCATION); + Backend::block_add_statements (catch_block, + std::vector{catch_call, + error_return_stmt}); + // emulate what cc1plus is doing for C++ try-catch + tree inner_eh_construct + = Backend::exception_handler_statement (catch_call, NULL_TREE, + error_return_stmt, + BUILTINS_LOCATION); + // TODO(liushuyu): eh_personality needs to be implemented as a runtime thing + auto eh_construct + = Backend::exception_handler_statement (try_block, inner_eh_construct, + NULL_TREE, BUILTINS_LOCATION); + ctx->add_statement (eh_construct); + // BUILTIN try_handler FN BODY END + finalize_intrinsic_block (ctx, fndecl); + + return fndecl; +} + } // namespace Compile } // namespace Rust diff --git a/gcc/rust/lang.opt b/gcc/rust/lang.opt index 35798f5e01a1..10a91f33585c 100644 --- a/gcc/rust/lang.opt +++ b/gcc/rust/lang.opt @@ -212,4 +212,17 @@ frust-borrowcheck Rust Var(flag_borrowcheck) Use the WIP borrow checker. +frust-panic= +Rust Joined RejectNegative Enum(frust_panic) Var(flag_rust_panic) +-frust-edition=[unwind|abort] Panic strategy to compile crate with + +Enum +Name(frust_panic) Type(int) UnknownError(unknown panic strategy %qs) + +EnumValue +Enum(frust_panic) String(unwind) Value(0) + +EnumValue +Enum(frust_panic) String(abort) Value(1) + ; This comment is to ensure we retain the blank line above. diff --git a/gcc/rust/rust-lang.cc b/gcc/rust/rust-lang.cc index 258b5f3b61f3..df06026387c1 100644 --- a/gcc/rust/rust-lang.cc +++ b/gcc/rust/rust-lang.cc @@ -146,6 +146,8 @@ grs_langhook_init_options_struct (struct gcc_options *opts) opts->x_warn_unused_result = 1; /* lets warn for infinite recursion*/ opts->x_warn_infinite_recursion = 1; + /* Enable exception handling (aka `panic!` in Rust) */ + opts->x_flag_exceptions = 1; // nothing yet - used by frontends to change specific options for the language Rust::Session::get_instance ().init_options (); diff --git a/gcc/rust/rust-session-manager.cc b/gcc/rust/rust-session-manager.cc index 1cf58e814f69..e88dc69356bf 100644 --- a/gcc/rust/rust-session-manager.cc +++ b/gcc/rust/rust-session-manager.cc @@ -265,6 +265,9 @@ Session::handle_option ( case OPT_frust_metadata_output_: options.set_metadata_output (arg); break; + case OPT_frust_panic_: + options.set_panic_strategy (flag_rust_panic); + break; default: break; diff --git a/gcc/rust/rust-session-manager.h b/gcc/rust/rust-session-manager.h index 17b52bae4b1d..c6ddccb62294 100644 --- a/gcc/rust/rust-session-manager.h +++ b/gcc/rust/rust-session-manager.h @@ -264,6 +264,13 @@ struct CompileOptions } compile_until = CompileStep::End; + enum class PanicStrategy + { + Unwind, + Abort, + } panic_strategy + = PanicStrategy::Unwind; + bool dump_option_enabled (DumpOption option) const { return dump_options.find (option) != dump_options.end (); @@ -320,6 +327,13 @@ struct CompileOptions const CompileStep &get_compile_until () const { return compile_until; } + void set_panic_strategy (int strategy) + { + panic_strategy = static_cast (strategy); + } + + const PanicStrategy &get_panic_strategy () const { return panic_strategy; } + void set_metadata_output (const std::string &path) { metadata_output_path = path; diff --git a/gcc/testsuite/rust/compile/try-catch-unwind-new.rs b/gcc/testsuite/rust/compile/try-catch-unwind-new.rs new file mode 100644 index 000000000000..b176f7a100ba --- /dev/null +++ b/gcc/testsuite/rust/compile/try-catch-unwind-new.rs @@ -0,0 +1,20 @@ +// { dg-options "-O2 -w -fdump-tree-optimized" } +#![feature(intrinsics)] + +extern "rust-intrinsic" { + // { dg-final { scan-tree-dump-times "__builtin_eh_pointer" 1 "optimized" } } + fn catch_unwind(try_fn: fn(_: *mut u8), data: *mut u8, catch_fn: fn(_: *mut u8, _: *mut u8)); +} + +extern "C" { + fn try_fn(data: *mut u8); + fn catch_fn(data: *mut u8, ex: *mut u8); +} + +pub fn not_main(d: &mut u8) { + unsafe { + // { dg-final { scan-tree-dump-times "try_fn" 1 "optimized" } } + catch_unwind(try_fn, d, catch_fn); + // { dg-final { scan-tree-dump-times "catch_fn" 1 "optimized" } } + } +} diff --git a/gcc/testsuite/rust/compile/try-catch-unwind-old.rs b/gcc/testsuite/rust/compile/try-catch-unwind-old.rs new file mode 100644 index 000000000000..e97d52cd64e3 --- /dev/null +++ b/gcc/testsuite/rust/compile/try-catch-unwind-old.rs @@ -0,0 +1,21 @@ +// { dg-options "-O2 -w -fdump-tree-optimized" } +#![feature(intrinsics)] + +extern "rust-intrinsic" { + // { dg-final { scan-tree-dump-times "__builtin_eh_pointer" 1 "optimized" } } + fn r#try(try_fn: fn(_: *mut u8), data: *mut u8, catch_fn: fn(_: *mut u8, _: *mut u8)) -> i32; +} + +extern "C" { + fn try_fn(data: *mut u8); + fn catch_fn(data: *mut u8, ex: *mut u8); +} + +pub fn not_main(d: &mut u8) -> i32 { + unsafe { + // { dg-final { scan-tree-dump-times "try_fn" 1 "optimized" } } + let _: i32 = r#try(try_fn, d, catch_fn); + // { dg-final { scan-tree-dump-times "catch_fn" 1 "optimized" } } + } + 42 +}