Message ID | 20221010141141.krpmtzmbgadlo3db@ws2202.lin.mbt.kalray.eu |
---|---|
State | New, archived |
Headers |
Return-Path: <gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org> Delivered-To: ouuuleilei@gmail.com Received: by 2002:a5d:4ac7:0:0:0:0:0 with SMTP id y7csp1603586wrs; Mon, 10 Oct 2022 07:12:46 -0700 (PDT) X-Google-Smtp-Source: AMsMyM6FLHMGs38EenZ/YEFMoBY0PpOqla21DKOz6JmS6TtPbTmt1gW72LQwHlwcVhvnqZPfgwBN X-Received: by 2002:a05:6402:5256:b0:459:42b3:3d86 with SMTP id t22-20020a056402525600b0045942b33d86mr17405601edd.370.1665411166153; Mon, 10 Oct 2022 07:12:46 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1665411166; cv=none; d=google.com; s=arc-20160816; b=NxqvI/AIv0g1RAKqPEQBxTRmo4Xr4zYdSgULPO6xo2TIz3xe5iZkiO+4Q4FswYsm4S tWqanOwN3+xk/1coxKe7qmXaBhzoNjKWpaeHDr25lykXnwcsSXkI2H0gqRKeUdPnDz/Y dCbizkLvJwI7lvO3n9S/3oWY4XmF2OmdFzHKT4deqBWyacC5FbuWNXHOr1Va5dvrW8p5 6qopr5V1BFWUZifiAU1Z1sZjYqZAr438AvCdexOhHDTPglae4uFsLqek/ER9VGLqqfT4 yvUIlzbm3HvVSqEWJMmH9/XrPpUokWJSiwy5pUOoXD3pdx8LojuPok5aiSSjL8lKQ8Np ss4A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:cc:reply-to:from:list-subscribe:list-help :list-post:list-archive:list-unsubscribe:list-id:precedence :content-transfer-encoding:user-agent:content-disposition :mime-version:message-id:subject:to:date:dkim-filter:secumail-id :dmarc-filter:delivered-to:dkim-signature:dkim-filter; bh=zze+bDcj4El020CQ8AiUcofZA1NuT2Xq1UTdAzCdoUU=; b=Am/HqXH5hBgkbe9dN2gxfUOh9//3WV4917PbLP4J7WOg508aqI0Aq9X0NbAgO2trkP HevKQvoC8QRiPRBciFvNtI8CQCUyS+vYwV44zv3jAdlmrvp4r6OzrNLUFxI9ViAsoY1G mSbj0iif/aPJ9ae8/k6Zq9VVdBeWDvjYx/7TxPvmm8uDIOF7I2pPg7e8gspRN5O2/BFN CU60rKGkbj/N+uTC0DLzK1Up9gYvpHMFOGDeKOdw0+P0vSSH2h8yrAjdTjsh9M+FJVAb TD587tmGNuOa7ZemQMBplL90kJpLehB/wZj3QLD6Tv5teZA7xJhVmRGD6pvH7l52B1PH dM4A== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=i9McOXhg; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from sourceware.org (server2.sourceware.org. [2620:52:3:1:0:246e:9693:128c]) by mx.google.com with ESMTPS id f12-20020a0564021e8c00b0045938ab7129si11396064edf.330.2022.10.10.07.12.45 for <ouuuleilei@gmail.com> (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 10 Oct 2022 07:12:46 -0700 (PDT) Received-SPF: pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) client-ip=2620:52:3:1:0:246e:9693:128c; Authentication-Results: mx.google.com; dkim=pass header.i=@gcc.gnu.org header.s=default header.b=i9McOXhg; spf=pass (google.com: domain of gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org designates 2620:52:3:1:0:246e:9693:128c as permitted sender) smtp.mailfrom="gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org"; dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=gnu.org Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id CFB1E3858D37 for <ouuuleilei@gmail.com>; Mon, 10 Oct 2022 14:12:44 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org CFB1E3858D37 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1665411164; bh=zze+bDcj4El020CQ8AiUcofZA1NuT2Xq1UTdAzCdoUU=; h=Date:To:Subject:List-Id:List-Unsubscribe:List-Archive:List-Post: List-Help:List-Subscribe:From:Reply-To:Cc:From; b=i9McOXhgUVfJugLQ1pRIMh2HfxjMrtxTie4UfvpGkkB/k6XY57tNpsi8ytuBY1Wif 95iFHF4vj7PbksFi/MD6htahECX855nh8IT5F0JqMoRwZuhITfHjB7dtq3ImXuo/sn gZuySWjy2rK8FxsZKhtffGAlRDtpbSYoZHIK1Gko= X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from fx308.security-mail.net (smtpout30.security-mail.net [85.31.212.38]) by sourceware.org (Postfix) with ESMTPS id 70C193858D37 for <gcc-patches@gcc.gnu.org>; Mon, 10 Oct 2022 14:11:53 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 70C193858D37 Received: from localhost (localhost [127.0.0.1]) by fx308.security-mail.net (Postfix) with ESMTP id 49ECB80600B for <gcc-patches@gcc.gnu.org>; Mon, 10 Oct 2022 16:11:52 +0200 (CEST) Received: from fx308 (localhost [127.0.0.1]) by fx308.security-mail.net (Postfix) with ESMTP id 33E61806009; Mon, 10 Oct 2022 16:11:52 +0200 (CEST) Received: from zimbra2.kalray.eu (unknown [217.181.231.53]) by fx308.security-mail.net (Postfix) with ESMTPS id AC6FC805FF9; Mon, 10 Oct 2022 16:11:51 +0200 (CEST) Received: from zimbra2.kalray.eu (localhost [127.0.0.1]) by zimbra2.kalray.eu (Postfix) with ESMTPS id 9248927E044D; Mon, 10 Oct 2022 16:11:51 +0200 (CEST) Received: from localhost (localhost [127.0.0.1]) by zimbra2.kalray.eu (Postfix) with ESMTP id 769B827E044F; Mon, 10 Oct 2022 16:11:51 +0200 (CEST) Received: from zimbra2.kalray.eu ([127.0.0.1]) by localhost (zimbra2.kalray.eu [127.0.0.1]) (amavisd-new, port 10026) with ESMTP id ak4PY_hg4Jtf; Mon, 10 Oct 2022 16:11:51 +0200 (CEST) Received: from localhost (unknown [192.168.37.51]) by zimbra2.kalray.eu (Postfix) with ESMTPSA id 5A52027E044D; Mon, 10 Oct 2022 16:11:51 +0200 (CEST) X-Virus-Scanned: E-securemail Secumail-id: <b31f.63442827.ac0dc.0> DKIM-Filter: OpenDKIM Filter v2.10.3 zimbra2.kalray.eu 769B827E044F Date: Mon, 10 Oct 2022 16:11:41 +0200 To: gcc-patches@gcc.gnu.org Subject: [RFC] Add support for vectors in comparisons (like the C++ frontend does) Message-ID: <20221010141141.krpmtzmbgadlo3db@ws2202.lin.mbt.kalray.eu> MIME-Version: 1.0 Content-Disposition: inline User-Agent: NeoMutt/20171215 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-ALTERMIMEV2_out: done X-Spam-Status: No, score=-11.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, GIT_PATCH_0, HEADER_FROM_DIFFERENT_DOMAINS, KAM_ASCII_DIVIDERS, RCVD_IN_DNSWL_LOW, SPF_HELO_NONE, SPF_PASS, TXREP 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.29 Precedence: list List-Id: Gcc-patches mailing list <gcc-patches.gcc.gnu.org> List-Unsubscribe: <https://gcc.gnu.org/mailman/options/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe> List-Archive: <https://gcc.gnu.org/pipermail/gcc-patches/> List-Post: <mailto:gcc-patches@gcc.gnu.org> List-Help: <mailto:gcc-patches-request@gcc.gnu.org?subject=help> List-Subscribe: <https://gcc.gnu.org/mailman/listinfo/gcc-patches>, <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe> From: Paul Iannetta via Gcc-patches <gcc-patches@gcc.gnu.org> Reply-To: Paul Iannetta <piannetta@kalrayinc.com> Cc: joseph@codesourcery.com Errors-To: gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org Sender: "Gcc-patches" <gcc-patches-bounces+ouuuleilei=gmail.com@gcc.gnu.org> X-getmail-retrieved-from-mailbox: =?utf-8?q?INBOX?= X-GMAIL-THRID: =?utf-8?q?1746310178695116207?= X-GMAIL-MSGID: =?utf-8?q?1746310178695116207?= |
Series |
[RFC] Add support for vectors in comparisons (like the C++ frontend does)
|
|
Commit Message
Paul Iannetta
Oct. 10, 2022, 2:11 p.m. UTC
Hi, I am trying to bridge the gap between the extensions supported by the C and C++ front-ends. When it comes to vector extensions, C++ supports vectors in comparisons and within conditional expressions whereas the C front-end does not. I have a patch to bring this feature to the C front-end as well, and would like to hear your opinion on it, especially since it may affect the feature-set of the objc front-end as well. I have tried to mirror as much as possible what the C++ front-end does and checked that both front-end produce the same GIMPLE for all the simple expressions as well as some more complex combinations of the operators (?:, !, ^, || and &&). Currently, this is only a tentative patch and I did not add any tests to the testsuite. Moreover, the aarch64's target-specific testsuite explicitly tests the non-presence of this feature, which will have to be removed. I've run the testsuite on x86 and I've not seen any regressions. Cheers, Paul # ------------------------ >8 ------------------------ Support for vector types in simple comparisons gcc/ * doc/extend.texi: Remove the C++ mention, since both C and C++ support the all the mentioned features. gcc/c/ * c-typeck.cc (build_unary_op): Add support for vector for the unary exclamation mark. (build_conditional_expr): Add support for vector in conditional expressions. (build_binary_op): Add support for vector for &&, || and ^. (c_objc_common_truthvalue_conversion): Remove the special gards preventing vector types. # ------------------------ >8 ------------------------
Comments
Hi Paul, Not a review of the patch - but a couple of observations. > On 10 Oct 2022, at 15:11, Paul Iannetta via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: > I am trying to bridge the gap between the extensions supported by the C > and C++ front-ends. When it comes to vector extensions, C++ supports > vectors in comparisons and within conditional expressions whereas the C > front-end does not. Equivalence seems, on the face of it, a reasonable objective - but I am curious as whether there is some more concrete motivation for the patch, e.g. some codebase that currently does not work but would with this change? > I have a patch to bring this feature to the C front-end as well, and > would like to hear your opinion on it, especially since it may affect > the feature-set of the objc front-end as well. Likewise, I am interested in the motivation for the ObjC change. The usual initial filter for consideration of functional changes (at least for the NeXT runtime on Darwin) is “What does current clang do?” or, even better, “what does current Xcode do?”. There are several (possibly many) cases where C in clang has extensions to C++ behaviour - and vice-versa (because there is a single front-end codebase, that happens easily, whether by design o accident). The reason for the ‘clang litmus test’ is that the effective language standard for ObjectiveC is determined by that compiler. cheers Iain > I have tried to mirror as much as possible what the C++ front-end does > and checked that both front-end produce the same GIMPLE for all the > simple expressions as well as some more complex combinations of the > operators (?:, !, ^, || and &&). > > Currently, this is only a tentative patch and I did not add any tests > to the testsuite. Moreover, the aarch64's target-specific testsuite > explicitly tests the non-presence of this feature, which will have to > be removed. > > I've run the testsuite on x86 and I've not seen any regressions. > > Cheers, > Paul > > # ------------------------ >8 ------------------------ > Support for vector types in simple comparisons > > gcc/ > > * doc/extend.texi: Remove the C++ mention, since both C and C++ > support the all the mentioned features. > > gcc/c/ > > * c-typeck.cc (build_unary_op): Add support for vector for the > unary exclamation mark. > (build_conditional_expr): Add support for vector in conditional > expressions. > (build_binary_op): Add support for vector for &&, || and ^. > (c_objc_common_truthvalue_conversion): Remove the special gards > preventing vector types. > > # ------------------------ >8 ------------------------ > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc > index 17185fd3da4..03ade14cae9 100644 > --- a/gcc/c/c-typeck.cc > +++ b/gcc/c/c-typeck.cc > @@ -4536,12 +4536,15 @@ build_unary_op (location_t location, enum tree_code code, tree xarg, > case TRUTH_NOT_EXPR: > if (typecode != INTEGER_TYPE && typecode != FIXED_POINT_TYPE > && typecode != REAL_TYPE && typecode != POINTER_TYPE > - && typecode != COMPLEX_TYPE) > + && typecode != COMPLEX_TYPE && typecode != VECTOR_TYPE) > { > error_at (location, > "wrong type argument to unary exclamation mark"); > return error_mark_node; > } > + if (gnu_vector_type_p (TREE_TYPE (arg))) > + return build_binary_op (location, EQ_EXPR, arg, > + build_zero_cst (TREE_TYPE (arg)), false); > if (int_operands) > { > arg = c_objc_common_truthvalue_conversion (location, xarg); > @@ -5477,6 +5480,129 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp, > result_type = type2; > } > > + if (gnu_vector_type_p (TREE_TYPE (ifexp)) > + && VECTOR_INTEGER_TYPE_P (TREE_TYPE (ifexp))) > + { > + tree ifexp_type = TREE_TYPE (ifexp); > + > + /* If ifexp is another cond_expr choosing between -1 and 0, > + then we can use its comparison. It may help to avoid > + additional comparison, produce more accurate diagnostics > + and enables folding. */ > + if (TREE_CODE (ifexp) == VEC_COND_EXPR > + && integer_minus_onep (TREE_OPERAND (ifexp, 1)) > + && integer_zerop (TREE_OPERAND (ifexp, 2))) > + ifexp = TREE_OPERAND (ifexp, 0); > + > + tree op1_type = TREE_TYPE (op1); > + tree op2_type = TREE_TYPE (op2); > + > + if (!VECTOR_TYPE_P (op1_type) && !VECTOR_TYPE_P (op2_type)) > + { > + /* Rely on the error messages of the scalar version. */ > + tree scal = > + build_conditional_expr (colon_loc, integer_one_node, ifexp_bcp, > + op1, op1_original_type, op1_loc, > + op2, op2_original_type, op2_loc); > + if (scal == error_mark_node) > + return error_mark_node; > + tree stype = TREE_TYPE (scal); > + tree ctype = TREE_TYPE (ifexp_type); > + if (TYPE_SIZE (stype) != TYPE_SIZE (ctype) > + || (!INTEGRAL_TYPE_P (stype) && !SCALAR_FLOAT_TYPE_P (stype))) > + { > + error_at (colon_loc, > + "inferred scalar type %qT is not an integer or " > + "floating-point type of the same size as %qT", stype, > + COMPARISON_CLASS_P (ifexp) > + ? TREE_TYPE (TREE_TYPE (TREE_OPERAND (ifexp, 0))) > + : ctype); > + return error_mark_node; > + } > + > + tree vtype = build_opaque_vector_type (stype, > + TYPE_VECTOR_SUBPARTS > + (ifexp_type)); > + /* The warnings (like Wsign-conversion) have already been > + given by the scalar build_conditional_expr. We still check > + unsafe_conversion_p to forbid truncating long long -> float. */ > + if (unsafe_conversion_p (stype, op1, NULL_TREE, false)) > + { > + error_at (colon_loc, "conversion of scalar %qT to vector %qT " > + "involves truncation", op1_type, vtype); > + return error_mark_node; > + } > + if (unsafe_conversion_p (stype, op2, NULL_TREE, false)) > + { > + error_at (colon_loc, "conversion of scalar %qT to vector %qT " > + "involves truncation", op2_type, vtype); > + return error_mark_node; > + } > + > + op1 = convert (stype, op1); > + op1 = save_expr (op1); > + op1 = build_vector_from_val (vtype, op1); > + op1_type = vtype; > + op2 = convert (stype, op2); > + op2 = save_expr (op2); > + op2 = build_vector_from_val (vtype, op2); > + op2_type = vtype; > + } > + > + if (gnu_vector_type_p (op1_type) ^ gnu_vector_type_p (op2_type)) > + { > + enum stv_conv convert_flag = > + scalar_to_vector (colon_loc, VEC_COND_EXPR, op1, op2, > + true); > + > + switch (convert_flag) > + { > + case stv_error: > + return error_mark_node; > + case stv_firstarg: > + { > + op1 = save_expr (op1); > + op1 = convert (TREE_TYPE (op2_type), op1); > + op1 = build_vector_from_val (op2_type, op1); > + op1_type = TREE_TYPE (op1); > + break; > + } > + case stv_secondarg: > + { > + op2 = save_expr (op2); > + op2 = convert (TREE_TYPE (op1_type), op2); > + op2 = build_vector_from_val (op1_type, op2); > + op2_type = TREE_TYPE (op2); > + break; > + } > + default: > + break; > + } > + } > + > + if (!gnu_vector_type_p (op1_type) > + || !gnu_vector_type_p (op2_type) > + || !comptypes (op1_type, op2_type) > + || maybe_ne (TYPE_VECTOR_SUBPARTS (ifexp_type), > + TYPE_VECTOR_SUBPARTS (op1_type)) > + || TYPE_SIZE (ifexp_type) != TYPE_SIZE (op1_type)) > + { > + error_at (colon_loc, > + "incompatible vector types in conditional expression: " > + "%qT, %qT and %qT", TREE_TYPE (ifexp), > + TREE_TYPE (orig_op1), TREE_TYPE (orig_op2)); > + return error_mark_node; > + } > + > + if (!COMPARISON_CLASS_P (ifexp)) > + { > + tree cmp_type = truth_type_for (ifexp_type); > + ifexp = build2 (NE_EXPR, cmp_type, ifexp, > + build_zero_cst (ifexp_type)); > + } > + return build3_loc (colon_loc, VEC_COND_EXPR, op1_type, ifexp, op1, op2); > + } > + > if (!result_type) > { > if (flag_cond_mismatch) > @@ -5522,17 +5648,6 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp, > && !TREE_OVERFLOW (orig_op2))); > } > > - /* Need to convert condition operand into a vector mask. */ > - if (VECTOR_TYPE_P (TREE_TYPE (ifexp))) > - { > - tree vectype = TREE_TYPE (ifexp); > - tree elem_type = TREE_TYPE (vectype); > - tree zero = build_int_cst (elem_type, 0); > - tree zero_vec = build_vector_from_val (vectype, zero); > - tree cmp_type = truth_type_for (vectype); > - ifexp = build2 (NE_EXPR, cmp_type, ifexp, zero_vec); > - } > - > if (int_const || (ifexp_bcp && TREE_CODE (ifexp) == INTEGER_CST)) > ret = fold_build3_loc (colon_loc, COND_EXPR, result_type, ifexp, op1, op2); > else > @@ -12105,6 +12220,54 @@ build_binary_op (location_t location, enum tree_code code, > && (op0 == truthvalue_true_node > || !TREE_OVERFLOW (orig_op1))); > } > + if (!VECTOR_TYPE_P (type0) && gnu_vector_type_p (type1)) > + { > + if (!COMPARISON_CLASS_P (op1)) > + op1 = build_binary_op (EXPR_LOCATION (op1), NE_EXPR, op1, > + build_zero_cst (type1), false); > + if (code == TRUTH_ANDIF_EXPR) > + { > + tree z = build_zero_cst (TREE_TYPE (op1)); > + return build_conditional_expr (location, op0, 0, > + op1, NULL_TREE, EXPR_LOCATION (op1), > + z, NULL_TREE, EXPR_LOCATION (z)); > + } > + else if (code == TRUTH_ORIF_EXPR) > + { > + tree m1 = build_all_ones_cst (TREE_TYPE (op1)); > + return build_conditional_expr (location, op0, 0, > + m1, NULL_TREE, EXPR_LOCATION (m1), > + op1, NULL_TREE, EXPR_LOCATION (op1)); > + } > + else > + gcc_unreachable (); > + } > + if (gnu_vector_type_p (type0) > + && (!VECTOR_TYPE_P (type1) || gnu_vector_type_p (type1))) > + { > + if (!COMPARISON_CLASS_P (op0)) > + op0 = build_binary_op (EXPR_LOCATION (op0), NE_EXPR, op0, > + build_zero_cst (type0), false); > + if (!VECTOR_TYPE_P (type1)) > + { > + tree m1 = build_all_ones_cst (TREE_TYPE (op0)); > + tree z = build_zero_cst (TREE_TYPE (op0)); > + op1 = build_conditional_expr (location, op1, 0, > + m1, NULL_TREE, EXPR_LOCATION (m1), > + z, NULL_TREE, EXPR_LOCATION(z)); > + } > + else if (!COMPARISON_CLASS_P (op1)) > + op1 = build_binary_op (EXPR_LOCATION (op1), NE_EXPR, op1, > + build_zero_cst (type1), false); > + if (code == TRUTH_ANDIF_EXPR) > + code = BIT_AND_EXPR; > + else if (code == TRUTH_ORIF_EXPR) > + code = BIT_IOR_EXPR; > + else > + gcc_unreachable (); > + > + return build_binary_op (location, code, op0, op1, false); > + } > break; > > /* Shift operations: result has same type as first operand; > @@ -12906,10 +13069,6 @@ c_objc_common_truthvalue_conversion (location_t location, tree expr) > case FUNCTION_TYPE: > gcc_unreachable (); > > - case VECTOR_TYPE: > - error_at (location, "used vector type where scalar is required"); > - return error_mark_node; > - > default: > break; > } > @@ -12924,8 +13083,6 @@ c_objc_common_truthvalue_conversion (location_t location, tree expr) > expr = note_integer_operands (expr); > } > else > - /* ??? Should we also give an error for vectors rather than leaving > - those to give errors later? */ > expr = c_common_truthvalue_conversion (location, expr); > > if (TREE_CODE (expr) == INTEGER_CST && int_operands && !int_const) > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi > index c89df8778b2..1e0d436c02c 100644 > --- a/gcc/doc/extend.texi > +++ b/gcc/doc/extend.texi > @@ -12007,7 +12007,7 @@ c = a > b; /* The result would be @{0, 0,-1, 0@} */ > c = a == b; /* The result would be @{0,-1, 0,-1@} */ > @end smallexample > > -In C++, the ternary operator @code{?:} is available. @code{a?b:c}, where > +The ternary operator @code{?:} is available. @code{a?b:c}, where > @code{b} and @code{c} are vectors of the same type and @code{a} is an > integer vector with the same number of elements of the same size as @code{b} > and @code{c}, computes all three arguments and creates a vector > @@ -12020,7 +12020,7 @@ vector. If both @code{b} and @code{c} are scalars and the type of > @code{b} and @code{c} are converted to a vector type whose elements have > this type and with the same number of elements as @code{a}. > > -In C++, the logic operators @code{!, &&, ||} are available for vectors. > +The logic operators @code{!, &&, ||} are available for vectors. > @code{!v} is equivalent to @code{v == 0}, @code{a && b} is equivalent to > @code{a!=0 & b!=0} and @code{a || b} is equivalent to @code{a!=0 | b!=0}. > For mixed operations between a scalar @code{s} and a vector @code{v}, > > > >
Hi Ian, Observations are also very welcomed, thank you! On Mon, Oct 10, 2022 at 03:37:24PM +0100, Iain Sandoe wrote: > Hi Paul, > > Not a review of the patch - but a couple of observations. > > > On 10 Oct 2022, at 15:11, Paul Iannetta via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: > > > I am trying to bridge the gap between the extensions supported by the C > > and C++ front-ends. When it comes to vector extensions, C++ supports > > vectors in comparisons and within conditional expressions whereas the C > > front-end does not. > > Equivalence seems, on the face of it, a reasonable objective - but I am > curious as whether there is some more concrete motivation for the patch, > e.g. some codebase that currently does not work but would with this change? > The main motivation behind the equivalence is that, we have C and C++ codebases, and it is not very convenient to have to remember which extension is allowed in C and not in C++ and vice-versa. And, in this case, it makes it harder for GCC to generate conditional moves for code using vectors, especially since `a ? b : c` is not recognized, and thus, we cannot rely on the `VEC_COND_EXPR` construction. > > I have a patch to bring this feature to the C front-end as well, and > > would like to hear your opinion on it, especially since it may affect > > the feature-set of the objc front-end as well. > > Likewise, I am interested in the motivation for the ObjC change. The usual > initial filter for consideration of functional changes (at least for the NeXT > runtime on Darwin) is “What does current clang do?” or, even better, “what > does current Xcode do?”. There are several (possibly many) cases where > C in clang has extensions to C++ behaviour - and vice-versa (because there > is a single front-end codebase, that happens easily, whether by design o > accident). Well, the only motivation was that it was easier to make the change for both front-ends at the same time and avoided some checks. I'll add them so that it won't introduce a divergence on what is accepted by the objc front-end. Thanks, Paul > > The reason for the ‘clang litmus test’ is that the effective language standard > for ObjectiveC is determined by that compiler. > > cheers > Iain > > > I have tried to mirror as much as possible what the C++ front-end does > > and checked that both front-end produce the same GIMPLE for all the > > simple expressions as well as some more complex combinations of the > > operators (?:, !, ^, || and &&). > > > > Currently, this is only a tentative patch and I did not add any tests > > to the testsuite. Moreover, the aarch64's target-specific testsuite > > explicitly tests the non-presence of this feature, which will have to > > be removed. > > > > I've run the testsuite on x86 and I've not seen any regressions. > > > > Cheers, > > Paul > > > > # ------------------------ >8 ------------------------ > > Support for vector types in simple comparisons > > > > gcc/ > > > > * doc/extend.texi: Remove the C++ mention, since both C and C++ > > support the all the mentioned features. > > > > gcc/c/ > > > > * c-typeck.cc (build_unary_op): Add support for vector for the > > unary exclamation mark. > > (build_conditional_expr): Add support for vector in conditional > > expressions. > > (build_binary_op): Add support for vector for &&, || and ^. > > (c_objc_common_truthvalue_conversion): Remove the special gards > > preventing vector types. > > > > # ------------------------ >8 ------------------------ > > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc > > index 17185fd3da4..03ade14cae9 100644 > > --- a/gcc/c/c-typeck.cc > > +++ b/gcc/c/c-typeck.cc > > @@ -4536,12 +4536,15 @@ build_unary_op (location_t location, enum tree_code code, tree xarg, > > case TRUTH_NOT_EXPR: > > if (typecode != INTEGER_TYPE && typecode != FIXED_POINT_TYPE > > && typecode != REAL_TYPE && typecode != POINTER_TYPE > > - && typecode != COMPLEX_TYPE) > > + && typecode != COMPLEX_TYPE && typecode != VECTOR_TYPE) > > { > > error_at (location, > > "wrong type argument to unary exclamation mark"); > > return error_mark_node; > > } > > + if (gnu_vector_type_p (TREE_TYPE (arg))) > > + return build_binary_op (location, EQ_EXPR, arg, > > + build_zero_cst (TREE_TYPE (arg)), false); > > if (int_operands) > > { > > arg = c_objc_common_truthvalue_conversion (location, xarg); > > @@ -5477,6 +5480,129 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp, > > result_type = type2; > > } > > > > + if (gnu_vector_type_p (TREE_TYPE (ifexp)) > > + && VECTOR_INTEGER_TYPE_P (TREE_TYPE (ifexp))) > > + { > > + tree ifexp_type = TREE_TYPE (ifexp); > > + > > + /* If ifexp is another cond_expr choosing between -1 and 0, > > + then we can use its comparison. It may help to avoid > > + additional comparison, produce more accurate diagnostics > > + and enables folding. */ > > + if (TREE_CODE (ifexp) == VEC_COND_EXPR > > + && integer_minus_onep (TREE_OPERAND (ifexp, 1)) > > + && integer_zerop (TREE_OPERAND (ifexp, 2))) > > + ifexp = TREE_OPERAND (ifexp, 0); > > + > > + tree op1_type = TREE_TYPE (op1); > > + tree op2_type = TREE_TYPE (op2); > > + > > + if (!VECTOR_TYPE_P (op1_type) && !VECTOR_TYPE_P (op2_type)) > > + { > > + /* Rely on the error messages of the scalar version. */ > > + tree scal = > > + build_conditional_expr (colon_loc, integer_one_node, ifexp_bcp, > > + op1, op1_original_type, op1_loc, > > + op2, op2_original_type, op2_loc); > > + if (scal == error_mark_node) > > + return error_mark_node; > > + tree stype = TREE_TYPE (scal); > > + tree ctype = TREE_TYPE (ifexp_type); > > + if (TYPE_SIZE (stype) != TYPE_SIZE (ctype) > > + || (!INTEGRAL_TYPE_P (stype) && !SCALAR_FLOAT_TYPE_P (stype))) > > + { > > + error_at (colon_loc, > > + "inferred scalar type %qT is not an integer or " > > + "floating-point type of the same size as %qT", stype, > > + COMPARISON_CLASS_P (ifexp) > > + ? TREE_TYPE (TREE_TYPE (TREE_OPERAND (ifexp, 0))) > > + : ctype); > > + return error_mark_node; > > + } > > + > > + tree vtype = build_opaque_vector_type (stype, > > + TYPE_VECTOR_SUBPARTS > > + (ifexp_type)); > > + /* The warnings (like Wsign-conversion) have already been > > + given by the scalar build_conditional_expr. We still check > > + unsafe_conversion_p to forbid truncating long long -> float. */ > > + if (unsafe_conversion_p (stype, op1, NULL_TREE, false)) > > + { > > + error_at (colon_loc, "conversion of scalar %qT to vector %qT " > > + "involves truncation", op1_type, vtype); > > + return error_mark_node; > > + } > > + if (unsafe_conversion_p (stype, op2, NULL_TREE, false)) > > + { > > + error_at (colon_loc, "conversion of scalar %qT to vector %qT " > > + "involves truncation", op2_type, vtype); > > + return error_mark_node; > > + } > > + > > + op1 = convert (stype, op1); > > + op1 = save_expr (op1); > > + op1 = build_vector_from_val (vtype, op1); > > + op1_type = vtype; > > + op2 = convert (stype, op2); > > + op2 = save_expr (op2); > > + op2 = build_vector_from_val (vtype, op2); > > + op2_type = vtype; > > + } > > + > > + if (gnu_vector_type_p (op1_type) ^ gnu_vector_type_p (op2_type)) > > + { > > + enum stv_conv convert_flag = > > + scalar_to_vector (colon_loc, VEC_COND_EXPR, op1, op2, > > + true); > > + > > + switch (convert_flag) > > + { > > + case stv_error: > > + return error_mark_node; > > + case stv_firstarg: > > + { > > + op1 = save_expr (op1); > > + op1 = convert (TREE_TYPE (op2_type), op1); > > + op1 = build_vector_from_val (op2_type, op1); > > + op1_type = TREE_TYPE (op1); > > + break; > > + } > > + case stv_secondarg: > > + { > > + op2 = save_expr (op2); > > + op2 = convert (TREE_TYPE (op1_type), op2); > > + op2 = build_vector_from_val (op1_type, op2); > > + op2_type = TREE_TYPE (op2); > > + break; > > + } > > + default: > > + break; > > + } > > + } > > + > > + if (!gnu_vector_type_p (op1_type) > > + || !gnu_vector_type_p (op2_type) > > + || !comptypes (op1_type, op2_type) > > + || maybe_ne (TYPE_VECTOR_SUBPARTS (ifexp_type), > > + TYPE_VECTOR_SUBPARTS (op1_type)) > > + || TYPE_SIZE (ifexp_type) != TYPE_SIZE (op1_type)) > > + { > > + error_at (colon_loc, > > + "incompatible vector types in conditional expression: " > > + "%qT, %qT and %qT", TREE_TYPE (ifexp), > > + TREE_TYPE (orig_op1), TREE_TYPE (orig_op2)); > > + return error_mark_node; > > + } > > + > > + if (!COMPARISON_CLASS_P (ifexp)) > > + { > > + tree cmp_type = truth_type_for (ifexp_type); > > + ifexp = build2 (NE_EXPR, cmp_type, ifexp, > > + build_zero_cst (ifexp_type)); > > + } > > + return build3_loc (colon_loc, VEC_COND_EXPR, op1_type, ifexp, op1, op2); > > + } > > + > > if (!result_type) > > { > > if (flag_cond_mismatch) > > @@ -5522,17 +5648,6 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp, > > && !TREE_OVERFLOW (orig_op2))); > > } > > > > - /* Need to convert condition operand into a vector mask. */ > > - if (VECTOR_TYPE_P (TREE_TYPE (ifexp))) > > - { > > - tree vectype = TREE_TYPE (ifexp); > > - tree elem_type = TREE_TYPE (vectype); > > - tree zero = build_int_cst (elem_type, 0); > > - tree zero_vec = build_vector_from_val (vectype, zero); > > - tree cmp_type = truth_type_for (vectype); > > - ifexp = build2 (NE_EXPR, cmp_type, ifexp, zero_vec); > > - } > > - > > if (int_const || (ifexp_bcp && TREE_CODE (ifexp) == INTEGER_CST)) > > ret = fold_build3_loc (colon_loc, COND_EXPR, result_type, ifexp, op1, op2); > > else > > @@ -12105,6 +12220,54 @@ build_binary_op (location_t location, enum tree_code code, > > && (op0 == truthvalue_true_node > > || !TREE_OVERFLOW (orig_op1))); > > } > > + if (!VECTOR_TYPE_P (type0) && gnu_vector_type_p (type1)) > > + { > > + if (!COMPARISON_CLASS_P (op1)) > > + op1 = build_binary_op (EXPR_LOCATION (op1), NE_EXPR, op1, > > + build_zero_cst (type1), false); > > + if (code == TRUTH_ANDIF_EXPR) > > + { > > + tree z = build_zero_cst (TREE_TYPE (op1)); > > + return build_conditional_expr (location, op0, 0, > > + op1, NULL_TREE, EXPR_LOCATION (op1), > > + z, NULL_TREE, EXPR_LOCATION (z)); > > + } > > + else if (code == TRUTH_ORIF_EXPR) > > + { > > + tree m1 = build_all_ones_cst (TREE_TYPE (op1)); > > + return build_conditional_expr (location, op0, 0, > > + m1, NULL_TREE, EXPR_LOCATION (m1), > > + op1, NULL_TREE, EXPR_LOCATION (op1)); > > + } > > + else > > + gcc_unreachable (); > > + } > > + if (gnu_vector_type_p (type0) > > + && (!VECTOR_TYPE_P (type1) || gnu_vector_type_p (type1))) > > + { > > + if (!COMPARISON_CLASS_P (op0)) > > + op0 = build_binary_op (EXPR_LOCATION (op0), NE_EXPR, op0, > > + build_zero_cst (type0), false); > > + if (!VECTOR_TYPE_P (type1)) > > + { > > + tree m1 = build_all_ones_cst (TREE_TYPE (op0)); > > + tree z = build_zero_cst (TREE_TYPE (op0)); > > + op1 = build_conditional_expr (location, op1, 0, > > + m1, NULL_TREE, EXPR_LOCATION (m1), > > + z, NULL_TREE, EXPR_LOCATION(z)); > > + } > > + else if (!COMPARISON_CLASS_P (op1)) > > + op1 = build_binary_op (EXPR_LOCATION (op1), NE_EXPR, op1, > > + build_zero_cst (type1), false); > > + if (code == TRUTH_ANDIF_EXPR) > > + code = BIT_AND_EXPR; > > + else if (code == TRUTH_ORIF_EXPR) > > + code = BIT_IOR_EXPR; > > + else > > + gcc_unreachable (); > > + > > + return build_binary_op (location, code, op0, op1, false); > > + } > > break; > > > > /* Shift operations: result has same type as first operand; > > @@ -12906,10 +13069,6 @@ c_objc_common_truthvalue_conversion (location_t location, tree expr) > > case FUNCTION_TYPE: > > gcc_unreachable (); > > > > - case VECTOR_TYPE: > > - error_at (location, "used vector type where scalar is required"); > > - return error_mark_node; > > - > > default: > > break; > > } > > @@ -12924,8 +13083,6 @@ c_objc_common_truthvalue_conversion (location_t location, tree expr) > > expr = note_integer_operands (expr); > > } > > else > > - /* ??? Should we also give an error for vectors rather than leaving > > - those to give errors later? */ > > expr = c_common_truthvalue_conversion (location, expr); > > > > if (TREE_CODE (expr) == INTEGER_CST && int_operands && !int_const) > > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi > > index c89df8778b2..1e0d436c02c 100644 > > --- a/gcc/doc/extend.texi > > +++ b/gcc/doc/extend.texi > > @@ -12007,7 +12007,7 @@ c = a > b; /* The result would be @{0, 0,-1, 0@} */ > > c = a == b; /* The result would be @{0,-1, 0,-1@} */ > > @end smallexample > > > > -In C++, the ternary operator @code{?:} is available. @code{a?b:c}, where > > +The ternary operator @code{?:} is available. @code{a?b:c}, where > > @code{b} and @code{c} are vectors of the same type and @code{a} is an > > integer vector with the same number of elements of the same size as @code{b} > > and @code{c}, computes all three arguments and creates a vector > > @@ -12020,7 +12020,7 @@ vector. If both @code{b} and @code{c} are scalars and the type of > > @code{b} and @code{c} are converted to a vector type whose elements have > > this type and with the same number of elements as @code{a}. > > > > -In C++, the logic operators @code{!, &&, ||} are available for vectors. > > +The logic operators @code{!, &&, ||} are available for vectors. > > @code{!v} is equivalent to @code{v == 0}, @code{a && b} is equivalent to > > @code{a!=0 & b!=0} and @code{a || b} is equivalent to @code{a!=0 | b!=0}. > > For mixed operations between a scalar @code{s} and a vector @code{v}, > > > > > > > > > > > > >
Hi Paul, > On 10 Oct 2022, at 16:20, Paul Iannetta <piannetta@kalrayinc.com> wrote: > On Mon, Oct 10, 2022 at 03:37:24PM +0100, Iain Sandoe wrote: >> Hi Paul, >> >> Not a review of the patch - but a couple of observations. >> >>> On 10 Oct 2022, at 15:11, Paul Iannetta via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: >> >>> I am trying to bridge the gap between the extensions supported by the C >>> and C++ front-ends. When it comes to vector extensions, C++ supports >>> vectors in comparisons and within conditional expressions whereas the C >>> front-end does not. >> >> Equivalence seems, on the face of it, a reasonable objective - but I am >> curious as whether there is some more concrete motivation for the patch, >> e.g. some codebase that currently does not work but would with this change? >> > > The main motivation behind the equivalence is that, we have C and C++ > codebases, and it is not very convenient to have to remember which > extension is allowed in C and not in C++ and vice-versa. And, in this > case, it makes it harder for GCC to generate conditional moves for > code using vectors, especially since `a ? b : c` is not recognized, > and thus, we cannot rely on the `VEC_COND_EXPR` construction. > >>> I have a patch to bring this feature to the C front-end as well, and >>> would like to hear your opinion on it, especially since it may affect >>> the feature-set of the objc front-end as well. >> >> Likewise, I am interested in the motivation for the ObjC change. The usual >> initial filter for consideration of functional changes (at least for the NeXT >> runtime on Darwin) is “What does current clang do?” or, even better, “what >> does current Xcode do?”. There are several (possibly many) cases where >> C in clang has extensions to C++ behaviour - and vice-versa (because there >> is a single front-end codebase, that happens easily, whether by design o >> accident). > > Well, the only motivation was that it was easier to make the change > for both front-ends at the same time and avoided some checks. I'll > add them so that it won't introduce a divergence on what is accepted > by the objc front-end. I have no objection to GCC growing extensions to Objective-C (even ones that clang does not have) provided that we check what clang does and (say under the -pedantic flag) reject extensions that are not present there. So there is no need to back out of this - but some example codes that we can check would be useful. In general, extensions to the “standard” language would be behind some flag that disables them when the “standard” version is selected - and gives warnings/errors as appropriate for ‘-pedantic’. So that, for example, -std=gnuCXX could enable the extensions, but -std=CXX would complain. thanks Iain > > Thanks, > Paul > >> >> The reason for the ‘clang litmus test’ is that the effective language standard >> for ObjectiveC is determined by that compiler. >> >> cheers >> Iain >> >>> I have tried to mirror as much as possible what the C++ front-end does >>> and checked that both front-end produce the same GIMPLE for all the >>> simple expressions as well as some more complex combinations of the >>> operators (?:, !, ^, || and &&). >>> >>> Currently, this is only a tentative patch and I did not add any tests >>> to the testsuite. Moreover, the aarch64's target-specific testsuite >>> explicitly tests the non-presence of this feature, which will have to >>> be removed. >>> >>> I've run the testsuite on x86 and I've not seen any regressions. >>> >>> Cheers, >>> Paul >>> >>> # ------------------------ >8 ------------------------ >>> Support for vector types in simple comparisons >>> >>> gcc/ >>> >>> * doc/extend.texi: Remove the C++ mention, since both C and C++ >>> support the all the mentioned features. >>> >>> gcc/c/ >>> >>> * c-typeck.cc (build_unary_op): Add support for vector for the >>> unary exclamation mark. >>> (build_conditional_expr): Add support for vector in conditional >>> expressions. >>> (build_binary_op): Add support for vector for &&, || and ^. >>> (c_objc_common_truthvalue_conversion): Remove the special gards >>> preventing vector types. >>> >>> # ------------------------ >8 ------------------------ >>> diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc >>> index 17185fd3da4..03ade14cae9 100644 >>> --- a/gcc/c/c-typeck.cc >>> +++ b/gcc/c/c-typeck.cc >>> @@ -4536,12 +4536,15 @@ build_unary_op (location_t location, enum tree_code code, tree xarg, >>> case TRUTH_NOT_EXPR: >>> if (typecode != INTEGER_TYPE && typecode != FIXED_POINT_TYPE >>> && typecode != REAL_TYPE && typecode != POINTER_TYPE >>> - && typecode != COMPLEX_TYPE) >>> + && typecode != COMPLEX_TYPE && typecode != VECTOR_TYPE) >>> { >>> error_at (location, >>> "wrong type argument to unary exclamation mark"); >>> return error_mark_node; >>> } >>> + if (gnu_vector_type_p (TREE_TYPE (arg))) >>> + return build_binary_op (location, EQ_EXPR, arg, >>> + build_zero_cst (TREE_TYPE (arg)), false); >>> if (int_operands) >>> { >>> arg = c_objc_common_truthvalue_conversion (location, xarg); >>> @@ -5477,6 +5480,129 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp, >>> result_type = type2; >>> } >>> >>> + if (gnu_vector_type_p (TREE_TYPE (ifexp)) >>> + && VECTOR_INTEGER_TYPE_P (TREE_TYPE (ifexp))) >>> + { >>> + tree ifexp_type = TREE_TYPE (ifexp); >>> + >>> + /* If ifexp is another cond_expr choosing between -1 and 0, >>> + then we can use its comparison. It may help to avoid >>> + additional comparison, produce more accurate diagnostics >>> + and enables folding. */ >>> + if (TREE_CODE (ifexp) == VEC_COND_EXPR >>> + && integer_minus_onep (TREE_OPERAND (ifexp, 1)) >>> + && integer_zerop (TREE_OPERAND (ifexp, 2))) >>> + ifexp = TREE_OPERAND (ifexp, 0); >>> + >>> + tree op1_type = TREE_TYPE (op1); >>> + tree op2_type = TREE_TYPE (op2); >>> + >>> + if (!VECTOR_TYPE_P (op1_type) && !VECTOR_TYPE_P (op2_type)) >>> + { >>> + /* Rely on the error messages of the scalar version. */ >>> + tree scal = >>> + build_conditional_expr (colon_loc, integer_one_node, ifexp_bcp, >>> + op1, op1_original_type, op1_loc, >>> + op2, op2_original_type, op2_loc); >>> + if (scal == error_mark_node) >>> + return error_mark_node; >>> + tree stype = TREE_TYPE (scal); >>> + tree ctype = TREE_TYPE (ifexp_type); >>> + if (TYPE_SIZE (stype) != TYPE_SIZE (ctype) >>> + || (!INTEGRAL_TYPE_P (stype) && !SCALAR_FLOAT_TYPE_P (stype))) >>> + { >>> + error_at (colon_loc, >>> + "inferred scalar type %qT is not an integer or " >>> + "floating-point type of the same size as %qT", stype, >>> + COMPARISON_CLASS_P (ifexp) >>> + ? TREE_TYPE (TREE_TYPE (TREE_OPERAND (ifexp, 0))) >>> + : ctype); >>> + return error_mark_node; >>> + } >>> + >>> + tree vtype = build_opaque_vector_type (stype, >>> + TYPE_VECTOR_SUBPARTS >>> + (ifexp_type)); >>> + /* The warnings (like Wsign-conversion) have already been >>> + given by the scalar build_conditional_expr. We still check >>> + unsafe_conversion_p to forbid truncating long long -> float. */ >>> + if (unsafe_conversion_p (stype, op1, NULL_TREE, false)) >>> + { >>> + error_at (colon_loc, "conversion of scalar %qT to vector %qT " >>> + "involves truncation", op1_type, vtype); >>> + return error_mark_node; >>> + } >>> + if (unsafe_conversion_p (stype, op2, NULL_TREE, false)) >>> + { >>> + error_at (colon_loc, "conversion of scalar %qT to vector %qT " >>> + "involves truncation", op2_type, vtype); >>> + return error_mark_node; >>> + } >>> + >>> + op1 = convert (stype, op1); >>> + op1 = save_expr (op1); >>> + op1 = build_vector_from_val (vtype, op1); >>> + op1_type = vtype; >>> + op2 = convert (stype, op2); >>> + op2 = save_expr (op2); >>> + op2 = build_vector_from_val (vtype, op2); >>> + op2_type = vtype; >>> + } >>> + >>> + if (gnu_vector_type_p (op1_type) ^ gnu_vector_type_p (op2_type)) >>> + { >>> + enum stv_conv convert_flag = >>> + scalar_to_vector (colon_loc, VEC_COND_EXPR, op1, op2, >>> + true); >>> + >>> + switch (convert_flag) >>> + { >>> + case stv_error: >>> + return error_mark_node; >>> + case stv_firstarg: >>> + { >>> + op1 = save_expr (op1); >>> + op1 = convert (TREE_TYPE (op2_type), op1); >>> + op1 = build_vector_from_val (op2_type, op1); >>> + op1_type = TREE_TYPE (op1); >>> + break; >>> + } >>> + case stv_secondarg: >>> + { >>> + op2 = save_expr (op2); >>> + op2 = convert (TREE_TYPE (op1_type), op2); >>> + op2 = build_vector_from_val (op1_type, op2); >>> + op2_type = TREE_TYPE (op2); >>> + break; >>> + } >>> + default: >>> + break; >>> + } >>> + } >>> + >>> + if (!gnu_vector_type_p (op1_type) >>> + || !gnu_vector_type_p (op2_type) >>> + || !comptypes (op1_type, op2_type) >>> + || maybe_ne (TYPE_VECTOR_SUBPARTS (ifexp_type), >>> + TYPE_VECTOR_SUBPARTS (op1_type)) >>> + || TYPE_SIZE (ifexp_type) != TYPE_SIZE (op1_type)) >>> + { >>> + error_at (colon_loc, >>> + "incompatible vector types in conditional expression: " >>> + "%qT, %qT and %qT", TREE_TYPE (ifexp), >>> + TREE_TYPE (orig_op1), TREE_TYPE (orig_op2)); >>> + return error_mark_node; >>> + } >>> + >>> + if (!COMPARISON_CLASS_P (ifexp)) >>> + { >>> + tree cmp_type = truth_type_for (ifexp_type); >>> + ifexp = build2 (NE_EXPR, cmp_type, ifexp, >>> + build_zero_cst (ifexp_type)); >>> + } >>> + return build3_loc (colon_loc, VEC_COND_EXPR, op1_type, ifexp, op1, op2); >>> + } >>> + >>> if (!result_type) >>> { >>> if (flag_cond_mismatch) >>> @@ -5522,17 +5648,6 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp, >>> && !TREE_OVERFLOW (orig_op2))); >>> } >>> >>> - /* Need to convert condition operand into a vector mask. */ >>> - if (VECTOR_TYPE_P (TREE_TYPE (ifexp))) >>> - { >>> - tree vectype = TREE_TYPE (ifexp); >>> - tree elem_type = TREE_TYPE (vectype); >>> - tree zero = build_int_cst (elem_type, 0); >>> - tree zero_vec = build_vector_from_val (vectype, zero); >>> - tree cmp_type = truth_type_for (vectype); >>> - ifexp = build2 (NE_EXPR, cmp_type, ifexp, zero_vec); >>> - } >>> - >>> if (int_const || (ifexp_bcp && TREE_CODE (ifexp) == INTEGER_CST)) >>> ret = fold_build3_loc (colon_loc, COND_EXPR, result_type, ifexp, op1, op2); >>> else >>> @@ -12105,6 +12220,54 @@ build_binary_op (location_t location, enum tree_code code, >>> && (op0 == truthvalue_true_node >>> || !TREE_OVERFLOW (orig_op1))); >>> } >>> + if (!VECTOR_TYPE_P (type0) && gnu_vector_type_p (type1)) >>> + { >>> + if (!COMPARISON_CLASS_P (op1)) >>> + op1 = build_binary_op (EXPR_LOCATION (op1), NE_EXPR, op1, >>> + build_zero_cst (type1), false); >>> + if (code == TRUTH_ANDIF_EXPR) >>> + { >>> + tree z = build_zero_cst (TREE_TYPE (op1)); >>> + return build_conditional_expr (location, op0, 0, >>> + op1, NULL_TREE, EXPR_LOCATION (op1), >>> + z, NULL_TREE, EXPR_LOCATION (z)); >>> + } >>> + else if (code == TRUTH_ORIF_EXPR) >>> + { >>> + tree m1 = build_all_ones_cst (TREE_TYPE (op1)); >>> + return build_conditional_expr (location, op0, 0, >>> + m1, NULL_TREE, EXPR_LOCATION (m1), >>> + op1, NULL_TREE, EXPR_LOCATION (op1)); >>> + } >>> + else >>> + gcc_unreachable (); >>> + } >>> + if (gnu_vector_type_p (type0) >>> + && (!VECTOR_TYPE_P (type1) || gnu_vector_type_p (type1))) >>> + { >>> + if (!COMPARISON_CLASS_P (op0)) >>> + op0 = build_binary_op (EXPR_LOCATION (op0), NE_EXPR, op0, >>> + build_zero_cst (type0), false); >>> + if (!VECTOR_TYPE_P (type1)) >>> + { >>> + tree m1 = build_all_ones_cst (TREE_TYPE (op0)); >>> + tree z = build_zero_cst (TREE_TYPE (op0)); >>> + op1 = build_conditional_expr (location, op1, 0, >>> + m1, NULL_TREE, EXPR_LOCATION (m1), >>> + z, NULL_TREE, EXPR_LOCATION(z)); >>> + } >>> + else if (!COMPARISON_CLASS_P (op1)) >>> + op1 = build_binary_op (EXPR_LOCATION (op1), NE_EXPR, op1, >>> + build_zero_cst (type1), false); >>> + if (code == TRUTH_ANDIF_EXPR) >>> + code = BIT_AND_EXPR; >>> + else if (code == TRUTH_ORIF_EXPR) >>> + code = BIT_IOR_EXPR; >>> + else >>> + gcc_unreachable (); >>> + >>> + return build_binary_op (location, code, op0, op1, false); >>> + } >>> break; >>> >>> /* Shift operations: result has same type as first operand; >>> @@ -12906,10 +13069,6 @@ c_objc_common_truthvalue_conversion (location_t location, tree expr) >>> case FUNCTION_TYPE: >>> gcc_unreachable (); >>> >>> - case VECTOR_TYPE: >>> - error_at (location, "used vector type where scalar is required"); >>> - return error_mark_node; >>> - >>> default: >>> break; >>> } >>> @@ -12924,8 +13083,6 @@ c_objc_common_truthvalue_conversion (location_t location, tree expr) >>> expr = note_integer_operands (expr); >>> } >>> else >>> - /* ??? Should we also give an error for vectors rather than leaving >>> - those to give errors later? */ >>> expr = c_common_truthvalue_conversion (location, expr); >>> >>> if (TREE_CODE (expr) == INTEGER_CST && int_operands && !int_const) >>> diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi >>> index c89df8778b2..1e0d436c02c 100644 >>> --- a/gcc/doc/extend.texi >>> +++ b/gcc/doc/extend.texi >>> @@ -12007,7 +12007,7 @@ c = a > b; /* The result would be @{0, 0,-1, 0@} */ >>> c = a == b; /* The result would be @{0,-1, 0,-1@} */ >>> @end smallexample >>> >>> -In C++, the ternary operator @code{?:} is available. @code{a?b:c}, where >>> +The ternary operator @code{?:} is available. @code{a?b:c}, where >>> @code{b} and @code{c} are vectors of the same type and @code{a} is an >>> integer vector with the same number of elements of the same size as @code{b} >>> and @code{c}, computes all three arguments and creates a vector >>> @@ -12020,7 +12020,7 @@ vector. If both @code{b} and @code{c} are scalars and the type of >>> @code{b} and @code{c} are converted to a vector type whose elements have >>> this type and with the same number of elements as @code{a}. >>> >>> -In C++, the logic operators @code{!, &&, ||} are available for vectors. >>> +The logic operators @code{!, &&, ||} are available for vectors. >>> @code{!v} is equivalent to @code{v == 0}, @code{a && b} is equivalent to >>> @code{a!=0 & b!=0} and @code{a || b} is equivalent to @code{a!=0 | b!=0}. >>> For mixed operations between a scalar @code{s} and a vector @code{v},
On Mon, 10 Oct 2022, Paul Iannetta via Gcc-patches wrote: > I have a patch to bring this feature to the C front-end as well, and > would like to hear your opinion on it, especially since it may affect > the feature-set of the objc front-end as well. > Currently, this is only a tentative patch and I did not add any tests > to the testsuite. I think tests (possibly existing C++ tests moved to c-c++-common?) are necessary to judge such a feature; it could better be judged based on tests without implementation than based on implementation without tests.
On Mon, Oct 10, 2022 at 4:12 PM Paul Iannetta via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: > > Hi, > > I am trying to bridge the gap between the extensions supported by the C > and C++ front-ends. When it comes to vector extensions, C++ supports > vectors in comparisons and within conditional expressions whereas the C > front-end does not. > > I have a patch to bring this feature to the C front-end as well, and > would like to hear your opinion on it, especially since it may affect > the feature-set of the objc front-end as well. > > I have tried to mirror as much as possible what the C++ front-end does > and checked that both front-end produce the same GIMPLE for all the > simple expressions as well as some more complex combinations of the > operators (?:, !, ^, || and &&). > > Currently, this is only a tentative patch and I did not add any tests > to the testsuite. Moreover, the aarch64's target-specific testsuite > explicitly tests the non-presence of this feature, which will have to > be removed. > > I've run the testsuite on x86 and I've not seen any regressions. As Joseph says testcases for this are necessary, possibly by moving existing tests to c-c++-common. Otherwise I welcome this change, the divergence in features has been annoying at least. Richard. > Cheers, > Paul > > # ------------------------ >8 ------------------------ > Support for vector types in simple comparisons > > gcc/ > > * doc/extend.texi: Remove the C++ mention, since both C and C++ > support the all the mentioned features. > > gcc/c/ > > * c-typeck.cc (build_unary_op): Add support for vector for the > unary exclamation mark. > (build_conditional_expr): Add support for vector in conditional > expressions. > (build_binary_op): Add support for vector for &&, || and ^. > (c_objc_common_truthvalue_conversion): Remove the special gards > preventing vector types. > > # ------------------------ >8 ------------------------ > diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc > index 17185fd3da4..03ade14cae9 100644 > --- a/gcc/c/c-typeck.cc > +++ b/gcc/c/c-typeck.cc > @@ -4536,12 +4536,15 @@ build_unary_op (location_t location, enum tree_code code, tree xarg, > case TRUTH_NOT_EXPR: > if (typecode != INTEGER_TYPE && typecode != FIXED_POINT_TYPE > && typecode != REAL_TYPE && typecode != POINTER_TYPE > - && typecode != COMPLEX_TYPE) > + && typecode != COMPLEX_TYPE && typecode != VECTOR_TYPE) > { > error_at (location, > "wrong type argument to unary exclamation mark"); > return error_mark_node; > } > + if (gnu_vector_type_p (TREE_TYPE (arg))) > + return build_binary_op (location, EQ_EXPR, arg, > + build_zero_cst (TREE_TYPE (arg)), false); > if (int_operands) > { > arg = c_objc_common_truthvalue_conversion (location, xarg); > @@ -5477,6 +5480,129 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp, > result_type = type2; > } > > + if (gnu_vector_type_p (TREE_TYPE (ifexp)) > + && VECTOR_INTEGER_TYPE_P (TREE_TYPE (ifexp))) > + { > + tree ifexp_type = TREE_TYPE (ifexp); > + > + /* If ifexp is another cond_expr choosing between -1 and 0, > + then we can use its comparison. It may help to avoid > + additional comparison, produce more accurate diagnostics > + and enables folding. */ > + if (TREE_CODE (ifexp) == VEC_COND_EXPR > + && integer_minus_onep (TREE_OPERAND (ifexp, 1)) > + && integer_zerop (TREE_OPERAND (ifexp, 2))) > + ifexp = TREE_OPERAND (ifexp, 0); > + > + tree op1_type = TREE_TYPE (op1); > + tree op2_type = TREE_TYPE (op2); > + > + if (!VECTOR_TYPE_P (op1_type) && !VECTOR_TYPE_P (op2_type)) > + { > + /* Rely on the error messages of the scalar version. */ > + tree scal = > + build_conditional_expr (colon_loc, integer_one_node, ifexp_bcp, > + op1, op1_original_type, op1_loc, > + op2, op2_original_type, op2_loc); > + if (scal == error_mark_node) > + return error_mark_node; > + tree stype = TREE_TYPE (scal); > + tree ctype = TREE_TYPE (ifexp_type); > + if (TYPE_SIZE (stype) != TYPE_SIZE (ctype) > + || (!INTEGRAL_TYPE_P (stype) && !SCALAR_FLOAT_TYPE_P (stype))) > + { > + error_at (colon_loc, > + "inferred scalar type %qT is not an integer or " > + "floating-point type of the same size as %qT", stype, > + COMPARISON_CLASS_P (ifexp) > + ? TREE_TYPE (TREE_TYPE (TREE_OPERAND (ifexp, 0))) > + : ctype); > + return error_mark_node; > + } > + > + tree vtype = build_opaque_vector_type (stype, > + TYPE_VECTOR_SUBPARTS > + (ifexp_type)); > + /* The warnings (like Wsign-conversion) have already been > + given by the scalar build_conditional_expr. We still check > + unsafe_conversion_p to forbid truncating long long -> float. */ > + if (unsafe_conversion_p (stype, op1, NULL_TREE, false)) > + { > + error_at (colon_loc, "conversion of scalar %qT to vector %qT " > + "involves truncation", op1_type, vtype); > + return error_mark_node; > + } > + if (unsafe_conversion_p (stype, op2, NULL_TREE, false)) > + { > + error_at (colon_loc, "conversion of scalar %qT to vector %qT " > + "involves truncation", op2_type, vtype); > + return error_mark_node; > + } > + > + op1 = convert (stype, op1); > + op1 = save_expr (op1); > + op1 = build_vector_from_val (vtype, op1); > + op1_type = vtype; > + op2 = convert (stype, op2); > + op2 = save_expr (op2); > + op2 = build_vector_from_val (vtype, op2); > + op2_type = vtype; > + } > + > + if (gnu_vector_type_p (op1_type) ^ gnu_vector_type_p (op2_type)) > + { > + enum stv_conv convert_flag = > + scalar_to_vector (colon_loc, VEC_COND_EXPR, op1, op2, > + true); > + > + switch (convert_flag) > + { > + case stv_error: > + return error_mark_node; > + case stv_firstarg: > + { > + op1 = save_expr (op1); > + op1 = convert (TREE_TYPE (op2_type), op1); > + op1 = build_vector_from_val (op2_type, op1); > + op1_type = TREE_TYPE (op1); > + break; > + } > + case stv_secondarg: > + { > + op2 = save_expr (op2); > + op2 = convert (TREE_TYPE (op1_type), op2); > + op2 = build_vector_from_val (op1_type, op2); > + op2_type = TREE_TYPE (op2); > + break; > + } > + default: > + break; > + } > + } > + > + if (!gnu_vector_type_p (op1_type) > + || !gnu_vector_type_p (op2_type) > + || !comptypes (op1_type, op2_type) > + || maybe_ne (TYPE_VECTOR_SUBPARTS (ifexp_type), > + TYPE_VECTOR_SUBPARTS (op1_type)) > + || TYPE_SIZE (ifexp_type) != TYPE_SIZE (op1_type)) > + { > + error_at (colon_loc, > + "incompatible vector types in conditional expression: " > + "%qT, %qT and %qT", TREE_TYPE (ifexp), > + TREE_TYPE (orig_op1), TREE_TYPE (orig_op2)); > + return error_mark_node; > + } > + > + if (!COMPARISON_CLASS_P (ifexp)) > + { > + tree cmp_type = truth_type_for (ifexp_type); > + ifexp = build2 (NE_EXPR, cmp_type, ifexp, > + build_zero_cst (ifexp_type)); > + } > + return build3_loc (colon_loc, VEC_COND_EXPR, op1_type, ifexp, op1, op2); > + } > + > if (!result_type) > { > if (flag_cond_mismatch) > @@ -5522,17 +5648,6 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp, > && !TREE_OVERFLOW (orig_op2))); > } > > - /* Need to convert condition operand into a vector mask. */ > - if (VECTOR_TYPE_P (TREE_TYPE (ifexp))) > - { > - tree vectype = TREE_TYPE (ifexp); > - tree elem_type = TREE_TYPE (vectype); > - tree zero = build_int_cst (elem_type, 0); > - tree zero_vec = build_vector_from_val (vectype, zero); > - tree cmp_type = truth_type_for (vectype); > - ifexp = build2 (NE_EXPR, cmp_type, ifexp, zero_vec); > - } > - > if (int_const || (ifexp_bcp && TREE_CODE (ifexp) == INTEGER_CST)) > ret = fold_build3_loc (colon_loc, COND_EXPR, result_type, ifexp, op1, op2); > else > @@ -12105,6 +12220,54 @@ build_binary_op (location_t location, enum tree_code code, > && (op0 == truthvalue_true_node > || !TREE_OVERFLOW (orig_op1))); > } > + if (!VECTOR_TYPE_P (type0) && gnu_vector_type_p (type1)) > + { > + if (!COMPARISON_CLASS_P (op1)) > + op1 = build_binary_op (EXPR_LOCATION (op1), NE_EXPR, op1, > + build_zero_cst (type1), false); > + if (code == TRUTH_ANDIF_EXPR) > + { > + tree z = build_zero_cst (TREE_TYPE (op1)); > + return build_conditional_expr (location, op0, 0, > + op1, NULL_TREE, EXPR_LOCATION (op1), > + z, NULL_TREE, EXPR_LOCATION (z)); > + } > + else if (code == TRUTH_ORIF_EXPR) > + { > + tree m1 = build_all_ones_cst (TREE_TYPE (op1)); > + return build_conditional_expr (location, op0, 0, > + m1, NULL_TREE, EXPR_LOCATION (m1), > + op1, NULL_TREE, EXPR_LOCATION (op1)); > + } > + else > + gcc_unreachable (); > + } > + if (gnu_vector_type_p (type0) > + && (!VECTOR_TYPE_P (type1) || gnu_vector_type_p (type1))) > + { > + if (!COMPARISON_CLASS_P (op0)) > + op0 = build_binary_op (EXPR_LOCATION (op0), NE_EXPR, op0, > + build_zero_cst (type0), false); > + if (!VECTOR_TYPE_P (type1)) > + { > + tree m1 = build_all_ones_cst (TREE_TYPE (op0)); > + tree z = build_zero_cst (TREE_TYPE (op0)); > + op1 = build_conditional_expr (location, op1, 0, > + m1, NULL_TREE, EXPR_LOCATION (m1), > + z, NULL_TREE, EXPR_LOCATION(z)); > + } > + else if (!COMPARISON_CLASS_P (op1)) > + op1 = build_binary_op (EXPR_LOCATION (op1), NE_EXPR, op1, > + build_zero_cst (type1), false); > + if (code == TRUTH_ANDIF_EXPR) > + code = BIT_AND_EXPR; > + else if (code == TRUTH_ORIF_EXPR) > + code = BIT_IOR_EXPR; > + else > + gcc_unreachable (); > + > + return build_binary_op (location, code, op0, op1, false); > + } > break; > > /* Shift operations: result has same type as first operand; > @@ -12906,10 +13069,6 @@ c_objc_common_truthvalue_conversion (location_t location, tree expr) > case FUNCTION_TYPE: > gcc_unreachable (); > > - case VECTOR_TYPE: > - error_at (location, "used vector type where scalar is required"); > - return error_mark_node; > - > default: > break; > } > @@ -12924,8 +13083,6 @@ c_objc_common_truthvalue_conversion (location_t location, tree expr) > expr = note_integer_operands (expr); > } > else > - /* ??? Should we also give an error for vectors rather than leaving > - those to give errors later? */ > expr = c_common_truthvalue_conversion (location, expr); > > if (TREE_CODE (expr) == INTEGER_CST && int_operands && !int_const) > diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi > index c89df8778b2..1e0d436c02c 100644 > --- a/gcc/doc/extend.texi > +++ b/gcc/doc/extend.texi > @@ -12007,7 +12007,7 @@ c = a > b; /* The result would be @{0, 0,-1, 0@} */ > c = a == b; /* The result would be @{0,-1, 0,-1@} */ > @end smallexample > > -In C++, the ternary operator @code{?:} is available. @code{a?b:c}, where > +The ternary operator @code{?:} is available. @code{a?b:c}, where > @code{b} and @code{c} are vectors of the same type and @code{a} is an > integer vector with the same number of elements of the same size as @code{b} > and @code{c}, computes all three arguments and creates a vector > @@ -12020,7 +12020,7 @@ vector. If both @code{b} and @code{c} are scalars and the type of > @code{b} and @code{c} are converted to a vector type whose elements have > this type and with the same number of elements as @code{a}. > > -In C++, the logic operators @code{!, &&, ||} are available for vectors. > +The logic operators @code{!, &&, ||} are available for vectors. > @code{!v} is equivalent to @code{v == 0}, @code{a && b} is equivalent to > @code{a!=0 & b!=0} and @code{a || b} is equivalent to @code{a!=0 | b!=0}. > For mixed operations between a scalar @code{s} and a vector @code{v}, > > > >
On Mon, Oct 10, 2022 at 11:07:06PM +0000, Joseph Myers wrote: > On Mon, 10 Oct 2022, Paul Iannetta via Gcc-patches wrote: > > > I have a patch to bring this feature to the C front-end as well, and > > would like to hear your opinion on it, especially since it may affect > > the feature-set of the objc front-end as well. > > > Currently, this is only a tentative patch and I did not add any tests > > to the testsuite. > > I think tests (possibly existing C++ tests moved to c-c++-common?) are > necessary to judge such a feature; it could better be judged based on > tests without implementation than based on implementation without tests. Currently, this feature has the following tests in g++.dg/ext/ - vector9.C - vector19.C - vector21.C - vector22.C - vector23.C - vector27.C - vector28.C provided by Marc Glisse when he implemented the feature for C++. They are all handled by my mirror implementation (after removing C++-only features), save for a case in vector19.C ( v ? '1' : '2', where v is a vector of unsigned char, but '1' and '2' are considered as int, which results in a type mismatch.) I'll move those tests to c-c++-common tomorrow, but will duplicate vector19.C and vector23.C which rely on C++-only features. During my tests, I've been using variations around this: typedef int v2si __attribute__((__vector_size__ (2 * sizeof(int)))); v2si f (v2si a, v2si b, v2si c) { v2si d = a + !b; v2si e = a || b; return c ? (a + !b) && (c - e && a) : (!!b ^ c && e); } It is already possible to express much of the same thing without the syntactic sugar but is is barely legible typedef int v2si __attribute__((__vector_size__ (2 * sizeof(int)))); v2si f (v2si a, v2si b, v2si c) { v2si d = a + (b == 0); v2si e = (a != 0) | (b != 0); return ((c != 0) & (((a + (b == 0)) != 0) & (((c - e) != 0) & (a != 0)))) | ((c == 0) & (((((b == 0) == 0) ^ c) != 0) & (e != 0))); } Paul
On Wed, Oct 12, 2022 at 01:18:19AM +0200, Paul Iannetta wrote: > On Mon, Oct 10, 2022 at 11:07:06PM +0000, Joseph Myers wrote: > > On Mon, 10 Oct 2022, Paul Iannetta via Gcc-patches wrote: > > > > > I have a patch to bring this feature to the C front-end as well, and > > > would like to hear your opinion on it, especially since it may affect > > > the feature-set of the objc front-end as well. > > > > > Currently, this is only a tentative patch and I did not add any tests > > > to the testsuite. > > > > I think tests (possibly existing C++ tests moved to c-c++-common?) are > > necessary to judge such a feature; it could better be judged based on > > tests without implementation than based on implementation without tests. > > Currently, this feature has the following tests in g++.dg/ext/ > - vector9.C > - vector19.C > - vector21.C > - vector22.C > - vector23.C > - vector27.C > - vector28.C > provided by Marc Glisse when he implemented the feature for C++. > > They are all handled by my mirror implementation (after removing > C++-only features), save for a case in vector19.C ( v ? '1' : '2', > where v is a vector of unsigned char, but '1' and '2' are considered > as int, which results in a type mismatch.) > > I'll move those tests to c-c++-common tomorrow, but will duplicate > vector19.C and vector23.C which rely on C++-only features. > > During my tests, I've been using variations around this: > > typedef int v2si __attribute__((__vector_size__ (2 * sizeof(int)))); > > v2si f (v2si a, v2si b, v2si c) > { > v2si d = a + !b; > v2si e = a || b; > return c ? (a + !b) && (c - e && a) : (!!b ^ c && e); > } > > It is already possible to express much of the same thing without the > syntactic sugar but is is barely legible > > typedef int v2si __attribute__((__vector_size__ (2 * sizeof(int)))); > > v2si f (v2si a, v2si b, v2si c) > { > v2si d = a + (b == 0); > v2si e = (a != 0) | (b != 0); > return ((c != 0) & (((a + (b == 0)) != 0) & (((c - e) != 0) & (a != 0)))) > | ((c == 0) & (((((b == 0) == 0) ^ c) != 0) & (e != 0))); > } > > Paul I still need to check what is done by clang on the objc side, but in order to not conflict with what was done before, a warning is triggered by c_obj_common_truthvalue_conversion and build_unary_operator warns if '!' is used with a vector. Both warnings are only triggered in pedantic mode as suggested by Iain Sandoe. The support of the binary ops and unary ops works as the C++ front-end does, there is however the case of the ternary conditional operator, where the C standard mandates the promotion of the operands if they have rank less than (unsigned) int, whereas C++ does not. In any case, as per the documentation of VEC_COND_EXPR, "vec0 = vector-condition ? vec1 : vec2" is equivalent to ``` (from tree.def) for (int i = 0 ; i < n ; ++i) vec0[i] = vector-condtion[i] ? vec1[i] : vec2[i]; ``` But this is currently not the case, even in C++ where ``` (Ex1) typedef signed char vec2 __attribute__((vector_size(16))); typedef float vec2f __attribute__((vector_size( 2 * sizeof (float)))); void j (vec2 *x, vec2 *z, vec2f *y, vec2f *t) { *x = (*y < *t) ? '1' : '0'; // error: inferred scalar type ‘char’ is // not an integer or floating-point type // of the same size as ‘float’. for (int i = 0 ; i < 2 ; ++i) // fine (*x)[i] = (*y)[i] < (*t)[i] ? '1' : '0'; // *z = (*x < *z) ? '1' : '0'; // fine } ``` The documentation explicitly says: > the ternary operator ?: is available. a?b:c, where b and c are > vectors of the same type and a is an integer vector with the same > number of elements of the same size as b and c, computes all three > arguments and creates a vector {a[0]?b[0]:c[0], a[1]?b[1]:c[1], …} Here, "*y < *t" is a boolean vector (and bool is an integral type ([basic.fundamental] 11), so this should be accepted. An other point is that if we look at ``` for (int i = 0 ; i < n ; ++i) vec0[i] = vector-condtion[i] ? vec1[i] : vec2[i]; ``` implicit conversions may happen, which is completely over-looked currently. That is, the type of (1): "v = v0 ? v1 : v2" is the lowest common type of v, v1 and v2; and the type of (2): "v0 ? v1 : v2" is the lowest common type of v1 and v2. (2) can appear as a parameter, but even in that case, I think that (2) should be constrained by the type of the parameter and we are back to case (1). My points are that: - the current implementation has a bug: " *x = (*y < *t) ? '1' : '0';" from (Ex1) should be fine. - the current implementation does not explicetly follow the documented behavior of VEC_COND_EXPR. What do you think? Paul
On Fri, Oct 14, 2022 at 4:18 PM Paul Iannetta via Gcc-patches <gcc-patches@gcc.gnu.org> wrote: > > On Wed, Oct 12, 2022 at 01:18:19AM +0200, Paul Iannetta wrote: > > On Mon, Oct 10, 2022 at 11:07:06PM +0000, Joseph Myers wrote: > > > On Mon, 10 Oct 2022, Paul Iannetta via Gcc-patches wrote: > > > > > > > I have a patch to bring this feature to the C front-end as well, and > > > > would like to hear your opinion on it, especially since it may affect > > > > the feature-set of the objc front-end as well. > > > > > > > Currently, this is only a tentative patch and I did not add any tests > > > > to the testsuite. > > > > > > I think tests (possibly existing C++ tests moved to c-c++-common?) are > > > necessary to judge such a feature; it could better be judged based on > > > tests without implementation than based on implementation without tests. > > > > Currently, this feature has the following tests in g++.dg/ext/ > > - vector9.C > > - vector19.C > > - vector21.C > > - vector22.C > > - vector23.C > > - vector27.C > > - vector28.C > > provided by Marc Glisse when he implemented the feature for C++. > > > > They are all handled by my mirror implementation (after removing > > C++-only features), save for a case in vector19.C ( v ? '1' : '2', > > where v is a vector of unsigned char, but '1' and '2' are considered > > as int, which results in a type mismatch.) > > > > I'll move those tests to c-c++-common tomorrow, but will duplicate > > vector19.C and vector23.C which rely on C++-only features. > > > > During my tests, I've been using variations around this: > > > > typedef int v2si __attribute__((__vector_size__ (2 * sizeof(int)))); > > > > v2si f (v2si a, v2si b, v2si c) > > { > > v2si d = a + !b; > > v2si e = a || b; > > return c ? (a + !b) && (c - e && a) : (!!b ^ c && e); > > } > > > > It is already possible to express much of the same thing without the > > syntactic sugar but is is barely legible > > > > typedef int v2si __attribute__((__vector_size__ (2 * sizeof(int)))); > > > > v2si f (v2si a, v2si b, v2si c) > > { > > v2si d = a + (b == 0); > > v2si e = (a != 0) | (b != 0); > > return ((c != 0) & (((a + (b == 0)) != 0) & (((c - e) != 0) & (a != 0)))) > > | ((c == 0) & (((((b == 0) == 0) ^ c) != 0) & (e != 0))); > > } > > > > Paul > > I still need to check what is done by clang on the objc side, but in > order to not conflict with what was done before, a warning is > triggered by c_obj_common_truthvalue_conversion and > build_unary_operator warns if '!' is used with a vector. Both warnings > are only triggered in pedantic mode as suggested by Iain Sandoe. > > The support of the binary ops and unary ops works as the C++ front-end > does, there is however the case of the ternary conditional operator, > where the C standard mandates the promotion of the operands if they > have rank less than (unsigned) int, whereas C++ does not. > > In any case, as per the documentation of VEC_COND_EXPR, > "vec0 = vector-condition ? vec1 : vec2" is equivalent to > ``` (from tree.def) > for (int i = 0 ; i < n ; ++i) > vec0[i] = vector-condtion[i] ? vec1[i] : vec2[i]; > ``` > But this is currently not the case, even in C++ where > ``` (Ex1) > typedef signed char vec2 __attribute__((vector_size(16))); > typedef float vec2f __attribute__((vector_size( 2 * sizeof (float)))); > > void j (vec2 *x, vec2 *z, vec2f *y, vec2f *t) > { > *x = (*y < *t) ? '1' : '0'; // error: inferred scalar type ‘char’ is > // not an integer or floating-point type > // of the same size as ‘float’. > > for (int i = 0 ; i < 2 ; ++i) // fine > (*x)[i] = (*y)[i] < (*t)[i] ? '1' : '0'; // > > *z = (*x < *z) ? '1' : '0'; // fine > } > ``` > > The documentation explicitly says: > > the ternary operator ?: is available. a?b:c, where b and c are > > vectors of the same type and a is an integer vector with the same > > number of elements of the same size as b and c, computes all three > > arguments and creates a vector {a[0]?b[0]:c[0], a[1]?b[1]:c[1], …} > Here, "*y < *t" is a boolean vector (and bool is an integral type > ([basic.fundamental] 11), so this should be accepted. > > An other point is that if we look at > ``` > for (int i = 0 ; i < n ; ++i) > vec0[i] = vector-condtion[i] ? vec1[i] : vec2[i]; > ``` > implicit conversions may happen, which is completely over-looked > currently. That is, the type of (1): "v = v0 ? v1 : v2" is the lowest > common type of v, v1 and v2; and the type of (2): "v0 ? v1 : v2" is the > lowest common type of v1 and v2. (2) can appear as a parameter, but > even in that case, I think that (2) should be constrained by the type > of the parameter and we are back to case (1). > > My points are that: > - the current implementation has a bug: " *x = (*y < *t) ? '1' : > '0';" from (Ex1) should be fine. > - the current implementation does not explicetly follow the > documented behavior of VEC_COND_EXPR. > > What do you think? Implicit promotion was explicitely not implemented for the vector extension as that would usually lead to unexpected slowness (scalar expansion). Instead I think we accept unpromoted operands when they match and diagnose other cases. ISTR this is documented somewhere but the ternary conditional operator docs may be indeed over-simplified here. The docs mention 'The operations behave like C++ @code{valarrays}.' it should probably be clarified that while v16qi a,b,c; a = b + c; computes the sum of the elements the implicit promotion/demotion required by the C/C++ standards for scalar operations is _not_ performed (and so technically considers signed vector char adds as invoking undefined behavior on overflow rather than implementation defined behavior on the truncation step - something we should mitigate by performing the adds in unsigned when C/C++ would promote) I think the non-promotion also follows what openCL does (the intent of the vector extension was to somewhat follow that spec). Richard. > > Paul > > > >
On Mon, Oct 17, 2022 at 09:22:23AM +0200, Richard Biener wrote: > On Fri, Oct 14, 2022 at 4:18 PM Paul Iannetta via Gcc-patches > <gcc-patches@gcc.gnu.org> wrote: > > > > On Wed, Oct 12, 2022 at 01:18:19AM +0200, Paul Iannetta wrote: > > > On Mon, Oct 10, 2022 at 11:07:06PM +0000, Joseph Myers wrote: > > > > On Mon, 10 Oct 2022, Paul Iannetta via Gcc-patches wrote: > > > > > > > > > I have a patch to bring this feature to the C front-end as well, and > > > > > would like to hear your opinion on it, especially since it may affect > > > > > the feature-set of the objc front-end as well. > > > > > > > > > Currently, this is only a tentative patch and I did not add any tests > > > > > to the testsuite. > > > > > > > > I think tests (possibly existing C++ tests moved to c-c++-common?) are > > > > necessary to judge such a feature; it could better be judged based on > > > > tests without implementation than based on implementation without tests. > > > > > > Currently, this feature has the following tests in g++.dg/ext/ > > > - vector9.C > > > - vector19.C > > > - vector21.C > > > - vector22.C > > > - vector23.C > > > - vector27.C > > > - vector28.C > > > provided by Marc Glisse when he implemented the feature for C++. > > > > > > They are all handled by my mirror implementation (after removing > > > C++-only features), save for a case in vector19.C ( v ? '1' : '2', > > > where v is a vector of unsigned char, but '1' and '2' are considered > > > as int, which results in a type mismatch.) > > > > > > I'll move those tests to c-c++-common tomorrow, but will duplicate > > > vector19.C and vector23.C which rely on C++-only features. > > > > > > During my tests, I've been using variations around this: > > > > > > typedef int v2si __attribute__((__vector_size__ (2 * sizeof(int)))); > > > > > > v2si f (v2si a, v2si b, v2si c) > > > { > > > v2si d = a + !b; > > > v2si e = a || b; > > > return c ? (a + !b) && (c - e && a) : (!!b ^ c && e); > > > } > > > > > > It is already possible to express much of the same thing without the > > > syntactic sugar but is is barely legible > > > > > > typedef int v2si __attribute__((__vector_size__ (2 * sizeof(int)))); > > > > > > v2si f (v2si a, v2si b, v2si c) > > > { > > > v2si d = a + (b == 0); > > > v2si e = (a != 0) | (b != 0); > > > return ((c != 0) & (((a + (b == 0)) != 0) & (((c - e) != 0) & (a != 0)))) > > > | ((c == 0) & (((((b == 0) == 0) ^ c) != 0) & (e != 0))); > > > } > > > > > > Paul > > > > I still need to check what is done by clang on the objc side, but in > > order to not conflict with what was done before, a warning is > > triggered by c_obj_common_truthvalue_conversion and > > build_unary_operator warns if '!' is used with a vector. Both warnings > > are only triggered in pedantic mode as suggested by Iain Sandoe. > > > > The support of the binary ops and unary ops works as the C++ front-end > > does, there is however the case of the ternary conditional operator, > > where the C standard mandates the promotion of the operands if they > > have rank less than (unsigned) int, whereas C++ does not. > > > > In any case, as per the documentation of VEC_COND_EXPR, > > "vec0 = vector-condition ? vec1 : vec2" is equivalent to > > ``` (from tree.def) > > for (int i = 0 ; i < n ; ++i) > > vec0[i] = vector-condtion[i] ? vec1[i] : vec2[i]; > > ``` > > But this is currently not the case, even in C++ where > > ``` (Ex1) > > typedef signed char vec2 __attribute__((vector_size(16))); > > typedef float vec2f __attribute__((vector_size( 2 * sizeof (float)))); > > > > void j (vec2 *x, vec2 *z, vec2f *y, vec2f *t) > > { > > *x = (*y < *t) ? '1' : '0'; // error: inferred scalar type ‘char’ is > > // not an integer or floating-point type > > // of the same size as ‘float’. > > > > for (int i = 0 ; i < 2 ; ++i) // fine > > (*x)[i] = (*y)[i] < (*t)[i] ? '1' : '0'; // > > > > *z = (*x < *z) ? '1' : '0'; // fine > > } > > ``` > > > > The documentation explicitly says: > > > the ternary operator ?: is available. a?b:c, where b and c are > > > vectors of the same type and a is an integer vector with the same > > > number of elements of the same size as b and c, computes all three > > > arguments and creates a vector {a[0]?b[0]:c[0], a[1]?b[1]:c[1], …} > > Here, "*y < *t" is a boolean vector (and bool is an integral type > > ([basic.fundamental] 11), so this should be accepted. > > > > An other point is that if we look at > > ``` > > for (int i = 0 ; i < n ; ++i) > > vec0[i] = vector-condtion[i] ? vec1[i] : vec2[i]; > > ``` > > implicit conversions may happen, which is completely over-looked > > currently. That is, the type of (1): "v = v0 ? v1 : v2" is the lowest > > common type of v, v1 and v2; and the type of (2): "v0 ? v1 : v2" is the > > lowest common type of v1 and v2. (2) can appear as a parameter, but > > even in that case, I think that (2) should be constrained by the type > > of the parameter and we are back to case (1). > > > > My points are that: > > - the current implementation has a bug: " *x = (*y < *t) ? '1' : > > '0';" from (Ex1) should be fine. > > - the current implementation does not explicetly follow the > > documented behavior of VEC_COND_EXPR. > > > > What do you think? > > Implicit promotion was explicitely not implemented for the vector > extension as that would usually lead to unexpected slowness > (scalar expansion). Instead I think we accept unpromoted operands > when they match and diagnose other cases. ISTR this is documented > somewhere but the ternary conditional operator docs may be indeed > over-simplified here. The docs mention > 'The operations behave like C++ @code{valarrays}.' it should > probably be clarified that while > > v16qi a,b,c; > a = b + c; > > computes the sum of the elements the implicit promotion/demotion > required by the C/C++ standards for scalar operations is _not_ > performed (and so technically considers signed vector char > adds as invoking undefined behavior on overflow rather than > implementation defined behavior on the truncation step - something > we should mitigate by performing the adds in unsigned when C/C++ > would promote) > > I think the non-promotion also follows what openCL does (the intent > of the vector extension was to somewhat follow that spec). > > Richard. > I understand better the context and the motivation behind the current implementation, thanks! If the goal is indeed to match the behavior of OpenCL C on how it treats vectors in the select operation, I should do the same in C. I think that I will add a flag controlling the promotion of operands in build_conditionnal_expr in order to avoid duplicating too much code. I'll also try to clarify the documentation with respect to those issues. Paul. > > > > Paul
diff --git a/gcc/c/c-typeck.cc b/gcc/c/c-typeck.cc index 17185fd3da4..03ade14cae9 100644 --- a/gcc/c/c-typeck.cc +++ b/gcc/c/c-typeck.cc @@ -4536,12 +4536,15 @@ build_unary_op (location_t location, enum tree_code code, tree xarg, case TRUTH_NOT_EXPR: if (typecode != INTEGER_TYPE && typecode != FIXED_POINT_TYPE && typecode != REAL_TYPE && typecode != POINTER_TYPE - && typecode != COMPLEX_TYPE) + && typecode != COMPLEX_TYPE && typecode != VECTOR_TYPE) { error_at (location, "wrong type argument to unary exclamation mark"); return error_mark_node; } + if (gnu_vector_type_p (TREE_TYPE (arg))) + return build_binary_op (location, EQ_EXPR, arg, + build_zero_cst (TREE_TYPE (arg)), false); if (int_operands) { arg = c_objc_common_truthvalue_conversion (location, xarg); @@ -5477,6 +5480,129 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp, result_type = type2; } + if (gnu_vector_type_p (TREE_TYPE (ifexp)) + && VECTOR_INTEGER_TYPE_P (TREE_TYPE (ifexp))) + { + tree ifexp_type = TREE_TYPE (ifexp); + + /* If ifexp is another cond_expr choosing between -1 and 0, + then we can use its comparison. It may help to avoid + additional comparison, produce more accurate diagnostics + and enables folding. */ + if (TREE_CODE (ifexp) == VEC_COND_EXPR + && integer_minus_onep (TREE_OPERAND (ifexp, 1)) + && integer_zerop (TREE_OPERAND (ifexp, 2))) + ifexp = TREE_OPERAND (ifexp, 0); + + tree op1_type = TREE_TYPE (op1); + tree op2_type = TREE_TYPE (op2); + + if (!VECTOR_TYPE_P (op1_type) && !VECTOR_TYPE_P (op2_type)) + { + /* Rely on the error messages of the scalar version. */ + tree scal = + build_conditional_expr (colon_loc, integer_one_node, ifexp_bcp, + op1, op1_original_type, op1_loc, + op2, op2_original_type, op2_loc); + if (scal == error_mark_node) + return error_mark_node; + tree stype = TREE_TYPE (scal); + tree ctype = TREE_TYPE (ifexp_type); + if (TYPE_SIZE (stype) != TYPE_SIZE (ctype) + || (!INTEGRAL_TYPE_P (stype) && !SCALAR_FLOAT_TYPE_P (stype))) + { + error_at (colon_loc, + "inferred scalar type %qT is not an integer or " + "floating-point type of the same size as %qT", stype, + COMPARISON_CLASS_P (ifexp) + ? TREE_TYPE (TREE_TYPE (TREE_OPERAND (ifexp, 0))) + : ctype); + return error_mark_node; + } + + tree vtype = build_opaque_vector_type (stype, + TYPE_VECTOR_SUBPARTS + (ifexp_type)); + /* The warnings (like Wsign-conversion) have already been + given by the scalar build_conditional_expr. We still check + unsafe_conversion_p to forbid truncating long long -> float. */ + if (unsafe_conversion_p (stype, op1, NULL_TREE, false)) + { + error_at (colon_loc, "conversion of scalar %qT to vector %qT " + "involves truncation", op1_type, vtype); + return error_mark_node; + } + if (unsafe_conversion_p (stype, op2, NULL_TREE, false)) + { + error_at (colon_loc, "conversion of scalar %qT to vector %qT " + "involves truncation", op2_type, vtype); + return error_mark_node; + } + + op1 = convert (stype, op1); + op1 = save_expr (op1); + op1 = build_vector_from_val (vtype, op1); + op1_type = vtype; + op2 = convert (stype, op2); + op2 = save_expr (op2); + op2 = build_vector_from_val (vtype, op2); + op2_type = vtype; + } + + if (gnu_vector_type_p (op1_type) ^ gnu_vector_type_p (op2_type)) + { + enum stv_conv convert_flag = + scalar_to_vector (colon_loc, VEC_COND_EXPR, op1, op2, + true); + + switch (convert_flag) + { + case stv_error: + return error_mark_node; + case stv_firstarg: + { + op1 = save_expr (op1); + op1 = convert (TREE_TYPE (op2_type), op1); + op1 = build_vector_from_val (op2_type, op1); + op1_type = TREE_TYPE (op1); + break; + } + case stv_secondarg: + { + op2 = save_expr (op2); + op2 = convert (TREE_TYPE (op1_type), op2); + op2 = build_vector_from_val (op1_type, op2); + op2_type = TREE_TYPE (op2); + break; + } + default: + break; + } + } + + if (!gnu_vector_type_p (op1_type) + || !gnu_vector_type_p (op2_type) + || !comptypes (op1_type, op2_type) + || maybe_ne (TYPE_VECTOR_SUBPARTS (ifexp_type), + TYPE_VECTOR_SUBPARTS (op1_type)) + || TYPE_SIZE (ifexp_type) != TYPE_SIZE (op1_type)) + { + error_at (colon_loc, + "incompatible vector types in conditional expression: " + "%qT, %qT and %qT", TREE_TYPE (ifexp), + TREE_TYPE (orig_op1), TREE_TYPE (orig_op2)); + return error_mark_node; + } + + if (!COMPARISON_CLASS_P (ifexp)) + { + tree cmp_type = truth_type_for (ifexp_type); + ifexp = build2 (NE_EXPR, cmp_type, ifexp, + build_zero_cst (ifexp_type)); + } + return build3_loc (colon_loc, VEC_COND_EXPR, op1_type, ifexp, op1, op2); + } + if (!result_type) { if (flag_cond_mismatch) @@ -5522,17 +5648,6 @@ build_conditional_expr (location_t colon_loc, tree ifexp, bool ifexp_bcp, && !TREE_OVERFLOW (orig_op2))); } - /* Need to convert condition operand into a vector mask. */ - if (VECTOR_TYPE_P (TREE_TYPE (ifexp))) - { - tree vectype = TREE_TYPE (ifexp); - tree elem_type = TREE_TYPE (vectype); - tree zero = build_int_cst (elem_type, 0); - tree zero_vec = build_vector_from_val (vectype, zero); - tree cmp_type = truth_type_for (vectype); - ifexp = build2 (NE_EXPR, cmp_type, ifexp, zero_vec); - } - if (int_const || (ifexp_bcp && TREE_CODE (ifexp) == INTEGER_CST)) ret = fold_build3_loc (colon_loc, COND_EXPR, result_type, ifexp, op1, op2); else @@ -12105,6 +12220,54 @@ build_binary_op (location_t location, enum tree_code code, && (op0 == truthvalue_true_node || !TREE_OVERFLOW (orig_op1))); } + if (!VECTOR_TYPE_P (type0) && gnu_vector_type_p (type1)) + { + if (!COMPARISON_CLASS_P (op1)) + op1 = build_binary_op (EXPR_LOCATION (op1), NE_EXPR, op1, + build_zero_cst (type1), false); + if (code == TRUTH_ANDIF_EXPR) + { + tree z = build_zero_cst (TREE_TYPE (op1)); + return build_conditional_expr (location, op0, 0, + op1, NULL_TREE, EXPR_LOCATION (op1), + z, NULL_TREE, EXPR_LOCATION (z)); + } + else if (code == TRUTH_ORIF_EXPR) + { + tree m1 = build_all_ones_cst (TREE_TYPE (op1)); + return build_conditional_expr (location, op0, 0, + m1, NULL_TREE, EXPR_LOCATION (m1), + op1, NULL_TREE, EXPR_LOCATION (op1)); + } + else + gcc_unreachable (); + } + if (gnu_vector_type_p (type0) + && (!VECTOR_TYPE_P (type1) || gnu_vector_type_p (type1))) + { + if (!COMPARISON_CLASS_P (op0)) + op0 = build_binary_op (EXPR_LOCATION (op0), NE_EXPR, op0, + build_zero_cst (type0), false); + if (!VECTOR_TYPE_P (type1)) + { + tree m1 = build_all_ones_cst (TREE_TYPE (op0)); + tree z = build_zero_cst (TREE_TYPE (op0)); + op1 = build_conditional_expr (location, op1, 0, + m1, NULL_TREE, EXPR_LOCATION (m1), + z, NULL_TREE, EXPR_LOCATION(z)); + } + else if (!COMPARISON_CLASS_P (op1)) + op1 = build_binary_op (EXPR_LOCATION (op1), NE_EXPR, op1, + build_zero_cst (type1), false); + if (code == TRUTH_ANDIF_EXPR) + code = BIT_AND_EXPR; + else if (code == TRUTH_ORIF_EXPR) + code = BIT_IOR_EXPR; + else + gcc_unreachable (); + + return build_binary_op (location, code, op0, op1, false); + } break; /* Shift operations: result has same type as first operand; @@ -12906,10 +13069,6 @@ c_objc_common_truthvalue_conversion (location_t location, tree expr) case FUNCTION_TYPE: gcc_unreachable (); - case VECTOR_TYPE: - error_at (location, "used vector type where scalar is required"); - return error_mark_node; - default: break; } @@ -12924,8 +13083,6 @@ c_objc_common_truthvalue_conversion (location_t location, tree expr) expr = note_integer_operands (expr); } else - /* ??? Should we also give an error for vectors rather than leaving - those to give errors later? */ expr = c_common_truthvalue_conversion (location, expr); if (TREE_CODE (expr) == INTEGER_CST && int_operands && !int_const) diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index c89df8778b2..1e0d436c02c 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -12007,7 +12007,7 @@ c = a > b; /* The result would be @{0, 0,-1, 0@} */ c = a == b; /* The result would be @{0,-1, 0,-1@} */ @end smallexample -In C++, the ternary operator @code{?:} is available. @code{a?b:c}, where +The ternary operator @code{?:} is available. @code{a?b:c}, where @code{b} and @code{c} are vectors of the same type and @code{a} is an integer vector with the same number of elements of the same size as @code{b} and @code{c}, computes all three arguments and creates a vector @@ -12020,7 +12020,7 @@ vector. If both @code{b} and @code{c} are scalars and the type of @code{b} and @code{c} are converted to a vector type whose elements have this type and with the same number of elements as @code{a}. -In C++, the logic operators @code{!, &&, ||} are available for vectors. +The logic operators @code{!, &&, ||} are available for vectors. @code{!v} is equivalent to @code{v == 0}, @code{a && b} is equivalent to @code{a!=0 & b!=0} and @code{a || b} is equivalent to @code{a!=0 | b!=0}. For mixed operations between a scalar @code{s} and a vector @code{v},