From 766f5f8726965dfe69e70f234a08f6bccedc144e Mon Sep 17 00:00:00 2001 From: Iain Buclaw Date: Sun, 29 Aug 2021 00:50:38 +0200 Subject: d: Call the assertp and boundsp variants for assert and array contract failures. gcc/d/ChangeLog: * d-codegen.cc: Include dmd/module.h. (build_filename_from_loc): New function. (d_assert_call): Rename to... (build_assert_call): ...this. (build_array_bounds_call): Call arrayboundsp variant of the array bounds failure callback. (build_bounds_condition): Rename to... (build_bounds_index_condition): ...this. Update signature. (build_bounds_slice_condition): New function. (checkaction_trap_p): New function. (d_assert_call): Call assertp variant of assert failure callback. * d-tree.h (class IndexExp): Declare. (class SliceExp): Declare. (build_bounds_condition): Remove. (build_assert_call): Declare. (build_bounds_index_condition): Declare. (build_bounds_slice_condition): Declare. (checkaction_trap_p): Declare. (d_assert_call): Remove. * expr.cc (ExprVisitor::visit(IndexExp *)): Call build_bounds_index_condition. (ExprVisitor::visit(SliceExp *)): Call build_bounds_slice_condition. (ExprVisitor::visit(AssertExp *)): Update setting of libcall. * runtime.cc (enum d_libcall_type): Add LCT_IMMUTABLE_CHARPTR. (get_libcall_type): Handle LCT_IMMUTABLE_CHARPTR. * runtime.def (ASSERT): Rename to... (ASSERTP): ...this. Update signature. (UNITTEST): Rename to... (UNITTESTP): ...this. Update signature. (ARRAY_BOUNDS): Rename to... (ARRAYBOUNDSP): ...this. Updates signature. * toir.cc (IRVisitor::visit(SwitchErrorStatement *)): Update call. --- gcc/d/d-codegen.cc | 185 ++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 139 insertions(+), 46 deletions(-) (limited to 'gcc/d/d-codegen.cc') diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc index ad20bd1..e633650 100644 --- a/gcc/d/d-codegen.cc +++ b/gcc/d/d-codegen.cc @@ -23,6 +23,7 @@ along with GCC; see the file COPYING3. If not see #include "dmd/ctfe.h" #include "dmd/declaration.h" #include "dmd/identifier.h" +#include "dmd/module.h" #include "dmd/target.h" #include "dmd/template.h" @@ -1831,50 +1832,149 @@ void_okay_p (tree t) return t; } -/* Builds a CALL_EXPR at location LOC in the source file to execute when an - array bounds check fails. */ +/* Builds a STRING_CST representing the filename of location LOC. When the + location is not valid, the name of the source module is used instead. */ + +static tree +build_filename_from_loc (const Loc &loc) +{ + const char *filename = loc.filename + ? loc.filename : d_function_chain->module->srcfile->toChars (); + + unsigned length = strlen (filename); + tree str = build_string (length, filename); + TREE_TYPE (str) = make_array_type (Type::tchar, length + 1); + + return build_address (str); +} + +/* Builds a CALL_EXPR at location LOC in the source file to call LIBCALL when + an assert check fails. When calling the msg variant functions, MSG is the + error message supplied by the user. */ tree -build_array_bounds_call (const Loc &loc) +build_assert_call (const Loc &loc, libcall_fn libcall, tree msg) { - switch (global.params.checkAction) + tree file; + tree line = size_int (loc.linnum); + + switch (libcall) { - case CHECKACTION_D: - return d_assert_call (loc, LIBCALL_ARRAY_BOUNDS); + case LIBCALL_ASSERT_MSG: + case LIBCALL_UNITTEST_MSG: + case LIBCALL_SWITCH_ERROR: + /* File location is passed as a D string. */ + if (loc.filename) + { + unsigned len = strlen (loc.filename); + tree str = build_string (len, loc.filename); + TREE_TYPE (str) = make_array_type (Type::tchar, len); - case CHECKACTION_C: - case CHECKACTION_halt: - return build_call_expr (builtin_decl_explicit (BUILT_IN_TRAP), 0); + file = d_array_value (build_ctype (Type::tchar->arrayOf ()), + size_int (len), build_address (str)); + } + else + file = null_array_node; + break; + + case LIBCALL_ASSERTP: + case LIBCALL_UNITTESTP: + file = build_filename_from_loc (loc); + break; default: gcc_unreachable (); } + + + if (msg != NULL_TREE) + return build_libcall (libcall, Type::tvoid, 3, msg, file, line); + else + return build_libcall (libcall, Type::tvoid, 2, file, line); } -/* Builds a bounds condition checking that INDEX is between 0 and LEN. - The condition returns the INDEX if true, or throws a RangeError. - If INCLUSIVE, we allow INDEX == LEN to return true also. */ +/* Builds a CALL_EXPR at location LOC in the source file to execute when an + array bounds check fails. */ tree -build_bounds_condition (const Loc &loc, tree index, tree len, bool inclusive) +build_array_bounds_call (const Loc &loc) { - if (!array_bounds_check ()) + /* Terminate the program with a trap if no D runtime present. */ + if (checkaction_trap_p ()) + return build_call_expr (builtin_decl_explicit (BUILT_IN_TRAP), 0); + else + { + return build_libcall (LIBCALL_ARRAYBOUNDSP, Type::tvoid, 2, + build_filename_from_loc (loc), + size_int (loc.linnum)); + } +} + +/* Builds a bounds condition checking that INDEX is between 0 and LENGTH + in the index expression IE. The condition returns the INDEX if true, or + throws a `RangeError`. */ + +tree +build_bounds_index_condition (IndexExp *ie, tree index, tree length) +{ + if (ie->indexIsInBounds || !array_bounds_check ()) return index; /* Prevent multiple evaluations of the index. */ index = d_save_expr (index); - /* Generate INDEX >= LEN && throw RangeError. + /* Generate INDEX >= LENGTH && throw RangeError. No need to check whether INDEX >= 0 as the front-end should have already taken care of implicit casts to unsigned. */ - tree condition = fold_build2 (inclusive ? GT_EXPR : GE_EXPR, - d_bool_type, index, len); - /* Terminate the program with a trap if no D runtime present. */ - tree boundserr = build_array_bounds_call (loc); + tree condition = fold_build2 (GE_EXPR, d_bool_type, index, length); + tree boundserr = build_array_bounds_call (ie->e2->loc); return build_condition (TREE_TYPE (index), condition, boundserr, index); } +/* Builds a bounds condition checking that the range LOWER..UPPER do not overlap + the slice expression SE of the source array length LENGTH. The condition + returns the new array length if true, or throws an `ArraySliceError`. */ + +tree +build_bounds_slice_condition (SliceExp *se, tree lower, tree upper, tree length) +{ + if (array_bounds_check ()) + { + tree condition = NULL_TREE; + + /* Enforces that `upper <= length`. */ + if (!se->upperIsInBounds && length != NULL_TREE) + condition = fold_build2 (GT_EXPR, d_bool_type, upper, length); + else + length = integer_zero_node; + + /* Enforces that `lower <= upper`. No need to check `lower <= length` as + we've already ensured that `upper <= length`. */ + if (!se->lowerIsLessThanUpper) + { + tree lwr_cond = fold_build2 (GT_EXPR, d_bool_type, lower, upper); + + if (condition != NULL_TREE) + condition = build_boolop (TRUTH_ORIF_EXPR, condition, lwr_cond); + else + condition = lwr_cond; + } + + if (condition != NULL_TREE) + { + tree boundserr = build_array_bounds_call (se->loc); + upper = build_condition (TREE_TYPE (upper), condition, + boundserr, upper); + } + } + + /* Need to ensure lower always gets evaluated first, as it may be a function + call. Generates (lower, upper) - lower. */ + return fold_build2 (MINUS_EXPR, TREE_TYPE (upper), + compound_expr (lower, upper), lower); +} + /* Returns TRUE if array bounds checking code generation is turned on. */ bool @@ -1905,6 +2005,26 @@ array_bounds_check (void) } } +/* Returns TRUE if we terminate the program with a trap if an array bounds or + contract check fails. */ + +bool +checkaction_trap_p (void) +{ + switch (global.params.checkAction) + { + case CHECKACTION_D: + return false; + + case CHECKACTION_C: + case CHECKACTION_halt: + return true; + + default: + gcc_unreachable (); + } +} + /* Returns the TypeFunction class for Type T. Assumes T is already ->toBasetype(). */ @@ -2093,33 +2213,6 @@ d_build_call (TypeFunction *tf, tree callable, tree object, return compound_expr (saved_args, result); } -/* Builds a call to AssertError or AssertErrorMsg. */ - -tree -d_assert_call (const Loc &loc, libcall_fn libcall, tree msg) -{ - tree file; - tree line = size_int (loc.linnum); - - /* File location is passed as a D string. */ - if (loc.filename) - { - unsigned len = strlen (loc.filename); - tree str = build_string (len, loc.filename); - TREE_TYPE (str) = make_array_type (Type::tchar, len); - - file = d_array_value (build_ctype (Type::tchar->arrayOf ()), - size_int (len), build_address (str)); - } - else - file = null_array_node; - - if (msg != NULL) - return build_libcall (libcall, Type::tvoid, 3, msg, file, line); - else - return build_libcall (libcall, Type::tvoid, 2, file, line); -} - /* Build and return the correct call to fmod depending on TYPE. ARG0 and ARG1 are the arguments pass to the function. */ -- cgit v1.1