From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 1873) id 3127D3858038; Sun, 29 Aug 2021 16:31:41 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 3127D3858038 MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Content-Type: text/plain; charset="utf-8" From: Iain Buclaw To: gcc-cvs@gcc.gnu.org Subject: [gcc r12-3212] d: Call the assertp and boundsp variants for assert and array contract failures. X-Act-Checkin: gcc X-Git-Author: Iain Buclaw X-Git-Refname: refs/heads/master X-Git-Oldrev: 118a559df998051430c2068cfa4e03479c0c85da X-Git-Newrev: 766f5f8726965dfe69e70f234a08f6bccedc144e Message-Id: <20210829163141.3127D3858038@sourceware.org> Date: Sun, 29 Aug 2021 16:31:41 +0000 (GMT) X-BeenThere: gcc-cvs@gcc.gnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gcc-cvs mailing list List-Unsubscribe: , List-Archive: List-Help: List-Subscribe: , X-List-Received-Date: Sun, 29 Aug 2021 16:31:41 -0000 https://gcc.gnu.org/g:766f5f8726965dfe69e70f234a08f6bccedc144e commit r12-3212-g766f5f8726965dfe69e70f234a08f6bccedc144e Author: Iain Buclaw Date: Sun Aug 29 00:50:38 2021 +0200 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. Diff: --- gcc/d/d-codegen.cc | 185 ++++++++++++++++++++++++++++++++++++++++------------- gcc/d/d-tree.h | 8 ++- gcc/d/expr.cc | 58 +++-------------- gcc/d/runtime.cc | 5 ++ gcc/d/runtime.def | 24 ++++--- gcc/d/toir.cc | 2 +- 6 files changed, 173 insertions(+), 109 deletions(-) diff --git a/gcc/d/d-codegen.cc b/gcc/d/d-codegen.cc index ad20bd15403..e63365055d3 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. */ diff --git a/gcc/d/d-tree.h b/gcc/d/d-tree.h index f210b8b1a6e..9b90ca530e6 100644 --- a/gcc/d/d-tree.h +++ b/gcc/d/d-tree.h @@ -31,6 +31,8 @@ class TypeInfoDeclaration; class VarDeclaration; class Expression; class ClassReferenceExp; +class IndexExp; +class SliceExp; class Module; class Statement; class Type; @@ -575,15 +577,17 @@ extern tree build_array_set (tree, tree, tree); extern tree build_array_from_val (Type *, tree); extern tree build_array_from_exprs (Type *, Expressions *, bool); extern tree void_okay_p (tree); +extern tree build_assert_call (const Loc &, libcall_fn, tree = NULL_TREE); extern tree build_array_bounds_call (const Loc &); -extern tree build_bounds_condition (const Loc &, tree, tree, bool); +extern tree build_bounds_index_condition (IndexExp *, tree, tree); +extern tree build_bounds_slice_condition (SliceExp *, tree, tree, tree); extern bool array_bounds_check (void); +extern bool checkaction_trap_p (void); extern tree bind_expr (tree, tree); extern TypeFunction *get_function_type (Type *); extern bool call_by_alias_p (FuncDeclaration *, FuncDeclaration *); extern tree d_build_call_expr (FuncDeclaration *, tree, Expressions *); extern tree d_build_call (TypeFunction *, tree, tree, Expressions *); -extern tree d_assert_call (const Loc &, libcall_fn, tree = NULL_TREE); extern tree build_float_modulus (tree, tree, tree); extern tree build_vthis_function (tree, tree); extern tree error_no_frame_access (Dsymbol *); diff --git a/gcc/d/expr.cc b/gcc/d/expr.cc index e293cf2a4cd..ea21bd5a8a1 100644 --- a/gcc/d/expr.cc +++ b/gcc/d/expr.cc @@ -1304,8 +1304,8 @@ public: /* If it's a static array and the index is constant, the front end has already checked the bounds. */ - if (tb1->ty != Tpointer && !e->indexIsInBounds) - index = build_bounds_condition (e->e2->loc, index, length, false); + if (tb1->ty != Tpointer) + index = build_bounds_index_condition (e, index, length); /* Index the .ptr. */ ptr = void_okay_p (ptr); @@ -1412,8 +1412,6 @@ public: ptr = build_array_index (void_okay_p (ptr), lwr_tree); ptr = build_nop (ptrtype, ptr); } - else - lwr_tree = NULL_TREE; /* Nothing more to do for static arrays, their bounds checking has been done at compile-time. */ @@ -1426,46 +1424,8 @@ public: gcc_assert (tb->ty == Tarray); /* Generate bounds checking code. */ - tree newlength; - - if (!e->upperIsInBounds) - { - if (length) - { - newlength = build_bounds_condition (e->upr->loc, upr_tree, - length, true); - } - else - { - /* Still need to check bounds lwr <= upr for pointers. */ - gcc_assert (tb1->ty == Tpointer); - newlength = upr_tree; - } - } - else - newlength = upr_tree; - - if (lwr_tree) - { - /* Enforces lwr <= upr. No need to check lwr <= length as - we've already ensured that upr <= length. */ - if (!e->lowerIsLessThanUpper) - { - tree cond = build_bounds_condition (e->lwr->loc, lwr_tree, - upr_tree, true); - - /* When bounds checking is off, the index value is - returned directly. */ - if (cond != lwr_tree) - newlength = compound_expr (cond, newlength); - } - - /* Need to ensure lwr always gets evaluated first, as it may be a - function call. Generates (lwr, upr) - lwr. */ - newlength = fold_build2 (MINUS_EXPR, TREE_TYPE (newlength), - compound_expr (lwr_tree, newlength), lwr_tree); - } - + tree newlength = build_bounds_slice_condition (e, lwr_tree, upr_tree, + length); tree result = d_array_value (build_ctype (e->type), newlength, ptr); this->result_ = compound_expr (array, result); } @@ -2023,8 +1983,7 @@ public: tree assert_pass = void_node; tree assert_fail; - if (global.params.useAssert == CHECKENABLEon - && global.params.checkAction == CHECKACTION_D) + if (global.params.useAssert == CHECKENABLEon && !checkaction_trap_p ()) { /* Generate: ((bool) e1 ? (void)0 : _d_assert (...)) or: (e1 != null ? e1._invariant() : _d_assert (...)) */ @@ -2037,10 +1996,10 @@ public: libcall = unittest_p ? LIBCALL_UNITTEST_MSG : LIBCALL_ASSERT_MSG; } else - libcall = unittest_p ? LIBCALL_UNITTEST : LIBCALL_ASSERT; + libcall = unittest_p ? LIBCALL_UNITTESTP : LIBCALL_ASSERTP; /* Build a call to _d_assert(). */ - assert_fail = d_assert_call (e->loc, libcall, tmsg); + assert_fail = build_assert_call (e->loc, libcall, tmsg); if (global.params.useInvariants == CHECKENABLEon) { @@ -2068,8 +2027,7 @@ public: } } } - else if (global.params.useAssert == CHECKENABLEon - && global.params.checkAction == CHECKACTION_C) + else if (global.params.useAssert == CHECKENABLEon && checkaction_trap_p ()) { /* Generate: __builtin_trap() */ tree fn = builtin_decl_explicit (BUILT_IN_TRAP); diff --git a/gcc/d/runtime.cc b/gcc/d/runtime.cc index d789fcae63b..5206b53b865 100644 --- a/gcc/d/runtime.cc +++ b/gcc/d/runtime.cc @@ -51,6 +51,7 @@ enum d_libcall_type LCT_ARRAY_VOID, /* void[] */ LCT_ARRAY_SIZE_T, /* size_t[] */ LCT_ARRAY_BYTE, /* byte[] */ + LCT_IMMUTABLE_CHARPTR, /* immutable(char*) */ LCT_ARRAY_STRING, /* string[] */ LCT_ARRAY_WSTRING, /* wstring[] */ LCT_ARRAY_DSTRING, /* dstring[] */ @@ -200,6 +201,10 @@ get_libcall_type (d_libcall_type type) libcall_types[type] = Type::tint8->arrayOf ()->pointerTo (); break; + case LCT_IMMUTABLE_CHARPTR: + libcall_types[type] = Type::tchar->pointerTo ()->immutableOf (); + break; + default: gcc_unreachable (); } diff --git a/gcc/d/runtime.def b/gcc/d/runtime.def index f872cfc3f4e..0296233f4e2 100644 --- a/gcc/d/runtime.def +++ b/gcc/d/runtime.def @@ -26,27 +26,31 @@ along with GCC; see the file COPYING3. If not see extern(C) - for those that are not, ensure to use correct mangling. */ /* Helper macros for parameter building. */ -#define P0() 0 -#define P1(T1) 1, LCT_ ## T1 -#define P2(T1, T2) 2, LCT_ ## T1, LCT_ ## T2 -#define P3(T1, T2, T3) 3, LCT_ ## T1, LCT_ ## T2, LCT_ ## T3 -#define P4(T1, T2, T3, T4) 4, LCT_ ## T1, LCT_ ## T2, LCT_ ## T3, LCT_ ## T4 -#define RT(T1) LCT_ ## T1 +#define P0() 0 +#define P1(T1) 1, LCT_ ## T1 +#define P2(T1, T2) \ + 2, LCT_ ## T1, LCT_ ## T2 +#define P3(T1, T2, T3) \ + 3, LCT_ ## T1, LCT_ ## T2, LCT_ ## T3 +#define P4(T1, T2, T3, T4) \ + 4, LCT_ ## T1, LCT_ ## T2, LCT_ ## T3, LCT_ ## T4 +#define RT(T1) LCT_ ## T1 /* Used when an assert() contract fails. */ -DEF_D_RUNTIME (ASSERT, "_d_assert", RT(VOID), P2(STRING, UINT), ECF_NORETURN) +DEF_D_RUNTIME (ASSERTP, "_d_assertp", RT(VOID), P2(IMMUTABLE_CHARPTR, UINT), + ECF_NORETURN) DEF_D_RUNTIME (ASSERT_MSG, "_d_assert_msg", RT(VOID), P3(STRING, STRING, UINT), ECF_NORETURN) /* Used when an assert() contract fails in a unittest function. */ -DEF_D_RUNTIME (UNITTEST, "_d_unittest", RT(VOID), P2(STRING, UINT), +DEF_D_RUNTIME (UNITTESTP, "_d_unittestp", RT(VOID), P2(IMMUTABLE_CHARPTR, UINT), ECF_NORETURN) DEF_D_RUNTIME (UNITTEST_MSG, "_d_unittest_msg", RT(VOID), P3(STRING, STRING, UINT), ECF_NORETURN) /* Used when an array index outside the bounds of its range. */ -DEF_D_RUNTIME (ARRAY_BOUNDS, "_d_arraybounds", RT(VOID), P2(STRING, UINT), - ECF_NORETURN) +DEF_D_RUNTIME (ARRAYBOUNDSP, "_d_arrayboundsp", RT(VOID), + P2(IMMUTABLE_CHARPTR, UINT), ECF_NORETURN) /* Used when calling new on a class. */ DEF_D_RUNTIME (NEWCLASS, "_d_newclass", RT(OBJECT), P1(CONST_CLASSINFO), 0) diff --git a/gcc/d/toir.cc b/gcc/d/toir.cc index eaee6f7e803..1c9da101f39 100644 --- a/gcc/d/toir.cc +++ b/gcc/d/toir.cc @@ -992,7 +992,7 @@ public: void visit (SwitchErrorStatement *s) { - add_stmt (d_assert_call (s->loc, LIBCALL_SWITCH_ERROR)); + add_stmt (build_assert_call (s->loc, LIBCALL_SWITCH_ERROR)); } /* A return statement exits the current function and supplies its return