Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add try intrinsics #2614

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions gcc/rust/backend/rust-compile-intrinsic.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<tree (Context *, TyTy::FnType *)>
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)
{
Expand Down Expand Up @@ -241,6 +253,8 @@ static const std::map<std::string,
{"likely", expect_handler (true)},
{"unlikely", expect_handler (false)},
{"assume", assume_handler},
{"try", try_handler (false)},
{"catch_unwind", try_handler (true)},
};

Intrinsics::Intrinsics (Context *ctx) : ctx (ctx) {}
Expand Down Expand Up @@ -1266,5 +1280,100 @@ assume_handler (Context *ctx, TyTy::FnType *fntype)
return fndecl;
}

static tree
try_handler_inner (Context *ctx, TyTy::FnType *fntype, bool is_new_api)
{
rust_assert (fntype->get_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<Bvariable *> param_vars;
compile_fn_params (ctx, fntype, fndecl, &param_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<tree>{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<tree>{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
13 changes: 13 additions & 0 deletions gcc/rust/lang.opt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
2 changes: 2 additions & 0 deletions gcc/rust/rust-lang.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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 ();
Expand Down
3 changes: 3 additions & 0 deletions gcc/rust/rust-session-manager.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
14 changes: 14 additions & 0 deletions gcc/rust/rust-session-manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 ();
Expand Down Expand Up @@ -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<PanicStrategy> (strategy);
}

const PanicStrategy &get_panic_strategy () const { return panic_strategy; }

void set_metadata_output (const std::string &path)
{
metadata_output_path = path;
Expand Down
20 changes: 20 additions & 0 deletions gcc/testsuite/rust/compile/try-catch-unwind-new.rs
Original file line number Diff line number Diff line change
@@ -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" } }
}
}
21 changes: 21 additions & 0 deletions gcc/testsuite/rust/compile/try-catch-unwind-old.rs
Original file line number Diff line number Diff line change
@@ -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
}
Loading