From patchwork Fri Oct 20 05:31:05 2023 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alexandre Oliva X-Patchwork-Id: 155813 Return-Path: Delivered-To: ouuuleilei@gmail.com Received: by 2002:a05:612c:2010:b0:403:3b70:6f57 with SMTP id fe16csp838518vqb; Thu, 19 Oct 2023 22:31:53 -0700 (PDT) X-Google-Smtp-Source: AGHT+IEVvbrltJ/2VPNOoe+lwkOm5oScEtU8wxDlR7NLXHk07KpMj0XFUP+0/dYZF/ZvgE3BoT8M X-Received: by 2002:ac8:5949:0:b0:419:52a9:9ffc with SMTP id 9-20020ac85949000000b0041952a99ffcmr855474qtz.35.1697779913472; Thu, 19 Oct 2023 22:31:53 -0700 (PDT) ARC-Seal: i=2; a=rsa-sha256; t=1697779913; cv=pass; d=google.com; s=arc-20160816; b=SPbKhA7z3KJ3/lSXEREcaLF2NMHrVZgAe5g1WH1JqvrYHFtCXE1fL5FVfmO76M+zrs O7tDUGkYoWRhUTnVStBoyy438hwO0dEDPf9rBHhXNYmLwFRODvvURTNOK9vbXl4HlltV QVSuOz6LxNVCwrAXYxILgc9Hte2z0e9pMBrhOpT2xQknycU5IYCZ5OldZarRKxy8Ij4M BT6E4eLvOT+hPEx/bugZ3IsycaMnCZrHiPhNikkmHHflzFAFSW52Ug48WrXBqHFQ8139 GUl7/Ua15bBaE3AN1NL5wTwoToNndlrvoyk5C00lwqG41TPIGOmUUiKNSlknrUzbS1Ry ZH7Q== ARC-Message-Signature: i=2; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=errors-to:list-subscribe:list-help:list-post:list-archive :list-unsubscribe:list-id:precedence:mime-version:user-agent :message-id:in-reply-to:date:references:organization:subject:cc:to :from:dkim-signature:arc-filter:dmarc-filter:delivered-to; bh=RDAk2RWdmsGitfUf89Lidy0klPz18mrAkq4wSby/DSA=; fh=bnjEEeVyF9Elc3posLjblZ+ejO3VFs3xT1wYM3ll1J4=; b=Zl8eSDG2bIszMql9c1gFDgSqUgnMOFJpsZNUDX8IkDTrRiovSmJIY9wG3YwNtonQpT WFJFu9Fdkta6hKkr9DxClCY/rqwPeEgmjdPZz/cDHYAPzyCutjqj/hQpAqNF9gR0VQJx MHa+gaxlmwGKl9XB7jGJPdOA+hhRHZYqsOA4upYGwo8T9QbW/CeRPZlyr40ddN2QdnN3 RvzGXDKr/JbttHyWQzrhkqAlR20FluvguyJgbXLyVZjWjvhOGo0iBIHR0OVT5Ee1HDJG SbJSjvkNIcjokN0DOwot03NWHo6bPFML/B3KJOl16fUpoRPtT6zVeOTM2dz+mOHWyFM1 mabw== ARC-Authentication-Results: i=2; mx.google.com; dkim=pass header.i=@adacore.com header.s=google header.b=i6f+Y+Y3; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=adacore.com Received: from server2.sourceware.org (ip-8-43-85-97.sourceware.org. [8.43.85.97]) by mx.google.com with ESMTPS id u3-20020a05622a198300b004039c492533si767639qtc.181.2023.10.19.22.31.53 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 19 Oct 2023 22:31:53 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) client-ip=8.43.85.97; Authentication-Results: mx.google.com; dkim=pass header.i=@adacore.com header.s=google header.b=i6f+Y+Y3; arc=pass (i=1); spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 8.43.85.97 as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=adacore.com Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 3B53D3858D35 for ; Fri, 20 Oct 2023 05:31:53 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-pl1-x633.google.com (mail-pl1-x633.google.com [IPv6:2607:f8b0:4864:20::633]) by sourceware.org (Postfix) with ESMTPS id 5A4203858D33 for ; Fri, 20 Oct 2023 05:31:21 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 5A4203858D33 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=adacore.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=adacore.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 5A4203858D33 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::633 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1697779885; cv=none; b=JbrjF8IIZyzKYLA6wYkpSsUoCfyOctv2yVO+fTgBlXh/F659+UomuBBfijJZdUI5HoqnOwK5AorDdwcI4l49rKgCvRwSH8T9ny2z3YodIu33YxErNmstEHV3r3hCdvrpD806NrbeqznXAds5NxfNA0aLhuFJJ7pPltbTtHyYdlo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1697779885; c=relaxed/simple; bh=yMhTk/FTCWJ4ljqjXm+Pzm0TvnTBIBQacjU+Dfx7ztg=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=poC9vKLAgB34fe88RXPRIfPBid7NI+G929rijEQ5Txe1/vvNdIG6WoQgJZAAxMKTel6dNXAXv95VQpEdLMc0k4r2VE2MxSKjvc3orZAItv9g0KWAFXrRKQWyiax6BM5ct+6+gAdWJjXTkx548M0AmIYRXAzFb1lMqN6d3qcDXQs= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pl1-x633.google.com with SMTP id d9443c01a7336-1c9b1e3a809so3099695ad.2 for ; Thu, 19 Oct 2023 22:31:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=adacore.com; s=google; t=1697779880; x=1698384680; darn=gcc.gnu.org; h=mime-version:user-agent:message-id:in-reply-to:date:references :organization:subject:cc:to:from:from:to:cc:subject:date:message-id :reply-to; bh=RDAk2RWdmsGitfUf89Lidy0klPz18mrAkq4wSby/DSA=; b=i6f+Y+Y3iTfNJQoXoPJIWc9AG/XVMh6GBGY45uciNpX9nzyDJoQhH4xM2T5SWVrw4i NM/N8HoHkzCrpq63oAla7KsZcYSv2Fufm3va+WHeoxRLPFckEYzwfBMn6WCEh5wVpDiG cdaG5sSAddokYNbKa/H+nnwWDh1RnygFb/xaPYLGoS2HHkhT0pHE8+BV23ICEvg5NUBn iVX/gfORjC3ZasEot6u+ItCKK/4LPRhR2RpCjOe1ZgsTWH5P/bkyuAT67ArWNFpFr1My BN4g8wS0kewoe/3YbnMBSBY3Pi7yBXAfXXa/nYalXRHVDF9+GWcsfvIo42Os1feXLWFt lckg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1697779880; x=1698384680; h=mime-version:user-agent:message-id:in-reply-to:date:references :organization:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=RDAk2RWdmsGitfUf89Lidy0klPz18mrAkq4wSby/DSA=; b=u1pLt/cYTQg7+bINoYELMP8Zep26WvdGbBnw+Aps4kWeHa3v7sNR8z9a+tiOaeMvPx kjGkLyATjE4ik3VV9SWaJithT6e3dv0yV3CAcVrUxuANcH4GvG41yagm/UFN141teMut XmA1nJa43+G7Vdt2tmIcqIU2JGUEFTodDa13fVQqD/A8/2gsRXDymb7robcg6DAE5Zra EygndE7288q+GM85W5+47TZAkyRBKFplvzKvNL8na4qq5Q3sMPwIOpjuiob9EOOvirxf V5d4P2S+EtgIUsTI2ILfvGLUiBe4jlg9FmfBPt+08i5Bw3veuZv81OQj72zK8gx/AUpB biCQ== X-Gm-Message-State: AOJu0Ywhb3TjXiBOXPAVx55U4fWbzXghuJjapy6jq/M6N9yDiIQ7UoAi 3WGSmBx2LV5F3PHGI141FYpJpTssKmyYBZ7o3ype+u15 X-Received: by 2002:a17:903:300c:b0:1ca:8e43:7660 with SMTP id o12-20020a170903300c00b001ca8e437660mr826440pla.10.1697779880184; Thu, 19 Oct 2023 22:31:20 -0700 (PDT) Received: from free.home ([2804:7f1:2080:7ba0:18cb:7459:1c6c:8dbf]) by smtp.gmail.com with ESMTPSA id b16-20020a170902d51000b001c9b8f76a89sm633476plg.82.2023.10.19.22.31.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 19 Oct 2023 22:31:19 -0700 (PDT) Received: from livre (livre.home [172.31.160.2]) by free.home (8.15.2/8.15.2) with ESMTPS id 39K5V5AZ1529024 (version=TLSv1.3 cipher=TLS_AES_256_GCM_SHA384 bits=256 verify=NOT); Fri, 20 Oct 2023 02:31:06 -0300 From: Alexandre Oliva To: gcc-patches@gcc.gnu.org Cc: Joseph Myers , Qing Zhao , Bernhard Reutner-Fischer Subject: [PATCH v4] Introduce hardbool attribute for C Organization: Free thinker, does not speak for AdaCore References: Date: Fri, 20 Oct 2023 02:31:05 -0300 In-Reply-To: (Alexandre Oliva's message of "Sat, 24 Jun 2023 01:42:59 -0300") Message-ID: User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux) MIME-Version: 1.0 X-Scanned-By: MIMEDefang 2.84 X-Spam-Status: No, score=-11.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, WEIRD_QUOTING autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org X-getmail-retrieved-from-mailbox: INBOX X-GMAIL-THRID: 1740691988782233476 X-GMAIL-MSGID: 1780251270616526934 Here's a refreshed and retested version of the patch for hardened booleans in C. It is unchanged aside from some conflict resolution, compared with the previous version posted back in June. https://gcc.gnu.org/pipermail/gcc-patches/2023-June/622668.html Regstrapped on x86_64-linux-gnu and ppc64le-linux-gnu. Ok to install? ---- This patch introduces hardened booleans in C. The hardbool attribute, when attached to an integral type, turns it into an enumerate type with boolean semantics, using the named or implied constants as representations for false and true. Expressions of such types decay to _Bool, trapping if the value is neither true nor false, and _Bool can convert implicitly back to them. Other conversions go through _Bool first. for gcc/c-family/ChangeLog * c-attribs.cc (c_common_attribute_table): Add hardbool. (handle_hardbool_attribute): New. (type_valid_for_vector_size): Reject hardbool. * c-common.cc (convert_and_check): Skip warnings for convert and check for hardbool. (c_hardbool_type_attr_1): New. * c-common.h (c_hardbool_type_attr): New. for gcc/c/ChangeLog * c-typeck.cc (convert_lvalue_to_rvalue): Decay hardbools. * c-convert.cc (convert): Convert to hardbool through truthvalue. * c-decl.cc (check_bitfield_type_and_width): Skip enumeral truncation warnings for hardbool. (finish_struct): Propagate hardbool attribute to bitfield types. (digest_init): Convert to hardbool. for gcc/ChangeLog * doc/extend.texi (hardbool): New type attribute. * doc/invoke.texi (-ftrivial-auto-var-init): Document representation vs values. for gcc/testsuite/ChangeLog * gcc.dg/hardbool-err.c: New. * gcc.dg/hardbool-trap.c: New. * gcc.dg/hardbool.c: New. * gcc.dg/hardbool-s.c: New. * gcc.dg/hardbool-us.c: New. * gcc.dg/hardbool-i.c: New. * gcc.dg/hardbool-ul.c: New. * gcc.dg/hardbool-ll.c: New. * gcc.dg/hardbool-5a.c: New. * gcc.dg/hardbool-s-5a.c: New. * gcc.dg/hardbool-us-5a.c: New. * gcc.dg/hardbool-i-5a.c: New. * gcc.dg/hardbool-ul-5a.c: New. * gcc.dg/hardbool-ll-5a.c: New. --- gcc/c-family/c-attribs.cc | 97 ++++++++++++++++++++- gcc/c-family/c-common.cc | 21 ++++ gcc/c-family/c-common.h | 18 ++++ gcc/c/c-convert.cc | 14 +++ gcc/c/c-decl.cc | 10 ++ gcc/c/c-typeck.cc | 31 ++++++- gcc/doc/extend.texi | 65 ++++++++++++++ gcc/doc/invoke.texi | 21 ++++ gcc/testsuite/gcc.dg/hardbool-err.c | 31 +++++++ gcc/testsuite/gcc.dg/hardbool-trap.c | 13 +++ gcc/testsuite/gcc.dg/torture/hardbool-5a.c | 6 + gcc/testsuite/gcc.dg/torture/hardbool-i-5a.c | 6 + gcc/testsuite/gcc.dg/torture/hardbool-i.c | 5 + gcc/testsuite/gcc.dg/torture/hardbool-ll-5a.c | 6 + gcc/testsuite/gcc.dg/torture/hardbool-ll.c | 5 + gcc/testsuite/gcc.dg/torture/hardbool-s-5a.c | 6 + gcc/testsuite/gcc.dg/torture/hardbool-s.c | 5 + gcc/testsuite/gcc.dg/torture/hardbool-ul-5a.c | 6 + gcc/testsuite/gcc.dg/torture/hardbool-ul.c | 5 + gcc/testsuite/gcc.dg/torture/hardbool-us-5a.c | 6 + gcc/testsuite/gcc.dg/torture/hardbool-us.c | 5 + gcc/testsuite/gcc.dg/torture/hardbool.c | 118 +++++++++++++++++++++++++ 22 files changed, 497 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/hardbool-err.c create mode 100644 gcc/testsuite/gcc.dg/hardbool-trap.c create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-5a.c create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-i-5a.c create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-i.c create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-ll-5a.c create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-ll.c create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-s-5a.c create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-s.c create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-ul-5a.c create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-ul.c create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-us-5a.c create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool-us.c create mode 100644 gcc/testsuite/gcc.dg/torture/hardbool.c diff --git a/gcc/c-family/c-attribs.cc b/gcc/c-family/c-attribs.cc index abf44d5426e82..232365d46e237 100644 --- a/gcc/c-family/c-attribs.cc +++ b/gcc/c-family/c-attribs.cc @@ -176,6 +176,7 @@ static tree handle_objc_root_class_attribute (tree *, tree, tree, int, bool *); static tree handle_objc_nullability_attribute (tree *, tree, tree, int, bool *); static tree handle_signed_bool_precision_attribute (tree *, tree, tree, int, bool *); +static tree handle_hardbool_attribute (tree *, tree, tree, int, bool *); static tree handle_retain_attribute (tree *, tree, tree, int, bool *); static tree handle_fd_arg_attribute (tree *, tree, tree, int, bool *); @@ -293,6 +294,8 @@ const struct attribute_spec c_common_attribute_table[] = affects_type_identity, handler, exclude } */ { "signed_bool_precision", 1, 1, false, true, false, true, handle_signed_bool_precision_attribute, NULL }, + { "hardbool", 0, 2, false, true, false, true, + handle_hardbool_attribute, NULL }, { "packed", 0, 0, false, false, false, false, handle_packed_attribute, attr_aligned_exclusions }, @@ -992,6 +995,96 @@ handle_signed_bool_precision_attribute (tree *node, tree name, tree args, return NULL_TREE; } +/* Handle a "hardbool" attribute; arguments as in struct + attribute_spec.handler. */ + +static tree +handle_hardbool_attribute (tree *node, tree name, tree args, + int /* flags */, bool *no_add_attrs) +{ + if (c_language != clk_c) + { + error ("%qE attribute only supported in C", name); + *no_add_attrs = TRUE; + return NULL_TREE; + } + + if (!TYPE_P (*node) || TREE_CODE (*node) != INTEGER_TYPE) + { + error ("%qE attribute only supported on " + "integral types", name); + *no_add_attrs = TRUE; + return NULL_TREE; + } + + tree orig = *node; + *node = build_duplicate_type (orig); + + TREE_SET_CODE (*node, ENUMERAL_TYPE); + ENUM_UNDERLYING_TYPE (*node) = orig; + + tree false_value; + if (args) + false_value = fold_convert (*node, TREE_VALUE (args)); + else + false_value = fold_convert (*node, integer_zero_node); + + if (TREE_OVERFLOW_P (false_value)) + { + warning (OPT_Wattributes, + "overflows in conversion from %qT to %qT " + "changes value from %qE to %qE", + TREE_TYPE (TREE_VALUE (args)), *node, + TREE_VALUE (args), false_value); + TREE_OVERFLOW (false_value) = false; + } + + tree true_value; + if (args && TREE_CHAIN (args)) + true_value = fold_convert (*node, TREE_VALUE (TREE_CHAIN (args))); + else + true_value = fold_build1 (BIT_NOT_EXPR, *node, false_value); + + if (TREE_OVERFLOW_P (true_value)) + { + warning (OPT_Wattributes, + "overflows in conversion from %qT to %qT " + "changes value from %qE to %qE", + TREE_TYPE (TREE_VALUE (TREE_CHAIN (args))), *node, + TREE_VALUE (TREE_CHAIN (args)), true_value); + TREE_OVERFLOW (true_value) = false; + } + + if (tree_int_cst_compare (false_value, true_value) == 0) + { + error ("%qE attribute requires different values for" + " % and % for type %qT", + name, *node); + *no_add_attrs = TRUE; + return NULL_TREE; + } + + tree values = build_tree_list (get_identifier ("false"), + false_value); + TREE_CHAIN (values) = build_tree_list (get_identifier ("true"), + true_value); + + /* Do *not* set TYPE_MIN_VALUE, TYPE_MAX_VALUE, nor TYPE_PRECISION according + to the false and true values. That might cause the constants to be the + only acceptable values, which would drop the very hardening checks this + attribute is supposed to add. */ + + TYPE_ATTRIBUTES (*node) = tree_cons (name, args, + TYPE_ATTRIBUTES (*node)); + *no_add_attrs = TRUE; + + gcc_checking_assert (!TYPE_CACHED_VALUES_P (*node)); + TYPE_VALUES (*node) = values; + TYPE_NAME (*node) = orig; + + return NULL_TREE; +} + /* Handle a "packed" attribute; arguments as in struct attribute_spec.handler. */ @@ -4403,7 +4496,8 @@ static tree type_valid_for_vector_size (tree type, tree atname, tree args, unsigned HOST_WIDE_INT *ptrnunits) { - bool error_p = ptrnunits != NULL; + bool hardbool_p = c_hardbool_type_attr (type); + bool error_p = ptrnunits != NULL || hardbool_p; /* Get the mode of the type being modified. */ machine_mode orig_mode = TYPE_MODE (type); @@ -4416,6 +4510,7 @@ type_valid_for_vector_size (tree type, tree atname, tree args, && !ALL_SCALAR_FIXED_POINT_MODE_P (orig_mode)) || !tree_fits_uhwi_p (TYPE_SIZE_UNIT (type)) || TREE_CODE (type) == BOOLEAN_TYPE + || hardbool_p || TREE_CODE (type) == BITINT_TYPE) { if (error_p) diff --git a/gcc/c-family/c-common.cc b/gcc/c-family/c-common.cc index f044db5b797b5..a24b9ece9e3d2 100644 --- a/gcc/c-family/c-common.cc +++ b/gcc/c-family/c-common.cc @@ -1772,7 +1772,8 @@ convert_and_check (location_t loc, tree type, tree expr, bool init_const) if (c_inhibit_evaluation_warnings == 0 && !TREE_OVERFLOW_P (expr) - && result != error_mark_node) + && result != error_mark_node + && !c_hardbool_type_attr (type)) warnings_for_convert_and_check (loc, type, expr_for_warning, result); return result; @@ -9808,4 +9809,22 @@ c_strict_flex_array_level_of (tree array_field) return strict_flex_array_level; } +/* This is the slow path of c-common.h's c_hardbool_type_attr. */ + +tree +c_hardbool_type_attr_1 (tree type, tree *false_value, tree *true_value) +{ + tree attr = lookup_attribute ("hardbool", TYPE_ATTRIBUTES (type)); + if (!attr) + return attr; + + if (false_value) + *false_value = TREE_VALUE (TYPE_VALUES (type)); + + if (true_value) + *true_value = TREE_VALUE (TREE_CHAIN (TYPE_VALUES (type))); + + return attr; +} + #include "gt-c-family-c-common.h" diff --git a/gcc/c-family/c-common.h b/gcc/c-family/c-common.h index 1fdba7ef3ea04..53c3d3754f8cb 100644 --- a/gcc/c-family/c-common.h +++ b/gcc/c-family/c-common.h @@ -911,6 +911,7 @@ extern bool get_attribute_operand (tree, unsigned HOST_WIDE_INT *); extern void c_common_finalize_early_debug (void); extern unsigned int c_strict_flex_array_level_of (tree); extern bool c_option_is_from_cpp_diagnostics (int); +extern tree c_hardbool_type_attr_1 (tree, tree *, tree *); /* Used by convert_and_check; in front ends. */ extern tree convert_init (tree, tree); @@ -1348,6 +1349,23 @@ c_tree_chain_next (tree t) return NULL; } +/* Return the hardbool attribute associated with TYPE, if there is one, provided + that TYPE looks like an enumeral type that might have been set up by + handle_hardbool_attribute. Return NULL otherwise. + + If FALSE_VALUE or TRUE_VALUE are non-NULL and TYPE is a hardened boolean + type, store the corresponding representation values. */ +static inline tree +c_hardbool_type_attr (tree type, + tree *false_value = NULL, tree *true_value = NULL) +{ + if (TREE_CODE (type) != ENUMERAL_TYPE + || TYPE_LANG_SPECIFIC (type)) + return NULL_TREE; + + return c_hardbool_type_attr_1 (type, false_value, true_value); +} + /* Mask used by tm_stmt_attr. */ #define TM_STMT_ATTR_OUTER 2 #define TM_STMT_ATTR_ATOMIC 4 diff --git a/gcc/c/c-convert.cc b/gcc/c/c-convert.cc index 71e618cf3b6d2..918effd5ebbaa 100644 --- a/gcc/c/c-convert.cc +++ b/gcc/c/c-convert.cc @@ -105,6 +105,20 @@ c_convert (tree type, tree expr, bool init_const) return error_mark_node; } + { + tree false_value, true_value; + if (c_hardbool_type_attr (type, &false_value, &true_value)) + { + bool save = in_late_binary_op; + in_late_binary_op = true; + expr = c_objc_common_truthvalue_conversion (input_location, expr); + in_late_binary_op = save; + + return fold_build3_loc (loc, COND_EXPR, type, + expr, true_value, false_value); + } + } + switch (code) { case VOID_TYPE: diff --git a/gcc/c/c-decl.cc b/gcc/c/c-decl.cc index 7a145bed281f9..134bae4c7653c 100644 --- a/gcc/c/c-decl.cc +++ b/gcc/c/c-decl.cc @@ -6450,6 +6450,12 @@ check_bitfield_type_and_width (location_t loc, tree *type, tree *width, else w = tree_to_uhwi (*width); + /* Truncation of hardbool false and true representation values is always safe: + either the values remain different, or we'll report a problem when creating + the narrower type. */ + if (c_hardbool_type_attr (*type)) + return; + if (TREE_CODE (*type) == ENUMERAL_TYPE) { struct lang_type *lt = TYPE_LANG_SPECIFIC (*type); @@ -9387,6 +9393,10 @@ finish_struct (location_t loc, tree t, tree fieldlist, tree attributes, TREE_TYPE (field) = c_build_bitfield_integer_type (width, TYPE_UNSIGNED (type)); + if (tree attr = c_hardbool_type_attr (type)) + decl_attributes (&TREE_TYPE (field), + copy_list (attr), + 0, NULL_TREE); SET_DECL_MODE (field, TYPE_MODE (TREE_TYPE (field))); } DECL_INITIAL (field) = NULL_TREE; diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index f7ce13ae73803..c436357a108ea 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -2209,6 +2209,35 @@ convert_lvalue_to_rvalue (location_t loc, struct c_expr exp, exp.value = convert (build_qualified_type (TREE_TYPE (exp.value), TYPE_UNQUALIFIED), exp.value); if (force_non_npc) exp.value = build1 (NOP_EXPR, TREE_TYPE (exp.value), exp.value); + + { + tree false_value, true_value; + if (convert_p && !error_operand_p (exp.value) + && c_hardbool_type_attr (TREE_TYPE (exp.value), + &false_value, &true_value)) + { + tree t = save_expr (exp.value); + + mark_exp_read (exp.value); + + tree trapfn = builtin_decl_explicit (BUILT_IN_TRAP); + tree expr = build_call_expr_loc (loc, trapfn, 0); + expr = build_compound_expr (loc, expr, boolean_true_node); + expr = fold_build3_loc (loc, COND_EXPR, boolean_type_node, + fold_build2_loc (loc, NE_EXPR, + boolean_type_node, + t, true_value), + expr, boolean_true_node); + expr = fold_build3_loc (loc, COND_EXPR, boolean_type_node, + fold_build2_loc (loc, NE_EXPR, + boolean_type_node, + t, false_value), + expr, boolean_false_node); + + exp.value = expr; + } + } + return exp; } @@ -8497,7 +8526,7 @@ digest_init (location_t init_loc, tree type, tree init, tree origtype, } } - if (code == VECTOR_TYPE) + if (code == VECTOR_TYPE || c_hardbool_type_attr (type)) /* Although the types are compatible, we may require a conversion. */ inside_init = convert (type, inside_init); diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index bf941e6b93a18..e1295898fc25c 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -8832,6 +8832,71 @@ initialization will result in future breakage. GCC emits warnings based on this attribute by default; use @option{-Wno-designated-init} to suppress them. +@cindex @code{hardbool} type attribute +@item hardbool +@itemx hardbool (@var{false_value}) +@itemx hardbool (@var{false_value}, @var{true_value}) +This attribute may only be applied to integral types in C, to introduce +hardened boolean types. It turns the integral type into a boolean-like +type with the same size and precision, that uses the specified values as +representations for @code{false} and @code{true}. Underneath, it is +actually an enumerate type, but its observable behavior is like that of +@code{_Bool}, except for the strict internal representations, verified +by runtime checks. + +If @var{true_value} is omitted, the bitwise negation of +@var{false_value} is used. If @var{false_value} is omitted, zero is +used. The named representation values must be different when converted +to the original integral type. Narrower bitfields are rejected if the +representations become indistinguishable. + +Values of such types automatically decay to @code{_Bool}, at which +point, the selected representation values are mapped to the +corresponding @code{_Bool} values. When the represented value is not +determined, at compile time, to be either @var{false_value} or +@var{true_value}, runtime verification calls @code{__builtin_trap} if it +is neither. This is what makes them hardened boolean types. + +When converting scalar types to such hardened boolean types, implicitly +or explicitly, behavior corresponds to a conversion to @code{_Bool}, +followed by a mapping from @code{false} and @code{true} to +@var{false_value} and @var{true_value}, respectively. + +@smallexample +typedef char __attribute__ ((__hardbool__ (0x5a))) hbool; +hbool first = 0; /* False, stored as (char)0x5a. */ +hbool second = !first; /* True, stored as ~(char)0x5a. */ + +static hbool zeroinit; /* False, stored as (char)0x5a. */ +auto hbool uninit; /* Undefined, may trap. */ +@end smallexample + +When zero-initializing a variable or field of hardened boolean type +(presumably held in static storage) the implied zero initializer gets +converted to @code{_Bool}, and then to the hardened boolean type, so +that the initial value is the hardened representation for @code{false}. +Using that value is well defined. This is @emph{not} the case when +variables and fields of such types are uninitialized (presumably held in +automatic or dynamic storage): their values are indeterminate, and using +them invokes undefined behavior. Using them may trap or not, depending +on the bits held in the storage (re)used for the variable, if any, and +on optimizations the compiler may perform on the grounds that using +uninitialized values invokes undefined behavior. + +Users of @option{-ftrivial-auto-var-init} should be aware that the bit +patterns used as initializers are @emph{not} converted to +@code{hardbool} types, so using a @code{hardbool} variable that is +implicitly initialized by the @option{-ftrivial-auto-var-init} may trap +if the representations values chosen for @code{false} and @code{true} do +not match the initializer. + +Since this is a language extension only available in C, interoperation +with other languages may pose difficulties. It should interoperate with +Ada Booleans defined with the same size and equivalent representation +clauses, and with enumerations or other languages' integral types that +correspond to C's chosen integral type. + + @cindex @code{may_alias} type attribute @item may_alias Accesses through pointers to types with this attribute are not subject diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index aebe9195ef0f2..1a762bdcc480f 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -13632,6 +13632,27 @@ The values used for pattern initialization might be changed in the future. The default is @samp{uninitialized}. +Note that the initializer values, whether @samp{zero} or @samp{pattern}, +refer to data representation (in memory or machine registers), rather +than to their interpretation as numerical values. This distinction may +be important in languages that support types with biases or implicit +multipliers, and with such extensions as @samp{hardbool} (@pxref{Type +Attributes}). For example, a variable that uses 8 bits to represent +(biased) quantities in the @code{range 160..400} will be initialized +with the bit patterns @code{0x00} or @code{0xFE}, depending on +@var{choice}, whether or not these representations stand for values in +that range, and even if they do, the interpretation of the value held by +the variable will depend on the bias. A @samp{hardbool} variable that +uses say @code{0X5A} and @code{0xA5} for @code{false} and @code{true}, +respectively, will trap with either @samp{choice} of trivial +initializer, i.e., @samp{zero} initialization will not convert to the +representation for @code{false}, even if it would for a @code{static} +variable of the same type. This means the initializer pattern doesn't +generally depend on the type of the initialized variable. One notable +exception is that (non-hardened) boolean variables that fit in registers +are initialized with @code{false} (zero), even when @samp{pattern} is +requested. + You can control this behavior for a specific variable by using the variable attribute @code{uninitialized} (@pxref{Variable Attributes}). diff --git a/gcc/testsuite/gcc.dg/hardbool-err.c b/gcc/testsuite/gcc.dg/hardbool-err.c new file mode 100644 index 0000000000000..e5bf58e55de62 --- /dev/null +++ b/gcc/testsuite/gcc.dg/hardbool-err.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "" } */ + +typedef _Bool __attribute__ ((__hardbool__)) +hbbl; /* { dg-error "integral types" } */ + +typedef double __attribute__ ((__hardbool__)) +hbdbl; /* { dg-error "integral types" } */ + +typedef _Complex int __attribute__ ((__hardbool__)) +hbcplx; /* { dg-error "integral types" } */ + +enum x; +typedef enum x __attribute__ ((__hardbool__)) +hbenum; /* { dg-error "integral types" } */ + +struct s; +typedef struct s __attribute__ ((__hardbool__)) +hbstruct; /* { dg-error "integral types" } */ + +typedef int __attribute__ ((__hardbool__ (0, 0))) +hb00; /* { dg-error "different values" } */ + +typedef int __attribute__ ((__hardbool__ (4, 16))) hb4x; +struct s { + hb4x m:2; +}; /* { dg-error "is a GCC extension|different values" } */ +/* { dg-warning "changes value" "warning" { target *-*-* } .-1 } */ + +hb4x __attribute__ ((vector_size (4 * sizeof (hb4x)))) +vvar; /* { dg-error "invalid vector type" } */ diff --git a/gcc/testsuite/gcc.dg/hardbool-trap.c b/gcc/testsuite/gcc.dg/hardbool-trap.c new file mode 100644 index 0000000000000..2eebd0ef64fff --- /dev/null +++ b/gcc/testsuite/gcc.dg/hardbool-trap.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-optimized" } */ + +typedef char __attribute__ ((__hardbool__ (1))) hbool; + +hbool var; + +int main () { + __builtin_memset (&var, 0, sizeof (var)); + (void)var; +} + +/* { dg-final { scan-tree-dump-times "__builtin_trap" 1 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-5a.c b/gcc/testsuite/gcc.dg/torture/hardbool-5a.c new file mode 100644 index 0000000000000..a03887cfbecc5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/hardbool-5a.c @@ -0,0 +1,6 @@ +/* { dg-do run } */ +/* { dg-options "-w" } */ + +#define falseval 0x5a + +#include "hardbool.c" diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-i-5a.c b/gcc/testsuite/gcc.dg/torture/hardbool-i-5a.c new file mode 100644 index 0000000000000..c0ba2a8b9148e --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/hardbool-i-5a.c @@ -0,0 +1,6 @@ +/* { dg-do run } */ +/* { dg-options "-w" } */ + +#define falseval 0xa53cc35a + +#include "hardbool-i.c" diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-i.c b/gcc/testsuite/gcc.dg/torture/hardbool-i.c new file mode 100644 index 0000000000000..39214d28c5627 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/hardbool-i.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ + +#define basetype int + +#include "hardbool.c" diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-ll-5a.c b/gcc/testsuite/gcc.dg/torture/hardbool-ll-5a.c new file mode 100644 index 0000000000000..14438c5104f07 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/hardbool-ll-5a.c @@ -0,0 +1,6 @@ +/* { dg-do run } */ +/* { dg-options "-w" } */ + +#define falseval 0x781ee187a53cc35all + +#include "hardbool-ll.c" diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-ll.c b/gcc/testsuite/gcc.dg/torture/hardbool-ll.c new file mode 100644 index 0000000000000..d4d498c6f2af1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/hardbool-ll.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ + +#define basetype long long + +#include "hardbool.c" diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-s-5a.c b/gcc/testsuite/gcc.dg/torture/hardbool-s-5a.c new file mode 100644 index 0000000000000..e38a56b5deb05 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/hardbool-s-5a.c @@ -0,0 +1,6 @@ +/* { dg-do run } */ +/* { dg-options "-w" } */ + +#define falseval 0x5aa5 + +#include "hardbool-s.c" diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-s.c b/gcc/testsuite/gcc.dg/torture/hardbool-s.c new file mode 100644 index 0000000000000..942300be2072a --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/hardbool-s.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ + +#define basetype short + +#include "hardbool.c" diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-ul-5a.c b/gcc/testsuite/gcc.dg/torture/hardbool-ul-5a.c new file mode 100644 index 0000000000000..7beec578ff89c --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/hardbool-ul-5a.c @@ -0,0 +1,6 @@ +/* { dg-do run } */ +/* { dg-options "-w" } */ + +#define falseval 0xa53cc35a + +#include "hardbool-ul.c" diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-ul.c b/gcc/testsuite/gcc.dg/torture/hardbool-ul.c new file mode 100644 index 0000000000000..841c1d4bc2ec8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/hardbool-ul.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ + +#define basetype unsigned long + +#include "hardbool.c" diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-us-5a.c b/gcc/testsuite/gcc.dg/torture/hardbool-us-5a.c new file mode 100644 index 0000000000000..5bfc922795d3d --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/hardbool-us-5a.c @@ -0,0 +1,6 @@ +/* { dg-do run } */ +/* { dg-options "-w" } */ + +#define falseval 0xa55a + +#include "hardbool-us.c" diff --git a/gcc/testsuite/gcc.dg/torture/hardbool-us.c b/gcc/testsuite/gcc.dg/torture/hardbool-us.c new file mode 100644 index 0000000000000..e9feec681c41e --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/hardbool-us.c @@ -0,0 +1,5 @@ +/* { dg-do run } */ + +#define basetype unsigned short + +#include "hardbool.c" diff --git a/gcc/testsuite/gcc.dg/torture/hardbool.c b/gcc/testsuite/gcc.dg/torture/hardbool.c new file mode 100644 index 0000000000000..01684952a2a9f --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/hardbool.c @@ -0,0 +1,118 @@ +/* { dg-do run } */ + +#include + +#ifndef basetype +#define basetype char +#endif + +#ifndef falseval +#define falseval 0 +#endif + +#ifndef trueval +#define trueval ~falseval +#endif + +/* hardbool may be #defined so as to drop parms in other tests. */ +typedef basetype __attribute__ ((hardbool (falseval, trueval))) hbool; + +typedef unsigned char __attribute__ ((__hardbool__ (1, 0))) zbool; + +struct hs { + hbool a[2]; + hbool x:2; + hbool y:5; + zbool z:1; +}; + +hbool var = 0; + +struct hs x = { { 1, 0 }, 2, 0, 2 }; + +int f(hbool v) { + return !v; +} + +int g(int i) { + return f(i); +} + +hbool h(hbool x) { + return x; +} + +hbool h2(hbool x) { + return h(x); +} + +int hsx(struct hs v) { + return v.x; +} + +int ghs(hbool s) { + struct hs v = { {s, !s}, s, !s, s }; + return hsx (v); +} + +int t = (hbool)2; + +void check_pfalse (hbool *p) +{ + assert (!*p); + assert (*(basetype*)p == (basetype)falseval); + assert (!(int)(hbool)*p); +} + +void check_ptrue (hbool *p) +{ + assert (*p); + assert (*(basetype*)p == (basetype)trueval); + assert ((int)(hbool)*p); +} + +void check_vfalse (hbool v) +{ + check_pfalse (&v); +} + +void check_vtrue (hbool v) +{ + check_ptrue (&v); +} + +int main () { + check_pfalse (&var); + var = !(int)(hbool)(_Bool)var; + check_ptrue (&var); + var = (zbool)var; + check_ptrue (&var); + + check_ptrue (&x.a[0]); + check_pfalse (&x.a[1]); + check_vtrue (x.x); + check_vfalse (x.y); + check_vtrue (x.z); + + check_vtrue (t); + + check_vtrue (var && t); + check_vfalse (!var || x.y); + + check_vfalse (f (2)); + check_vfalse (f (1)); + check_vtrue (f (0)); + + check_vfalse (g (2)); + check_vfalse (g (1)); + check_vtrue (g (0)); + + check_vtrue (h (2)); + check_vtrue (h (1)); + check_vfalse (h (0)); + + check_vtrue (h2 (2)); + check_vtrue (h2 (1)); + check_vfalse (h2 (0)); +} +