From patchwork Fri Oct 18 15:00:54 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mariam Arutunian X-Patchwork-Id: 1999249 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=QR2ymRiZ; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XVSbF2z0sz1xth for ; Sat, 19 Oct 2024 02:01:44 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 8A10C3858C53 for ; Fri, 18 Oct 2024 15:01:40 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-lj1-x235.google.com (mail-lj1-x235.google.com [IPv6:2a00:1450:4864:20::235]) by sourceware.org (Postfix) with ESMTPS id 181F93858C42 for ; Fri, 18 Oct 2024 15:01:09 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 181F93858C42 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 181F93858C42 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::235 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263674; cv=none; b=rtSY/+pACYLWk4BLjjWNlRBuk8qdpufqq+CUJa6/VZSCRYd1i+68Vr50MVQfigWyWvGfaJGHpY0XwgsAbinrWTuPsF2UkrLtnKJXByPINMo/5M530G0D8Z5MC99E4ugdFmD03BqIXGmZ+Wv/b5TdSg+IxatKhoFgspDhmHv6uNo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263674; c=relaxed/simple; bh=preYdFXOv3LzYVjohUNgJhIlq5e92XPxZfh9nDy8iLM=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=gn0tRQcN5aWbjINm0x4c2407jDGmmDGG4uVztDyf7LzEEdc6ev+1tP/pOMwvsLA04x1TmAHLkbDvXY7MRuSpjbNKu1iVW5KXJHjE1EcqT1aFE6DLldDWofZ5pMIvzA5wLZBC29fqAxY+e2qbWLbYEVs2agP6yejTF8vg9RC/Uos= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-lj1-x235.google.com with SMTP id 38308e7fff4ca-2fb4fa17044so25047241fa.3 for ; Fri, 18 Oct 2024 08:01:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1729263667; x=1729868467; darn=gcc.gnu.org; h=cc:to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=92SwIJz9q3ss0cnVPPNHurWDPM1y2CNZtRjmZGTrOJg=; b=QR2ymRiZ+xgKH0/HaA4CIRLQ1qwZlUWR0gzRqINopE7JxUuC6/LUsI2GmtaG5CohmU 122RoI7jBLsJ/hgCCGX9LLUYC+a8wmdyW6reyv4xM5bP/A2bIY4T6xJmMSG34r0LHG11 5Nyd380suPY88zhakN87z3ySfsQX6smiNp8Wjzt3OxyQmyjHcMBnUujxmBlVILHqu5jM YsZpbqIChSzXZh9iSb9joe+ftRdcV9xhxU5chsYkcCG9TIIZG4eEbayfy0M9XH/Uab2G r8GiIqFwIRvzxVO0DhnpYiqnWRvD0Pw8+C4yrpf7c1f22L9iubajL2mIdk9SzBT4ECUJ C1xQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729263667; x=1729868467; h=cc:to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=92SwIJz9q3ss0cnVPPNHurWDPM1y2CNZtRjmZGTrOJg=; b=GnBHZAQcGMP4nfvIg2P0gsKlYBtGk6EWdRwpdZA23nJYLz8AF+0hy2Uvp7vIXVnKkE tzDGcwZMdOgzfsRlpxb3B9CuTivrYabnuV11D8PeyGHr01h6m8LZuhjTxleHYw5scz8N IUMRsIi6OM/ipLFOJMDGr6n0L40PdJa3IZUjAie2mh4Fq0x25th/RY/DW+3s5SgBE5IH 0l7MYNBdYIenMvKwXyI4zH8l353q/535szp+uv0S2VnL3DFo5aK0c26Xx7WAdh9g1KXT W5mhh/8+xhpb3YaDbwR9KZqB01o2csleObX0DutX2vWwDJ0ESH652K64ZPOTtIyGhJOi d91A== X-Gm-Message-State: AOJu0Yw/AVgZTb52uewUiFraT7ipQZfCco7zlR6Iss1kuAcNVmg2GX/R fbabqhHacJuwCKd6GFBjOu21jn0nVVc8EOKhNRMLQmnx3/n0KU2dDNULL17Yhq1MTotzgS2qq++ rV8ZMj4fBaorf80fVHOA40jJR1Xjw9qSi X-Google-Smtp-Source: AGHT+IE2wsW/i/2GHgGTxnin9MODdL0iWLm9h9v5kvqThtQYI9kkPb96PGOERrT1n9tUUxsNX+8PRdCbiZMxNMTczrA= X-Received: by 2002:a05:651c:2109:b0:2f7:4cf1:d6b1 with SMTP id 38308e7fff4ca-2fb82e90ab8mr17531541fa.1.1729263665531; Fri, 18 Oct 2024 08:01:05 -0700 (PDT) MIME-Version: 1.0 From: Mariam Arutunian Date: Fri, 18 Oct 2024 19:00:54 +0400 Message-ID: Subject: [RFC/RFA] [PATCH v5 01/12] Implement internal functions for efficient CRC computation. To: GCC Patches Cc: Jeff Law X-Spam-Status: No, score=-7.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, HTML_MESSAGE, RCVD_IN_DNSWL_NONE, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org Add two new internal functions (IFN_CRC, IFN_CRC_REV), to provide faster CRC generation. One performs bit-forward and the other bit-reversed CRC computation. If CRC optabs are supported, they are used for the CRC computation. Otherwise, table-based CRC is generated. The supported data and CRC sizes are 8, 16, 32, and 64 bits. The polynomial is without the leading 1. A table with 256 elements is used to store precomputed CRCs. For the reflection of inputs and the output, a simple algorithm involving SHIFT, AND, and OR operations is used. gcc/ * doc/md.texi (crc@var{m}@var{n}4, crc_rev@var{m}@var{n}4): Document. * expr.cc (calculate_crc): New function. (assemble_crc_table): Likewise. (generate_crc_table): Likewise. (calculate_table_based_CRC): Likewise. (emit_crc): Likewise. (expand_crc_table_based): Likewise. (gen_common_operation_to_reflect): Likewise. (reflect_64_bit_value): Likewise. (reflect_32_bit_value): Likewise. (reflect_16_bit_value): Likewise. (reflect_8_bit_value): Likewise. (generate_reflecting_code_standard): Likewise. (expand_reversed_crc_table_based): Likewise. * expr.h (generate_reflecting_code_standard): New function declaration. (expand_crc_table_based): Likewise. (expand_reversed_crc_table_based): Likewise. * internal-fn.cc: (crc_direct): Define. (direct_crc_optab_supported_p): Likewise. (expand_crc_optab_fn): New function * internal-fn.def (CRC, CRC_REV): New internal functions. * optabs.def (crc_optab, crc_rev_optab): New optabs. Signed-off-by: Mariam Arutunian Co-authored-by: Joern Rennecke Mentored-by: Jeff Law --- gcc/doc/md.texi | 14 ++ gcc/expr.cc | 371 ++++++++++++++++++++++++++++++++++++++++++++ gcc/expr.h | 6 + gcc/internal-fn.cc | 54 +++++++ gcc/internal-fn.def | 2 + gcc/optabs.def | 2 + 6 files changed, 449 insertions(+) diff --git a/gcc/doc/md.texi b/gcc/doc/md.texi index a9259112251..913d1f96373 100644 --- a/gcc/doc/md.texi +++ b/gcc/doc/md.texi @@ -8591,6 +8591,20 @@ Return 1 if operand 1 is a normal floating point number and 0 otherwise. @var{m} is a scalar floating point mode. Operand 0 has mode @code{SImode}, and operand 1 has mode @var{m}. +@cindex @code{crc@var{m}@var{n}4} instruction pattern +@item @samp{crc@var{m}@var{n}4} +Calculate a bit-forward CRC using operands 1, 2 and 3, +then store the result in operand 0. +Operands 1 is the initial CRC, operands 2 is the data and operands 3 is the +polynomial without leading 1. +Operands 0, 1 and 3 have mode @var{n} and operand 2 has mode @var{m}, where +both modes are integers. The size of CRC to be calculated is determined by the +mode; for example, if @var{n} is 'hi', a CRC16 is calculated. + +@cindex @code{crc_rev@var{m}@var{n}4} instruction pattern +@item @samp{crc_rev@var{m}@var{n}4} +Similar to @samp{crc@var{m}@var{n}4}, but calculates a bit-reversed CRC. + @end table @end ifset diff --git a/gcc/expr.cc b/gcc/expr.cc index 7a471f20e79..3a0ddfaf132 100644 --- a/gcc/expr.cc +++ b/gcc/expr.cc @@ -14124,3 +14124,374 @@ int_expr_size (const_tree exp) return tree_to_shwi (size); } + +/* Calculate CRC for the initial CRC and given POLYNOMIAL. + CRC_BITS is CRC size. */ + +static unsigned HOST_WIDE_INT +calculate_crc (unsigned HOST_WIDE_INT crc, + unsigned HOST_WIDE_INT polynomial, + unsigned short crc_bits) +{ + unsigned HOST_WIDE_INT msb = HOST_WIDE_INT_1U << (crc_bits - 1); + crc = crc << (crc_bits - 8); + for (short i = 8; i > 0; --i) + { + if (crc & msb) + crc = (crc << 1) ^ polynomial; + else + crc <<= 1; + } + /* Zero out bits in crc beyond the specified number of crc_bits. */ + if (crc_bits < sizeof (crc) * CHAR_BIT) + crc &= (HOST_WIDE_INT_1U << crc_bits) - 1; + return crc; +} + +/* Assemble CRC table with 256 elements for the given POLYNOM and CRC_BITS with + given ID. + ID is the identifier of the table, the name of the table is unique, + contains CRC size and the polynomial. + POLYNOM is the polynomial used to calculate the CRC table's elements. + CRC_BITS is the size of CRC, may be 8, 16, ... . */ + +rtx +assemble_crc_table (tree id, unsigned HOST_WIDE_INT polynom, + unsigned short crc_bits) +{ + unsigned table_el_n = 0x100; + tree ar = build_array_type (make_unsigned_type (crc_bits), + build_index_type (size_int (table_el_n - 1))); + tree decl = build_decl (UNKNOWN_LOCATION, VAR_DECL, id, ar); + SET_DECL_ASSEMBLER_NAME (decl, id); + DECL_ARTIFICIAL (decl) = 1; + rtx tab = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id)); + TREE_ASM_WRITTEN (decl) = 0; + + /* Initialize the table. */ + vec *initial_values; + vec_alloc (initial_values, table_el_n); + for (size_t i = 0; i < table_el_n; ++i) + { + unsigned HOST_WIDE_INT crc = calculate_crc (i, polynom, crc_bits); + tree element = build_int_cstu (make_unsigned_type (crc_bits), crc); + vec_safe_push (initial_values, element); + } + DECL_INITIAL (decl) = build_constructor_from_vec (ar, initial_values); + + TREE_READONLY (decl) = 1; + TREE_STATIC (decl) = 1; + + if (TREE_PUBLIC (id)) + { + TREE_PUBLIC (decl) = 1; + make_decl_one_only (decl, DECL_ASSEMBLER_NAME (decl)); + } + + mark_decl_referenced (decl); + varpool_node::finalize_decl (decl); + + return tab; +} + +/* Generate CRC lookup table by calculating CRC for all possible + 8-bit data values. The table is stored with a specific name in the read-only + static data section. + POLYNOM is the polynomial used to calculate the CRC table's elements. + CRC_BITS is the size of CRC, may be 8, 16, ... . */ + +rtx +generate_crc_table (unsigned HOST_WIDE_INT polynom, unsigned short crc_bits) +{ + gcc_assert (crc_bits <= 64); + + /* Buf size - 24 letters + 6 '_' + + 20 numbers (2 for crc bit size + 2 for 0x + 16 for 64-bit polynomial) + + 1 for \0. */ + char buf[51]; + sprintf (buf, "crc_table_for_crc_%u_polynomial_" HOST_WIDE_INT_PRINT_HEX, + crc_bits, polynom); + + tree id = maybe_get_identifier (buf); + if (id) + return gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id)); + + id = get_identifier (buf); + return assemble_crc_table (id, polynom, crc_bits); +} + +/* Generate table-based CRC code for the given CRC, INPUT_DATA and the + POLYNOMIAL (without leading 1). + + First, using POLYNOMIAL's value generates CRC table of 256 elements, + then generates the assembly for the following code, + where crc_bit_size and data_bit_size may be 8, 16, 32, 64, depending on CRC: + + for (int i = 0; i < data_bit_size / 8; i++) + crc = (crc << 8) ^ crc_table[(crc >> (crc_bit_size - 8)) + ^ (data >> (data_bit_size - (i + 1) * 8) + & 0xFF))]; + + So to take values from the table, we need 8-bit data. + If input data size is not 8, then first we extract upper 8 bits, + then the other 8 bits, and so on. */ + +void +calculate_table_based_CRC (rtx *crc, const rtx &input_data, + const rtx &polynomial, + machine_mode crc_mode, machine_mode data_mode) +{ + unsigned short crc_bit_size = GET_MODE_BITSIZE (crc_mode).to_constant (); + unsigned short data_size = GET_MODE_SIZE (data_mode).to_constant (); + + rtx tab = generate_crc_table (UINTVAL (polynomial), crc_bit_size); + + for (unsigned short i = 0; i < data_size; i++) + { + /* crc >> (crc_bit_size - 8). */ + rtx op1 = expand_shift (RSHIFT_EXPR, word_mode, *crc, crc_bit_size - 8, + NULL_RTX, 1); + + /* data >> (8 * (GET_MODE_SIZE (data_mode).to_constant () - i - 1)). */ + unsigned range_8 = 8 * (data_size - i - 1); + rtx data = expand_shift (RSHIFT_EXPR, word_mode, input_data, range_8, + NULL_RTX, 1); + + /* data >> (8 * (GET_MODE_SIZE (data_mode) + .to_constant () - i - 1)) & 0xFF. */ + rtx data_final = expand_and (word_mode, data, + gen_int_mode (255, data_mode), NULL_RTX); + + /* (crc >> (crc_bit_size - 8)) ^ data_8bit. */ + rtx in = expand_binop (Pmode, xor_optab, op1, data_final, + NULL_RTX, 1, OPTAB_WIDEN); + + /* ((crc >> (crc_bit_size - 8)) ^ data_8bit) & 0xFF. */ + rtx index = expand_and (Pmode, in, gen_int_mode (255, word_mode), + NULL_RTX); + int log_crc_size = exact_log2 (GET_MODE_SIZE (crc_mode).to_constant ()); + index = expand_shift (LSHIFT_EXPR, Pmode, index, + log_crc_size, NULL_RTX, 0); + + index = expand_binop (Pmode, add_optab, index, tab, NULL_RTX, + 0, OPTAB_DIRECT); + + /* crc_table[(crc >> (crc_bit_size - 8)) ^ data_8bit] */ + rtx tab_el = validize_mem (gen_rtx_MEM (crc_mode, index)); + + /* (crc << 8) if CRC is larger than 8, otherwise crc = 0. */ + rtx high = NULL_RTX; + if (crc_bit_size != 8) + { + high = expand_shift (LSHIFT_EXPR, word_mode, *crc, 8, NULL_RTX, 0); + if (crc_mode != word_mode) + { + rtx crc_mode_mask = gen_int_mode (GET_MODE_MASK (crc_mode), + word_mode); + high = expand_and (word_mode, high, crc_mode_mask, NULL_RTX); + } + } + else + high = gen_int_mode (0, word_mode); + + /* crc = (crc << 8) + ^ crc_table[(crc >> (crc_bit_size - 8)) ^ data_8bit]; */ + *crc = expand_binop (word_mode, xor_optab, tab_el, high, NULL_RTX, 1, + OPTAB_WIDEN); + } +} + +/* Converts and moves a CRC value to a target register. + + CRC_MODE is the mode (data type) of the CRC value. + CRC is the initial CRC value. + OP0 is the target register. */ + +void +emit_crc (machine_mode crc_mode, rtx* crc, rtx* op0) +{ + if (GET_MODE_BITSIZE (crc_mode).to_constant () == 32 + && GET_MODE_BITSIZE (word_mode) == 64) + { + rtx a_low = gen_lowpart (crc_mode, *crc); + *crc = gen_rtx_SIGN_EXTEND (word_mode, a_low); + } + rtx tgt = *op0; + if (word_mode != crc_mode) + tgt = simplify_gen_subreg (word_mode, *op0, crc_mode, 0); + emit_move_insn (tgt, *crc); +} + +/* Generate table-based CRC code for the given CRC, INPUT_DATA and the + POLYNOMIAL (without leading 1). + + CRC is OP1, data is OP2 and the polynomial is OP3. + This must generate a CRC table and an assembly for the following code, + where crc_bit_size and data_bit_size may be 8, 16, 32, 64: + uint_crc_bit_size_t + crc_crc_bit_size (uint_crc_bit_size_t crc_init, + uint_data_bit_size_t data, size_t size) + { + uint_crc_bit_size_t crc = crc_init; + for (int i = 0; i < data_bit_size / 8; i++) + crc = (crc << 8) ^ crc_table[(crc >> (crc_bit_size - 8)) + ^ (data >> (data_bit_size - (i + 1) * 8) + & 0xFF))]; + return crc; + } */ + +void +expand_crc_table_based (rtx op0, rtx op1, rtx op2, rtx op3, + machine_mode data_mode) +{ + gcc_assert (!CONST_INT_P (op0)); + gcc_assert (CONST_INT_P (op3)); + machine_mode crc_mode = GET_MODE (op0); + rtx crc = gen_reg_rtx (word_mode); + convert_move (crc, op1, 0); + calculate_table_based_CRC (&crc, op2, op3, crc_mode, data_mode); + emit_crc (crc_mode, &crc, &op0); +} + +/* Generate the common operation for reflecting values: + *OP = (*OP & AND1_VALUE) << SHIFT_VAL | (*OP & AND2_VALUE) >> SHIFT_VAL; */ + +void +gen_common_operation_to_reflect (rtx *op, + unsigned HOST_WIDE_INT and1_value, + unsigned HOST_WIDE_INT and2_value, + unsigned shift_val) +{ + rtx op1 = expand_and (word_mode, *op, gen_int_mode (and1_value, word_mode), + NULL_RTX); + op1 = expand_shift (LSHIFT_EXPR, word_mode, op1, shift_val, op1, 0); + rtx op2 = expand_and (word_mode, *op, gen_int_mode (and2_value, word_mode), + NULL_RTX); + op2 = expand_shift (RSHIFT_EXPR, word_mode, op2, shift_val, op2, 1); + *op = expand_binop (word_mode, ior_optab, op1, op2, *op, 0, OPTAB_DIRECT); +} + +/* Reflect 64-bit value for the 64-bit target. */ + +void +reflect_64_bit_value (rtx *op) +{ + gen_common_operation_to_reflect (op, 0x00000000FFFFFFFF, + 0xFFFFFFFF00000000, 32); + gen_common_operation_to_reflect (op, 0x0000FFFF0000FFFF, + 0xFFFF0000FFFF0000, 16); + gen_common_operation_to_reflect (op, 0x00FF00FF00FF00FF, + 0xFF00FF00FF00FF00, 8); + gen_common_operation_to_reflect (op, 0x0F0F0F0F0F0F0F0F, + 0xF0F0F0F0F0F0F0F0, 4); + gen_common_operation_to_reflect (op, 0x3333333333333333, + 0xCCCCCCCCCCCCCCCC, 2); + gen_common_operation_to_reflect (op, 0x5555555555555555, + 0xAAAAAAAAAAAAAAAA, 1); +} + +/* Reflect 32-bit value for the 32-bit target. */ + +void +reflect_32_bit_value (rtx *op) +{ + gen_common_operation_to_reflect (op, 0x0000FFFF, 0xFFFF0000, 16); + gen_common_operation_to_reflect (op, 0x00FF00FF, 0xFF00FF00, 8); + gen_common_operation_to_reflect (op, 0x0F0F0F0F, 0xF0F0F0F0, 4); + gen_common_operation_to_reflect (op, 0x33333333, 0xCCCCCCCC, 2); + gen_common_operation_to_reflect (op, 0x55555555, 0xAAAAAAAA, 1); +} + +/* Reflect 16-bit value for the 16-bit target. */ + +void +reflect_16_bit_value (rtx *op) +{ + gen_common_operation_to_reflect (op, 0x00FF, 0xFF00, 8); + gen_common_operation_to_reflect (op, 0x0F0F, 0xF0F0, 4); + gen_common_operation_to_reflect (op, 0x3333, 0xCCCC, 2); + gen_common_operation_to_reflect (op, 0x5555, 0xAAAA, 1); +} + +/* Reflect 8-bit value for the 8-bit target. */ + +void +reflect_8_bit_value (rtx *op) +{ + gen_common_operation_to_reflect (op, 0x0F, 0xF0, 4); + gen_common_operation_to_reflect (op, 0x33, 0xCC, 2); + gen_common_operation_to_reflect (op, 0x55, 0xAA, 1); +} + +/* Generate instruction sequence + which reflects the value of the OP using shift, and, or operations. + OP's mode may be less than word_mode. To get the correct number, + after reflecting we shift right the value by SHIFT_VAL. + E.g. we have 1111 0001, after reflection (target 32-bit) we will get + 1000 1111 0000 0000, if we shift-out 16 bits, + we will get the desired one: 1000 1111. */ + +void +generate_reflecting_code_standard (rtx *op, int shift_val) +{ + gcc_assert (BITS_PER_WORD >= 8 && BITS_PER_WORD <= 64); + + if (BITS_PER_WORD == 64) + reflect_64_bit_value (op); + else if (BITS_PER_WORD == 32) + reflect_32_bit_value (op); + else if (BITS_PER_WORD == 16) + reflect_16_bit_value (op); + else + reflect_8_bit_value (op); + + *op = expand_shift (RSHIFT_EXPR, word_mode, *op, shift_val, *op, 1); +} + +/* Generate table-based reversed CRC code for the given CRC, INPUT_DATA and + the POLYNOMIAL (without leading 1). + + CRC is OP1, data is OP2 and the polynomial is OP3. + This must generate CRC table and assembly for the following code, + where crc_bit_size and data_bit_size may be 8, 16, 32, 64: + uint_crc_bit_size_t + crc_crc_bit_size (uint_crc_bit_size_t crc_init, + uint_data_bit_size_t data, size_t size) + { + reflect (crc_init) + uint_crc_bit_size_t crc = crc_init; + reflect (data); + for (int i = 0; i < data_bit_size / 8; i++) + crc = (crc << 8) ^ crc_table[(crc >> (crc_bit_size - 8)) + ^ (data >> (data_bit_size - (i + 1) * 8) & 0xFF))]; + reflect (crc); + return crc; + } */ + +void +expand_reversed_crc_table_based (rtx op0, rtx op1, rtx op2, rtx op3, + machine_mode data_mode, + void (*gen_reflecting_code) (rtx *op, + int shift_val)) +{ + gcc_assert (!CONST_INT_P (op0)); + gcc_assert (CONST_INT_P (op3)); + machine_mode crc_mode = GET_MODE (op0); + + unsigned short crc_bit_size = GET_MODE_BITSIZE (crc_mode).to_constant (); + unsigned short data_bit_size = GET_MODE_BITSIZE (data_mode).to_constant (); + unsigned short word_size = GET_MODE_BITSIZE (word_mode); + + rtx crc = gen_reg_rtx (word_mode); + convert_move (crc, op1, 0); + gen_reflecting_code (&crc, word_size - crc_bit_size); + + rtx data = gen_reg_rtx (word_mode); + convert_move (data, op2, 0); + gen_reflecting_code (&data, word_size - data_bit_size); + + calculate_table_based_CRC (&crc, data, op3, crc_mode, data_mode); + + gen_reflecting_code (&crc, word_size - crc_bit_size); + emit_crc (crc_mode, &crc, &op0); +} diff --git a/gcc/expr.h b/gcc/expr.h index 04782b15f19..9373c7913a2 100644 --- a/gcc/expr.h +++ b/gcc/expr.h @@ -377,4 +377,10 @@ extern rtx expr_size (tree); extern bool mem_ref_refers_to_non_mem_p (tree); extern bool non_mem_decl_p (tree); +/* Generate table-based CRC. */ +extern void generate_reflecting_code_standard (rtx *, int); +extern void expand_crc_table_based (rtx, rtx, rtx, rtx, machine_mode); +extern void expand_reversed_crc_table_based (rtx, rtx, rtx, rtx, machine_mode, + void (*) (rtx *, int)); + #endif /* GCC_EXPR_H */ diff --git a/gcc/internal-fn.cc b/gcc/internal-fn.cc index 4e33db365ac..d59a9243cf9 100644 --- a/gcc/internal-fn.cc +++ b/gcc/internal-fn.cc @@ -189,6 +189,7 @@ init_internal_fns () #define mask_fold_left_direct { 1, 1, false } #define mask_len_fold_left_direct { 1, 1, false } #define check_ptrs_direct { 0, 0, false } +#define crc_direct { 1, -1, true } const direct_internal_fn_info direct_internal_fn_array[IFN_LAST + 1] = { #define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) not_direct, @@ -3961,6 +3962,58 @@ expand_convert_optab_fn (internal_fn fn, gcall *stmt, convert_optab optab, expand_fn_using_insn (stmt, icode, 1, nargs); } +/* Expand CRC call STMT. */ + +static void +expand_crc_optab_fn (internal_fn fn, gcall *stmt, convert_optab optab) +{ + tree lhs = gimple_call_lhs (stmt); + tree rhs1 = gimple_call_arg (stmt, 0); // crc + tree rhs2 = gimple_call_arg (stmt, 1); // data + tree rhs3 = gimple_call_arg (stmt, 2); // polynomial + + tree result_type = TREE_TYPE (lhs); + tree data_type = TREE_TYPE (rhs2); + + gcc_assert (TYPE_MODE (result_type) >= TYPE_MODE (data_type)); + gcc_assert (word_mode >= TYPE_MODE (result_type)); + + rtx dest = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE); + rtx crc = expand_normal (rhs1); + rtx data = expand_normal (rhs2); + gcc_assert (TREE_CODE (rhs3) == INTEGER_CST); + rtx polynomial = gen_rtx_CONST_INT (TYPE_MODE (result_type), + TREE_INT_CST_LOW (rhs3)); + + /* Use target specific expansion if it exists. + Otherwise, generate table-based CRC. */ + if (direct_internal_fn_supported_p (fn, tree_pair (data_type, result_type), + OPTIMIZE_FOR_SPEED)) + { + class expand_operand ops[4]; + create_call_lhs_operand (&ops[0], dest, TYPE_MODE (result_type)); + create_input_operand (&ops[1], crc, TYPE_MODE (result_type)); + create_input_operand (&ops[2], data, TYPE_MODE (data_type)); + create_input_operand (&ops[3], polynomial, TYPE_MODE (result_type)); + insn_code icode = convert_optab_handler (optab, TYPE_MODE (data_type), + TYPE_MODE (result_type)); + expand_insn (icode, 4, ops); + assign_call_lhs (lhs, dest, &ops[0]); + } + else + { + /* If it's IFN_CRC generate bit-forward CRC. */ + if (fn == IFN_CRC) + expand_crc_table_based (dest, crc, data, polynomial, + TYPE_MODE (data_type)); + else + /* If it's IFN_CRC_REV generate bit-reversed CRC. */ + expand_reversed_crc_table_based (dest, crc, data, polynomial, + TYPE_MODE (data_type), + generate_reflecting_code_standard); + } +} + /* Expanders for optabs that can use expand_direct_optab_fn. */ #define expand_unary_optab_fn(FN, STMT, OPTAB) \ @@ -4097,6 +4150,7 @@ multi_vector_optab_supported_p (convert_optab optab, tree_pair types, #define direct_cond_len_unary_optab_supported_p direct_optab_supported_p #define direct_cond_len_binary_optab_supported_p direct_optab_supported_p #define direct_cond_len_ternary_optab_supported_p direct_optab_supported_p +#define direct_crc_optab_supported_p convert_optab_supported_p #define direct_mask_load_optab_supported_p convert_optab_supported_p #define direct_load_lanes_optab_supported_p multi_vector_optab_supported_p #define direct_mask_load_lanes_optab_supported_p multi_vector_optab_supported_p diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def index 23b4ab02b30..5749a1d2cab 100644 --- a/gcc/internal-fn.def +++ b/gcc/internal-fn.def @@ -200,6 +200,8 @@ along with GCC; see the file COPYING3. If not see cond_len_##UNSIGNED_OPTAB, cond_len_##TYPE) #endif +DEF_INTERNAL_OPTAB_FN (CRC, ECF_CONST | ECF_NOTHROW, crc, crc) +DEF_INTERNAL_OPTAB_FN (CRC_REV, ECF_CONST | ECF_NOTHROW, crc_rev, crc) DEF_INTERNAL_OPTAB_FN (MASK_LOAD, ECF_PURE, maskload, mask_load) DEF_INTERNAL_OPTAB_FN (LOAD_LANES, ECF_CONST, vec_load_lanes, load_lanes) DEF_INTERNAL_OPTAB_FN (MASK_LOAD_LANES, ECF_PURE, diff --git a/gcc/optabs.def b/gcc/optabs.def index 58a939442bd..9b5b1673ccb 100644 --- a/gcc/optabs.def +++ b/gcc/optabs.def @@ -85,6 +85,8 @@ OPTAB_CD(smsub_widen_optab, "msub$b$a4") OPTAB_CD(umsub_widen_optab, "umsub$b$a4") OPTAB_CD(ssmsub_widen_optab, "ssmsub$b$a4") OPTAB_CD(usmsub_widen_optab, "usmsub$a$b4") +OPTAB_CD(crc_optab, "crc$a$b4") +OPTAB_CD(crc_rev_optab, "crc_rev$a$b4") OPTAB_CD(vec_load_lanes_optab, "vec_load_lanes$a$b") OPTAB_CD(vec_store_lanes_optab, "vec_store_lanes$a$b") OPTAB_CD(vec_mask_load_lanes_optab, "vec_mask_load_lanes$a$b") -- 2.25.1 From patchwork Fri Oct 18 15:01:15 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mariam Arutunian X-Patchwork-Id: 1999251 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=IQOGnByH; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XVSc060phz1xth for ; Sat, 19 Oct 2024 02:02:24 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id D5471385702B for ; Fri, 18 Oct 2024 15:02:11 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-lj1-x22d.google.com (mail-lj1-x22d.google.com [IPv6:2a00:1450:4864:20::22d]) by sourceware.org (Postfix) with ESMTPS id C25B73858C3A for ; Fri, 18 Oct 2024 15:01:30 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C25B73858C3A Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org C25B73858C3A Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::22d ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263701; cv=none; b=wK2WilmlzVXeAsF00M4KkCrHE/1R4BXhUFtqPeWZIOTC5qyd1PDSsjnHiX+rpnh7/y7+EgHHHSrK2/dFs58EaW9czHwlwCAU5DciJoYMpLKQLM5IPJef9gl6N6SH83XXy71j5vQTPtgc6X5qTG/MdFzi91zBitO19g0m8wCBvt4= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263701; c=relaxed/simple; bh=NQjkCI0sYU4BZYnNqh63wAnY8a/GMnta9ZJIFOY3gPw=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=ZatMmi5ulnZcL8NeDjdUTJJ2HW0cGGZ3GUQSQ+u2d9yayLQiZLBRz7koyBXdwUtb4WLID8oVV+4+ztusbE1y6wwWxAIeg0sW5ScCAeax4Z0bjFn0INERVVwb68LREnWep+hkguhBwuFXd4upoFJiFg4uhwywwltb7vepB1c97LQ= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-lj1-x22d.google.com with SMTP id 38308e7fff4ca-2fb5014e2daso25031401fa.0 for ; Fri, 18 Oct 2024 08:01:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1729263689; x=1729868489; darn=gcc.gnu.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=4VSSXUL1/DMc1OIednhWkTB43g3PQ3xFrxVmpVC/i/g=; b=IQOGnByH/os3vPy8OnK68V9rzhDCvbsxkAy19WDhQEhjofxQKKENbAkO5ceR0zBWAd q8VPUNlLa5WStdhEBoEdd6HKscpROpvcHhUQ9nBbGg4aE8mE6kUq2jzxjXPg4DLPlR1j 8pF5ZUxZB7z+n16x581QgXuJhVyPDr8IOkyqE7R2zKAuWzZhkGkJuKR6VA+cfkmSUaDx fCpr+3y/FCuOAXpdoQk2cwMHBSOH2dL4upkawx+4ib/Yu7etW8tzelUBoiCv8QI8QTEy NBq83mO56JHPR0uXXdYC7SWnGDXg7nkaHmAp0kTzAXEsrt/2r0y85XKyfoWwCPVS5YI6 PbUQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729263689; x=1729868489; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=4VSSXUL1/DMc1OIednhWkTB43g3PQ3xFrxVmpVC/i/g=; b=Vakl/LjLXp4qmkVWMFQMku91BWdtOPPHF91yNFE778INAMiyCwJlzD0bKUHr7K0kAI q/7IHeuMnUVgIgdtC56z9M5fPe9zrjBRd840kqhfdgZkP7Zx07lXm3mPyHryYFwywgaL aS2nkiJWjHzBCzxbaj1jNu2bLjGspeGEMNuKeIjVqGimEldMC1YEXYcZX+BEku2utK9P tqmU6txtFV6wUN8PdNNLgwmQdeseFodA+oJjDOr9gG74QsPh+N7ijReamLBKilleHw7/ Imc44IwVAaYqG676iiw0F9hbfOXPsQ229tw0TIXuKVWFPJyDvok3EEvCDeJa1t7rGo5V gEIA== X-Gm-Message-State: AOJu0YzWftsQuAcB7IA5xZY6WSiN5sX0Q5zCBH8DAq5AwgZVSZOXCLW7 Ue/DRZ93f1jQnNpse7jf1dq7Xffi9wxsdppMNnb/hdgTPXujIuad9AQoCkPXnZ6PHFTABaz/vwe ksg5kbVV4nt9EFC1LQbxMhkNOZJFgxn2y1F0= X-Google-Smtp-Source: AGHT+IGjJaP0qc+qP59/RhVOKNMYWrMm1WISnaV0C5AmMLba/OQroU0dpV16UiyF0Jz9H5MJ7bsFiSVSVDD7wLkqpyQ= X-Received: by 2002:a05:651c:2203:b0:2f7:53b8:ca57 with SMTP id 38308e7fff4ca-2fb82eaed6dmr12659221fa.19.1729263687005; Fri, 18 Oct 2024 08:01:27 -0700 (PDT) MIME-Version: 1.0 From: Mariam Arutunian Date: Fri, 18 Oct 2024 19:01:15 +0400 Message-ID: Subject: [RFC/RFA][PATCH v5 05/12] i386: Implement new expander for efficient CRC computation. To: GCC Patches , Jeff Law X-Spam-Status: No, score=-7.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, HTML_MESSAGE, KAM_SHORT, RCVD_IN_DNSWL_NONE, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org This patch introduces two new expanders for the i386 backend, dedicated to generating optimized code for CRC computations. The new expanders are designed to leverage specific hardware capabilities to achieve faster CRC calculations, particularly using the pclmulqdq or crc32 instructions when supported by the target architecture. Expander 1: Bit-Forward CRC (crc4) For targets that support both pclmulqdq instruction (TARGET_PCLMUL) and are 64-bit (TARGET_64BIT), the expander will generate code that uses the pclmulqdq instruction for CRC computation. Expander 2: Bit-Reversed CRC (crc_rev4) The expander first checks if the target supports the CRC32 instruction set (TARGET_CRC32) and the polynomial in use is 0x1EDC6F41 (iSCSI). If the conditions are met, it emits calls to the corresponding crc32 instruction (crc32b, crc32w, or crc32l depending on the data size). If the target does not support crc32 but supports pclmulqdq, it then uses the pclmulqdq instruction for bit-reversed CRC computation. Otherwise table-based CRC is generated. gcc/config/i386/ * i386-protos.h (ix86_expand_crc_using_pclmul): New extern function declaration. (ix86_expand_reversed_crc_using_pclmul): Likewise. * i386.cc (ix86_expand_crc_using_pclmul): New function. (ix86_expand_reversed_crc_using_pclmul): Likewise. * i386.md (UNSPEC_CRC, UNSPEC_CRC_REV): New unspecs. (SWI124dup): New iterator. (crc4): New expander for bit-forward CRC. (crc_rev4): New expander for reversed CRC. gcc/testsuite/gcc.target/i386/ * crc-crc32-data16.c: New test. * crc-crc32-data32.c: Likewise. * crc-crc32-data8.c: Likewise. * crc-1-pclmul.c: Likewise. * crc-10-pclmul.c: Likewise. * crc-12-pclmul.c: Likewise. * crc-13-pclmul.c: Likewise. * crc-14-pclmul.c: Likewise. * crc-17-pclmul.c: Likewise. * crc-18-pclmul.c: Likewise. * crc-21-pclmul.c: Likewise. * crc-22-pclmul.c: Likewise. * crc-23-pclmul.c: Likewise. * crc-4-pclmul.c: Likewise. * crc-5-pclmul.c: Likewise. * crc-6-pclmul.c: Likewise. * crc-7-pclmul.c: Likewise. * crc-8-pclmul.c: Likewise. * crc-9-pclmul.c: Likewise. * crc-CCIT-data16-pclmul.c: Likewise. * crc-CCIT-data8-pclmul.c: Likewise. * crc-coremark-16bitdata-pclmul.c: Likewise. Signed-off-by: Mariam Arutunian Mentored-by: Jeff Law --- gcc/config/i386/i386-protos.h | 2 + gcc/config/i386/i386.cc | 129 ++++++++++++++++++ gcc/config/i386/i386.md | 59 ++++++++ gcc/testsuite/gcc.target/i386/crc-1-pclmul.c | 8 ++ gcc/testsuite/gcc.target/i386/crc-10-pclmul.c | 8 ++ gcc/testsuite/gcc.target/i386/crc-12-pclmul.c | 9 ++ gcc/testsuite/gcc.target/i386/crc-13-pclmul.c | 8 ++ gcc/testsuite/gcc.target/i386/crc-14-pclmul.c | 8 ++ gcc/testsuite/gcc.target/i386/crc-17-pclmul.c | 8 ++ gcc/testsuite/gcc.target/i386/crc-18-pclmul.c | 8 ++ gcc/testsuite/gcc.target/i386/crc-21-pclmul.c | 8 ++ gcc/testsuite/gcc.target/i386/crc-22-pclmul.c | 8 ++ gcc/testsuite/gcc.target/i386/crc-23-pclmul.c | 8 ++ gcc/testsuite/gcc.target/i386/crc-4-pclmul.c | 8 ++ gcc/testsuite/gcc.target/i386/crc-5-pclmul.c | 9 ++ gcc/testsuite/gcc.target/i386/crc-6-pclmul.c | 8 ++ gcc/testsuite/gcc.target/i386/crc-7-pclmul.c | 8 ++ gcc/testsuite/gcc.target/i386/crc-8-pclmul.c | 8 ++ gcc/testsuite/gcc.target/i386/crc-9-pclmul.c | 8 ++ .../gcc.target/i386/crc-CCIT-data16-pclmul.c | 9 ++ .../gcc.target/i386/crc-CCIT-data8-pclmul.c | 9 ++ .../i386/crc-coremark-16bitdata-pclmul.c | 9 ++ .../gcc.target/i386/crc-crc32-data16.c | 53 +++++++ .../gcc.target/i386/crc-crc32-data32.c | 53 +++++++ .../gcc.target/i386/crc-crc32-data8.c | 53 +++++++ 25 files changed, 506 insertions(+) create mode 100644 gcc/testsuite/gcc.target/i386/crc-1-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-10-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-12-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-13-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-14-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-17-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-18-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-21-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-22-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-23-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-4-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-5-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-6-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-7-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-8-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-9-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-CCIT-data16-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-CCIT-data8-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-coremark-16bitdata-pclmul.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-crc32-data16.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-crc32-data32.c create mode 100644 gcc/testsuite/gcc.target/i386/crc-crc32-data8.c diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index 3a7bc949e56..9190788b9a7 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -303,6 +303,8 @@ extern bool ix86_expand_vector_init_duplicate (bool, machine_mode, rtx, extern bool ix86_expand_vector_init_one_nonzero (bool, machine_mode, rtx, rtx, int); extern bool ix86_extract_perm_from_pool_constant (int*, rtx); +extern void ix86_expand_crc_using_pclmul (rtx *); +extern void ix86_expand_reversed_crc_using_pclmul (rtx *); /* In i386-c.cc */ extern void ix86_target_macros (void); diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc index 707b75a6d5d..559af97c595 100644 --- a/gcc/config/i386/i386.cc +++ b/gcc/config/i386/i386.cc @@ -26280,6 +26280,135 @@ ix86_run_selftests (void) } // namespace selftest +/* Generate assembly to calculate CRC using pclmulqdq instruction. + OPERANDS[1] is input CRC, + OPERANDS[2] is data (message), + OPERANDS[3] is the polynomial without the leading 1. */ + +void +ix86_expand_crc_using_pclmul (rtx *operands) +{ +/* Check and keep arguments. */ + gcc_assert (!CONST_INT_P (operands[0])); + gcc_assert (CONST_INT_P (operands[3])); + rtx crc = operands[1]; + rtx data = operands[2]; + unsigned HOST_WIDE_INT crc_size = GET_MODE_BITSIZE (GET_MODE (operands[0])); + gcc_assert (crc_size <= 32); + unsigned HOST_WIDE_INT data_size = GET_MODE_BITSIZE (GET_MODE (data)); + unsigned HOST_WIDE_INT DImode_size = GET_MODE_BITSIZE (DImode); + + /* Calculate the quotient. */ + unsigned HOST_WIDE_INT + q = gf2n_poly_long_div_quotient (UINTVAL (operands[3]), crc_size); + + if (crc_size > data_size) + crc = expand_shift (RSHIFT_EXPR, DImode, crc, crc_size - data_size, + NULL_RTX, 1); + + /* Keep the quotient in V2DImode. */ + rtx q_v2di = gen_reg_rtx (V2DImode); + rtx quotient = gen_reg_rtx (DImode); + convert_move (quotient, gen_int_mode (q, DImode), 0); + emit_insn (gen_vec_concatv2di (q_v2di, quotient, const0_rtx)); + + /* crc ^ data and keep in V2DImode. */ + rtx cd_xor = expand_binop (DImode, xor_optab, crc, data, NULL_RTX, 1, + OPTAB_WIDEN); + rtx res = gen_reg_rtx (V2DImode); + emit_insn (gen_vec_concatv2di (res, cd_xor, const0_rtx)); + /* Perform carry-less multiplication. */ + emit_insn (gen_pclmulqdq (res, res, q_v2di, gen_int_mode (0, DImode))); + + res = expand_shift (RSHIFT_EXPR, V2DImode, res, crc_size, NULL_RTX, 0); + + /* Keep the polynomial in V2DImode. */ + rtx polynomial = gen_reg_rtx (DImode); + convert_move (polynomial, operands[3], 0); + rtx p_v2di = gen_reg_rtx (V2DImode); + emit_insn (gen_vec_concatv2di (p_v2di, polynomial, const0_rtx)); + + /* Perform carry-less multiplication and get low part. */ + emit_insn (gen_pclmulqdq (res, res, p_v2di, gen_int_mode (0, DImode))); + rtx crc_part = gen_reg_rtx (DImode); + emit_insn (gen_vec_extractv2didi (crc_part, res, const0_rtx)); + + if (crc_size > data_size) + { + rtx shift = expand_shift (LSHIFT_EXPR, DImode, operands[1], data_size, + NULL_RTX, 1); + crc_part = expand_binop (DImode, xor_optab, crc_part, shift, NULL_RTX, 1, + OPTAB_DIRECT); + } + /* Zero upper bits beyond crc_size. */ + res = expand_shift (RSHIFT_EXPR, DImode, crc_part, DImode_size - crc_size, + NULL_RTX, 1); + res = expand_shift (LSHIFT_EXPR, DImode, crc_part, DImode_size - crc_size, + NULL_RTX, 0); + emit_move_insn (operands[0], gen_lowpart (GET_MODE (operands[0]), crc_part)); +} + +/* Generate assembly to calculate reversed CRC using pclmulqdq instruction. + OPERANDS[1] is input CRC, + OPERANDS[2] is data (message), + OPERANDS[3] is the polynomial without the leading 1. */ + +void +ix86_expand_reversed_crc_using_pclmul (rtx *operands) +{ + /* Check and keep arguments. */ + gcc_assert (!CONST_INT_P (operands[0])); + gcc_assert (CONST_INT_P (operands[3])); + rtx crc = operands[1]; + rtx data = operands[2]; + unsigned HOST_WIDE_INT crc_size = GET_MODE_BITSIZE (GET_MODE (operands[0])); + gcc_assert (crc_size <= 32); + unsigned HOST_WIDE_INT data_size = GET_MODE_BITSIZE (GET_MODE (data)); + + /* Calculate the quotient. */ + unsigned HOST_WIDE_INT + q = gf2n_poly_long_div_quotient (UINTVAL (operands[3]), crc_size); + + /* Reflect the calculated quotient. */ + q = reflect_hwi (q, crc_size + 1); + rtx q_v2di = gen_reg_rtx (V2DImode); + rtx quotient = gen_reg_rtx (DImode); + convert_move (quotient, gen_int_mode (q, DImode), 0); + emit_insn (gen_vec_concatv2di (q_v2di, quotient, const0_rtx)); + + /* crc ^ data and keep in V2DImode. */ + rtx cd_xor = expand_binop (DImode, xor_optab, crc, data, NULL_RTX, 1, + OPTAB_WIDEN); + + /* Perform carry-less multiplication. */ + rtx res = gen_reg_rtx (V2DImode); + emit_insn (gen_vec_concatv2di (res, cd_xor, const0_rtx)); + emit_insn (gen_pclmulqdq (res, res, q_v2di, gen_int_mode (0, DImode))); + + res = expand_shift (LSHIFT_EXPR, V2DImode, res, 64 - data_size, NULL_RTX, 0); + + /* Reflect the polynomial and keep in V2DImode. */ + unsigned HOST_WIDE_INT reflected_op3 = reflect_hwi (UINTVAL (operands[3]), + crc_size); + rtx ref_polynomial = gen_reg_rtx (DImode); + convert_move (ref_polynomial, gen_int_mode (reflected_op3 << 1, DImode), 0); + rtx p_v2di = gen_reg_rtx (V2DImode); + emit_insn (gen_vec_concatv2di (p_v2di, ref_polynomial, const0_rtx)); + + /* Perform carry-less multiplication and get high part. */ + emit_insn (gen_pclmulqdq (res, res, p_v2di, gen_int_mode (0, DImode))); + rtx res_high = gen_reg_rtx (DImode); + emit_insn (gen_vec_extractv2didi (res_high, res, const1_rtx)); + + if (crc_size > data_size) + { + rtx shift = expand_shift (RSHIFT_EXPR, DImode, crc, data_size, + NULL_RTX, 1); + res_high = expand_binop (DImode, xor_optab, res_high, shift, NULL_RTX, 1, + OPTAB_DIRECT); + } + emit_move_insn (operands[0], gen_lowpart (GET_MODE (operands[0]), res_high)); +} #endif /* CHECKING_P */ static const scoped_attribute_specs *const ix86_attribute_table[] = diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 0fae3c1eb87..429f00422a1 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -191,6 +191,10 @@ ;; For CRC32 support UNSPEC_CRC32 + ;; For CRC support + UNSPEC_CRC + UNSPEC_CRC_REV + ;; For LZCNT suppoprt UNSPEC_LZCNT @@ -28605,6 +28609,61 @@ (set_attr "prefix_extra" "1") (set_attr "mode" "DI")]) + +;; Same as SWI124. Added for different combinations. +(define_mode_iterator SWI124dup [QI HI SI]) + +;; CRC 8, 16, 32 for TARGET_64 +(define_expand "crc4" + ;; return value (calculated CRC) + [(set (match_operand:SWI124 0 "register_operand" "=r") + ;; initial CRC + (unspec:SWI124 [(match_operand:SWI124 1 "register_operand" "r") + ;; data + (match_operand:SWI124dup 2 "register_operand" "r") + ;; polynomial without leading 1 + (match_operand:SWI124 3)] + UNSPEC_CRC))] + /* The case when data's size is bigger than CRC's size is not supported. */ + "TARGET_PCLMUL && TARGET_64BIT && mode >= mode" +{ + ix86_expand_crc_using_pclmul (operands); + DONE; +}) + +;; Reversed CRC 8, 16, 32 for TARGET_64 +(define_expand "crc_rev4" + ;; return value (calculated CRC) + [(set (match_operand:SWI124 0 "register_operand" "=r") + ;; initial CRC + (unspec:SWI124 [(match_operand:SWI124 1 "register_operand" "r") + ;; data + (match_operand:SWI124dup 2 "register_operand" "r") + ;; polynomial without leading 1 + (match_operand:SWI124 3)] + UNSPEC_CRC_REV))] + /* The case when data's size is bigger than CRC's size is not supported. */ + "((TARGET_PCLMUL && TARGET_64BIT) || TARGET_CRC32) + && mode >= mode" +{ /* If it is iSCSI polynomial (0x1EDC6F41), generate crc32 instruction. */ + if (TARGET_CRC32 && INTVAL (operands[3]) == 517762881) + { + rtx crc_part = gen_reg_rtx (SImode); + rtx crc = operands[1]; + rtx data = operands[2]; + emit_insn (gen_sse4_2_crc32 (crc_part, crc, data)); + emit_move_insn (operands[0], + gen_lowpart (GET_MODE (operands[0]), crc_part)); + } + else if (TARGET_PCLMUL && TARGET_64BIT) + ix86_expand_reversed_crc_using_pclmul (operands); + else + expand_reversed_crc_table_based (operands[0], operands[1], operands[2], + operands[3], GET_MODE (operands[2]), + generate_reflecting_code_standard); + DONE; +}) + (define_insn "rdpmc" [(set (match_operand:DI 0 "register_operand" "=A") (unspec_volatile:DI [(match_operand:SI 1 "register_operand" "c")] diff --git a/gcc/testsuite/gcc.target/i386/crc-1-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-1-pclmul.c new file mode 100644 index 00000000000..21edf417f0c --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-1-pclmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc -fdisable-tree-phiopt2 -fdisable-tree-phiopt3" } */ + +#include "../../gcc.dg/torture/crc-1.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.target/i386/crc-10-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-10-pclmul.c new file mode 100644 index 00000000000..39bfd386d9c --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-10-pclmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-10.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-12-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-12-pclmul.c new file mode 100644 index 00000000000..1ac9a6bf56d --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-12-pclmul.c @@ -0,0 +1,9 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc -fdisable-tree-phiopt2 -fdisable-tree-phiopt3" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include "../../gcc.dg/torture/crc-12.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-13-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-13-pclmul.c new file mode 100644 index 00000000000..c290539fad3 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-13-pclmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-13.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-14-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-14-pclmul.c new file mode 100644 index 00000000000..cc62ee471c7 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-14-pclmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-14.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-17-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-17-pclmul.c new file mode 100644 index 00000000000..660bff55e1b --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-17-pclmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-17.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-18-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-18-pclmul.c new file mode 100644 index 00000000000..e54392b32a9 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-18-pclmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-18.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-21-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-21-pclmul.c new file mode 100644 index 00000000000..4db1504cd49 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-21-pclmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-21.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-22-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-22-pclmul.c new file mode 100644 index 00000000000..d0f27d96069 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-22-pclmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-22.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-23-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-23-pclmul.c new file mode 100644 index 00000000000..666f3620597 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-23-pclmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-23.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-4-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-4-pclmul.c new file mode 100644 index 00000000000..398ecaa4aba --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-4-pclmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-4.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-5-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-5-pclmul.c new file mode 100644 index 00000000000..b80368e81bc --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-5-pclmul.c @@ -0,0 +1,9 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -w -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include "../../gcc.dg/torture/crc-5.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.target/i386/crc-6-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-6-pclmul.c new file mode 100644 index 00000000000..20b851e3596 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-6-pclmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-6.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.target/i386/crc-7-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-7-pclmul.c new file mode 100644 index 00000000000..3e5cc75338e --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-7-pclmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-7.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-8-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-8-pclmul.c new file mode 100644 index 00000000000..4b1145553fc --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-8-pclmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-8.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-9-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-9-pclmul.c new file mode 100644 index 00000000000..204d6c84d47 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-9-pclmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-9.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-CCIT-data16-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-CCIT-data16-pclmul.c new file mode 100644 index 00000000000..ca728120858 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-CCIT-data16-pclmul.c @@ -0,0 +1,9 @@ +/* { dg-do run } */ +/* { dg-options "-w -mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include "../../gcc.dg/torture/crc-CCIT-data16.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.target/i386/crc-CCIT-data8-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-CCIT-data8-pclmul.c new file mode 100644 index 00000000000..816e0561d8f --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-CCIT-data8-pclmul.c @@ -0,0 +1,9 @@ +/* { dg-do run } */ +/* { dg-options "-w -mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto" } } */ + +#include "../../gcc.dg/torture/crc-CCIT-data8.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.target/i386/crc-coremark-16bitdata-pclmul.c b/gcc/testsuite/gcc.target/i386/crc-coremark-16bitdata-pclmul.c new file mode 100644 index 00000000000..817d960b0aa --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-coremark-16bitdata-pclmul.c @@ -0,0 +1,9 @@ +/* { dg-do run } */ +/* { dg-options "-w -mpclmul -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include "../../gcc.dg/torture/crc-coremark16-data16.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pclmulqdq" "dfinish"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.target/i386/crc-crc32-data16.c b/gcc/testsuite/gcc.target/i386/crc-crc32-data16.c new file mode 100644 index 00000000000..49ab5f31ef0 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-crc32-data16.c @@ -0,0 +1,53 @@ +/* { dg-do run } */ +/* { dg-options "-mcrc32 -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint32_t _crc32_O0 (uint32_t crc, uint16_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 8; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0x82F63B78; + else + crc = (crc >> 1); + } + + return crc; +} + +uint32_t _crc32 (uint32_t crc, uint16_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 8; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0x82F63B78; + else + crc = (crc >> 1); + } + + return crc; +} + +int main () +{ + uint32_t crc = 0x0D800D80; + for (uint16_t i = 0; i < 0xffff; i++) + { + uint32_t res1 = _crc32_O0 (crc, i); + uint32_t res2 = _crc32 (crc, i); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "UNSPEC_CRC32" "dfinish"} } */ +/* { dg-final { scan-rtl-dump-times "pclmulqdq" 0 "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-crc32-data32.c b/gcc/testsuite/gcc.target/i386/crc-crc32-data32.c new file mode 100644 index 00000000000..08d6c193a77 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-crc32-data32.c @@ -0,0 +1,53 @@ +/* { dg-do run } */ +/* { dg-options "-mcrc32 -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint32_t _crc32_O0 (uint32_t crc, uint32_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 32; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0x82F63B78; + else + crc = (crc >> 1); + } + + return crc; +} + +uint32_t _crc32 (uint32_t crc, uint32_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 32; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0x82F63B78; + else + crc = (crc >> 1); + } + + return crc; +} + +int main () +{ + uint32_t crc = 0x0D800D80; + for (uint8_t i = 0; i < 0xff; i++) + { + uint32_t res1 = _crc32_O0 (crc, i); + uint32_t res2 = _crc32 (crc, i); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "UNSPEC_CRC32" "dfinish"} } */ +/* { dg-final { scan-rtl-dump-times "pclmulqdq" 0 "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/i386/crc-crc32-data8.c b/gcc/testsuite/gcc.target/i386/crc-crc32-data8.c new file mode 100644 index 00000000000..7a76b27fd28 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/crc-crc32-data8.c @@ -0,0 +1,53 @@ +/* { dg-do run } */ +/* { dg-options "-mcrc32 -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint32_t _crc32_O0 (uint32_t crc, uint8_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 8; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0x82F63B78; + else + crc = (crc >> 1); + } + + return crc; +} + +uint32_t _crc32 (uint32_t crc, uint8_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 8; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0x82F63B78; + else + crc = (crc >> 1); + } + + return crc; +} + +int main () +{ + uint32_t crc = 0x0D800D80; + for (uint8_t i = 0; i < 0xff; i++) + { + uint32_t res1 = _crc32_O0 (crc, i); + uint32_t res2 = _crc32 (crc, i); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "UNSPEC_CRC32" "dfinish"} } */ +/* { dg-final { scan-rtl-dump-times "pclmulqdq" 0 "dfinish"} } */ -- 2.25.1 From patchwork Fri Oct 18 15:01:21 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mariam Arutunian X-Patchwork-Id: 1999253 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=KYgRDPT3; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XVSdM6M64z1xth for ; Sat, 19 Oct 2024 02:03:35 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 1657A3857C7A for ; Fri, 18 Oct 2024 15:03:34 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-lj1-x234.google.com (mail-lj1-x234.google.com [IPv6:2a00:1450:4864:20::234]) by sourceware.org (Postfix) with ESMTPS id 9899E3857C7F for ; Fri, 18 Oct 2024 15:01:35 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 9899E3857C7F Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 9899E3857C7F Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::234 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263708; cv=none; b=JXgRX1VCTqUpHNcNQrBKlojzxdEwhBlpVE1lKR5tNndjJSUiTwqSFWLp431oNohwUP52MGY3ngnEcortp+2M7ZiXYfbSdI7VICiPf+GWrV79IavDc8NHR3z9p9ZMhjMsFLueX88lT+Cy2ZZN71MrX1p8XXqrBuKcwJttFkfmJX8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263708; c=relaxed/simple; bh=VeMrbWvgOCfqfd5KHXdlkdd09KP9Ot44VnI1QELvAcI=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=s2/+hRidWxmxsRMu0RbnjbZ4s7jAl2yVKHMewSPzR5hSQ5KuHJKhfZbTDaH7DKwnHH32WIZFUO7hy3RpDumAThH9Bx3ZDNve0MmEEpWO2QLWp/eIo0y3HNwvoetnp8j00WPmcMttjMcVL6P033Jb6YVvlLibmOP68EFVifv5bNQ= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-lj1-x234.google.com with SMTP id 38308e7fff4ca-2fb4af0b6beso37222091fa.3 for ; Fri, 18 Oct 2024 08:01:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1729263694; x=1729868494; darn=gcc.gnu.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=tWiuObWZxalyxcz6stY+FhNPYgX7pQQrwL/PaBbHSFk=; b=KYgRDPT3wpil4pF7ZHidxGgVX1LhCFw6alme8uc1uKWfoEB7KwdQZp5IWZbwwk2+2V nRtFSMHbof2dJxURcTYBnCyqhwgP1oNTiRRP+mAtXI+Jr7UeZLPGxHTSizUfkROKK/Fs 99Au5Grc0a7nahKKHemmmoLLZXcJwfc+kiGktrPWHuunxcLIXy7VzgvHebojAPKe3PIZ RHvwjKuZfJREKWvpkCWMmQq2c5RG98YspnXnNTeW9CNsB6fofQ/6C64pJML/ifWt81by HjGXOcOOl87JD2mjlLDVm9nAaq7bGz8N4wtHzxp/KfxeZdiF0M4VyEXtxDdIH6/3QS05 NVDw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729263694; x=1729868494; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=tWiuObWZxalyxcz6stY+FhNPYgX7pQQrwL/PaBbHSFk=; b=LVXGYW6RF+vuHz7YdBMaulysm9J1CiHnljLlvVJuPQiWhzIiMdL4ksrPNJ4e96vu+F EvClp2P7Sh0ZZknQIEZe5aDjRKrhkoCHwMbI2m8RDxh8SnuDefpzS/ZePILoK6NR+3tj 5LvSXM61YjYmZ9S8jsrXutnP+YHKwLPT88d9RD2noWd1+3NLxRVVYAy3nPjji7xB6A3A FQZUuXHo3Oilu0ncFOoQfcs4A0glCncyInP3p/xu2au/VJbnSl5sjTW23iDVtWzqBpCs gcpbdm+cmo/tg06s8z1Psuaa+rBlciWmmlFYIk4Pm/yzoa3uE8WxLlnaGSkxHInA/Lyw qVXg== X-Gm-Message-State: AOJu0YyJZ3cbGbD08gxM3mOrU1Gc1d9vDpnJqp375MNxuzSyRsIcdMhp oSXT0PuMeemYxmJxT/TlT8hmuUCcPYYS0cxvDpCGYojM2/65tvJq0+HByuxUhAp8q75s7Ls4ezF PHMaFS4HcCg9gIVr+AbdTILlE+j9fb3LN9M4= X-Google-Smtp-Source: AGHT+IGLB76KQU/J5LAN2mJ2V9pTNVg4AbpwkjiwwaIr1guhnor/1mxk0KyXGVQpm8qKvYiZQIqF0GoMJwMUm8vrxmk= X-Received: by 2002:a2e:9fca:0:b0:2fb:5bd:8ff2 with SMTP id 38308e7fff4ca-2fb82ea1dcdmr18328761fa.16.1729263693109; Fri, 18 Oct 2024 08:01:33 -0700 (PDT) MIME-Version: 1.0 From: Mariam Arutunian Date: Fri, 18 Oct 2024 19:01:21 +0400 Message-ID: Subject: [RFC/RFA][PATCH v5 06/12] aarch64: Implement new expander for efficient CRC computation. To: GCC Patches , Jeff Law X-Spam-Status: No, score=-7.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, HTML_MESSAGE, KAM_SHORT, RCVD_IN_DNSWL_NONE, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org This patch introduces two new expanders for the aarch64 backend, dedicated to generate optimized code for CRC computations. The new expanders are designed to leverage specific hardware capabilities to achieve faster CRC calculations, particularly using the crc32, crc32c and pmull instructions when supported by the target architecture. Expander 1: Bit-Forward CRC (crc4) For targets that support pmul instruction (TARGET_AES), the expander will generate code that uses the pmull (crypto_pmulldi) instruction for CRC computation. Expander 2: Bit-Reversed CRC (crc_rev4) The expander first checks if the target supports the CRC32* instruction set (TARGET_CRC32) and the polynomial in use is 0x1EDC6F41 (iSCSI) or 0x04C11DB7 (HDLC). If the conditions are met, it emits calls to the corresponding crc32* instruction (depending on the data size and the polynomial). If the target does not support crc32* but supports pmull, it then uses the pmull (crypto_pmulldi) instruction for bit-reversed CRC computation. Otherwise table-based CRC is generated. gcc/config/aarch64/ * aarch64-protos.h (aarch64_expand_crc_using_pmull): New extern function declaration. (aarch64_expand_reversed_crc_using_pmull): Likewise. * aarch64.cc (aarch64_expand_crc_using_pmull): New function. (aarch64_expand_reversed_crc_using_pmull): Likewise. * aarch64.md (crc_rev4): New expander for reversed CRC. (crc4): New expander for bit-forward CRC. * iterators.md (crc_data_type): New mode attribute. gcc/testsuite/gcc.target/aarch64/ * crc-1-pmul.c: New test. * crc-10-pmul.c: Likewise. * crc-12-pmul.c: Likewise. * crc-13-pmul.c: Likewise. * crc-14-pmul.c: Likewise. * crc-17-pmul.c: Likewise. * crc-18-pmul.c: Likewise. * crc-21-pmul.c: Likewise. * crc-22-pmul.c: Likewise. * crc-23-pmul.c: Likewise. * crc-4-pmul.c: Likewise. * crc-5-pmul.c: Likewise. * crc-6-pmul.c: Likewise. * crc-7-pmul.c: Likewise. * crc-8-pmul.c: Likewise. * crc-9-pmul.c: Likewise. * crc-CCIT-data16-pmul.c: Likewise. * crc-CCIT-data8-pmul.c: Likewise. * crc-coremark-16bitdata-pmul.c: Likewise. * crc-crc32-data16.c: Likewise. * crc-crc32-data32.c: Likewise. * crc-crc32-data8.c: Likewise. * crc-crc32c-data16.c: Likewise. * crc-crc32c-data32.c: Likewise. * crc-crc32c-data8.c: Likewise. Signed-off-by: Mariam Arutunian Co-authored-by: Richard Sandiford --- gcc/config/aarch64/aarch64-protos.h | 3 + gcc/config/aarch64/aarch64.cc | 131 ++++++++++++++++++ gcc/config/aarch64/aarch64.md | 57 ++++++++ gcc/config/aarch64/iterators.md | 4 + gcc/testsuite/gcc.target/aarch64/crc-1-pmul.c | 8 ++ .../gcc.target/aarch64/crc-10-pmul.c | 8 ++ .../gcc.target/aarch64/crc-12-pmul.c | 9 ++ .../gcc.target/aarch64/crc-13-pmul.c | 8 ++ .../gcc.target/aarch64/crc-14-pmul.c | 8 ++ .../gcc.target/aarch64/crc-17-pmul.c | 8 ++ .../gcc.target/aarch64/crc-18-pmul.c | 8 ++ .../gcc.target/aarch64/crc-21-pmul.c | 8 ++ .../gcc.target/aarch64/crc-22-pmul.c | 8 ++ .../gcc.target/aarch64/crc-23-pmul.c | 8 ++ gcc/testsuite/gcc.target/aarch64/crc-4-pmul.c | 8 ++ gcc/testsuite/gcc.target/aarch64/crc-5-pmul.c | 8 ++ gcc/testsuite/gcc.target/aarch64/crc-6-pmul.c | 8 ++ gcc/testsuite/gcc.target/aarch64/crc-7-pmul.c | 8 ++ gcc/testsuite/gcc.target/aarch64/crc-8-pmul.c | 8 ++ gcc/testsuite/gcc.target/aarch64/crc-9-pmul.c | 8 ++ .../gcc.target/aarch64/crc-CCIT-data16-pmul.c | 9 ++ .../gcc.target/aarch64/crc-CCIT-data8-pmul.c | 9 ++ .../aarch64/crc-coremark-16bitdata-pmul.c | 9 ++ .../gcc.target/aarch64/crc-crc32-data16.c | 53 +++++++ .../gcc.target/aarch64/crc-crc32-data32.c | 52 +++++++ .../gcc.target/aarch64/crc-crc32-data8.c | 53 +++++++ .../gcc.target/aarch64/crc-crc32c-data16.c | 53 +++++++ .../gcc.target/aarch64/crc-crc32c-data32.c | 52 +++++++ .../gcc.target/aarch64/crc-crc32c-data8.c | 53 +++++++ 29 files changed, 667 insertions(+) create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-1-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-10-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-12-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-13-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-14-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-17-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-18-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-21-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-22-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-23-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-4-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-5-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-6-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-7-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-8-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-9-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-CCIT-data16-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-CCIT-data8-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-coremark-16bitdata-pmul.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-crc32-data16.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-crc32-data32.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-crc32-data8.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-crc32c-data16.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-crc32c-data32.c create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-crc32c-data8.c diff --git a/gcc/config/aarch64/aarch64-protos.h b/gcc/config/aarch64/aarch64-protos.h index d03c1fe798b..7c157073cc6 100644 --- a/gcc/config/aarch64/aarch64-protos.h +++ b/gcc/config/aarch64/aarch64-protos.h @@ -1124,5 +1124,8 @@ extern void aarch64_adjust_reg_alloc_order (); bool aarch64_optimize_mode_switching (aarch64_mode_entity); void aarch64_restore_za (rtx); +void aarch64_expand_crc_using_pmull (scalar_mode, scalar_mode, rtx *); +void aarch64_expand_reversed_crc_using_pmull (scalar_mode, scalar_mode, rtx *); + #endif /* GCC_AARCH64_PROTOS_H */ diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc index 6a3f1a23a9f..1cc549c5023 100644 --- a/gcc/config/aarch64/aarch64.cc +++ b/gcc/config/aarch64/aarch64.cc @@ -30386,6 +30386,137 @@ aarch64_retrieve_sysreg (const char *regname, bool write_p, bool is128op) return sysreg->encoding; } +/* Generate assembly to calculate CRC + using carry-less multiplication instruction. + OPERANDS[1] is input CRC, + OPERANDS[2] is data (message), + OPERANDS[3] is the polynomial without the leading 1. */ + +void +aarch64_expand_crc_using_pmull (scalar_mode crc_mode, + scalar_mode data_mode, + rtx *operands) +{ + /* Check and keep arguments. */ + gcc_assert (!CONST_INT_P (operands[0])); + gcc_assert (CONST_INT_P (operands[3])); + rtx crc = operands[1]; + rtx data = operands[2]; + rtx polynomial = operands[3]; + + unsigned HOST_WIDE_INT crc_size = GET_MODE_BITSIZE (crc_mode); + unsigned HOST_WIDE_INT data_size = GET_MODE_BITSIZE (data_mode); + gcc_assert (crc_size <= 32); + gcc_assert (data_size <= crc_size); + + /* Calculate the quotient. */ + unsigned HOST_WIDE_INT + q = gf2n_poly_long_div_quotient (UINTVAL (polynomial), crc_size); + /* CRC calculation's main part. */ + if (crc_size > data_size) + crc = expand_shift (RSHIFT_EXPR, DImode, crc, crc_size - data_size, + NULL_RTX, 1); + + rtx t0 = force_reg (DImode, gen_int_mode (q, DImode)); + polynomial = simplify_gen_unary (ZERO_EXTEND, DImode, polynomial, + GET_MODE (polynomial)); + rtx t1 = force_reg (DImode, polynomial); + + rtx a0 = expand_binop (DImode, xor_optab, crc, data, NULL_RTX, 1, + OPTAB_WIDEN); + + rtx pmull_res = gen_reg_rtx (TImode); + emit_insn (gen_aarch64_crypto_pmulldi (pmull_res, a0, t0)); + a0 = gen_lowpart (DImode, pmull_res); + + a0 = expand_shift (RSHIFT_EXPR, DImode, a0, crc_size, NULL_RTX, 1); + + emit_insn (gen_aarch64_crypto_pmulldi (pmull_res, a0, t1)); + a0 = gen_lowpart (DImode, pmull_res); + + if (crc_size > data_size) + { + rtx crc_part = expand_shift (LSHIFT_EXPR, DImode, operands[1], data_size, + NULL_RTX, 0); + a0 = expand_binop (DImode, xor_optab, a0, crc_part, NULL_RTX, 1, + OPTAB_DIRECT); + } + + aarch64_emit_move (operands[0], gen_lowpart (crc_mode, a0)); +} + +/* Generate assembly to calculate reversed CRC + using carry-less multiplication instruction. + OPERANDS[1] is input CRC, + OPERANDS[2] is data, + OPERANDS[3] is the polynomial without the leading 1. */ + +void +aarch64_expand_reversed_crc_using_pmull (scalar_mode crc_mode, + scalar_mode data_mode, + rtx *operands) +{ + /* Check and keep arguments. */ + gcc_assert (!CONST_INT_P (operands[0])); + gcc_assert (CONST_INT_P (operands[3])); + rtx crc = operands[1]; + rtx data = operands[2]; + rtx polynomial = operands[3]; + + unsigned HOST_WIDE_INT crc_size = GET_MODE_BITSIZE (crc_mode); + unsigned HOST_WIDE_INT data_size = GET_MODE_BITSIZE (data_mode); + gcc_assert (crc_size <= 32); + gcc_assert (data_size <= crc_size); + + /* Calculate the quotient. */ + unsigned HOST_WIDE_INT + q = gf2n_poly_long_div_quotient (UINTVAL (polynomial), crc_size); + /* Reflect the calculated quotient. */ + q = reflect_hwi (q, crc_size + 1); + rtx t0 = force_reg (DImode, gen_int_mode (q, DImode)); + + /* Reflect the polynomial. */ + unsigned HOST_WIDE_INT ref_polynomial = reflect_hwi (UINTVAL (polynomial), + crc_size); + /* An unshifted multiplier would require the final result to be extracted + using a shift right by DATA_SIZE - 1 bits. Shift the multiplier left + so that the shift right can be by CRC_SIZE bits instead. */ + ref_polynomial <<= crc_size - data_size + 1; + rtx t1 = force_reg (DImode, gen_int_mode (ref_polynomial, DImode)); + + /* CRC calculation's main part. */ + rtx a0 = expand_binop (DImode, xor_optab, crc, data, NULL_RTX, 1, + OPTAB_WIDEN); + + /* Perform carry-less multiplication and get low part. */ + rtx pmull_res = gen_reg_rtx (TImode); + emit_insn (gen_aarch64_crypto_pmulldi (pmull_res, a0, t0)); + a0 = gen_lowpart (DImode, pmull_res); + + a0 = expand_binop (DImode, and_optab, a0, + gen_int_mode (GET_MODE_MASK (data_mode), DImode), + NULL_RTX, 1, OPTAB_WIDEN); + + /* Perform carry-less multiplication. */ + emit_insn (gen_aarch64_crypto_pmulldi (pmull_res, a0, t1)); + + /* Perform a shift right by CRC_SIZE as an extraction of lane 1. */ + machine_mode crc_vmode = aarch64_vq_mode (crc_mode).require (); + a0 = (crc_size > data_size ? gen_reg_rtx (crc_mode) : operands[0]); + emit_insn (gen_aarch64_get_lane (crc_vmode, a0, + gen_lowpart (crc_vmode, pmull_res), + aarch64_endian_lane_rtx (crc_vmode, 1))); + + if (crc_size > data_size) + { + rtx crc_part = expand_shift (RSHIFT_EXPR, crc_mode, crc, data_size, + NULL_RTX, 1); + a0 = expand_binop (crc_mode, xor_optab, a0, crc_part, operands[0], 1, + OPTAB_WIDEN); + aarch64_emit_move (operands[0], a0); + } +} + /* Target-specific selftests. */ #if CHECKING_P diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md index c54b29cd64b..d390d45f77f 100644 --- a/gcc/config/aarch64/aarch64.md +++ b/gcc/config/aarch64/aarch64.md @@ -4566,6 +4566,63 @@ [(set_attr "type" "crc")] ) +;; Reversed CRC +(define_expand "crc_rev4" + [;; return value (calculated CRC) + (match_operand:ALLX 0 "register_operand" "=r") + ;; initial CRC + (match_operand:ALLX 1 "register_operand" "r") + ;; data + (match_operand:ALLI 2 "register_operand" "r") + ;; polynomial without leading 1 + (match_operand:ALLX 3)] + "" + { + /* If the polynomial is the same as the polynomial of crc32c* instruction, + put that instruction. crc32c uses iSCSI polynomial. */ + if (TARGET_CRC32 && INTVAL (operands[3]) == 0x1EDC6F41 + && mode == SImode) + emit_insn (gen_aarch64_crc32c (operands[0], + operands[1], + operands[2])); + /* If the polynomial is the same as the polynomial of crc32* instruction, + put that instruction. crc32 uses HDLC etc. polynomial. */ + else if (TARGET_CRC32 && INTVAL (operands[3]) == 0x04C11DB7 + && mode == SImode) + emit_insn (gen_aarch64_crc32 (operands[0], + operands[1], + operands[2])); + else if (TARGET_AES && <= ) + aarch64_expand_reversed_crc_using_pmull (mode, + mode, + operands); + else + /* Otherwise, generate table-based CRC. */ + expand_reversed_crc_table_based (operands[0], operands[1], operands[2], + operands[3], mode, + generate_reflecting_code_standard); + DONE; + } +) + +;; Bit-forward CRC +(define_expand "crc4" + [;; return value (calculated CRC) + (match_operand:ALLX 0 "register_operand" "=r") + ;; initial CRC + (match_operand:ALLX 1 "register_operand" "r") + ;; data + (match_operand:ALLI 2 "register_operand" "r") + ;; polynomial without leading 1 + (match_operand:ALLX 3)] + "TARGET_AES && <= " + { + aarch64_expand_crc_using_pmull (mode, mode, + operands); + DONE; + } +) + (define_insn "*csinc2_insn" [(set (match_operand:GPI 0 "register_operand" "=r") (plus:GPI (match_operand 2 "aarch64_comparison_operation" "") diff --git a/gcc/config/aarch64/iterators.md b/gcc/config/aarch64/iterators.md index 20a318e023b..9c439c45dd3 100644 --- a/gcc/config/aarch64/iterators.md +++ b/gcc/config/aarch64/iterators.md @@ -1280,6 +1280,10 @@ ;; Map a mode to a specific constraint character. (define_mode_attr cmode [(QI "q") (HI "h") (SI "s") (DI "d")]) +;; Map a mode to a specific constraint character for calling +;; appropriate version of crc. +(define_mode_attr crc_data_type [(QI "b") (HI "h") (SI "w") (DI "x")]) + ;; Map modes to Usg and Usj constraints for SISD right shifts (define_mode_attr cmode_simd [(SI "g") (DI "j")]) diff --git a/gcc/testsuite/gcc.target/aarch64/crc-1-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-1-pmul.c new file mode 100644 index 00000000000..4043251dbd8 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-1-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc -fdisable-tree-phiopt2 -fdisable-tree-phiopt3" } */ + +#include "../../gcc.dg/torture/crc-1.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.target/aarch64/crc-10-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-10-pmul.c new file mode 100644 index 00000000000..0078eebe35c --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-10-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-10.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-12-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-12-pmul.c new file mode 100644 index 00000000000..16d901eeaef --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-12-pmul.c @@ -0,0 +1,9 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc -fdisable-tree-phiopt2 -fdisable-tree-phiopt3" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include "../../gcc.dg/torture/crc-12.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-13-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-13-pmul.c new file mode 100644 index 00000000000..bd8f32e6924 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-13-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-13.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-14-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-14-pmul.c new file mode 100644 index 00000000000..d35c1110c89 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-14-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-14.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-17-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-17-pmul.c new file mode 100644 index 00000000000..99b84c8dde0 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-17-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-17.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-18-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-18-pmul.c new file mode 100644 index 00000000000..888c99a7dd7 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-18-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-18.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-21-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-21-pmul.c new file mode 100644 index 00000000000..4b92deceaac --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-21-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-21.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-22-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-22-pmul.c new file mode 100644 index 00000000000..b42b8525b24 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-22-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-22.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-23-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-23-pmul.c new file mode 100644 index 00000000000..eb2efae0c41 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-23-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-23.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-4-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-4-pmul.c new file mode 100644 index 00000000000..c7d50017fe8 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-4-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-4.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-5-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-5-pmul.c new file mode 100644 index 00000000000..2a4b87cc5d6 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-5-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -w -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-5.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.target/aarch64/crc-6-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-6-pmul.c new file mode 100644 index 00000000000..84604af525a --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-6-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-6.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.target/aarch64/crc-7-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-7-pmul.c new file mode 100644 index 00000000000..e1263fca91d --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-7-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-7.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-8-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-8-pmul.c new file mode 100644 index 00000000000..141b474578b --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-8-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-8.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-9-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-9-pmul.c new file mode 100644 index 00000000000..2fdcd425a3b --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-9-pmul.c @@ -0,0 +1,8 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ + +#include "../../gcc.dg/torture/crc-9.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-CCIT-data16-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-CCIT-data16-pmul.c new file mode 100644 index 00000000000..21520474564 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-CCIT-data16-pmul.c @@ -0,0 +1,9 @@ +/* { dg-do run } */ +/* { dg-options "-w -march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include "../../gcc.dg/torture/crc-CCIT-data16.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.target/aarch64/crc-CCIT-data8-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-CCIT-data8-pmul.c new file mode 100644 index 00000000000..3dcc92320f3 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-CCIT-data8-pmul.c @@ -0,0 +1,9 @@ +/* { dg-do run } */ +/* { dg-options "-w -march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto" } } */ + +#include "../../gcc.dg/torture/crc-CCIT-data8.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.target/aarch64/crc-coremark-16bitdata-pmul.c b/gcc/testsuite/gcc.target/aarch64/crc-coremark-16bitdata-pmul.c new file mode 100644 index 00000000000..e5196aaafef --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-coremark-16bitdata-pmul.c @@ -0,0 +1,9 @@ +/* { dg-do run } */ +/* { dg-options "-w -march=armv8-a+crypto -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include "../../gcc.dg/torture/crc-coremark16-data16.c" + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "pmull" "dfinish"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.target/aarch64/crc-crc32-data16.c b/gcc/testsuite/gcc.target/aarch64/crc-crc32-data16.c new file mode 100644 index 00000000000..e82cb04fcc3 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-crc32-data16.c @@ -0,0 +1,53 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crc -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint32_t _crc32_O0 (uint32_t crc, uint16_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 8; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0xEDB88320; + else + crc = (crc >> 1); + } + + return crc; +} + +uint32_t _crc32 (uint32_t crc, uint16_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 8; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0xEDB88320; + else + crc = (crc >> 1); + } + + return crc; +} + +int main () +{ + uint32_t crc = 0x0D800D80; + for (uint16_t i = 0; i < 0xffff; i++) + { + uint32_t res1 = _crc32_O0 (crc, i); + uint32_t res2 = _crc32 (crc, i); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "UNSPEC_CRC32" "dfinish"} } */ +/* { dg-final { scan-rtl-dump-times "pmull" 0 "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-crc32-data32.c b/gcc/testsuite/gcc.target/aarch64/crc-crc32-data32.c new file mode 100644 index 00000000000..a7564a7e28a --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-crc32-data32.c @@ -0,0 +1,52 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crc -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include +#include +__attribute__ ((noinline,optimize(0))) +uint32_t _crc32_O0 (uint32_t crc, uint32_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 32; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0xEDB88320; + else + crc = (crc >> 1); + } + + return crc; +} + +uint32_t _crc32 (uint32_t crc, uint32_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 32; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0xEDB88320; + else + crc = (crc >> 1); + } + + return crc; +} + +int main () +{ + uint32_t crc = 0x0D800D80; + for (uint8_t i = 0; i < 0xff; i++) + { + uint32_t res1 = _crc32_O0 (crc, i); + uint32_t res2 = _crc32 (crc, i); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "UNSPEC_CRC32" "dfinish"} } */ +/* { dg-final { scan-rtl-dump-times "pmull" 0 "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-crc32-data8.c b/gcc/testsuite/gcc.target/aarch64/crc-crc32-data8.c new file mode 100644 index 00000000000..c88cafadedc --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-crc32-data8.c @@ -0,0 +1,53 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crc -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint32_t _crc32_O0 (uint32_t crc, uint8_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 8; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0xEDB88320; + else + crc = (crc >> 1); + } + + return crc; +} + +uint32_t _crc32 (uint32_t crc, uint8_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 8; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0xEDB88320; + else + crc = (crc >> 1); + } + + return crc; +} + +int main () +{ + uint32_t crc = 0x0D800D80; + for (uint8_t i = 0; i < 0xff; i++) + { + uint32_t res1 = _crc32_O0 (crc, i); + uint32_t res2 = _crc32 (crc, i); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "UNSPEC_CRC32" "dfinish"} } */ +/* { dg-final { scan-rtl-dump-times "pmull" 0 "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-crc32c-data16.c b/gcc/testsuite/gcc.target/aarch64/crc-crc32c-data16.c new file mode 100644 index 00000000000..d82e6252603 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-crc32c-data16.c @@ -0,0 +1,53 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crc -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint32_t _crc32_O0 (uint32_t crc, uint16_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 8; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0x82F63B78; + else + crc = (crc >> 1); + } + + return crc; +} + +uint32_t _crc32 (uint32_t crc, uint16_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 8; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0x82F63B78; + else + crc = (crc >> 1); + } + + return crc; +} + +int main () +{ + uint32_t crc = 0x0D800D80; + for (uint16_t i = 0; i < 0xffff; i++) + { + uint32_t res1 = _crc32_O0 (crc, i); + uint32_t res2 = _crc32 (crc, i); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "UNSPEC_CRC32C" "dfinish"} } */ +/* { dg-final { scan-rtl-dump-times "pmull" 0 "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-crc32c-data32.c b/gcc/testsuite/gcc.target/aarch64/crc-crc32c-data32.c new file mode 100644 index 00000000000..7acb6fc239c --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-crc32c-data32.c @@ -0,0 +1,52 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crc -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include +#include +__attribute__ ((noinline,optimize(0))) +uint32_t _crc32_O0 (uint32_t crc, uint32_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 32; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0x82F63B78; + else + crc = (crc >> 1); + } + + return crc; +} + +uint32_t _crc32 (uint32_t crc, uint32_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 32; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0x82F63B78; + else + crc = (crc >> 1); + } + + return crc; +} + +int main () +{ + uint32_t crc = 0x0D800D80; + for (uint8_t i = 0; i < 0xff; i++) + { + uint32_t res1 = _crc32_O0 (crc, i); + uint32_t res2 = _crc32 (crc, i); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "UNSPEC_CRC32C" "dfinish"} } */ +/* { dg-final { scan-rtl-dump-times "pmull" 0 "dfinish"} } */ diff --git a/gcc/testsuite/gcc.target/aarch64/crc-crc32c-data8.c b/gcc/testsuite/gcc.target/aarch64/crc-crc32c-data8.c new file mode 100644 index 00000000000..e8a8901e453 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-crc32c-data8.c @@ -0,0 +1,53 @@ +/* { dg-do run } */ +/* { dg-options "-march=armv8-a+crc -O2 -fdump-rtl-dfinish -fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-flto"} } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint32_t _crc32_O0 (uint32_t crc, uint8_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 8; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0x82F63B78; + else + crc = (crc >> 1); + } + + return crc; +} + +uint32_t _crc32 (uint32_t crc, uint8_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 8; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0x82F63B78; + else + crc = (crc >> 1); + } + + return crc; +} + +int main () +{ + uint32_t crc = 0x0D800D80; + for (uint8_t i = 0; i < 0xff; i++) + { + uint32_t res1 = _crc32_O0 (crc, i); + uint32_t res2 = _crc32 (crc, i); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ +/* { dg-final { scan-rtl-dump "UNSPEC_CRC32C" "dfinish"} } */ +/* { dg-final { scan-rtl-dump-times "pmull" 0 "dfinish"} } */ -- 2.25.1 From patchwork Fri Oct 18 15:01:31 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mariam Arutunian X-Patchwork-Id: 1999257 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=H2qwlsCy; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XVSfc5Gq2z1xth for ; Sat, 19 Oct 2024 02:04:40 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E3E65385B836 for ; Fri, 18 Oct 2024 15:04:38 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-lj1-x236.google.com (mail-lj1-x236.google.com [IPv6:2a00:1450:4864:20::236]) by sourceware.org (Postfix) with ESMTPS id 691FF3858294 for ; Fri, 18 Oct 2024 15:01:50 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 691FF3858294 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 691FF3858294 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::236 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263713; cv=none; b=blAqXUTwpf3fYpeU1R+QkpOcKJi/eEg1pm3zPvfCwf/mkvovJPL+j3SxNW2KYZxQK/+X1wh9hNa31VHxkZIji8UXABBnuPuBseHDjrvpHv4F5dSdK6CdT5HUny1CjQDfrobrAOrJmsugWnvN8XbNAVCqVtFnOWX6x1b9cYjDYeM= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263713; c=relaxed/simple; bh=wN9fubvWHQePNtQCWYdCR8CRxUo1AymbMNcwBZe4Bb0=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=lgPVqcEVfrsxnu6qkE5ZDCATAO+FMkpdWy75boMZWOzTfVAHur0vFPGmlPnLBxrNtcbh48HA3i0LzeOcAA0F+y0iXENYYzhjPzNXmSGZsrXNHIzXnmNZfXO41GUZwiy3Jz8OechI3RbYjWisGePUxneJ4hDf83wV2xxxYQw4l60= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-lj1-x236.google.com with SMTP id 38308e7fff4ca-2fb5111747cso28652351fa.2 for ; Fri, 18 Oct 2024 08:01:50 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1729263708; x=1729868508; darn=gcc.gnu.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=elKr5cO7+1qIz8m3Pk8s0r6JRxtLbuArLfNl70+38QY=; b=H2qwlsCyf1rOV6BBCLuQSeCwTJhasq7GTduCIyS1Xs7iEf89xfmrfsFrG+s8cAz7XZ afKfSY4/qck7DWUOVPf4t/1wnmbohJAS3WeeaQ+3WyCWiJcN2ghsZlAhlrcGjrqrsqsj D69KNkjXvFlRshwaC3wcOVBphaBTGfcPmgQ7OQhw9uTH+/dNCj/xlg9FsujNAsW84cdM r9pLc/Ne8HJSIwAvIYNZHobEufj2rlrp+D2GfMThzs2H9+B6UeHuwkuNqcpFJP180ydO xMuEYwBrU7QVNeoaW5Zhr2DrCiCtJN98NFmflN+gSYAr4TjxJE3cl7iOvV6cWHPPwRT9 UNWA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729263708; x=1729868508; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=elKr5cO7+1qIz8m3Pk8s0r6JRxtLbuArLfNl70+38QY=; b=hZbtWN02aQAVl75yums0luBZa7+nT3TD6oaQP9DJ7qEQiDb7x79lYJLHBR7A3WgDPw ueOY3Qj0uP/B4UqbCgshvjo0maHDEfnLkQbLXUO+FNzRcIJc1u/79aOb7cEVEqANPk8Z D9I2KeU0lfmuRJqXQVKovL55l9I5VEfE7DuOvaUQXHJ6KCma8Jo9f0Vv4W3FT+omgW3L 8GWR5MWpXZ45Lz3qYIAI535POk9v/a/O/HNb/b58XuwvL+KtVFFL0uxCjA/HJnqulGZ8 ukL2ONzAE26KYXufpQ/3e2ik/3xLjZaEibx4WjjYmdAQ4eQmuNNy9KMBs9eXG/iVB+1x C8Dg== X-Gm-Message-State: AOJu0YzIBbxgSWue7nfDf4enLTMe5pRRR2AmSr5T3EKqHquz5M+Hhk2t 84UM593qkT2Gj49CK8/rkyKK1T3O4kqz4oy62Tmg9EBM3OInJ953FwGEHLgRuBtHCWYL2JlNENx 6WRzwhyMb4NndrVfW6/axo3zfeTI6M/mjT6U= X-Google-Smtp-Source: AGHT+IExeQGZJ2F8UXtxxX+mc148LQJ2i7TrYVbHhqFuMOE5DgqZksOQ9wGht906K+F7NqOJZiNlpZ0Gr1SSAgaaVoA= X-Received: by 2002:a05:651c:1543:b0:2fb:30d5:669a with SMTP id 38308e7fff4ca-2fb82da59camr13844791fa.0.1729263707982; Fri, 18 Oct 2024 08:01:47 -0700 (PDT) MIME-Version: 1.0 From: Mariam Arutunian Date: Fri, 18 Oct 2024 19:01:31 +0400 Message-ID: Subject: [RFC/RFA] [PATCH v5 07/12] aarch64: Add CRC built-ins test for the target AES. To: GCC Patches , Jeff Law X-Spam-Status: No, score=-7.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, HTML_MESSAGE, KAM_SHORT, RCVD_IN_DNSWL_NONE, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org gcc/testsuite/gcc.target/aarch64/ * crc-builtin-pmul64.c: New test. Signed-off-by: Mariam Arutunian --- .../gcc.target/aarch64/crc-builtin-pmul64.c | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 gcc/testsuite/gcc.target/aarch64/crc-builtin-pmul64.c diff --git a/gcc/testsuite/gcc.target/aarch64/crc-builtin-pmul64.c b/gcc/testsuite/gcc.target/aarch64/crc-builtin-pmul64.c new file mode 100644 index 00000000000..d8bb1724a65 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/crc-builtin-pmul64.c @@ -0,0 +1,61 @@ +/* { dg-options "-march=armv8-a+crypto" } */ + +#include +int8_t crc8_data8 () +{ + return __builtin_crc8_data8 ('a', 0xff, 0x12); +} +int16_t crc16_data8 () +{ + return __builtin_crc16_data8 (0x1234, 'a', 0x1021); +} + +int16_t crc16_data16 () +{ + return __builtin_crc16_data16 (0x1234, 0x3214, 0x1021); +} + +int32_t crc32_data8 () +{ + return __builtin_crc32_data8 (0xffffffff, 0x32, 0x4002123); +} +int32_t crc32_data16 () +{ + return __builtin_crc32_data16 (0xffffffff, 0x3232, 0x4002123); +} + +int32_t crc32_data32 () +{ + return __builtin_crc32_data32 (0xffffffff, 0x123546ff, 0x4002123); +} + +int8_t rev_crc8_data8 () +{ + return __builtin_rev_crc8_data8 (0x34, 'a', 0x12); +} + +int16_t rev_crc16_data8 () +{ + return __builtin_rev_crc16_data8 (0x1234, 'a', 0x1021); +} + +int16_t rev_crc16_data16 () +{ + return __builtin_rev_crc16_data16 (0x1234, 0x3214, 0x1021); +} + +int32_t rev_crc32_data8 () +{ + return __builtin_rev_crc32_data8 (0xffffffff, 0x32, 0x4002123); +} + +int32_t rev_crc32_data16 () +{ + return __builtin_rev_crc32_data16 (0xffffffff, 0x3232, 0x4002123); +} + +int32_t rev_crc32_data32 () +{ + return __builtin_rev_crc32_data32 (0xffffffff, 0x123546ff, 0x4002123); +} +/* { dg-final { scan-assembler-times "pmull" 24 } } */ \ No newline at end of file -- 2.25.1 From patchwork Fri Oct 18 15:01:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mariam Arutunian X-Patchwork-Id: 1999252 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=IhnTIRgE; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XVScC4Pj9z1xth for ; Sat, 19 Oct 2024 02:02:35 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id BE1083856DCD for ; Fri, 18 Oct 2024 15:02:33 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-lj1-x232.google.com (mail-lj1-x232.google.com [IPv6:2a00:1450:4864:20::232]) by sourceware.org (Postfix) with ESMTPS id 8C42E3857C7B for ; Fri, 18 Oct 2024 15:01:52 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 8C42E3857C7B Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 8C42E3857C7B Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::232 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263724; cv=none; b=cyvovhAljogJVUBivBEzkeq22ypO9A5NKEpqLtrVjv06JqGTJ18f6BGZtKG6abaUBjCLLT93+orpRROgsQevgqrufvpXQmPjrG1ksv4bWOCHJOatLvGd83XzpQENGiVYBXyQP8Xlwe8BJ+ON4NMf+kBZOc+XZ/f/5EpHtnlUzPg= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263724; c=relaxed/simple; bh=3TY3Dl96bg5GNbytdB8XFdJ+8o7qkUrzDiZTs+PZraA=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=DOYe60huqSL1zmA4ZLOTjwM46s9/XUyZwZxs3YuszI2ZKxIR7p3MIiDSmcYpzK2qmyMxZOiEw8Vr6mTST/ZhsDjcVpyuzf3tv+vv+T4WBASSq84UyXXxlwpginPbT2H4ByFvJgKR8RxBYzRm42WkEYGegOqynuSu0upB8iA3zAc= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-lj1-x232.google.com with SMTP id 38308e7fff4ca-2fb5f647538so22704491fa.0 for ; Fri, 18 Oct 2024 08:01:52 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1729263711; x=1729868511; darn=gcc.gnu.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=HTp/JlhXtxNeDlc07l1Gvrhl25Gzt1O36m02+lqozd4=; b=IhnTIRgE776tsonL7UyjUiI/kis7U1eUjnZ4AyMhV3zRpxBgZuPcExEPgyhmJju9JC aSYLT9GVhSKqJ1rUbabK/H7mg0jDCABsnU0F0QyoFrfBZ59hXJu+5x8i/Eiu/dDzg/Fp DIMzy0iPkP5n8ulYEXTiLotdqIvM7gM7pJ71wqb1c2YaHOtVlmik10gAqe2QMLqSSUJn YRenphuRgWDeJjRDRUXs9EY38IflauaH2ZWSBA0WJhU4iTQvoHujfM5wPRTo5AY74dD3 skkjbfU9DAGvPFufj0gJhuQ9Znt1UCYRFwmcv6ilLaU7d5Haf2cHz0u+18okwz/KA4ym mANw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729263711; x=1729868511; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=HTp/JlhXtxNeDlc07l1Gvrhl25Gzt1O36m02+lqozd4=; b=DNZArYjy82TO5EbjHs/ZtJ0cgYRAVQEBU0Pj3kEEI6u6wSvvcsbdCQOmAKR3njq9AC Cwsv/ByNlT5OVQQ+ti15NgMa4/ofDo0cvtWi2vvnZ2Mmzovn3RlSbPo1qHDJJN9fU+6d cbFtXeG3D2La+b+kpjIq98bWQsf4BzzbWI7iNXOlrP7nnBJRuRwaNZHRnR8nRRjwROVR mNrMWrfbUby7SJjujEoFsi7qoJVCHCZ/vaOSzhsC3CEc/IJwblfceH05dn714ZS7X+jk udr7W6dwGQ0ik0kTnq73HRK6tXEzvVlDG3322ivjiFaJJfgf1braWvk+nSlw1fIQ2e5B 2/Nw== X-Gm-Message-State: AOJu0YzI2Cfx08aRt4JncW7+x3oylC13tkUdXX12aQ9lrtO+HpHda8to LoBDxLK5VxAK/YxqcMJAGOvUAiHmDMPfOE+3GJ9h6pe+V9z0w5QeEVCYxV9YhyDJTbBgU/nY5Kt Jzna9RBKQ0Loe2kuHz3B1UWhmZV7Hmfxsb70= X-Google-Smtp-Source: AGHT+IEN3YzfoJJLLPQJ9C7UKlQJ/sD1XfKrpfePA3Si1cz/RQfiiDGt8ligH+rAQMKTynLjn7XfLOYk2OCughGrvaU= X-Received: by 2002:a2e:a545:0:b0:2fb:50e9:34cc with SMTP id 38308e7fff4ca-2fb6dc74e7bmr24645611fa.17.1729263710381; Fri, 18 Oct 2024 08:01:50 -0700 (PDT) MIME-Version: 1.0 From: Mariam Arutunian Date: Fri, 18 Oct 2024 19:01:37 +0400 Message-ID: Subject: [RFC/RFA] [PATCH v5 08/12] Add a new pass for naive CRC loops detection. To: GCC Patches , Jeff Law X-Spam-Status: No, score=-7.4 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, HTML_MESSAGE, KAM_SHORT, RCVD_IN_DNSWL_NONE, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org This patch adds a new compiler pass aimed at identifying naive CRC implementations, characterized by the presence of a loop calculating a CRC (polynomial long division). Upon detection of a potential CRC, the pass prints an informational message. Performs CRC optimization if optimization level is >= 2 and if fno_gimple_crc_optimization given. This pass is added for the detection and optimization of naive CRC implementations, improving the efficiency of CRC-related computations. This patch includes only initial fast checks for filtering out non-CRCs, detected possible CRCs verification and optimization parts will be provided in subsequent patches. gcc/ * Makefile.in (OBJS): Add gimple-crc-optimization.o. * common.opt (foptimize-crc): New option. * common.opt.urls: Regenerate to add foptimize-crc. * doc/invoke.texi (-foptimize-crc): Add documentation. * gimple-crc-optimization.cc: New file. * opts.cc (default_options_table): Add OPT_foptimize_crc. (enable_fdo_optimizations): Enable optimize_crc. * passes.def (pass_crc_optimization): Add new pass. * timevar.def (TV_GIMPLE_CRC_OPTIMIZATION): New timevar. * tree-pass.h (make_pass_crc_optimization): New extern function declaration. Signed-off-by: Mariam Arutunian Mentored-by: Jeff Law --- gcc/Makefile.in | 1 + gcc/common.opt | 10 + gcc/common.opt.urls | 3 + gcc/doc/invoke.texi | 16 +- gcc/gimple-crc-optimization.cc | 1000 ++++++++++++++++++++++++++++++++ gcc/opts.cc | 2 + gcc/passes.def | 1 + gcc/timevar.def | 1 + gcc/tree-pass.h | 1 + 9 files changed, 1034 insertions(+), 1 deletion(-) create mode 100644 gcc/gimple-crc-optimization.cc diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 68fda1a7591..a7054eda9c5 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1717,6 +1717,7 @@ OBJS = \ tree-iterator.o \ tree-logical-location.o \ tree-loop-distribution.o \ + gimple-crc-optimization.o \ tree-nested.o \ tree-nrv.o \ tree-object-size.o \ diff --git a/gcc/common.opt b/gcc/common.opt index ea39f87ae71..8395c100fe0 100644 --- a/gcc/common.opt +++ b/gcc/common.opt @@ -2399,6 +2399,16 @@ fsave-optimization-record Common Var(flag_save_optimization_record) Optimization Write a SRCFILE.opt-record.json file detailing what optimizations were performed. +foptimize-crc +Common Var(flag_optimize_crc) Optimization +Detect loops calculating CRC and replace with faster implementation. +If the target supports CRC instruction and the CRC loop uses the same +polynomial as the one used in the CRC instruction, directly replace with the +corresponding CRC instruction. +Otherwise, if the target supports carry-less-multiplication instruction, +generate CRC using it. +If neither case applies, generate table-based CRC. + foptimize-register-move Common Ignore Does nothing. Preserved for backward compatibility. diff --git a/gcc/common.opt.urls b/gcc/common.opt.urls index b917f90b0ff..8b61a371c05 100644 --- a/gcc/common.opt.urls +++ b/gcc/common.opt.urls @@ -1007,6 +1007,9 @@ UrlSuffix(gcc/Developer-Options.html#index-fopt-info) fsave-optimization-record UrlSuffix(gcc/Developer-Options.html#index-fsave-optimization-record) +foptimize-crc +UrlSuffix(gcc/Optimize-Options.html#index-foptimize-crc) + foptimize-sibling-calls UrlSuffix(gcc/Optimize-Options.html#index-foptimize-sibling-calls) diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi index 019e0a5ca80..2b10e7d4bdc 100644 --- a/gcc/doc/invoke.texi +++ b/gcc/doc/invoke.texi @@ -598,7 +598,7 @@ Objective-C and Objective-C++ Dialects}. -fno-peephole2 -fno-printf-return-value -fno-sched-interblock -fno-sched-spec -fno-signed-zeros -fno-toplevel-reorder -fno-trapping-math -fno-zero-initialized-in-bss --fomit-frame-pointer -foptimize-sibling-calls +-fomit-frame-pointer -foptimize-crc -foptimize-sibling-calls -fpartial-inlining -fpeel-loops -fpredictive-commoning -fprefetch-loop-arrays -fprofile-correction @@ -12664,6 +12664,7 @@ also turns on the following optimization flags: -fipa-ra -fipa-sra -fipa-vrp -fisolate-erroneous-paths-dereference -flra-remat +-foptimize-crc -foptimize-sibling-calls -foptimize-strlen -fpartial-inlining @@ -12828,6 +12829,19 @@ leaf functions. Enabled by default at @option{-O1} and higher. +@opindex foptimize-crc +@item -foptimize-crc +Detect loops calculating CRC (performing polynomial long division) and +replace them with a faster implementation. Detect 8, 16, 32, and 64 bit CRC, +with a constant polynomial without the leading 1 bit, +for both bit-forward and bit-reversed cases. +If the target supports a CRC instruction and the polynomial used in the source +code matches the polynomial used in the CRC instruction, generate that CRC +instruction. Otherwise, if the target supports a carry-less-multiplication +instruction, generate CRC using it; otherwise generate table-based CRC. + +Enabled by default at @option{-O2} and higher. + @opindex foptimize-sibling-calls @item -foptimize-sibling-calls Optimize sibling and tail recursive calls. diff --git a/gcc/gimple-crc-optimization.cc b/gcc/gimple-crc-optimization.cc new file mode 100644 index 00000000000..c67b0fd38c3 --- /dev/null +++ b/gcc/gimple-crc-optimization.cc @@ -0,0 +1,1000 @@ +/* CRC optimization. + Copyright (C) 2022-2024 Free Software Foundation, Inc. + Contributed by Mariam Arutunian + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +/* This pass performs CRC optimization. */ +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "gimple.h" +#include "tree-pass.h" +#include "ssa.h" +#include "gimple-iterator.h" +#include "tree-cfg.h" +#include "cfgloop.h" +#include "tree-scalar-evolution.h" + +class crc_optimization { + private: + /* The statement doing shift 1 operation before/after xor operation. */ + gimple *m_shift_stmt; + + /* Phi statement from the head of the loop for CRC. */ + gphi *m_phi_for_crc; + + /* Phi statement for the data from the head of the loop if exists, + otherwise - nullptr. */ + gphi *m_phi_for_data; + + /* The loop, which probably calculates CRC. */ + class loop *m_crc_loop; + + /* Depending on the value of M_IS_BIT_FORWARD, may be forward or reversed CRC. + If M_IS_BIT_FORWARD, then it is bit-forward implementation, + otherwise bit-reversed. */ + bool m_is_bit_forward; + + /* Sets initial values for CRC analyses. */ + void set_initial_values (); + + /* This is the main function that checks whether the given LOOP + calculates CRC and extracts details of the CRC calculation. + + The main idea is to find the innermost loop with 8, 16, 24, 32, 64 + iterations and xor instruction (xor is the key operation for naive CRC + calculation). Then, checks that the variable is shifted by one before/after + being used in xor. + Xor must be done under the condition of MSB/LSB being 1. */ + bool loop_may_calculate_crc (class loop *loop); + + /* Returns true if there is only two conditional blocks in the loop + (one may be for the CRC bit check and the other for the loop counter). + This may filter out some real CRCs, where more than one condition + is checked for the CRC calculation. */ + static bool loop_contains_two_conditional_bb (basic_block *loop_bbs, + unsigned loop_num_nodes); + + /* Checks the FUNC_LOOP loop's iteration number. + The loop for CRC calculation may do 8, 16, 24, 32, 64 iterations. */ + bool satisfies_crc_loop_iteration_count (class loop *func_loop); + + /* This function checks if the XOR_STMT is used for CRC calculation. + It verifies the presence of a shift operation in the CRC_FUN function + inside the CRC loop. It examines operands of XOR, its dependencies, the + relative position of the shift operation, and the existence of a shift + operation in the opposite branch of conditional statements. It also + checks if XOR is performed when MSB/LSB is one. + If these conditions are met, the XOR operation may be part of a CRC + calculation. The function returns true if these conditions are fulfilled, + otherwise, it returns false. */ + bool xor_calculates_crc (function *crc_fun, const gimple *xor_stmt); + + /* Returns true if we can get definition of the VARIABLE, and the definition + it's not outside the loop. Otherwise, returns false. */ + bool passes_checks_for_def_chain (tree variable); + + /* This function goes up through the def-use chains of the parameter NAME. + Gathers all the statements within the loop, + from which the variable depends on and adds to the USE_DEFS. + Returns false, if there is a statement that may not exist in the CRC + loop. Otherwise, returns true. */ + bool set_defs (tree name, auto_vec& use_defs, + bool keep_only_header_phis); + + /* Set M_PHI_FOR_CRC and M_PHI_FOR_DATA fields. + Returns false if there are more than two (as in CRC calculation only CRC's + and data's phi may exist) or no phi statements in STMTS (at least there + must be CRC's phi). + Otherwise, returns true. */ + bool set_crc_and_data_phi (auto_vec& stmts); + + /* Returns true if the variable checked in the condition depends on possible + CRC value. Otherwise, returns false. */ + bool cond_depends_on_crc (auto_vec& stmts); + + + /* Checks that the condition is for checking CRC. + Returns true if xor is done under the condition of MSB/LSB being 1, and + the condition's variable and xor-ed variable depend on the same variable. + Otherwise, returns false. + XOR_BB is the basic block, where the xor operation is done. + PRED_BB is the predecessor basic block of the XOR_BB, it is assumed that + the last stmt of PRED_BB checks the condition under which xor is done. */ + bool crc_cond (basic_block pred_bb, basic_block xor_bb); + + /* Returns true if xor is done in case the MSB/LSB is 1. + Otherwise, returns false. + In CRC calculation algorithms CRC is xor-ed with the polynomial only + if MSB/LSB is 1. + + PRED_BB is the block containing the condition for the xor. + XOR_BB is the one of the successor blocks of PRED_BB, it is assumed that + CRC is xor-ed with the polynomial in XOR_BB. + COND is the condition, which is checked to satisfy the CRC condition. */ + bool is_crc_satisfiable_cond (basic_block pred_bb, basic_block xor_bb, + gcond *cond); + + /* Checks that the variable used in the condition COND is the assumed CRC + (or depends on the assumed CRC). + Also sets data member m_phi_for_data if it isn't set and exists. */ + bool is_crc_checked (gcond *cond); + + /* Returns true if condition COND checks MSB/LSB bit is 1. + Otherwise, return false. */ + static bool cond_true_is_checked_for_bit_one (const gcond *cond); + + /* Returns opposite block of the XOR_BB from PRED_BB's dest blocks. */ + static basic_block get_xor_bb_opposite (basic_block pred_bb, + basic_block xor_bb); + + /* Checks whether the pair of xor's shift exists in the opposite + basic block (OPPOSITE_BB). + If there is a shift and xor in the same block, + then in the opposite block must be another shift. */ + bool exists_shift_for_opp_xor_shift (basic_block opposite_bb); + + /* Follow def-use chains of XORED_CRC and return the statement where + XORED_CRC is shifted by one bit position. Only PHI statements are + allowed between XORED_CRC and the shift in the def-use chain. + + If no such statement is found, return NULL. */ + gimple *find_shift_after_xor (tree xored_crc); + + /* Returns the statement which does shift 1 operation. + If there is no such statement, returns nullptr. + STMTS - are the statements within the loop before xor operation on + which possible CRC variable depends. */ + gimple *find_shift_before_xor (const auto_vec &stmts); + + /* Returns true if ASSIGN_STMT does shift with 1. + Otherwise, returns false. */ + bool can_be_crc_shift (gimple *assign_stmt); + + /* Returns true if the operation done in ASSIGN_STMT can occur during CRC + calculation. Otherwise, returns false. */ + bool can_not_be_crc_stmt (gimple *assign_stmt); + + /* Returns true if the statement with STMT_CODE may occur in CRC calculation. + Otherwise, returns false. */ + static bool is_acceptable_stmt_code (const tree_code &stmt_code); + + /* Prints extracted details of CRC calculation. */ + void dump_crc_information (); + + public: + crc_optimization () : m_visited_stmts (BITMAP_ALLOC (NULL)), + m_crc_loop (nullptr) + { + set_initial_values (); + } + ~crc_optimization () + { + BITMAP_FREE (m_visited_stmts); + } + unsigned int execute (function *fun); +}; + +/* Prints extracted details of CRC calculation. */ + +void +crc_optimization::dump_crc_information () +{ + if (dump_file) + { + fprintf (dump_file, + "Loop iteration number is " HOST_WIDE_INT_PRINT_UNSIGNED ".\n", + tree_to_uhwi (m_crc_loop->nb_iterations)); + if (m_is_bit_forward) + fprintf (dump_file, "Bit forward.\n"); + else + fprintf (dump_file, "Bit reversed.\n"); + } +} + +/* Goes down by def-use chain (within the CRC loop) and returns the statement + where variable (dependent on xor-ed variable) is shifted with 1. + Between xor and shift operations only phi statements are allowed. + Otherwise, returns nullptr. */ + +gimple * +crc_optimization::find_shift_after_xor (tree xored_crc) +{ + imm_use_iterator imm_iter; + use_operand_p use_p; + + gcc_assert (TREE_CODE (xored_crc) == SSA_NAME); + + unsigned v = SSA_NAME_VERSION (xored_crc); + if (bitmap_bit_p (m_visited_stmts, v)) + return nullptr; + bitmap_set_bit (m_visited_stmts, v); + + /* Iterate through the immediate uses of the XORED_CRC. + If there is a shift return true, + if before shift there is other instruction (besides phi) return false. */ + FOR_EACH_IMM_USE_FAST (use_p, imm_iter, xored_crc) + { + gimple *stmt = USE_STMT (use_p); + // Consider only statements within the loop + if (!flow_bb_inside_loop_p (m_crc_loop, gimple_bb (stmt))) + continue; + + /* If encountered phi statement, check immediate use of its result. + Otherwise, if encountered assign statement, check whether it does shift + (some other operations are allowed to be between shift and xor). */ + if (gimple_code (stmt) == GIMPLE_PHI) + { + /* Don't continue searching if encountered the loop's beginning. */ + if (bb_loop_header_p (gimple_bb (stmt))) + continue; + + return find_shift_after_xor (gimple_phi_result (stmt)); + } + else if (is_gimple_assign (stmt)) + { + /* Check that stmt does shift by 1. + There are no other statements between + xor and shift, in CRC cases we detect. */ + if (can_be_crc_shift (stmt)) + return stmt; + return nullptr; + } + else if (!is_gimple_debug (stmt)) + return nullptr; + } + return nullptr; +} + +/* Returns opposite block of the XOR_BB from PRED_BB's dest blocks. */ + +basic_block +crc_optimization::get_xor_bb_opposite (basic_block pred_bb, basic_block xor_bb) +{ + /* Check that the predecessor block has exactly two successors. */ + if (EDGE_COUNT (pred_bb->succs) != 2) + return nullptr; + + edge e0 = EDGE_SUCC (pred_bb, 0); + edge e1 = EDGE_SUCC (pred_bb, 1); + + /* Ensure neither outgoing edge is marked as complex. */ + if ((e0->flags & EDGE_COMPLEX) + || (e1->flags & EDGE_COMPLEX)) + return nullptr; + + /* Check that one of the successors is indeed XOR_BB. */ + gcc_assert ((e0->dest == xor_bb) + || (e1->dest == xor_bb)); + + /* Return the opposite block of XOR_BB. */ + if (EDGE_SUCC (pred_bb, 0)->dest != xor_bb) + return e0->dest; + return e1->dest; +} + +/* Checks whether the pair of xor's shift exists in the opposite + basic block (OPPOSITE_BB). + If there is a shift and xor in the same block, + then in the opposite block must be another shift. */ + +bool +crc_optimization::exists_shift_for_opp_xor_shift (basic_block opposite_bb) +{ + if (!opposite_bb) + return false; + + /* Walk through the instructions of given basic block. */ + for (gimple_stmt_iterator bsi = gsi_start_nondebug_bb (opposite_bb); + !gsi_end_p (bsi); gsi_next_nondebug (&bsi)) + { + gimple *stmt = gsi_stmt (bsi); + /* Find assignment statement with shift operation. + Check that shift's direction is same with the shift done + on the path with xor, and it is a shift by one. */ + if (is_gimple_assign (stmt)) + { + if (gimple_assign_rhs_code (stmt) + == gimple_assign_rhs_code (m_shift_stmt) + && integer_onep (gimple_assign_rhs2 (stmt))) + return true; + } + } + /* If there is no shift, return false. */ + return false; +} + +/* Returns true if condition COND checks MSB/LSB bit is 1. + Otherwise, return false. */ + +bool +crc_optimization::cond_true_is_checked_for_bit_one (const gcond *cond) +{ + if (!cond) + return false; + + tree rhs = gimple_cond_rhs (cond); + enum tree_code code = gimple_cond_code (cond); + + /* If the condition is something == 1 -> return true. */ + if (code == EQ_EXPR && integer_onep (rhs)) + return true; + + /* If the condition is something != 0 or something < 0 -> return true. */ + if ((code == NE_EXPR || code == LT_EXPR) + && integer_zerop (rhs)) + return true; + + return false; +} + +/* Returns true if xor is done in case the MSB/LSB is 1. + Otherwise, returns false. + In CRC calculation algorithms CRC is xor-ed with the polynomial only + if MSB/LSB is 1. + + PRED_BB is the block containing the condition for the xor. + XOR_BB is the one of the successor blocks of PRED_BB, it is assumed that CRC + is xor-ed with the polynomial in XOR_BB. + COND is the condition, which is checked to satisfy the CRC condition. */ + +bool +crc_optimization::is_crc_satisfiable_cond (basic_block pred_bb, + basic_block xor_bb, + gcond *cond) +{ + edge true_edge; + edge false_edge; + extract_true_false_edges_from_block (pred_bb, &true_edge, &false_edge); + bool cond_is_checked_for_bit_one = cond_true_is_checked_for_bit_one (cond); + /* Check that xor is done in case the MSB/LSB is 1. */ + if (cond_is_checked_for_bit_one && true_edge->dest == xor_bb) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Xor is done on true branch.\n"); + } + else if (!cond_is_checked_for_bit_one && false_edge->dest == xor_bb) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Xor is done on false branch.\n"); + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "Xor is done if MSB/LSB is not one, not CRC.\n"); + return false; + } + return true; +} + +/* Checks that the variable used in the condition COND is the assumed CRC + (or depends on the assumed CRC). + Also sets data member m_phi_for_data if it isn't set and exists. */ + +bool +crc_optimization::is_crc_checked (gcond *cond) +{ + tree lhs = gimple_cond_lhs (cond); + + /* As conditions are in canonical form, only left part must be an + SSA_NAME. */ + if (TREE_CODE (lhs) == SSA_NAME) + { + /* Return whether there is a dependence between if condition's variable + and xor-ed variable. Also set phi statement of data if it is not + determined earlier and is used in the loop. */ + auto_vec cond_dep_stmts (m_crc_loop->num_nodes); + bool set_defs_succeeded = set_defs (lhs, cond_dep_stmts, true); + bitmap_clear (m_visited_stmts); + if (!set_defs_succeeded) + return false; + return cond_depends_on_crc (cond_dep_stmts); + } + + /* Return false if there is no dependence between if condition's variable + and xor-ed variable. */ + return false; +} + +/* Checks that the condition is for checking CRC. + Returns true if xor is done under the condition of MSB/LSB being 1, and + the condition's variable and xor-ed variable depend on the same variable. + Otherwise, returns false. + XOR_BB is the basic block, where the xor operation is done. + PRED_BB is the predecessor basic block of the XOR_BB, it is assumed that + the last stmt of PRED_BB checks the condition under which xor is done. */ + +bool +crc_optimization::crc_cond (basic_block pred_bb, basic_block xor_bb) +{ + /* Check whether PRED_BB contains condition. We will consider only those + cases when xor is done immediately under the condition. */ + gcond *cond = safe_dyn_cast (gsi_stmt (gsi_last_bb (pred_bb))); + if (!cond) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "No condition.\n"); + return false; + } + + /* Check that xor is done in case the MSB/LSB is 1. */ + if (!is_crc_satisfiable_cond (pred_bb, xor_bb, cond)) + return false; + + /* Check that CRC's MSB/LSB is checked in the condition. + Set data member if not set and exists. */ + if (!is_crc_checked (cond)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "The condition is not related to the CRC check.\n"); + return false; + } + return true; +} + +/* Returns true if the statement with STMT_CODE may occur in CRC calculation. + Otherwise, returns false. */ + +bool +crc_optimization::is_acceptable_stmt_code (const tree_code &stmt_code) +{ + return (stmt_code == BIT_IOR_EXPR) + || (stmt_code == BIT_AND_EXPR) + || (stmt_code == BIT_XOR_EXPR) + || (stmt_code == MINUS_EXPR) + || (stmt_code == PLUS_EXPR) + || (stmt_code == RSHIFT_EXPR) + || (stmt_code == LSHIFT_EXPR) + || (TREE_CODE_CLASS (stmt_code) == tcc_unary); +} + +/* Returns true if ASSIGN_STMT does shift with 1. Otherwise, returns false. */ + +bool +crc_optimization::can_be_crc_shift (gimple *assign_stmt) +{ + tree_code stmt_code = gimple_assign_rhs_code (assign_stmt); + if (stmt_code == LSHIFT_EXPR || stmt_code == RSHIFT_EXPR) + { + m_is_bit_forward = (stmt_code == LSHIFT_EXPR); + /* Check that shift one is done, keep shift statement. */ + if (integer_onep (gimple_assign_rhs2 (assign_stmt))) + { + if (m_shift_stmt) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "Already there is one shift.\n"); + return false; + } + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "Found <<1 or >>1.\n"); + return true; + } + /* This filters out cases, when xor-ed variable is shifted by values + other than 1. */ + } + return false; +} + +/* Returns true if the operation done in ASSIGN_STMT can occur during CRC + calculation. Otherwise, returns false. */ + +bool +crc_optimization::can_not_be_crc_stmt (gimple *assign_stmt) +{ + tree_code stmt_code = gimple_assign_rhs_code (assign_stmt); + if (!is_acceptable_stmt_code (stmt_code)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "\nStmt with the following operation " + "code %s between xor and shift, " + "may not be CRC.\n", get_tree_code_name (stmt_code)); + + return true; + } + return false; +} + +/* Returns true if we can get definition of the VARIABLE, and the definition + is not outside the loop. Otherwise, returns false. */ + +bool +crc_optimization::passes_checks_for_def_chain (tree variable) +{ + if (!(variable && TREE_CODE (variable) == SSA_NAME)) + return false; + + /* No definition chain for default defs. */ + if (SSA_NAME_IS_DEFAULT_DEF (variable)) + return false; + + gimple *stmt = SSA_NAME_DEF_STMT (variable); + + if (!stmt) + return false; + + /* Don't go outside the loop. */ + if (!flow_bb_inside_loop_p (m_crc_loop, gimple_bb (stmt))) + return false; + + return true; +} + +/* This function goes up through the def-use chains of the parameter NAME. + Gathers all the statements within the loop, + from which the variable depends on and adds to the USE_DEFS. + Returns false, if there is a statement that may not exist in the CRC + loop. Otherwise, returns true. */ + +bool +crc_optimization::set_defs (tree name, auto_vec &use_defs, + bool keep_only_header_phis = false) +{ + if (!passes_checks_for_def_chain (name)) + return true; + + /* Don't consider already visited names. */ + unsigned v = SSA_NAME_VERSION (name); + if (bitmap_bit_p (m_visited_stmts, v)) + return true; + bitmap_set_bit (m_visited_stmts, v); + + /* In CRC implementations with constant polynomial maximum 12 use_def + statements may occur. This limit is based on an analysis of various CRC + implementations as well as theoretical possibilities. + TODO: Find a better solution. */ + if (use_defs.length () > 12) + return false; + + gimple *stmt = SSA_NAME_DEF_STMT (name); + + /* If it's not specified to keep only header phi's, + then keep all statements. */ + if (!keep_only_header_phis) + use_defs.safe_push (stmt); + + /* If it is an assignment statement, + get and check def-use chain for the first and second operands. */ + if (is_a (stmt)) + { + if (can_not_be_crc_stmt (stmt)) + return false; + + tree ssa1 = gimple_assign_rhs1 (stmt); + tree ssa2 = gimple_assign_rhs2 (stmt); + if (!set_defs (ssa1, use_defs, keep_only_header_phis)) + return false; + if (!set_defs (ssa2, use_defs, keep_only_header_phis)) + return false; + return true; + } + /* If it's a phi statement, not declared in loop's header, + get and check def-use chain for its values. For the one declared in loop's + header just return true and keep it, if keep_only_header_phis is true. */ + else if (is_a (stmt)) + { + if (bb_loop_header_p (gimple_bb (stmt))) + { + /* If it's specified to keep only header phi's, keep it. */ + if (keep_only_header_phis) + use_defs.safe_push (stmt); + } + else + { + for (unsigned i = 0; i < gimple_phi_num_args (stmt); i++) + { + tree val = gimple_phi_arg_def (stmt, i); + if (!set_defs (val, use_defs, keep_only_header_phis)) + return false; + } + } + return true; + } + + /* Return false for other than assigment and phi statement. */ + return false; +} + +/* Returns the statement which does shift 1 operation. + If there is no such statement, returns nullptr. + STMTS - are the statements within the loop before xor operation on + which possible CRC variable depends. */ + +gimple * +crc_optimization::find_shift_before_xor (const auto_vec &stmts) +{ + for (auto stmt_it = stmts.begin (); stmt_it != stmts.end (); stmt_it++) + { + /* If it is an assignment statement, check that is does shift 1. */ + if (is_a (*stmt_it)) + { + if (can_be_crc_shift (*stmt_it)) + return *stmt_it; + } + } + return nullptr; +} + +/* This function sets M_PHI_FOR_CRC and M_PHI_FOR_DATA fields. + At this step phi nodes for CRC and data may be mixed in places. + It is fixed later with the "swap_crc_and_data_if_needed" function. + The function returns false if there are more than two (as in CRC calculation + only CRC's and data's phi may exist) or no phi statements in STMTS (at least + there must be CRC's phi). + Otherwise, returns true. */ + +bool +crc_optimization::set_crc_and_data_phi (auto_vec &stmts) +{ + for (auto stmt_it = stmts.begin (); stmt_it != stmts.end (); stmt_it++) + { + if (is_a (*stmt_it) && bb_loop_header_p (gimple_bb (*stmt_it))) + { + if (!m_phi_for_crc) + m_phi_for_crc = as_a (*stmt_it); + else if (!m_phi_for_data) + m_phi_for_data = as_a (*stmt_it); + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Xor-ed variable depends on more than 2 " + "phis.\n"); + return false; + } + } + } + return m_phi_for_crc; +} + +/* Returns true if the variable checked in the condition depends on possible + CRC value. Otherwise, returns false. */ + +bool +crc_optimization::cond_depends_on_crc (auto_vec& stmts) +{ + bool con_depends_on_crc = false; + + /* The condition may depend maximum on data and CRC phi's. */ + if (stmts.length () > 2) + return false; + + for (auto stmt_it = stmts.begin (); stmt_it != stmts.end (); stmt_it++) + { + if (is_a (*stmt_it) && bb_loop_header_p (gimple_bb (*stmt_it))) + { + /* Check whether variable checked in the condition depends on + M_PHI_FOR_CRC. + Here don't stop the check, to set data if needed. */ + if (m_phi_for_crc == (*stmt_it)) + con_depends_on_crc = true; + else if (m_phi_for_data && m_phi_for_data == (*stmt_it)) + return true; + /* If M_PHI_FOR_DATA isn't determined, the phi statement maybe for the + data. Just set it. */ + else if (!m_phi_for_data) + m_phi_for_data = as_a (*stmt_it); + } + } + return con_depends_on_crc; +} + +/* Sets initial values for the CRC analysis. + This function is used multiple times during the analyses. */ + +void +crc_optimization::set_initial_values () +{ + m_shift_stmt = nullptr; + m_phi_for_crc = nullptr; + m_phi_for_data = nullptr; + m_is_bit_forward = false; +} + +/* This function checks if the XOR_STMT is used for CRC calculation. + It verifies the presence of a shift operation in the CRC_FUN function inside + the CRC loop. It examines operands of XOR, its dependencies, the relative + position of the shift operation, and the existence of a shift operation in + the opposite branch of conditional statements. It also checks if XOR is + performed when MSB/LSB is one. + If these conditions are met, the XOR operation may be part of a CRC + calculation. The function returns true if these conditions are fulfilled, + otherwise, it returns false. */ + +bool +crc_optimization::xor_calculates_crc (function *crc_fun, + const gimple *xor_stmt) +{ + tree crc_var = gimple_assign_lhs (xor_stmt); + set_initial_values (); + tree ssa1 = gimple_assign_rhs1 (xor_stmt); + tree ssa2 = gimple_assign_rhs2 (xor_stmt); + if (TREE_CODE (ssa2) != INTEGER_CST) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Second operand of the " + "xor statement isn't an integer constant.\n"); + return false; + } + + /* Get the statements within the loop on which xor-ed variable depends. */ + auto_vec xor_dep_stmts (m_crc_loop->num_nodes); + bool set_defs_succeeded = set_defs (ssa1, xor_dep_stmts); + bitmap_clear (m_visited_stmts); + if (!set_defs_succeeded) + { + xor_dep_stmts.release (); + return false; + } + + m_shift_stmt = find_shift_before_xor (xor_dep_stmts); + + if (!set_crc_and_data_phi (xor_dep_stmts)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Xor isn't used for CRC calculation.\n"); + return false; + } + + /* Check the case when shift is done after xor. */ + if (!m_shift_stmt) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "No shift before xor, trying to find after xor.\n"); + + m_shift_stmt = find_shift_after_xor (crc_var); + bitmap_clear (m_visited_stmts); + if (!m_shift_stmt) + return false; + } + + /* Get the basic block where xor operation is done. */ + basic_block xor_bb = gimple_bb (xor_stmt); + + /* Get the predecessor basic block of xor's block. */ + if (!single_pred_p (xor_bb)) + return false; + basic_block block_of_condition = single_pred (xor_bb); + + + /* If the found shift operation is in the same block as the xor operation, + verify whether another shift exists in the opposite branch of the + conditional statements. */ + if (m_shift_stmt && gimple_bb (m_shift_stmt) == xor_bb) + { + basic_block opposite_block = get_xor_bb_opposite (block_of_condition, + xor_bb); + if (!exists_shift_for_opp_xor_shift (opposite_block)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "Opposite block doesn't contain shift's pair.\n"); + return false; + } + } + + /* Check that xor is done if MSB/LSB is one. + If all checks succeed, then it may be a CRC. */ + if (crc_cond (block_of_condition, xor_bb)) + { + if (dump_file) + fprintf (dump_file, + "\n%s function maybe contains CRC calculation.\n", + function_name (crc_fun)); + return true; + } + + return false; +} + +/* Returns true if there is only two conditional blocks in the loop, + one may be for the CRC bit check and the other for the loop counter. + This may filter out some real CRCs, where more than one condition + is checked for the CRC calculation and branch-less CRCs. */ + +bool +crc_optimization::loop_contains_two_conditional_bb (basic_block *loop_bbs, + unsigned loop_num_nodes) +{ + unsigned conditional_bb_count = 0; + /* Iterate through the loop until the conditional branches count + is below 3. */ + for (unsigned i = 0; i < loop_num_nodes && conditional_bb_count <= 2; i++) + { + basic_block bb = loop_bbs[i]; + if (!single_succ_p (bb)) + conditional_bb_count++; + } + return conditional_bb_count == 2; +} + +/* Checks the FUNC_LOOP loop's iteration number. + The loop for CRC calculation may do 8, 16, 24, 32, 64 iterations. */ + +bool +crc_optimization::satisfies_crc_loop_iteration_count (class loop *func_loop) +{ + /* Calculate the number of times the latch of the loop is executed. + The function sets NB_ITERATIONS field of the loop. */ + number_of_latch_executions (func_loop); + tree n_inters = func_loop->nb_iterations; + if (n_inters == NULL_TREE || n_inters == chrec_dont_know) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "Loop iteration number is chrec_dont_know.\n"); + return false; + + } + else if (tree_fits_uhwi_p (n_inters)) + { + unsigned HOST_WIDE_INT + loop_iteration_number = tree_to_uhwi (n_inters); + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Loop iteration number is " + HOST_WIDE_INT_PRINT_UNSIGNED ".\n", loop_iteration_number); + + if ((loop_iteration_number == 7 || loop_iteration_number == 15 + || loop_iteration_number == 23 || loop_iteration_number == 31 + || loop_iteration_number == 63)) + return true; + } + if (stderr && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Loop iteration number isn't a constant.\n"); + return false; +} + +/* This is the main function that checks whether the given LOOP + calculates CRC and extracts details of the CRC calculation. + + The main idea is to find the innermost loop with 8, 16, 24, 32, 64 + iterations and xor instruction (xor is the key operation for naive CRC + calculation). Then, checks that the variable is shifted by one before/after + being used in xor. + Xor must be done under the condition of MSB/LSB being 1. */ + +bool +crc_optimization::loop_may_calculate_crc (class loop *loop) +{ + /* Only examine innermost loops. */ + if (!loop || loop->inner) + return false; + + if (!satisfies_crc_loop_iteration_count (loop)) + return false; + + m_crc_loop = loop; + basic_block *loop_bbs = get_loop_body_in_dom_order (m_crc_loop); + + /* Filter out the cases, which don't have exactly two conditions in the loop. + One for the CRC bit check, the other for the loop counter. */ + if (!loop_contains_two_conditional_bb (loop_bbs, m_crc_loop->num_nodes)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "The number of conditional " + "branches in the loop isn't 2.\n"); + return false; + } + + unsigned short checked_xor_count = 0; + /* Walk bbs of the loop. */ + for (unsigned int i = 0; i < m_crc_loop->num_nodes; i++) + { + basic_block bb = loop_bbs[i]; + /* Walk instructions of the bb. */ + for (gimple_stmt_iterator bsi = gsi_start_nondebug_bb (bb); + !gsi_end_p (bsi); gsi_next_nondebug (&bsi)) + { + gimple *stmt = gsi_stmt (bsi); + /* If there is an xor instruction, + check that it is calculating CRC. */ + if (is_gimple_assign (stmt) + && gimple_assign_rhs_code (stmt) == BIT_XOR_EXPR) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "Found xor, " + "checking whether it is for CRC calculation.\n"); + + if (xor_calculates_crc (cfun, stmt)) + { + dump_crc_information (); + free (loop_bbs); + return true; + } + + if (++checked_xor_count == 2) + return false; + } + } + } + free (loop_bbs); + return false; +} + +unsigned int +crc_optimization::execute (function *fun) +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\nExamining %s function.\n", + function_name (fun)); + + if (number_of_loops (fun) <= 1) + return 0; + + /* Get loops of the function. */ + auto loop_list = loops_list (fun, LI_ONLY_INNERMOST); + for (auto loop: loop_list) + { + /* Perform initial checks to filter out non-CRCs. */ + loop_may_calculate_crc (loop); + } + return 0; +} + +namespace +{ + + const pass_data pass_data_crc_optimization + = { + GIMPLE_PASS, /* type */ + "crc", /* name */ + OPTGROUP_NONE, /* optinfo_flags */ + TV_GIMPLE_CRC_OPTIMIZATION, /* tv_id */ + (PROP_cfg | PROP_ssa), /* properties_required */ + 0, /* properties_provided */ + 0, /* properties_destroyed */ + 0, /* todo_flags_start */ + 0, /* todo_flags_finish */ + }; + + class pass_crc_optimization : public gimple_opt_pass { + public: + pass_crc_optimization (gcc::context *ctxt) + : gimple_opt_pass (pass_data_crc_optimization, ctxt) + {} + + /* opt_pass methods: */ + virtual bool gate (function *) + { + return flag_optimize_crc; + } + + virtual unsigned int execute (function *); + + }; // class pass_crc_optimization + + unsigned int + pass_crc_optimization::execute (function *fun) + { + return crc_optimization ().execute (fun); + } + +} // anon namespace + +gimple_opt_pass * +make_pass_crc_optimization (gcc::context *ctxt) +{ + return new pass_crc_optimization (ctxt); +} diff --git a/gcc/opts.cc b/gcc/opts.cc index fc6abf6f582..66ba8773bab 100644 --- a/gcc/opts.cc +++ b/gcc/opts.cc @@ -666,6 +666,7 @@ static const struct default_options default_options_table[] = VECT_COST_MODEL_VERY_CHEAP }, { OPT_LEVELS_2_PLUS, OPT_finline_functions, NULL, 1 }, { OPT_LEVELS_2_PLUS, OPT_ftree_loop_distribute_patterns, NULL, 1 }, + { OPT_LEVELS_2_PLUS, OPT_foptimize_crc, NULL, 1 }, { OPT_LEVELS_2_PLUS, OPT_flate_combine_instructions, NULL, 1 }, /* -O2 and above optimizations, but not -Os or -Og. */ @@ -2097,6 +2098,7 @@ enable_fdo_optimizations (struct gcc_options *opts, SET_OPTION_IF_UNSET (opts, opts_set, flag_loop_interchange, value); SET_OPTION_IF_UNSET (opts, opts_set, flag_unroll_jam, value); SET_OPTION_IF_UNSET (opts, opts_set, flag_tree_loop_distribution, value); + SET_OPTION_IF_UNSET (opts, opts_set, flag_optimize_crc, value); } /* -f{,no-}sanitize{,-recover}= suboptions. */ diff --git a/gcc/passes.def b/gcc/passes.def index 40162ac20a0..6c5dd46edc7 100644 --- a/gcc/passes.def +++ b/gcc/passes.def @@ -294,6 +294,7 @@ along with GCC; see the file COPYING3. If not see NEXT_PASS (pass_cd_dce, false /* update_address_taken_p */); NEXT_PASS (pass_iv_canon); NEXT_PASS (pass_loop_distribution); + NEXT_PASS (pass_crc_optimization); NEXT_PASS (pass_linterchange); NEXT_PASS (pass_copy_prop); NEXT_PASS (pass_graphite); diff --git a/gcc/timevar.def b/gcc/timevar.def index 0f9d2c0b032..37460def292 100644 --- a/gcc/timevar.def +++ b/gcc/timevar.def @@ -313,6 +313,7 @@ DEFTIMEVAR (TV_INITIALIZE_RTL , "initialize rtl") DEFTIMEVAR (TV_GIMPLE_LADDRESS , "address lowering") DEFTIMEVAR (TV_TREE_LOOP_IFCVT , "tree loop if-conversion") DEFTIMEVAR (TV_WARN_ACCESS , "access analysis") +DEFTIMEVAR (TV_GIMPLE_CRC_OPTIMIZATION, "crc optimization") /* Everything else in rest_of_compilation not included above. */ DEFTIMEVAR (TV_EARLY_LOCAL , "early local passes") diff --git a/gcc/tree-pass.h b/gcc/tree-pass.h index a928cbe4557..7c778f600b5 100644 --- a/gcc/tree-pass.h +++ b/gcc/tree-pass.h @@ -388,6 +388,7 @@ extern gimple_opt_pass *make_pass_graphite_transforms (gcc::context *ctxt); extern gimple_opt_pass *make_pass_if_conversion (gcc::context *ctxt); extern gimple_opt_pass *make_pass_if_to_switch (gcc::context *ctxt); extern gimple_opt_pass *make_pass_loop_distribution (gcc::context *ctxt); +extern gimple_opt_pass *make_pass_crc_optimization (gcc::context *ctxt); extern gimple_opt_pass *make_pass_vectorize (gcc::context *ctxt); extern gimple_opt_pass *make_pass_simduid_cleanup (gcc::context *ctxt); extern gimple_opt_pass *make_pass_slp_vectorize (gcc::context *ctxt); -- 2.25.1 From patchwork Fri Oct 18 15:01:42 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mariam Arutunian X-Patchwork-Id: 1999258 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=jTWkrOR7; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XVSg95JJkz1xw2 for ; Sat, 19 Oct 2024 02:05:09 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E88333857C7B for ; Fri, 18 Oct 2024 15:05:07 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-lj1-x22d.google.com (mail-lj1-x22d.google.com [IPv6:2a00:1450:4864:20::22d]) by sourceware.org (Postfix) with ESMTPS id 290FA3858023 for ; Fri, 18 Oct 2024 15:01:56 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 290FA3858023 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 290FA3858023 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::22d ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263725; cv=none; b=xURaIoL89rGLIcU/YkwFJCR0gtDca6l5BCDQkW/NtZs/qBipdEPvJuSpVEtvu5vM3a5rXzhnKw6+ybhz/atFdxS3ToCno4uHSRoBE43aajlrrypScpaZt9EyfTQY/tsYRQcLKfgpp9gqfRrgbUtCfHIvf1OixoTT583+7sJWaIQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263725; c=relaxed/simple; bh=biTWkxg59JuUfyP0YwX0kNApv/7GwyqgXN6r3jbTw7o=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=O1Dw+3mFUwm06m0RAcNjXXZYCK6RDdlVgD+HIWOnVchG7sFRY+5I81m4yvZ3JcSm1xRkKZZnqL0K54BPLnX0R/PLq8cbaux0tFbaLlZ0poODDjeZojv3kI7RqH6Qhv6xKMm7EMNYXlsNVP+EKhgkkf9xm7PCcOSLquAihHEl8OI= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-lj1-x22d.google.com with SMTP id 38308e7fff4ca-2fb5014e2daso25038651fa.0 for ; Fri, 18 Oct 2024 08:01:56 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1729263714; x=1729868514; darn=gcc.gnu.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=tLseK28hMiNOGdRQ5A0FELIX+vkYe6d9v+/Fq2sUsZ0=; b=jTWkrOR7zoql4zPvon860MdR6OLw1RJufDIXAUIBuvI9V6Z3Wl/ZK6R+kSn21YTFix ithPGPm3H6M9f0QZgwyMlrFiHc5YCUXTVysKDNc2SJ7v1xEEgzwgd/epHLlaAfnIo++7 8xkpFOrxSZuUaIjJuek1qffsvjujY0el/IJ8uRqy9Zz1GjP03ZII9AYUKwEDsRfO7gZ4 e6hZSPDwLZng0I+0oXEFFDTzpUfYux4XH5RH3sgGtSKoTEH+UDt9cfb1PzihRxHKFUSH u3LaMqmlACB4d+eCo9Y/Nc3SuyR7WaJBrQZGbgzpX0VCV7x0SllMp1IEiadmbQ44hk9Y 3Flg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729263714; x=1729868514; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=tLseK28hMiNOGdRQ5A0FELIX+vkYe6d9v+/Fq2sUsZ0=; b=lJcJeIu1Jy52IVWFyMCLvDWWsaAwTj8ZXSpZVmY7vDg2HluUCv3g/FTISk8veJ1fnh d6RvGrDG3n9md7qG9U0CDIunPwvzvYIEaKe0IwmjOYkGDPObzoL63nIIk29+XvMdKARi SFXLWtKvNkmOo4tgsgMzbDTVSCciGVJXeHXATzQKlgk1TTLumoJnfcrcjVhM0JIS/cbG vecdD83bgpK1Vz9Cy4Ha3KELvY+zTnMIEWhrKFr77bKY6NQXB57DMTRN7p6aqNg3FFz7 wuLOfOgrzxNol2jTOv6IrAN9MVQt03gEBvwyvgU09m/4lmZzgxtprfSAn8DsQgfi0xnD orwg== X-Gm-Message-State: AOJu0YyL+k/f3t8NOxYUFKf8pVctUfPrYp6DoR4pJWpNQdkU1krs6LvB /yozrlAVymor6hYNOhp8uB/iilj4R1rFN2/orxd1W0ztit3xpeGIj2BLrMfi1t+b8+HY/tRawTQ D11uUGU7oBByil13GUX0xoercB4vwAKdN1O4= X-Google-Smtp-Source: AGHT+IEk6PynjaEuQyBtnewzSXu1Fr+QlZ3DJOvUBfBrsZStkxX842t0LxleYizrzfiq7bdkmR1DL2/1KPL+/MgX3D8= X-Received: by 2002:a05:651c:2109:b0:2fb:61c0:103 with SMTP id 38308e7fff4ca-2fb82e908fdmr17377411fa.4.1729263714041; Fri, 18 Oct 2024 08:01:54 -0700 (PDT) MIME-Version: 1.0 From: Mariam Arutunian Date: Fri, 18 Oct 2024 19:01:42 +0400 Message-ID: Subject: [RFC/RFA] [PATCH v5 09/12] Add symbolic execution support. To: GCC Patches , Jeff Law X-Spam-Status: No, score=-6.8 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, HTML_MESSAGE, KAM_STOCKGEN, RCVD_IN_DNSWL_NONE, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org Gives an opportunity to execute the code on bit level, assigning symbolic values to the variables which don't have initial values. Supports only CRC specific operations. Example: uint8_t crc; uint8_t pol = 1; crc = crc ^ pol; during symbolic execution crc's value will be: crc(8), crc(7), ... crc(1), crc(0) ^ 1 gcc/ * Makefile.in (OBJS): Add sym-exec/sym-exec-expression.o, sym-exec/sym-exec-state.o, sym-exec/sym-exec-condition.o. * configure (sym-exec): New subdir. gcc/sym-exec/ * sym-exec-condition.cc: New file. * sym-exec-condition.h: New file. * sym-exec-expression-is-a-helper.h: New file. * sym-exec-expression.cc: New file. * sym-exec-expression.h: New file. * sym-exec-state.cc: New file. * sym-exec-state.h: New file. Signed-off-by: Mariam Arutunian Author: Matevos Mehrabyan Co-authored-by: Mariam Arutunian Mentored-by: Jeff Law --- gcc/Makefile.in | 3 + gcc/configure | 2 +- gcc/sym-exec/sym-exec-condition.cc | 59 + gcc/sym-exec/sym-exec-condition.h | 33 + gcc/sym-exec/sym-exec-expr-is-a-helper.h | 204 ++ gcc/sym-exec/sym-exec-expression.cc | 426 +++++ gcc/sym-exec/sym-exec-expression.h | 260 +++ gcc/sym-exec/sym-exec-state.cc | 2148 ++++++++++++++++++++++ gcc/sym-exec/sym-exec-state.h | 436 +++++ 9 files changed, 3570 insertions(+), 1 deletion(-) create mode 100644 gcc/sym-exec/sym-exec-condition.cc create mode 100644 gcc/sym-exec/sym-exec-condition.h create mode 100644 gcc/sym-exec/sym-exec-expr-is-a-helper.h create mode 100644 gcc/sym-exec/sym-exec-expression.cc create mode 100644 gcc/sym-exec/sym-exec-expression.h create mode 100644 gcc/sym-exec/sym-exec-state.cc create mode 100644 gcc/sym-exec/sym-exec-state.h diff --git a/gcc/Makefile.in b/gcc/Makefile.in index a7054eda9c5..6eab34d62bb 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1718,6 +1718,9 @@ OBJS = \ tree-logical-location.o \ tree-loop-distribution.o \ gimple-crc-optimization.o \ + sym-exec/sym-exec-expression.o \ + sym-exec/sym-exec-state.o \ + sym-exec/sym-exec-condition.o \ tree-nested.o \ tree-nrv.o \ tree-object-size.o \ diff --git a/gcc/configure b/gcc/configure index 3d301b6ecd3..c781f4c24b6 100755 --- a/gcc/configure +++ b/gcc/configure @@ -36232,7 +36232,7 @@ $as_echo "$as_me: executing $ac_file commands" >&6;} "depdir":C) $SHELL $ac_aux_dir/mkinstalldirs $DEPDIR ;; "gccdepdir":C) ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs build/$DEPDIR - for lang in $subdirs c-family common analyzer text-art rtl-ssa + for lang in $subdirs c-family common analyzer text-art rtl-ssa sym-exec do ${CONFIG_SHELL-/bin/sh} $ac_aux_dir/mkinstalldirs $lang/$DEPDIR done ;; diff --git a/gcc/sym-exec/sym-exec-condition.cc b/gcc/sym-exec/sym-exec-condition.cc new file mode 100644 index 00000000000..ef3f1e3fda5 --- /dev/null +++ b/gcc/sym-exec/sym-exec-condition.cc @@ -0,0 +1,59 @@ +#include "sym-exec-condition.h" + +bit_condition::bit_condition (value_bit *left, value_bit *right, tree_code code) +{ + this->m_left = left; + this->m_right = right; + this->m_code = code; + m_type = BIT_CONDITION; +} + + +bit_condition::bit_condition (const bit_condition &expr) +{ + bit_expression::copy (&expr); + m_code = expr.get_code (); +} + + +/* Returns the condition's code. */ + +tree_code +bit_condition::get_code () const +{ + return m_code; +} + + +/* Returns a copy of the condition. */ + +value_bit * +bit_condition::copy () const +{ + return new bit_condition (*this); +} + + +/* Prints the condition's sign. */ + +void +bit_condition::print_expr_sign () +{ + switch (m_code) + { + case GT_EXPR: + fprintf (dump_file, " > "); + break; + case LT_EXPR: + fprintf (dump_file, " < "); + break; + case EQ_EXPR: + fprintf (dump_file, " == "); + break; + case NE_EXPR: + fprintf (dump_file, " != "); + break; + default: + fprintf (dump_file, " ? "); + } +} \ No newline at end of file diff --git a/gcc/sym-exec/sym-exec-condition.h b/gcc/sym-exec/sym-exec-condition.h new file mode 100644 index 00000000000..1d9d59512bb --- /dev/null +++ b/gcc/sym-exec/sym-exec-condition.h @@ -0,0 +1,33 @@ +#ifndef SYM_EXEC_CONDITION_H +#define SYM_EXEC_CONDITION_H + +#include "sym-exec-expression.h" + +enum condition_status { + CS_NO_COND, + CS_TRUE, + CS_FALSE, + CS_SYM +}; + + +class bit_condition : public bit_expression { + private: + /* Condition's code. */ + tree_code m_code; + + /* Prints the condition's sign. */ + void print_expr_sign (); + + public: + bit_condition (value_bit *left, value_bit *right, tree_code type); + bit_condition (const bit_condition &expr); + + /* Returns the condition's code. */ + tree_code get_code () const; + + /* Returns a copy of the condition. */ + value_bit *copy () const; +}; + +#endif /* SYM_EXEC_CONDITION_H. */ \ No newline at end of file diff --git a/gcc/sym-exec/sym-exec-expr-is-a-helper.h b/gcc/sym-exec/sym-exec-expr-is-a-helper.h new file mode 100644 index 00000000000..369213e5146 --- /dev/null +++ b/gcc/sym-exec/sym-exec-expr-is-a-helper.h @@ -0,0 +1,204 @@ +#ifndef SYM_EXEC_EXPRESSION_IS_A_HELPER_H +#define SYM_EXEC_EXPRESSION_IS_A_HELPER_H + +#include "sym-exec-condition.h" + +/* Defining test functions for value conversion via dyn_cast. */ + +template<> +template<> +inline bool +is_a_helper::test (value_bit *ptr) +{ + return ptr->get_type () == value_type::SYMBOLIC_BIT; +} + + +template<> +template<> +inline bool +is_a_helper::test (value_bit *ptr) +{ + return ptr->get_type () == value_type::BIT; +} + + +template<> +template<> +inline bool +is_a_helper::test (value_bit *ptr) +{ + value_type type = ptr->get_type (); + return type == value_type::BIT_AND_EXPRESSION + || type == value_type::BIT_OR_EXPRESSION + || type == value_type::BIT_XOR_EXPRESSION + || type == value_type::BIT_COMPLEMENT_EXPRESSION + || type == value_type::SHIFT_RIGHT_EXPRESSION + || type == value_type::SHIFT_LEFT_EXPRESSION + || type == value_type::ADD_EXPRESSION + || type == value_type::SUB_EXPRESSION + || type == value_type::BIT_CONDITION; +} + + +template<> +template<> +inline bool +is_a_helper::test (value_bit *ptr) +{ + return ptr->get_type () == value_type::BIT_AND_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (value_bit *ptr) +{ + return ptr->get_type () == value_type::BIT_OR_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (value_bit *ptr) +{ + return ptr->get_type () == value_type::BIT_XOR_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (value_bit *ptr) +{ + return ptr->get_type () == value_type::BIT_COMPLEMENT_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (value_bit *ptr) +{ + return ptr->get_type () == value_type::SHIFT_LEFT_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (value_bit *ptr) +{ + return ptr->get_type () == value_type::SHIFT_RIGHT_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (value_bit *ptr) +{ + return ptr->get_type () == value_type::ADD_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (value_bit *ptr) +{ + return ptr->get_type () == value_type::SUB_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (value_bit *ptr) +{ + return ptr->get_type () == value_type::BIT_CONDITION; +} + + +template<> +template<> +inline bool +is_a_helper::test (bit_expression *ptr) +{ + return ptr->get_type () == value_type::BIT_AND_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (bit_expression *ptr) +{ + return ptr->get_type () == value_type::BIT_OR_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (bit_expression *ptr) +{ + return ptr->get_type () == value_type::BIT_XOR_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (bit_expression *ptr) +{ + return ptr->get_type () == value_type::BIT_COMPLEMENT_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (bit_expression *ptr) +{ + return ptr->get_type () == value_type::SHIFT_LEFT_EXPRESSION; +} + +template<> +template<> +inline bool +is_a_helper::test (bit_expression *ptr) +{ + return ptr->get_type () == value_type::SHIFT_RIGHT_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (bit_expression *ptr) +{ + return ptr->get_type () == value_type::ADD_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (bit_expression *ptr) +{ + return ptr->get_type () == value_type::SUB_EXPRESSION; +} + + +template<> +template<> +inline bool +is_a_helper::test (bit_expression *ptr) +{ + return ptr->get_type () == value_type::BIT_CONDITION; +} + +#endif /* SYM_EXEC_EXPRESSION_IS_A_HELPER_H. */ \ No newline at end of file diff --git a/gcc/sym-exec/sym-exec-expression.cc b/gcc/sym-exec/sym-exec-expression.cc new file mode 100644 index 00000000000..d34ebce6d3c --- /dev/null +++ b/gcc/sym-exec/sym-exec-expression.cc @@ -0,0 +1,426 @@ +/* Every class defined here represents a single bit value of a variable. + Every variable will be represented as a vector of these classes which later + will be used for bit-level symbolic execution. */ + +#include "sym-exec-expr-is-a-helper.h" + +/* Returns type of the bit. */ + +value_type +value_bit::get_type () const +{ + return m_type; +} + + +symbolic_bit::symbolic_bit (size_t i, tree orig) + : value_bit (i), m_origin (orig) +{ + m_type = SYMBOLIC_BIT; +} + + +bit::bit (unsigned char i) : m_val (i) +{ + m_type = BIT; +} + + +/* Returns left operand of the expression. */ + +value_bit * +bit_expression::get_left () +{ + return m_left; +} + + +/* Returns right operand of the expression. */ + +value_bit * +bit_expression::get_right () +{ + return m_right; +} + + +/* Sets left operand of the expression. */ + +void +bit_expression::set_left (value_bit *expr) +{ + m_left = expr; +} + + +/* Sets right operand of the expression. */ + +void +bit_expression::set_right (value_bit *expr) +{ + m_right = expr; +} + + +/* Returns the bit's initial index in bit-vector. */ + +size_t +value_bit::get_index () const +{ + return m_index; +} + + +/* Returns the value of the bit. */ + +unsigned char +bit::get_val () const +{ + return m_val; +} + + +/* Sets the value of the bit. */ + +void +bit::set_val (unsigned char new_val) +{ + m_val = new_val; +} + + +bit_complement_expression::bit_complement_expression (value_bit *right) +{ + /* As complement has only one argument, we use only the m_right. */ + this->m_left = nullptr; + this->m_right = right; + m_type = BIT_COMPLEMENT_EXPRESSION; +} + + +bit_complement_expression::bit_complement_expression ( + const bit_complement_expression &expr) +{ + bit_expression::copy (&expr); +} + + +bit_expression::~bit_expression () +{ + delete m_left; + m_left = nullptr; + delete m_right; + m_right = nullptr; +} + + +/* Returns a copy of the bit. */ + +value_bit * +symbolic_bit::copy () const +{ + return new symbolic_bit (*this); +} + + +/* Return a copy of the bit. */ + +value_bit * +bit::copy () const +{ + return new bit (*this); +} + + +/* Copies the given expression to it by copying the left and right operands. */ + +void +bit_expression::copy (const bit_expression *expr) +{ + if (expr->m_left) + m_left = expr->m_left->copy (); + + if (expr->m_right) + m_right = expr->m_right->copy (); + + m_type = expr->m_type; +} + + +/* Returns a copy of the expression. */ + +value_bit * +bit_xor_expression::copy () const +{ + return new bit_xor_expression (*this); +} + + +/* Returns a copy of the expression. */ + +value_bit * +bit_and_expression::copy () const +{ + return new bit_and_expression (*this); +} + + +/* Returns a copy of the expression. */ + +value_bit * +bit_or_expression::copy () const +{ + return new bit_or_expression (*this); +} + + +/* Returns a copy of the expression. */ + +value_bit * +shift_right_expression::copy () const +{ + return new shift_right_expression (*this); +} + + +/* Returns a copy of the expression. */ + +value_bit * +shift_left_expression::copy () const +{ + return new shift_left_expression (*this); +} + + +/* Returns a copy of the expression. */ + +value_bit * +add_expression::copy () const +{ + return new add_expression (*this); +} + + +/* Returns a copy of the expression. */ + +value_bit * +sub_expression::copy () const +{ + return new sub_expression (*this); +} + + +/* Returns a copy of the expression. */ + +value_bit * +bit_complement_expression::copy () const +{ + return new bit_complement_expression (*this); +} + + +bit_xor_expression::bit_xor_expression (value_bit *left, value_bit *right) +{ + this->m_left = left; + this->m_right = right; + m_type = BIT_XOR_EXPRESSION; +} + + +bit_xor_expression::bit_xor_expression (const bit_xor_expression &expr) +{ + bit_expression::copy (&expr); +} + + +bit_and_expression::bit_and_expression (value_bit *left, value_bit *right) +{ + this->m_left = left; + this->m_right = right; + m_type = BIT_AND_EXPRESSION; +} + + +bit_and_expression::bit_and_expression (const bit_and_expression &expr) +{ + bit_expression::copy (&expr); +} + + +bit_or_expression::bit_or_expression (value_bit *left, value_bit *right) +{ + this->m_left = left; + this->m_right = right; + m_type = BIT_OR_EXPRESSION; +} + + +bit_or_expression::bit_or_expression (const bit_or_expression &expr) +{ + bit_expression::copy (&expr); +} + + +shift_right_expression::shift_right_expression (value_bit *left, + value_bit *right) +{ + this->m_left = left; + this->m_right = right; + m_type = SHIFT_RIGHT_EXPRESSION; +} + + +shift_right_expression::shift_right_expression ( + const shift_right_expression &expr) +{ + bit_expression::copy (&expr); +} + + +shift_left_expression::shift_left_expression (value_bit *left, value_bit *right) +{ + this->m_left = left; + this->m_right = right; + m_type = SHIFT_LEFT_EXPRESSION; +} + + +shift_left_expression::shift_left_expression (const shift_left_expression &expr) +{ + bit_expression::copy (&expr); +} + + +add_expression::add_expression (value_bit *left, value_bit *right) +{ + this->m_left = left; + this->m_right = right; + m_type = ADD_EXPRESSION; +} + + +add_expression::add_expression (const add_expression &expr) +{ + bit_expression::copy (&expr); +} + + +sub_expression::sub_expression (value_bit *left, value_bit *right) +{ + this->m_left = left; + this->m_right = right; + m_type = SUB_EXPRESSION; +} + + +sub_expression::sub_expression (const sub_expression &expr) +{ + bit_expression::copy (&expr); +} + + +/* Returns the origin of the bit, to whom it belongs. */ + +tree +symbolic_bit::get_origin () +{ + return m_origin; +} + + +/* Prints the bit. */ + +void +symbolic_bit::print () +{ + if (dump_file) + { + print_generic_expr (dump_file, m_origin, dump_flags); + fprintf (dump_file, "[%zu]", m_index); + } +} + + +/* Prints the bit. */ + +void +bit::print () +{ + if (dump_file) + fprintf (dump_file, "%u", m_val); +} + + +/* Depending on the expression, prints its sign. */ + +void +bit_expression::print_expr_sign () +{ + switch (m_type) + { + case BIT_XOR_EXPRESSION: + fprintf (dump_file, " ^ "); + break; + case BIT_AND_EXPRESSION: + fprintf (dump_file, " & "); + break; + case BIT_OR_EXPRESSION: + fprintf (dump_file, " | "); + break; + case SHIFT_RIGHT_EXPRESSION: + fprintf (dump_file, " >> "); + break; + case SHIFT_LEFT_EXPRESSION: + fprintf (dump_file, " << "); + break; + case ADD_EXPRESSION: + fprintf (dump_file, " + "); + break; + case SUB_EXPRESSION: + fprintf (dump_file, " - "); + break; + default: + fprintf (dump_file, " ?? "); + } +} + + +/* Prints the expression. */ + +void +bit_expression::print () +{ + if (dump_file) + { + fprintf (dump_file, "("); + if (m_left) + m_left->print (); + else + fprintf (dump_file, "null"); + + print_expr_sign (); + + if (m_right) + m_right->print (); + else + fprintf (dump_file, "null"); + + fprintf (dump_file, ")"); + } +} + + +/* Prints the expression. */ + +void +bit_complement_expression::print () +{ + if (dump_file) + { + fprintf (dump_file, "!"); + if (m_right) + m_right->print (); + else + fprintf (dump_file, "null"); + } +} \ No newline at end of file diff --git a/gcc/sym-exec/sym-exec-expression.h b/gcc/sym-exec/sym-exec-expression.h new file mode 100644 index 00000000000..36b90ffb374 --- /dev/null +++ b/gcc/sym-exec/sym-exec-expression.h @@ -0,0 +1,260 @@ +/* Every class defined here represents a single bit value of a variable. + Every variable will be represented as a vector of these classes which later + will be used for bit-level symbolic execution. */ + +#ifndef SYM_EXEC_EXPRESSION_H +#define SYM_EXEC_EXPRESSION_H + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "hwint.h" +#include "gimple-pretty-print.h" +#include "is-a.h" +#include "vec.h" +#include "hash-map.h" +#include "hash-set.h" +#include "stddef.h" + +/* Enum used for identifying the class of the bit. */ + +enum value_type { + SYMBOLIC_BIT, + BIT, + BIT_XOR_EXPRESSION, + BIT_AND_EXPRESSION, + BIT_OR_EXPRESSION, + BIT_COMPLEMENT_EXPRESSION, + SHIFT_RIGHT_EXPRESSION, + SHIFT_LEFT_EXPRESSION, + ADD_EXPRESSION, + SUB_EXPRESSION, + BIT_CONDITION +}; + +/* Base class for single bit value. */ + +class value_bit { + protected: + /* This will help us to understand where is moved the bit + from its initial position. */ + const size_t m_index; + + /* Type of the bit. Used by type checkers. */ + value_type m_type; + + public: + value_bit () : m_index (0) + {}; + value_bit (size_t i) : m_index (i) + {}; + value_bit (const value_bit &val) : m_index (val.m_index) + {}; + + /* Returns the bit's initial index in bit-vector. */ + size_t get_index () const; + + /* Returns type of the bit. */ + value_type get_type () const; + + /* This will support deep copy of objects' values. */ + virtual value_bit *copy () const = 0; + + /* Prints the bit. Inherited classes must implement it. */ + virtual void print () = 0; + virtual ~value_bit () = default; +}; + +/* Represents value of a single bit of symbolic marked variables. */ + +class symbolic_bit : public value_bit { + tree m_origin = nullptr; + + public: + symbolic_bit (size_t i, tree orig); + symbolic_bit (const symbolic_bit &sym_bit) : symbolic_bit (sym_bit.m_index, + sym_bit.m_origin) + {}; + + /* Returns a copy of the bit. */ + value_bit *copy () const; + + /* Prints the bit. */ + void print (); + + /* Returns the origin of the bit, to whom it belongs. */ + tree get_origin (); +}; + + +/* Represents value of a single bit. */ + +class bit : public value_bit { + private: + /* This is the value of a bit. It must be either 1 or 0. */ + unsigned char m_val = 0; + + public: + bit (unsigned char i); + bit (const bit &b) : bit (b.m_val) + {}; + + /* Returns the value of the bit. */ + unsigned char get_val () const; + + /* Sets the value of the bit. */ + void set_val (unsigned char new_val); + + /* Return a copy of the bit. */ + value_bit *copy () const; + + /* Prints the bit. */ + void print (); +}; + + +/* Bit-level base expression class. In general expressions consist of + two operands. Here we named them m_left and m_right. */ + +class bit_expression : public value_bit { + protected: + value_bit *m_left = nullptr; + value_bit *m_right = nullptr; + + /* Copies the given expression to it by copying + the left and right operands. */ + void copy (const bit_expression *expr); + + /* Depending on the expression, prints its sign. */ + virtual void print_expr_sign (); + + public: + + /* Returns left operand of the expression. */ + value_bit *get_left (); + + /* Returns right operand of the expression. */ + value_bit *get_right (); + + ~bit_expression (); + + /* Sets left operand of the expression. */ + void set_left (value_bit *expr); + + /* Sets right operand of the expression. */ + void set_right (value_bit *expr); + + /* Returns a deep copy of the expression. */ + value_bit *copy () const = 0; + + /* Prints the expression. */ + void print (); +}; + + +/* Bit-level XOR expression. XOR operation on two variables (when one of + them is symbolic) can be represented by XOR operations on + each of their bits. */ + +class bit_xor_expression : public bit_expression { + public: + bit_xor_expression (value_bit *left, value_bit *right); + bit_xor_expression (const bit_xor_expression &expr); + + /* Returns a copy of the expression. */ + value_bit *copy () const; +}; + + +/* Bit-level AND expression. AND operation on two variables (when one of + them is symbolic) can be represented by AND operations on + each of their bits. */ + +class bit_and_expression : public bit_expression { + public: + bit_and_expression (value_bit *left, value_bit *right); + bit_and_expression (const bit_and_expression &expr); + + /* Returns a copy of the expression. */ + value_bit *copy () const; +}; + + +/* Bit-level OR expression. OR operation on two variables (when one of + them is symbolic) can be represented by OR operations on + each of their bits. */ + +class bit_or_expression : public bit_expression { + public: + bit_or_expression (value_bit *left, value_bit *right); + bit_or_expression (const bit_or_expression &expr); + + /* Returns a copy of the expression. */ + value_bit *copy () const; +}; + + +/* SHIFT_RIGHT expression. Result must be stored bit by bit. */ + +class shift_right_expression : public bit_expression { + public: + shift_right_expression (value_bit *left, value_bit *right); + shift_right_expression (const shift_right_expression &expr); + + /* Returns a copy of the expression. */ + value_bit *copy () const; +}; + + +/* SHIFT_LEFT expression. Result must be stored bit by bit. */ + +class shift_left_expression : public bit_expression { + public: + shift_left_expression (value_bit *left, value_bit *right); + shift_left_expression (const shift_left_expression &expr); + + /* Returns a copy of the expression. */ + value_bit *copy () const; +}; + + +/* ADD expression. Result must be stored bit by bit. */ + +class add_expression : public bit_expression { + public: + add_expression (value_bit *left, value_bit *right); + add_expression (const add_expression &expr); + + /* Returns a copy of the expression. */ + value_bit *copy () const; +}; + + +/* SUB expression. Result must be stored bit by bit. */ + +class sub_expression : public bit_expression { + public: + sub_expression (value_bit *left, value_bit *right); + sub_expression (const sub_expression &expr); + + /* Returns a copy of the expression. */ + value_bit *copy () const; +}; + +/* Bit-level negation expression. */ + +class bit_complement_expression : public bit_expression { + public: + bit_complement_expression (value_bit *right); + bit_complement_expression (const bit_complement_expression &expr); + + /* Returns a copy of the expression. */ + value_bit *copy () const; + + /* Prints the expression. */ + void print (); +}; + +#endif /* SYM_EXEC_EXPRESSION_H. */ \ No newline at end of file diff --git a/gcc/sym-exec/sym-exec-state.cc b/gcc/sym-exec/sym-exec-state.cc new file mode 100644 index 00000000000..15df4d6a87e --- /dev/null +++ b/gcc/sym-exec/sym-exec-state.cc @@ -0,0 +1,2148 @@ +/* State will store states of variables for a function's single execution path. + It will be used for bit-level symbolic execution to determine values of bits + of function's return value and symbolic marked arguments. */ + +#include "sym-exec-state.h" + +size_t min (size_t a, size_t b, size_t c) +{ + size_t min = (a < b ? a : b); + return min < c ? min : c; +} + + +state::state (const state &s) +{ + for (auto iter = s.var_states.begin (); iter != s.var_states.end (); ++iter) + { + value val ((*iter).second.length (), (*iter).second.is_unsigned); + for (size_t i = 0; i < (*iter).second.length (); i++) + val.push ((*iter).second[i]->copy ()); + + var_states.put ((*iter).first, val); + } + + for (auto iter = s.conditions.begin (); iter != s.conditions.end (); ++iter) + conditions.add (as_a ((*iter)->copy ())); +} + + +state::~state () +{ + clear_conditions (); +} + + +/* Checks whether state for variable with specified name already + exists or not. */ + +bool +state::is_declared (tree var) +{ + return var_states.get (var) != NULL; +} + + +/* Declares given variable if it has not been declared yet. */ + +void +state::declare_if_needed (tree var, size_t size) +{ + if (TREE_CODE (var) != INTEGER_CST && !is_declared (var)) + { + make_symbolic (var, size); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, + "Declaring var "); + print_generic_expr (dump_file, var, dump_flags); + fprintf (dump_file, + " with size %zd\n", size); + } + } +} + + +/* Get value of the given variable. */ + +value * +state::get_value (tree var) +{ + return var_states.get (var); +} + + +/* Get the value of the tree, which is in the beginning of the var_states. */ + +value * +state::get_first_value () +{ + return &(*(var_states.begin ())).second; +} + + +/* Returns the list of conditions in the state. */ + +const hash_set & +state::get_conditions () +{ + return conditions; +} + + +/* Checks if sizes of arguments and destination are compatible. */ + +bool +state::check_args_compatibility (tree arg1, tree arg2, tree dest) +{ + if (!(get_var_size (arg1) == get_var_size (dest) + || TREE_CODE (arg1) == INTEGER_CST) + || !(get_var_size (arg2) == get_var_size (dest) + || TREE_CODE (arg2) == INTEGER_CST)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Sym-Exec: Incompatible destination" + "and argument sizes.\n"); + + return false; + } + + return true; +} + + +/* Creates value for given constant tree. */ + +value +state::create_val_for_const (tree var, size_t size) +{ + unsigned HOST_WIDE_INT val = TYPE_UNSIGNED (TREE_TYPE (var)) + ? tree_to_uhwi (var) : tree_to_shwi (var); + value result (size, TYPE_UNSIGNED (TREE_TYPE (var))); + + for (size_t i = 0; i < size; i++) + { + result.push (new bit (val & 1)); + val >>= 1; + } + + return result; +} + + +/* Adds the given variable to state. */ + +bool +state::add_var_state (tree var, value *vstate) +{ + size_t size = vstate->length (); + value val (size, vstate->is_unsigned); + for (size_t i = 0; i < size; i++) + val.push ((*vstate)[i]->copy ()); + + return var_states.put (var, val); +} + + +/* Adds the given condition to the state. */ + +bool +state::add_condition (bit_expression *cond) +{ + return conditions.add (as_a (cond->copy ())); +} + + +/* Bulk add the given conditions to the state. */ + +bool +state::bulk_add_conditions (const hash_set &conds) +{ + bool status = true; + for (auto iter = conds.begin (); iter != conds.end (); ++iter) + status &= add_condition (*iter); + + return status; +} + + +/* Remove all states from the states' vector. */ + +void +state::remove_states (vec *states) +{ + while (!states->is_empty ()) + { + delete states->last (); + states->pop (); + } +} + + +/* Remove all states from the states' vector and release the vector. */ + +void +state::clear_states (vec *states) +{ + remove_states (states); + states->release (); +} + + +/* Removes all variables added to the state. */ + +void +state::clear_var_states () +{ + var_states.empty (); +} + + +/* Removes all conditions added to the state. */ + +void +state::clear_conditions () +{ + for (auto iter = conditions.begin (); iter != conditions.end (); ++iter) + delete (*iter); + conditions.empty (); +} + + +/* Performs AND operation for 2 symbolic_bit operands. */ + +value_bit * +state::and_sym_bits (const value_bit *var1, const value_bit *var2) +{ + return new bit_and_expression (var1->copy (), var2->copy ()); +} + + +/* Performs AND operation for a symbolic_bit and const_bit operands. */ + +value_bit * +state::and_var_const (const value_bit *var1, const bit *const_bit) +{ + if (const_bit->get_val () == 1) + return var1->copy (); + + return new bit (0); +} + + +/* Performs AND operation for 2 constant bit operands. */ + +bit * +state::and_const_bits (const bit *const_bit1, const bit *const_bit2) +{ + if (const_bit1->get_val () == const_bit2->get_val ()) + return new bit (*const_bit1); + + return new bit (0); +} + + +/* Performs OR operation for 2 symbolic_bit operands. */ + +value_bit * +state::or_sym_bits (const value_bit *var1, const value_bit *var2) +{ + return new bit_or_expression (var1->copy (), var2->copy ()); +} + + +/* Performs OR operation for a symbolic_bit and a constant bit operands. */ + +value_bit * +state::or_var_const (const value_bit *var1, const bit *const_bit) +{ + if (const_bit->get_val () == 0) + return var1->copy (); + + return new bit (1); +} + + +/* Performs OR operation for 2 constant bit operands. */ + +bit * +state::or_const_bits (const bit *const_bit1, const bit *const_bit2) +{ + if (const_bit1->get_val () == const_bit2->get_val ()) + return new bit (*const_bit1); + + return new bit (1); +} + + +/* Adds an empty state for the given variable. */ + +bool +state::decl_var (tree var, unsigned size) +{ + if (is_declared (var)) + return false; + + value val (size, TYPE_UNSIGNED (TREE_TYPE (var))); + for (unsigned i = 0; i < size; i++) + val.push (nullptr); + + return var_states.put (var, val); +} + + +/* Returns size of the given variable. */ + +unsigned +state::get_var_size (tree var) +{ + value *content = var_states.get (var); + if (content == NULL) + return 0; + + return content->allocated (); +} + + +/* Adds a variable with unknown value to state. Such variables are + represented as sequence of symbolic bits. */ + +bool +state::make_symbolic (tree var, unsigned size) +{ + if (is_declared (var)) + return false; + + value val (size, TYPE_UNSIGNED (TREE_TYPE (var))); + /* Initialize each bit of a variable with unknown value. */ + for (size_t i = 0; i < size; i++) + val.push (new symbolic_bit (i, var)); + + return var_states.put (var, val); +} + + +/* Performs AND operation on two bits. */ + +value_bit * +state::and_two_bits (value_bit *arg1, value_bit *arg2) +{ + value_bit *result = nullptr; + + if (is_a (arg1) && is_a (arg2)) + result = and_const_bits (as_a (arg1), as_a (arg2)); + + else if (is_a (arg1) && (is_a (arg2) + || (is_a (arg2)))) + result = and_var_const (arg2, as_a (arg1)); + + else if ((is_a (arg1) + || (is_a (arg1))) && is_a (arg2)) + result = and_var_const (arg1, as_a (arg2)); + + else + result = and_sym_bits (arg1, arg2); + + return result; +} + + +/* A wrapper for operations on two bits. + Operation and operands are passed as arguments. */ + +value_bit * +state::operate_bits (bit_func bit_op, value_bit *bit1, value_bit *bit2, + value_bit **) +{ + return (bit_op) (bit1, bit2); +} + + +/* A wrapper for operations on three bits. + Operation and operands are passed as arguments. */ + +value_bit * +state::operate_bits (bit_func3 bit_op, value_bit *bit1, value_bit *bit2, + value_bit **bit3) +{ + return (bit_op) (bit1, bit2, bit3); +} + + +/* Does preprocessing and postprocessing for expressions with tree operands. + Handles value creation for constant and their removement in the end. */ + +bool +state::do_binary_operation (tree arg1, tree arg2, tree dest, + binary_func bin_func) +{ + declare_if_needed (dest, tree_to_uhwi (TYPE_SIZE (TREE_TYPE (dest)))); + declare_if_needed (arg1, var_states.get (dest)->allocated ()); + declare_if_needed (arg2, var_states.get (dest)->allocated ()); + + if (!check_args_compatibility (arg1, arg2, dest)) + return false; + + size_t dest_size = var_states.get (dest)->length (); + + value *arg1_val = var_states.get (arg1); + value arg1_const_val (dest_size, false); + if (arg1_val == NULL && TREE_CODE (arg1) == INTEGER_CST) + { + arg1_const_val = create_val_for_const (arg1, dest_size); + arg1_val = &arg1_const_val; + } + + value *arg2_val = var_states.get (arg2); + value arg2_const_val (dest_size, false); + if (arg2_val == NULL && TREE_CODE (arg2) == INTEGER_CST) + { + arg2_const_val = create_val_for_const (arg2, dest_size); + arg2_val = &arg2_const_val; + } + + (this->*bin_func) (arg1_val, arg2_val, dest); + print_value (var_states.get (dest)); + return true; +} + + +/* Performs AND operation on given values. The result is stored in dest. */ + +void +state::do_and (value *arg1, value *arg2, tree dest) +{ + /* Creating AND expressions for every bit pair of given arguments + and store them as a new state for given destination. */ + + operate (arg1, arg2, nullptr, dest, &state::and_two_bits); +} + + +/* Performs OR operation on two bits. */ + +value_bit * +state::or_two_bits (value_bit *arg1_bit, value_bit *arg2_bit) +{ + value_bit *result = nullptr; + + if (is_a (arg1_bit) && is_a (arg2_bit)) + result = or_const_bits (as_a (arg1_bit), as_a (arg2_bit)); + + else if (is_a (arg1_bit) && (is_a (arg2_bit) + || is_a (arg2_bit))) + result = or_var_const (arg2_bit, as_a (arg1_bit)); + + else if ((is_a (arg1_bit) + || is_a (arg1_bit)) + && is_a (arg2_bit)) + result = or_var_const (arg1_bit, as_a (arg2_bit)); + + else + result = or_sym_bits (arg1_bit, arg2_bit); + + return result; +} + + +/* Performs OR operation on given values. The result is stored in dest. */ + +void +state::do_or (value *arg1, value *arg2, tree dest) +{ + /* Creating OR expressions for every bit pair of given arguments + and store them as a new state for given destination. */ + operate (arg1, arg2, nullptr, dest, &state::or_two_bits); +} + + +/* Performs XOR operation on two bits. */ + +value_bit * +state::xor_two_bits (value_bit *arg1_bit, value_bit *arg2_bit) +{ + value_bit *result = nullptr; + + if (is_a (arg1_bit) && is_a (arg2_bit)) + result = xor_const_bits (as_a (arg1_bit), as_a (arg2_bit)); + + else if (is_a (arg1_bit) && (is_a (arg2_bit) + || is_a (arg2_bit))) + result = xor_var_const (arg2_bit, as_a (arg1_bit)); + + else if ((is_a (arg1_bit) + || is_a (arg1_bit)) + && is_a (arg2_bit)) + result = xor_var_const (arg1_bit, as_a (arg2_bit)); + + else + result = xor_sym_bits (arg1_bit, arg2_bit); + + return result; +} + + +/* Performs XOR operation on given values. The result is stored in dest. */ + +void +state::do_xor (value *arg1, value *arg2, tree dest) +{ + operate (arg1, arg2, nullptr, dest, &state::xor_two_bits); +} + + +/* Shifts value right by size of shift_value. */ + +value * +state::shift_right_by_const (value *var, size_t shift_value) +{ + value *shift_result = new value (var->length (), var->is_unsigned); + if (var->length () <= shift_value) + for (size_t i = 0; i < var->length (); i++) + shift_result->push (new bit (0)); + else + { + size_t i = 0; + for (; i < var->length () - shift_value; ++i) + shift_result->push (((*var)[shift_value + i])->copy ()); + + for (; i < var->length (); ++i) + shift_result->push (var->is_unsigned ? new bit (0) + : var->last ()->copy ()); + } + return shift_result; +} + + +/* Checks if all bits of the given value have constant bit type. */ + +bool +state::is_bit_vector (const value *var) +{ + if (var == nullptr || !var->exists ()) + return false; + + for (size_t i = 0; i < var->length (); i++) + if (!(is_a ((*var)[i]))) + return false; + return true; +} + + +/* Returns the number represented by the value. */ + +unsigned HOST_WIDE_INT +state::make_number (const value *var) +{ + unsigned HOST_WIDE_INT + number = 0; + int value_size = var->length (); + for (int i = value_size - 1; i >= 0; i--) + { + if (is_a ((*var)[i])) + number = (number << 1) | as_a ((*var)[i])->get_val (); + else + return 0; + } + return number; +} + + +/* Shift_left operation. Case: var2 is a symbolic value. */ + +value_bit * +state::shift_left_sym_bits (value_bit *var1, value_bit *var2) +{ + return new shift_left_expression (var1->copy (), var2->copy ()); +} + + +/* Performs shift left operation on given values. + The result is stored in dest. */ + +void +state::do_shift_left (value *arg1, value *arg2, tree dest) +{ + if (is_bit_vector (arg2)) + { + size_t shift_value = make_number (arg2); + value *result = shift_left_by_const (arg1, shift_value); + for (size_t i = 0; i < get_var_size (dest); i++) + { + delete (*var_states.get (dest))[i]; + (*var_states.get (dest))[i] = (*result)[i]->copy (); + } + delete result; + } + else + operate (arg1, arg2, nullptr, dest, &state::shift_left_sym_bits); +} + + +/* Performs shift right operation on given values. + The result is stored in dest. */ + +void +state::do_shift_right (value *arg1, value *arg2, tree dest) +{ + if (is_bit_vector (arg2)) + { + size_t shift_value = make_number (arg2); + value *result = shift_right_by_const (arg1, shift_value); + for (size_t i = 0; i < get_var_size (dest); i++) + { + delete (*var_states.get (dest))[i]; + (*var_states.get (dest))[i] = (*result)[i]->copy (); + } + + delete result; + } + else + operate (arg1, arg2, nullptr, dest, &state::shift_right_sym_bits); +} + + +/* Adds two bits and carry value. + Resturn result and stores new carry bit in "carry". */ + +value_bit * +state::full_adder (value_bit *var1, value_bit *var2, value_bit **carry) +{ + value_bit *new_carry = and_two_bits (var1, var2); + value_bit *sum = xor_two_bits (var1, var2); + + value_bit *result = xor_two_bits (sum, *carry); + value_bit *sum_and_carry = and_two_bits (sum, *carry); + + delete *carry; + delete sum; + + *carry = or_two_bits (sum_and_carry, new_carry); + + delete sum_and_carry; + delete new_carry; + + return result; +} + + +/* Adds given values. The result is stored in dest. */ + +void +state::do_add (value *arg1, value *arg2, tree dest) +{ + value_bit *carry = new bit (0); + operate (arg1, arg2, &carry, dest, &state::full_adder); + delete carry; +} + + +/* Returns the additive inverse of the given number. */ + +value * +state::additive_inverse (const value *number) +{ + value *result = new value (number->length (), number->is_unsigned); + value one (number->length (), number->is_unsigned); + + size_t size = number->length (); + one.push (new bit (1)); + result->push (complement_a_bit ((*number)[0])); + + for (size_t i = 1; i < size; i++) + { + one.push (new bit (0)); + result->push (complement_a_bit ((*number)[i])); + } + + value_bit *carry = new bit (0); + for (size_t i = 0; i < size; ++i) + { + value_bit *cur_bit = (*result)[i]; + (*result)[i] = full_adder (cur_bit, one[i], &carry); + delete cur_bit; + } + + delete carry; + return result; +} + + +/* Subtracks second value from the first. The result is stored in dest. */ + +void +state::do_sub (value *arg1, value *arg2, tree dest) +{ + value *neg_arg2 = additive_inverse (arg2); + do_add (arg1, neg_arg2, dest); + delete neg_arg2; +} + + +/* Performs complement operation on a bit. */ + +value_bit * +state::complement_a_bit (value_bit *var) +{ + value_bit *result = nullptr; + if (is_a (var)) + result = complement_const_bit (as_a (var)); + else + result = complement_sym_bit (var); + + return result; +} + + +/* Negates given variable. */ + +bool +state::do_complement (tree arg, tree dest) +{ + declare_if_needed (dest, tree_to_uhwi (TYPE_SIZE (TREE_TYPE (dest)))); + declare_if_needed (arg, var_states.get (dest)->allocated ()); + + /* Creating complement expressions for every bit the given argument + and store it as a new state for given destination. */ + size_t iter_count = min (get_var_size (dest), get_var_size (arg), + get_var_size (arg)); + + size_t i = 0; + for (; i < iter_count; i++) + { + value_bit *result = complement_a_bit ((*var_states.get (arg))[i]); + delete (*var_states.get (dest))[i]; + (*var_states.get (dest))[i] = result; + } + + if (i >= get_var_size (dest)) + { + print_value (var_states.get (dest)); + return true; + } + + for (; i < get_var_size (dest); i++) + { + delete (*var_states.get (dest))[i]; + bit tmp (0); + (*var_states.get (dest))[i] = complement_a_bit (&tmp); + } + + print_value (var_states.get (dest)); + return true; +} + + +/* Does Assignment. */ + +bool +state::do_assign (tree arg, tree dest) +{ + declare_if_needed (dest, tree_to_uhwi (TYPE_SIZE (TREE_TYPE (dest)))); + if (TREE_CODE (arg) != INTEGER_CST) + declare_if_needed (arg, tree_to_uhwi (TYPE_SIZE (TREE_TYPE (arg)))); + else + declare_if_needed (arg, var_states.get (dest)->allocated ()); + + value *dest_val = var_states.get (dest); + + /* If the argument is already defined, then we must just copy its bits. */ + if (auto arg_val = var_states.get (arg)) + { + for (size_t i = 0; i < dest_val->length (); i++) + { + value_bit *new_val = nullptr; + if (i < arg_val->length ()) + new_val = (*arg_val)[i]->copy (); + else + new_val = new bit (0); + + delete (*dest_val)[i]; + (*dest_val)[i] = new_val; + } + } + /* If the argument is a constant, we must save it as sequence of bits. */ + else if (TREE_CODE (arg) == INTEGER_CST) + { + value arg_val + = create_val_for_const (arg, dest_val->length ()); + for (size_t i = 0; i < dest_val->length (); i++) + { + delete (*dest_val)[i]; + (*dest_val)[i] = arg_val[i]->copy (); + } + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Sym-Exec: Unsupported assignment" + " for given argument.\n"); + + return false; + } + + print_value (var_states.get (dest)); + return true; +} + + +/* Assigns pow 2 value. */ + +bool +state::do_assign_pow2 (tree dest, unsigned pow) +{ + value *dest_val = var_states.get (dest); + unsigned dest_size = dest_val ? dest_val->allocated () + : tree_to_uhwi (TYPE_SIZE (TREE_TYPE (dest))); + if (pow > dest_size) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Sym-Exec: pow %u of 2 won't fit in" + " specified destination\n", pow); + return false; + } + + if (!dest_val) + { + decl_var (dest, tree_to_uhwi (TYPE_SIZE (TREE_TYPE (dest)))); + dest_val = var_states.get (dest); + } + else + dest_val->free_bits (); + + for (unsigned i = 0; i < dest_val->length (); i++) + { + if (i == pow) + (*dest_val)[i] = new bit (1); + else + (*dest_val)[i] = new bit (0); + } + + print_value (dest_val); + return true; +} + + +/* Performs NOT operation for constant bit. */ + +bit * +state::complement_const_bit (const bit *const_bit) +{ + return new bit (1u ^ const_bit->get_val ()); +} + + +/* Performs NOT operation for symbolic_bit. */ + +value_bit * +state::complement_sym_bit (const value_bit *var) +{ + return new bit_complement_expression (var->copy ()); +} + + +/* Performs XOR operation for 2 symbolic_bit operands. */ + +value_bit * +state::xor_sym_bits (const value_bit *var1, const value_bit *var2) +{ + value_bit *var2_copy = var2->copy (); + bit_expression *node2_with_const_child = nullptr; + bit_expression *parent_of_node2_with_child = nullptr; + get_parent_with_const_child (var2_copy, node2_with_const_child, + parent_of_node2_with_child); + + if (node2_with_const_child != nullptr) + { + value_bit *var1_copy = var1->copy (); + bit_expression *node1_with_const_child = nullptr; + bit_expression *parent_of_node1_with_child = nullptr; + get_parent_with_const_child (var1_copy, node1_with_const_child, + parent_of_node1_with_child); + + /* If both subtrees have constant bit nodes, + we can merge them together. */ + if (node1_with_const_child != nullptr) + { + value_bit *var1_reformed = nullptr; + value_bit *var2_reformed = nullptr; + + /* If var1's const bit is in its left subtree. */ + value_bit *var1_left = node1_with_const_child->get_left (); + if (var1_left != nullptr && is_a (var1_left)) + { + var1_reformed = node1_with_const_child->get_right ()->copy (); + value_bit *var2_left = node2_with_const_child->get_left (); + + /* If var2's const bit is in its left subtree. */ + if (var2_left != nullptr && is_a (var2_left)) + var2_reformed = node2_with_const_child->get_right ()->copy (); + else /* Var2's const bit is in its right subtree. */ + var2_reformed = node2_with_const_child->get_left ()->copy (); + } + else /* Var1's const bit is in its right subtree. */ + { + var1_reformed = node1_with_const_child->get_left ()->copy (); + value_bit *var2_left = node2_with_const_child->get_left (); + + /* If var2's const bit is in its left subtree. */ + if (var2_left != nullptr && is_a (var2_left)) + var2_reformed = node2_with_const_child->get_right ()->copy (); + else /* Var2's const bit is in its right subtree. */ + var2_reformed = node2_with_const_child->get_left ()->copy (); + } + + if (parent_of_node1_with_child) + { + parent_of_node1_with_child->get_left () + == node1_with_const_child + ? parent_of_node1_with_child->set_left (var1_reformed) + : parent_of_node1_with_child->set_right (var1_reformed); + delete node1_with_const_child; + } + else + { + delete var1_copy; + var1_copy = var1_reformed; + } + + if (parent_of_node2_with_child) + { + parent_of_node2_with_child->get_left () + == node2_with_const_child + ? parent_of_node2_with_child->set_left (var2_reformed) + : parent_of_node2_with_child->set_right (var2_reformed); + delete node2_with_const_child; + } + else + { + delete var2_copy; + var2_copy = var2_reformed; + } + + return new bit_xor_expression (var1_copy, var2_copy); + } + delete var1_copy; + } + + delete var2_copy; + return new bit_xor_expression (var1->copy (), var2->copy ()); +} + + +/* Performs XOR operation for 2 constant bit operands. */ + +bit * +state::xor_const_bits (const bit *const_bit1, const bit *const_bit2) +{ + return new bit (const_bit1->get_val () ^ const_bit2->get_val ()); +} + + +/* Performs XOR operation for a symbolic_bit and const_bit operands. */ + +value_bit * +state::xor_var_const (const value_bit *var, const bit *const_bit) +{ + if (const_bit->get_val () == 0) + return var->copy (); + + value_bit *var_copy = var->copy (); + bit_expression *node_with_const_child = nullptr; + bit_expression *tmp = nullptr; + get_parent_with_const_child (var_copy, node_with_const_child, tmp); + + if (node_with_const_child != nullptr) + { + value_bit *left = node_with_const_child->get_left (); + if (left != nullptr && is_a (left)) + { + bit *new_left = xor_const_bits (as_a (left), const_bit); + delete left; + node_with_const_child->set_left (new_left); + } + else + { + value_bit *right = node_with_const_child->get_right (); + bit *new_right = xor_const_bits (as_a (right), const_bit); + delete right; + node_with_const_child->set_right (new_right); + } + return var_copy; + } + + delete var_copy; + return new bit_xor_expression (var->copy (), const_bit->copy ()); +} + + +/* Return node which has a const bit child. Traversal is done based + on safe branching. */ + +void +state::get_parent_with_const_child (value_bit *root, bit_expression *&parent, + bit_expression *&parent_of_parent) +{ + parent_of_parent = nullptr; + parent = nullptr; + + if (!is_a (root)) + return; + + bit_expression *expr_root = as_a (root); + hash_set < bit_expression * > nodes_to_consider; + nodes_to_consider.add (expr_root); + + hash_map < bit_expression * , bit_expression * > node_to_parent; + node_to_parent.put (expr_root, nullptr); + + /* Traversing expression tree: + considering only comutative expression nodes. */ + while (!nodes_to_consider.is_empty ()) + { + bit_expression *cur_element = *nodes_to_consider.begin (); + nodes_to_consider.remove (cur_element); + + value_bit *left = cur_element->get_left (); + value_bit *right = cur_element->get_right (); + + if ((left != nullptr && is_a (left)) + || (right != nullptr && is_a (right))) + { + parent = cur_element; + parent_of_parent = *node_to_parent.get (cur_element); + } + + if (left != nullptr && is_a (left)) + { + nodes_to_consider.add (as_a (left)); + node_to_parent.put (as_a (left), cur_element); + } + + if (right != nullptr && is_a (right)) + { + nodes_to_consider.add (as_a (right)); + node_to_parent.put (as_a (right), cur_element); + } + } +} + + +/* Shifts number left by size of shift_value. */ + +value * +state::shift_left_by_const (const value *number, size_t shift_value) +{ + value *shift_result = new value (number->length (), number->is_unsigned); + if (number->length () <= shift_value) + for (size_t i = 0; i < number->length (); i++) + shift_result->push (new bit (0)); + + else + { + size_t i = 0; + for (; i < shift_value; ++i) + shift_result->push (new bit (0)); + for (size_t j = 0; i < number->length (); ++i, j++) + shift_result->push (((*number)[j])->copy ()); + } + return shift_result; +} + + +/* Shift_right operation. Case: var2 is a symbolic value. */ + +value_bit * +state::shift_right_sym_bits (value_bit *var1, value_bit *var2) +{ + return new shift_right_expression (var1->copy (), var2->copy ()); +} + + +/* Adds two values, stores the result in the first one. */ + +void +state::add_numbers (value *var1, const value *var2) +{ + value_bit *carry = new bit (0); + for (unsigned i = 0; i < var1->length (); i++) + { + value_bit *temp = (*var1)[i]; + (*var1)[i] = full_adder ((*var1)[i], (*var2)[i], &carry); + delete temp; + } + delete carry; +} + + +/* ANDs every bit of the vector with var_bit, stroes the result in var1. */ + +void +state::and_number_bit (value *var1, value_bit *var_bit) +{ + for (unsigned i = 0; i < var1->length (); i++) + { + value_bit *tmp = (*var1)[i]; + (*var1)[i] = and_two_bits ((*var1)[i], var_bit); + delete tmp; + } + +} + + +void +state::do_mul (value *arg1, value *arg2, tree dest) +{ + value *shifted = new value (*arg1); + value *dest_val = var_states.get (dest); + + for (unsigned i = 0; i < dest_val->length (); i++) + { + delete (*dest_val)[i]; + (*dest_val)[i] = new bit (0); + } + + for (unsigned i = arg2->length (); i != 0; --i) + { + if (is_a ((*arg2)[i - 1]) + && as_a ((*arg2)[i - 1])->get_val () != 0) + add_numbers (dest_val, shifted); + else if (is_a ((*arg2)[i - 1])) + { + and_number_bit (shifted, as_a ((*arg2)[i - 1])); + add_numbers (dest_val, shifted); + } + + value *temp = shifted; + shifted = shift_left_by_const (shifted, 1); + delete temp; + } + delete shifted; +} + + +/* Checks whether the given two constant values are equal. */ + +bool +state::check_const_value_equality (value *arg1, value *arg2) +{ + for (size_t i = 0; i < arg1->length (); i++) + if (as_a ((*arg1)[i])->get_val () + != as_a ((*arg2)[i])->get_val ()) + return false; + return true; +} + + +/* Adds EQUAL condition of given variables to state. */ + +bool +state::add_equal_cond (tree arg1, tree arg2) +{ + return add_binary_cond (arg1, arg2, &state::add_equal_cond); +} + + +/* Adds equality condition for two values. */ + +void +state::add_equal_cond (value *arg1, value *arg2) +{ + + /* If both arguments are constants then we can evaluate it. */ + if (is_bit_vector (arg1) && is_bit_vector (arg2)) + { + bool result = check_const_value_equality (arg1, arg2); + last_cond_status = result ? condition_status::CS_TRUE + : condition_status::CS_FALSE; + return; + } + + /* When some of bits are constants and they differ by value, + then we can evalate it to be false. */ + for (size_t i = 0; i < arg1->length (); i++) + { + if (is_a ((*arg1)[i]) && is_a ((*arg2)[i]) + && as_a ((*arg1)[i])->get_val () + != as_a ((*arg2)[i])->get_val ()) + { + last_cond_status = condition_status::CS_FALSE; + return; + } + } + + for (size_t i = 0; i < arg1->length (); i++) + { + /* If there is a constant bit pair, then they are equal + as we checked not equality above. */ + if (is_a ((*arg1)[i]) && is_a ((*arg2)[i])) + continue; + + conditions.add (new bit_condition ((*arg1)[i]->copy (), + (*arg2)[i]->copy (), + EQ_EXPR)); + } + last_cond_status = condition_status::CS_SYM; +} + + +/* Checks whether the given two constant values are not equal. */ + +bool +state::check_const_value_are_not_equal (value *arg1, value *arg2) +{ + for (size_t i = 0; i < arg1->length (); i++) + if (as_a ((*arg1)[i])->get_val () + != as_a ((*arg2)[i])->get_val ()) + return true; + return false; +} + + +/* Adds NOT EQUAL condition of given variables to state. */ + +bool +state::add_not_equal_cond (tree arg1, tree arg2) +{ + return add_binary_cond (arg1, arg2, &state::add_not_equal_cond); +} + + +/* Adds not equal condition for two values. */ + +void +state::add_not_equal_cond (value *arg1, value *arg2) +{ + if (is_bit_vector (arg1) && is_bit_vector (arg2)) + { + bool result = check_const_value_are_not_equal (arg1, arg2); + last_cond_status = result ? condition_status::CS_TRUE + : condition_status::CS_FALSE; + return; + } + + /* When some of bits are constants and they differ by value, + then we can evalate it to be true. */ + for (size_t i = 0; i < arg1->length (); i++) + { + if (is_a ((*arg1)[i]) && is_a ((*arg2)[i]) + && as_a ((*arg1)[i])->get_val () + != as_a ((*arg2)[i])->get_val ()) + { + last_cond_status = condition_status::CS_TRUE; + return; + } + } + + bit_expression *prev = nullptr; + for (size_t i = 0; i < arg1->length (); i++) + { + /* If there is a constant bit pair, then they are equal + as we checked not equality above. */ + if (is_a ((*arg1)[i]) && is_a ((*arg2)[i])) + continue; + + bit_condition *new_cond = new bit_condition ((*arg1)[i]->copy (), + (*arg2)[i]->copy (), + NE_EXPR); + if (prev) + prev = new bit_or_expression (prev, new_cond); + else + prev = new_cond; + } + + last_cond_status = condition_status::CS_SYM; + conditions.add (prev); +} + + +/* Checks whether the first given constant value + is greater than the second one. */ + +bool +state::check_const_value_is_greater_than (value *arg1, value *arg2) +{ + for (int i = arg1->length () - 1; i >= 0; i--) + { + if (as_a ((*arg1)[i])->get_val () + > as_a ((*arg2)[i])->get_val ()) + return true; + else if (as_a ((*arg1)[i])->get_val () + < as_a ((*arg2)[i])->get_val ()) + return false; + } + return false; +} + + +/* Adds GREATER THAN condition of given variables to state. */ + +bool +state::add_greater_than_cond (tree arg1, tree arg2) +{ + return add_binary_cond (arg1, arg2, &state::add_greater_than_cond); +} + + +/* Adds greater than condition for two values. */ + +void +state::add_greater_than_cond (value *arg1, value *arg2) +{ + if (is_bit_vector (arg1) && is_bit_vector (arg2)) + { + bool result = check_const_value_is_greater_than (arg1, arg2); + last_cond_status = result ? condition_status::CS_TRUE + : condition_status::CS_FALSE; + return; + } + + if (is_bit_vector (arg2) && is_a (arg1->last ()) + && make_number (arg2) == 0 && !arg1->is_unsigned) + { + if (as_a (arg1->last ())->get_val () == 1) + last_cond_status = condition_status::CS_FALSE; + else + { + for (size_t i = 0; i < arg1->length (); i++) + if (is_a ((*arg1)[i]) + && as_a ((*arg1)[i])->get_val ()) + { + last_cond_status = condition_status::CS_TRUE; + return; + } + } + } + + bit_expression *gt_cond = construct_great_than_cond (arg1, arg2); + if (gt_cond) + { + /* Otherwise its status is already set. */ + last_cond_status = condition_status::CS_SYM; + conditions.add (gt_cond); + } +} + + +/* Constructs expression trees of greater than condition for given values. */ + +bit_expression * +state::construct_great_than_cond (value *arg1, value *arg2) +{ + bit_expression *prev = nullptr; + int i = arg1->length () - 1; + for (; i >= 0; i--) + { + if (is_a ((*arg1)[i]) && is_a ((*arg2)[i])) + { + if (as_a ((*arg1)[i])->get_val () + > as_a ((*arg2)[i])->get_val ()) + { + if (!prev) + last_cond_status = condition_status::CS_TRUE; + return prev; + } + else if (as_a ((*arg1)[i])->get_val () + < as_a ((*arg2)[i])->get_val ()) + { + if (prev) + { + bit_expression *ret_val + = as_a (prev->get_left ()->copy ()); + delete prev; + return ret_val; + } + else + { + last_cond_status = condition_status::CS_FALSE; + return nullptr; + } + } + } + else + { + bit_condition *gt_cond + = new bit_condition ((*arg1)[i]->copy (), (*arg2)[i]->copy (), + GT_EXPR); + bit_expression *expr = nullptr; + if (i) + { + bit_condition *eq_cond + = new bit_condition ((*arg1)[i]->copy (), (*arg2)[i]->copy (), + EQ_EXPR); + expr = new bit_or_expression (gt_cond, eq_cond); + } + else + expr = gt_cond; + + if (prev) + prev = new bit_and_expression (expr, prev); + else + prev = expr; + } + } + + return prev; +} + + +/* Checks whether the first given constant value + is less than the second one. */ + +bool +state::check_const_value_is_less_than (value *arg1, value *arg2) +{ + for (int i = arg1->length () - 1; i >= 0; i--) + { + if (as_a ((*arg1)[i])->get_val () + < as_a ((*arg2)[i])->get_val ()) + return true; + else if (as_a ((*arg1)[i])->get_val () + > as_a ((*arg2)[i])->get_val ()) + return false; + } + return false; +} + + +/* Adds LESS THAN condition of given variables to state. */ + +bool +state::add_less_than_cond (tree arg1, tree arg2) +{ + return add_binary_cond (arg1, arg2, &state::add_less_than_cond); +} + + +/* Adds less than condition for two values. */ + +void +state::add_less_than_cond (value *arg1, value *arg2) +{ + if (is_bit_vector (arg1) && is_bit_vector (arg2) + && (make_number (arg2) != 0 || arg1->is_unsigned)) + { + bool result = check_const_value_is_less_than (arg1, arg2); + last_cond_status = result ? condition_status::CS_TRUE + : condition_status::CS_FALSE; + return; + } + + last_cond_status = condition_status::CS_SYM; + if (is_bit_vector (arg2) && make_number (arg2) == 0 && !arg1->is_unsigned) + { + if (is_a (arg1->last ())) + { + if (as_a (arg1->last ())->get_val () == 1) + last_cond_status = condition_status::CS_TRUE; + else + last_cond_status = condition_status::CS_FALSE; + } + else + conditions.add (new bit_condition (arg1->last ()->copy (), new bit (1), + EQ_EXPR)); + + return; + } + + bit_expression *lt_cond = construct_less_than_cond (arg1, arg2); + if (lt_cond) + /* Otherwise its status is already set. */ + conditions.add (lt_cond); +} + + +/* Constructs expression trees of less than condition for given values. */ + +bit_expression * +state::construct_less_than_cond (value *arg1, value *arg2) +{ + bit_expression *prev = nullptr; + int i = arg1->length () - 1; + for (; i >= 0; i--) + { + if (is_a ((*arg1)[i]) && is_a ((*arg2)[i])) + { + if (as_a ((*arg1)[i])->get_val () + < as_a ((*arg2)[i])->get_val ()) + { + if (!prev) + last_cond_status = condition_status::CS_TRUE; + return prev; + } + else if (as_a ((*arg1)[i])->get_val () + > as_a ((*arg2)[i])->get_val ()) + { + if (prev) + { + bit_expression *ret_val + = as_a (prev->get_left ()->copy ()); + delete prev; + return ret_val; + } + else + { + last_cond_status = condition_status::CS_FALSE; + return nullptr; + } + } + } + else + { + bit_condition *lt_cond + = new bit_condition ((*arg1)[i]->copy (), (*arg2)[i]->copy (), + LT_EXPR); + bit_expression *expr = nullptr; + if (i) + { + bit_condition *eq_cond + = new bit_condition ((*arg1)[i]->copy (), (*arg2)[i]->copy (), + EQ_EXPR); + expr = new bit_or_expression (lt_cond, eq_cond); + } + else + expr = lt_cond; + + if (prev) + prev = new bit_and_expression (expr, prev); + else + prev = expr; + } + } + + return prev; +} + + +/* Adds GREATER OR EQUAL condition of given variables to state. */ + +bool +state::add_greater_or_equal_cond (tree arg1, tree arg2) +{ + return add_binary_cond (arg1, arg2, &state::add_greater_or_equal_cond); +} + + +/* Adds greater or equal condition for two values. */ + +void +state::add_greater_or_equal_cond (value *arg1, value *arg2) +{ + if (is_bit_vector (arg1) && is_bit_vector (arg2) + && (make_number (arg2) != 0 || arg1->is_unsigned)) + { + bool is_greater_than = check_const_value_is_greater_than (arg1, + arg2); + bool is_equal = check_const_value_equality (arg1, arg2); + last_cond_status = (is_greater_than | is_equal) + ? condition_status::CS_TRUE + : condition_status::CS_FALSE; + return; + } + + last_cond_status = condition_status::CS_SYM; + if (is_bit_vector (arg2) && make_number (arg2) == 0 && !arg1->is_unsigned) + { + if (is_a (arg1->last ())) + { + if (as_a (arg1->last ())->get_val () == 1) + last_cond_status = condition_status::CS_FALSE; + else + last_cond_status = condition_status::CS_TRUE; + } + else + conditions.add (new bit_condition (arg1->last ()->copy (), new bit (0), + EQ_EXPR)); + + return; + } + + bit_expression *eq_cond = construct_equal_cond (arg1, arg2); + if (!eq_cond) + return; + + bit_expression *gt_cond = construct_great_than_cond (arg1, arg2); + if (gt_cond) + /* Otherwise its status is already set. */ + conditions.add (new bit_or_expression (eq_cond, gt_cond)); +} + + +/* Adds LESS OR EQUAL condition of given variables to state. */ + +bool +state::add_less_or_equal_cond (tree arg1, tree arg2) +{ + return add_binary_cond (arg1, arg2, &state::add_less_or_equal_cond); +} + + +/* Adds less or equal condition for two values. */ + +void +state::add_less_or_equal_cond (value *arg1, value *arg2) +{ + if (is_bit_vector (arg1) && is_bit_vector (arg2)) + { + bool is_less_than = check_const_value_is_less_than (arg1, arg2); + bool is_equal = check_const_value_equality (arg1, arg2); + last_cond_status = (is_less_than | is_equal) + ? condition_status::CS_TRUE + : condition_status::CS_FALSE; + return; + } + + last_cond_status = condition_status::CS_SYM; + bit_expression *eq_cond = construct_equal_cond (arg1, arg2); + if (!eq_cond) + return; + + bit_expression *lt_cond = construct_less_than_cond (arg1, arg2); + if (lt_cond) + /* Otherwise its status is already set. */ + conditions.add (new bit_or_expression (eq_cond, lt_cond)); +} + + +/* Adds a bool condition to state. */ + +bool +state::add_bool_cond (tree arg) +{ + if (!is_declared (arg)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Sym-Exec: Argument must be declared " + "for bool condition.\n"); + + return false; + } + + value *arg_bits = var_states.get (arg); + for (size_t i = 0; i < arg_bits->length (); i++) + if (is_a ((*arg_bits)[i]) + && as_a ((*arg_bits)[i])->get_val () != 0) + { + last_cond_status = condition_status::CS_TRUE; + print_conditions (); + return true; + } + + if (is_bit_vector (arg_bits)) + { + last_cond_status = condition_status::CS_FALSE; + print_conditions (); + return true; + } + + bit_expression *prev = nullptr; + for (size_t i = 0; i < arg_bits->length (); i++) + { + if (is_a ((*arg_bits)[i])) + continue; + + bit_condition *not_eq_cond + = new bit_condition ((*arg_bits)[i], new bit (0), NE_EXPR); + if (prev) + prev = new bit_or_expression (not_eq_cond, prev); + else + prev = not_eq_cond; + } + + last_cond_status = condition_status::CS_SYM; + conditions.add (prev); + print_conditions (); + return true; +} + + +/* Does preprocessing and postprocessing for condition adding. + Handles value creation for constants and their removement in the end. */ + +bool +state::add_binary_cond (tree arg1, tree arg2, binary_cond_func cond_func) +{ + bool arg1_is_declared = is_declared (arg1); + bool arg2_is_declared = is_declared (arg2); + + if (!arg1_is_declared && !arg2_is_declared) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Sym-Exec: At least one of arguments must be" + " declared for adding the condition.\n"); + + return false; + } + + if (arg1_is_declared) + declare_if_needed (arg2, var_states.get (arg1)->length ()); + + if (arg2_is_declared) + declare_if_needed (arg1, var_states.get (arg2)->length ()); + + value *arg1_val = var_states.get (arg1); + value arg1_const_val (MAX_VALUE_SIZE, false); + + if (arg1_val == NULL && TREE_CODE (arg1) == INTEGER_CST) + { + arg1_const_val = create_val_for_const (arg1, + var_states.get (arg2)->length ()); + arg1_val = &arg1_const_val; + } + + value *arg2_val = var_states.get (arg2); + value arg2_const_val (MAX_VALUE_SIZE, false); + if (arg2_val == NULL && TREE_CODE (arg2) == INTEGER_CST) + { + arg2_const_val = create_val_for_const (arg2, + var_states.get (arg1)->length ()); + arg2_val = &arg2_const_val; + } + + (this->*cond_func) (arg1_val, arg2_val); + print_conditions (); + return true; +} + + +/* Constructs expression trees of equal condition for given values. */ + +bit_expression * +state::construct_equal_cond (value *arg1, value *arg2) +{ + /* If both arguments are constants then we can evaluate it. */ + if (is_bit_vector (arg1) && is_bit_vector (arg2)) + { + bool result = check_const_value_equality (arg1, arg2); + last_cond_status = result ? condition_status::CS_TRUE + : condition_status::CS_FALSE; + return nullptr; + } + + /* When some bits are constants, and they differ by value, + then we can evaluate it to be false. */ + for (size_t i = 0; i < arg1->length (); i++) + { + if (is_a ((*arg1)[i]) && is_a ((*arg2)[i]) + && as_a ((*arg1)[i])->get_val () + != as_a ((*arg2)[i])->get_val ()) + { + last_cond_status = condition_status::CS_FALSE; + return nullptr; + } + } + + bit_expression *prev = nullptr; + for (size_t i = 0; i < arg1->length (); i++) + { + bit_condition *eq_expr = new bit_condition ((*arg1)[i]->copy (), + (*arg2)[i]->copy (), EQ_EXPR); + if (prev) + prev = new bit_and_expression (eq_expr, prev); + else + prev = eq_expr; + } + + return prev; +} + + +value::value (unsigned size, bool is_unsigned) : is_unsigned (is_unsigned) +{ + number.create (size); +} + + +value::value (const value &other) : is_unsigned (other.is_unsigned) +{ + number.create (other.length ()); + for (size_t i = 0; i < other.length (); ++i) + { + value_bit *temp = other[i] ? other[i]->copy () : other[i]; + number.quick_push (temp); + } +} + + +/* Returns pushed bits count. */ + +unsigned +value::length () const +{ + return number.length (); +} + + +/* Wrapper of vec<..>::operator[] for the bit-vector. */ + +value_bit *& +value::operator[] (unsigned i) +{ + return number[i]; +} + + +/* Assignment operator. If the specified value's size is smaller, + then 0 constant bit will be assigned to the remaining upper bits. */ + +value & +value::operator= (const value &other) +{ + unsigned smallest = number.allocated () < other.length () + ? number.allocated () : other.length (); + + for (size_t i = 0; i < smallest; i++) + if (i < number.length ()) + { + delete number[i]; + number[i] = other[i]->copy (); + } + else + number.quick_push (other[i]->copy ()); + + for (size_t i = smallest; i < number.allocated (); i++) + if (i < number.length ()) + { + delete number[i]; + number[i] = other.is_unsigned ? new bit (0) + : other[other.length () - 1]->copy (); + } + else + number.quick_push (other.is_unsigned + ? new bit (0) : other[other.length () - 1]->copy ()); + + return (*this); +} + + +/* Wrapper of vec<..>::operator[] const for the bit-vector. */ + +value_bit * +value::operator[] (unsigned i) const +{ + return number[i]; +} + + +/* Wrapper of vec<..>.exists for the bit-vector. */ + +bool +value::exists () const +{ + return number.exists (); +} + + +/* Returns the size in bits. */ + +unsigned +value::allocated () const +{ + return number.allocated (); +} + + +/* Returns a reference the last bit. */ + +value_bit *& +value::last () +{ + return number.last (); +} + + +/* Make a copy of given bits. */ + +vec * +state::make_copy (vec *bits) +{ + vec < value_bit * > *copied_bits = new vec (); + copied_bits->create (bits->length ()); + for (size_t i = 0; i < bits->length (); i++) + copied_bits->quick_push ((*bits)[i]->copy ()); + + return copied_bits; +} + + +/* Returns status of last added condition. */ + +condition_status +state::get_last_cond_status () +{ + return last_cond_status; +} + + +/* Prints the given value. */ + +void +state::print_value (value *var) +{ + if (!dump_file || !(dump_flags & TDF_DETAILS)) + return; + + fprintf (dump_file, "{"); + for (int i = var->length () - 1; i >= 0; i--) + { + (*var)[i]->print (); + + if (i) + fprintf (dump_file, ", "); + } + fprintf (dump_file, "}\n"); +} + + +/* Get the last 1 bit index. */ + +size_t +last_set_bit (const value &polynomial) +{ + for (size_t i = 0; i < polynomial.length (); ++i) + { + if (as_a (polynomial[polynomial.length () - i - 1])->get_val ()) + return polynomial.length () - i - 1; + } + return 0; +} + + +/* Prints added conditions. */ + +void +state::print_conditions () +{ + if (!dump_file || !(dump_flags & TDF_DETAILS)) + return; + + fprintf (dump_file, "Conditions {"); + auto iter = conditions.begin (); + while (true) + { + if (iter != conditions.end ()) + { + (*iter)->print (); + ++iter; + } + + if (iter != conditions.end ()) + fprintf (dump_file, ", "); + else + break; + } + fprintf (dump_file, "}\n"); +} + + +/* Pushes the given bit to the end of the bit vector. */ + +value_bit ** +value::push (value_bit *elem) +{ + return number.quick_push (elem); +} + + +value::~value () +{ + free_bits (); + number.release (); +} + + +/* Removes given sequence of bits. */ + +void +value::free_bits () +{ + if (!number.exists ()) + return; + + for (size_t i = 0; i < number.length (); i++) + { + delete number[i]; + number[i] = nullptr; + } +} + + +/* For the given value_bit, iterates over its expression tree, complements + those bit which came from the given origin. */ + +value_bit * +state::complement_bits_with_origin (value_bit *root, tree origin) +{ + /* Be careful. This function doesn't make a full copy of the bit. */ + if (!is_a (root)) + { + if (is_a (root) + && as_a (root)->get_origin () == origin) + root = new bit_complement_expression (root); + + return root; + } + + bit_expression *expr_root = as_a (root); + hash_set nodes_to_consider; + nodes_to_consider.add (expr_root); + hash_map node_to_parent; + node_to_parent.put (expr_root, nullptr); + + /* Traversing expression tree. */ + while (!nodes_to_consider.is_empty ()) + { + value_bit *cur_element = *nodes_to_consider.begin (); + nodes_to_consider.remove (cur_element); + + if (is_a (cur_element)) + { + if (as_a (cur_element)->get_origin () != origin) + continue; + + bit_expression *parent + = as_a (*node_to_parent.get (cur_element)); + if (is_a (parent)) + { + value_bit *parent_of_parent = *node_to_parent.get (parent); + if (parent_of_parent) + { + bit_expression *parent_of_parent_expr + = as_a (parent_of_parent); + parent->set_right (nullptr); + delete parent; + parent_of_parent_expr->get_left () == parent + ? parent_of_parent_expr->set_left (cur_element) + : parent_of_parent_expr->set_right (cur_element); + } + else + { + /* Parent is our root. */ + as_a (root)->set_right (nullptr); + delete root; + root = cur_element; + } + } + else + { + value_bit* new_bit = new bit_complement_expression (cur_element); + parent->get_left () == cur_element ? parent->set_left (new_bit) + : parent->set_right (new_bit); + } + continue; + } + + bit_expression* cur_elem_expr = as_a (cur_element); + value_bit *left = cur_elem_expr->get_left (); + value_bit *right = cur_elem_expr->get_right (); + if (left != nullptr && !is_a (left)) + { + nodes_to_consider.add (left); + node_to_parent.put (left, cur_element); + } + + if (right != nullptr && !is_a (right)) + { + nodes_to_consider.add (right); + node_to_parent.put (right, cur_element); + } + } + + return root; +} + + +/* Iterates over every bit of the given value and complements their + expression trees' those bits, that came from the given origin. */ + +void +state::complement_val_bits_with_origin (value *val, tree origin) +{ + for (size_t i = 0; i < val->length (); i++) + { + (*val)[i] = complement_bits_with_origin ((*val)[i], origin); + } +} + + +/* Complements all bits of all values that came from the given origin. */ + +void +state::complement_all_vars_bits_with_origin (tree origin) +{ + for (auto iter = var_states.begin (); iter != var_states.end (); ++iter) + { + complement_val_bits_with_origin (&(*iter).second, origin); + } +} + + +/* Complements all bits with the given origin of all added conditions. */ + +void +state::complement_conditions_with_origin (tree origin) +{ + hash_set updated_conditions; + for (auto iter = conditions.begin (); iter != conditions.end (); ++iter) + updated_conditions.add (as_a ( + complement_bits_with_origin (*iter, origin))); + + conditions.empty (); + for (auto iter = updated_conditions.begin (); + iter != updated_conditions.end (); ++iter) + conditions.add (*iter); +} + + +/* Complements all bits with the given origin of all values + and added conditions. */ + +void +state::complement_state_with_origin (tree origin) +{ + complement_all_vars_bits_with_origin (origin); + complement_conditions_with_origin (origin); +} + + +/* Performs the specified operation on passed variables. */ + +bool +state::do_operation (tree_code op_code, tree arg1, tree arg2, tree dest) +{ + switch (op_code) + { + case BIT_NOT_EXPR: + return do_complement (arg1, dest); + case NOP_EXPR: + case SSA_NAME: + case VAR_DECL: + case INTEGER_CST: + return do_assign (arg1, dest); + case LSHIFT_EXPR: + return do_binary_operation (arg1, arg2, dest, &state::do_shift_left); + case RSHIFT_EXPR: + return do_binary_operation (arg1, arg2, dest, &state::do_shift_right); + case BIT_AND_EXPR: + return do_binary_operation (arg1, arg2, dest, &state::do_and); + case BIT_IOR_EXPR: + return do_binary_operation (arg1, arg2, dest, &state::do_or); + case BIT_XOR_EXPR: + return do_binary_operation (arg1, arg2, dest, &state::do_xor); + case PLUS_EXPR: + return do_binary_operation (arg1, arg2, dest, &state::do_add); + case MINUS_EXPR: + return do_binary_operation (arg1, arg2, dest, &state::do_sub); + case MULT_EXPR: + return do_binary_operation (arg1, arg2, dest, &state::do_mul); + default: + { + if (dump_file) + fprintf (dump_file, + "Warning, encountered unsupported operation " + "with %s code while executing assign statement!\n", + get_tree_code_name (op_code)); + return false; + } + } +} diff --git a/gcc/sym-exec/sym-exec-state.h b/gcc/sym-exec/sym-exec-state.h new file mode 100644 index 00000000000..5a3f9ebbbff --- /dev/null +++ b/gcc/sym-exec/sym-exec-state.h @@ -0,0 +1,436 @@ +/* State will store states of variables for a function's single execution path. + It will be used for bit-level symbolic execution to determine values of bits + of function's return value and symbolic marked arguments. */ + + +#ifndef SYM_EXEC_STATE_H +#define SYM_EXEC_STATE_H + +#define MAX_VALUE_SIZE 64 + +#include "sym-exec-expr-is-a-helper.h" + +struct value { + private: + vec number; + + public: + const bool is_unsigned; + + value (unsigned size, bool is_unsigned); + value (const value &other); + + /* Pushes the given bit to the end of the bit-vector. */ + value_bit **push (value_bit *elem); + + /* Returns pushed bits count. */ + unsigned length () const; + + /* Returns a reference the last bit. */ + value_bit *&last (); + + /* Returns the size in bits. */ + unsigned allocated () const; + + /* Wrapper of vec<..>.exists for the bit-vector. */ + bool exists () const; + + /* Wrapper of vec<..>::operator[] for the bit-vector. */ + value_bit *&operator[] (unsigned i); + + /* Assignment operator. If the specified value's size is smaller, + then 0 constant bit will be assigned to the remaining upper bits. */ + value &operator= (const value &other); + + /* Wrapper of vec<..>::operator[] const for the bit-vector. */ + value_bit *operator[] (unsigned i) const; + ~value (); + + /* Removes given sequence of bits. */ + void free_bits (); +}; + +/* Stores states of variables' values on bit-level. */ + +class state { + typedef void (state::*binary_func) (value *arg1, value *arg2, tree dest); + typedef value_bit *(*bit_func) (value_bit *bit1, value_bit *bit2); + typedef value_bit *(*bit_func3) (value_bit *var1, value_bit *var2, + value_bit **var3); + typedef void (state::*binary_cond_func) (value *arg1, value *arg2); + + private: + + /* Here is stored values by bits of each variable. */ + hash_map var_states; + + /* Here is stored conditions of symbolic bits. */ + hash_set conditions; + + /* The result of last added condition. */ + condition_status last_cond_status = condition_status::CS_NO_COND; + + /* Creates value for given constant tree. */ + static value create_val_for_const (tree var, size_t size); + + /* Checks if sizes of arguments and destination are compatible. */ + bool check_args_compatibility (tree arg1, tree arg2, tree dest); + + /* Adds equality condition for two values. */ + void add_equal_cond (value *arg1, value *arg2); + + /* Adds not equal condition for two values. */ + void add_not_equal_cond (value *arg1, value *arg2); + + /* Adds greater than condition for two values. */ + void add_greater_than_cond (value *arg1, value *arg2); + + /* Adds less than condition for two values. */ + void add_less_than_cond (value *arg1, value *arg2); + + /* Adds greater or equal condition for two values. */ + void add_greater_or_equal_cond (value *arg1, value *arg2); + + /* Adds less or equal condition for two values. */ + void add_less_or_equal_cond (value *arg1, value *arg2); + + /* Does preprocessing and postprocessing for condition adding. + Handles value creation for constants and their removement in the end. */ + bool add_binary_cond (tree arg1, tree arg2, binary_cond_func cond_func); + + /* Constructs expression trees of greater than condition for given values. */ + bit_expression *construct_great_than_cond (value *arg1, value *arg2); + + /* Constructs expression trees of less than condition for given values. */ + bit_expression *construct_less_than_cond (value *arg1, value *arg2); + + /* Constructs expression trees of equal condition for given values. */ + bit_expression *construct_equal_cond (value *arg1, value *arg2); + + /* A wrapper for operations on two bits. + Operation and operands are passed as arguments. */ + static value_bit *operate_bits (bit_func bit_op, value_bit *bit1, + value_bit *bit2, value_bit **bit3); + + /* A wrapper for operations on three bits. + Operation and operands are passed as arguments. */ + static value_bit *operate_bits (bit_func3 bit_op, value_bit *bit1, + value_bit *bit2, value_bit **bit3); + + /* Performs the given operation on passed arguments. + The result is stored in dest. */ + template + void operate (value *arg1, value *arg2, value_bit **bit_arg, tree dest, + func bit_op); + + /* Does preprocessing and postprocessing for expressions with tree operands. + Handles value creation for constant and their removement in the end. */ + bool do_binary_operation (tree arg1, tree arg2, tree dest, + binary_func bin_func); + + /* Performs AND operation on given values. The result is stored in dest. */ + void do_and (value *arg1, value *arg2, tree dest); + + /* Performs OR operation on given values. The result is stored in dest. */ + void do_or (value *arg1, value *arg2, tree dest); + + /* Performs XOR operation on given values. The result is stored in dest. */ + void do_xor (value *arg1, value *arg2, tree dest); + + /* Performs shift right operation on given values. + The result is stored in dest. */ + void do_shift_right (value *arg1, value *arg2, tree dest); + + /* Performs shift left operation on given values. + The result is stored in dest. */ + void do_shift_left (value *arg1, value *arg2, tree dest); + + /* Adds given values. The result is stored in dest. */ + void do_add (value *arg1, value *arg2, tree dest); + + /* Subtracks second value from the first. The result is stored in dest. */ + void do_sub (value *arg1, value *arg2, tree dest); + + /* Performs AND operation on two bits. */ + static value_bit *and_two_bits (value_bit *arg1, value_bit *arg2); + + /* ANDs every bit of the value with var_bit, stroes the result in var1. */ + void and_number_bit (value *var1, value_bit *var_bit); + + /* Multiplies given values. The result is stored in dest. */ + void do_mul (value *arg1, value *arg2, tree dest); + + /* Performs AND operation for 2 symbolic_bit operands. */ + static value_bit *and_sym_bits (const value_bit *var1, + const value_bit *var2); + + /* Performs AND operation for a symbolic_bit and const_bit operands. */ + static value_bit *and_var_const (const value_bit *var1, + const bit *const_bit); + + /* Performs AND operation for 2 constant bit operands. */ + static bit *and_const_bits (const bit *const_bit1, const bit *const_bit2); + + /* Performs OR operation on two bits. */ + static value_bit *or_two_bits (value_bit *arg1_bit, value_bit *arg2_bit); + + /* Performs OR operation for 2 symbolic_bit operands. */ + static value_bit *or_sym_bits (const value_bit *var1, + const value_bit *var2); + + /* Performs OR operation for a symbolic_bit and a constant bit operands. */ + static value_bit *or_var_const (const value_bit *var1, + const bit *const_bit); + + /* Performs OR operation for 2 constant bit operands. */ + static bit *or_const_bits (const bit *const_bit1, const bit *const_bit2); + + /* Performs complement operation on a bit. */ + static value_bit *complement_a_bit (value_bit *var); + + /* Performs NOT operation for constant bit. */ + static bit *complement_const_bit (const bit *const_bit); + + /* Performs NOT operation for symbolic_bit. */ + static value_bit *complement_sym_bit (const value_bit *var); + + /* Performs XOR operation on two bits. */ + static value_bit *xor_two_bits (value_bit *var1, value_bit *var2); + + /* Performs XOR operation for 2 symbolic_bit operands. */ + static value_bit *xor_sym_bits (const value_bit *var1, + const value_bit *var2); + + /* Performs XOR operation for 2 constant bit operands. */ + static bit *xor_const_bits (const bit *const_bit1, const bit *const_bit2); + + /* Performs XOR operation for a symbolic_bit and const_bit operands. */ + static value_bit *xor_var_const (const value_bit *var, + const bit *const_bit); + + /* Shift_right operation. Case: var2 is a symbolic value. */ + static value_bit *shift_right_sym_bits (value_bit *var1, value_bit *var2); + + /* Shift_left operation. Case: var2 is a symbolic value. */ + static value_bit *shift_left_sym_bits (value_bit *var1, value_bit *var2); + + /* Shifts var right by size of shift_value. */ + value *shift_right_by_const (value *var, size_t shift_value); + + /* Return node which has a const bit child. Traversal is done based + on safe branching. */ + static void get_parent_with_const_child (value_bit *root, + bit_expression *&parent, + bit_expression *&parent_of_parent); + + /* Checks whether state for variable with specified name already + exists or not. */ + bool is_declared (tree var); + + /* Declares given variable if it has not been declared yet. */ + void declare_if_needed (tree var, size_t size); + + /* Shifts number left by size of shift_value. */ + value *shift_left_by_const (const value *number, size_t shift_value); + + /* Adds two bits and carry value. + Resturn result and stores new carry bit in "carry". */ + static value_bit *full_adder (value_bit *var1, value_bit *var2, + value_bit **carry); + + /* Returns the additive inverse of the given number. */ + value *additive_inverse (const value *number); + + /* Adds two values, stores the result in the first one. */ + void add_numbers (value *var1, const value *var2); + + /* Make a copy of given bits. */ + static vec *make_copy (vec *bits); + + public: + state () = default; + + ~state (); + + /* Adds an empty state for the given variable. */ + bool decl_var (tree name, unsigned size); + + state (const state &s); + + /* Adds the given variable to state. */ + bool add_var_state (tree var, value *state); + + /* Remove all states from the states' vector. */ + static void remove_states (vec *states); + + /* Remove all states from the states' vector and release the vector. */ + static void clear_states (vec *states); + + /* Removes all variables added to the state. */ + void clear_var_states (); + + /* Removes all conditions added to the state. */ + void clear_conditions (); + + /* Adds the given condition to the state. */ + bool add_condition (bit_expression *cond); + + /* Bulk add the given conditions to the state. */ + bool bulk_add_conditions (const hash_set &conds); + + /* Get value of the given variable. */ + value *get_value (tree var); + + /* Get the value of the tree, which is in the beginning of the var_states. */ + value *get_first_value (); + + /* Returns the list of conditions in the state. */ + const hash_set &get_conditions (); + + /* Adds a variable with unknown value to state. Such variables are + represented as sequence of symbolic bits. */ + bool make_symbolic (tree var, unsigned size); + + /* Returns size of the given variable. */ + unsigned get_var_size (tree var); + + /* Prints the given value. */ + static void print_value (value *var); + + /* Prints added conditions. */ + void print_conditions (); + + /* Returns the number represented by the value. */ + static unsigned HOST_WIDE_INT + make_number (const value *var); + + /* Checks if all bits of the given value have constant bit type. */ + static bool is_bit_vector (const value *var); + + /* Performs the specified operation on passed variables. */ + bool do_operation (tree_code op_code, tree arg1, tree arg2, tree dest); + + /* Does Assignment. */ + bool do_assign (tree arg, tree dest); + + /* Assigns pow 2 value. */ + bool do_assign_pow2 (tree dest, unsigned pow); + + /* Negates given variable. */ + bool do_complement (tree arg, tree dest); + + /* Adds EQUAL condition of given variables to state. */ + bool add_equal_cond (tree arg1, tree arg2); + + /* Adds NOT EQUAL condition of given variables to state. */ + bool add_not_equal_cond (tree arg1, tree arg2); + + /* Adds GREATER THAN condition of given variables to state. */ + bool add_greater_than_cond (tree arg1, tree arg2); + + /* Adds LESS THAN condition of given variables to state. */ + bool add_less_than_cond (tree arg1, tree arg2); + + /* Adds GREATER OR EQUAL condition of given variables to state. */ + bool add_greater_or_equal_cond (tree arg1, tree arg2); + + /* Adds LESS OR EQUAL condition of given variables to state. */ + bool add_less_or_equal_cond (tree arg1, tree arg2); + + /* Adds a bool condition to state. */ + bool add_bool_cond (tree arg); + + /* Checks whether the given two constant values are equal. */ + static bool check_const_value_equality (value *arg1, value *arg2); + + /* Checks whether the given two constant values are not equal. */ + static bool check_const_value_are_not_equal (value *arg1, value *arg2); + + /* Checks whether the first given constant value + is greater than the second one. */ + static bool check_const_value_is_greater_than (value *arg1, value *arg2); + + /* Checks whether the first given constant value + is less than the second one. */ + static bool check_const_value_is_less_than (value *arg1, value *arg2); + + /* For the given value_bit, iterates over its expression tree, complements + those bit which came from the given origin. */ + static value_bit *complement_bits_with_origin (value_bit *root, tree origin); + + /* Iterates over every bit of the given value and complements their + expression trees' those bits, that came from the given origin. */ + static void complement_val_bits_with_origin (value *val, tree origin); + + /* Complements all bits of all values that came from the given origin. */ + void complement_all_vars_bits_with_origin (tree origin); + + /* Complements all bits with the given origin of all added conditions. */ + void complement_conditions_with_origin (tree origin); + + /* Complements all bits with the given origin of all values + and added conditions. */ + void complement_state_with_origin (tree origin); + + /* Returns status of last added condition. */ + condition_status get_last_cond_status (); +}; + + +size_t min (size_t a, size_t b, size_t c); + + +/* Performs the given operation on passed arguments. + The result is stored in dest. */ + +template +void +state::operate (value *arg1, value *arg2, value_bit **bit_arg, tree dest, + func bit_op) +{ + value *dest_var = var_states.get (dest); + size_t min_iter = min (arg1->length (), arg2->length (), dest_var->length ()); + + size_t i = 0; + for (; i < min_iter; i++) + { + value_bit *temp = (*var_states.get (dest))[i]; + (*var_states.get (dest))[i] = operate_bits (bit_op, (*arg1)[i], + (*arg2)[i], bit_arg); + delete temp; + } + + if (i >= dest_var->length ()) + return; + + value *biggest = arg1; + value_bit *sign_bit = (*arg2)[i - 1]; + if (arg2->length () > arg1->length ()) + { + biggest = arg2; + sign_bit = (*arg1)[i - 1]; + } + + min_iter = min (biggest->length (), dest_var->length (), dest_var->length ()); + for (; i < min_iter; i++) + { + value_bit *temp = (*var_states.get (dest))[i]; + (*var_states.get (dest))[i] = operate_bits (bit_op, (*biggest)[i], + sign_bit, bit_arg); + delete temp; + } + + if (i >= dest_var->length ()) + return; + + sign_bit = (*biggest)[i - 1]; + for (; i < dest_var->length (); i++) + { + value_bit *temp = (*var_states.get (dest))[i]; + (*var_states.get (dest))[i] = operate_bits (bit_op, sign_bit, sign_bit, + bit_arg); + delete temp; + } +} + +#endif /* SYM_EXEC_STATE_H. */ -- 2.25.1 From patchwork Fri Oct 18 15:01:48 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Mariam Arutunian X-Patchwork-Id: 1999259 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=BrGMkA0S; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XVShL1PLCz1xvV for ; Sat, 19 Oct 2024 02:06:10 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 652B33857C6C for ; Fri, 18 Oct 2024 15:06:08 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-lj1-x235.google.com (mail-lj1-x235.google.com [IPv6:2a00:1450:4864:20::235]) by sourceware.org (Postfix) with ESMTPS id 1982D3858C42 for ; Fri, 18 Oct 2024 15:02:02 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 1982D3858C42 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 1982D3858C42 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::235 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263736; cv=none; b=dxYoj20TQ0bG5uCbTe7K/doab5OcB/t4sdhqGlBEFOtoTHRpL+9IQRXcjSKlehkUKJpr1IoqtYaZCYHcWOU0XkDWbg/iLBBrcWCrK/Pso50QRiC44pMN94LmPmugs9jL3LEYFgtVzB8auTZmtMBX2Kov2BvR/UJ1/mHHFlqSp0E= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263736; c=relaxed/simple; bh=0sfADzGVLPG5GleWfLNkIv2npi3wMBXsYfB3V6b9V4s=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=qtp+Quw2dKJUAOTmb46/JruVzzwAnI4vVC+MmgJzqQXs3KzEUdBA5L8pGtmnkY0kV1rAUAbi7ov0C9jE2O49DbrzWtt1tDTrEoE0S7jqxIqBhwz9ryU+z5IaZiUlap0OGGZU8IZ8a7ssxdug4bY29ZLdUUEKQzrqYbft4KoExP4= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-lj1-x235.google.com with SMTP id 38308e7fff4ca-2fb584a8f81so26893541fa.3 for ; Fri, 18 Oct 2024 08:02:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1729263720; x=1729868520; darn=gcc.gnu.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=U1GVS96PrJ+97blLA+9kD2AB5N+wFHvX/h1C6kAjUKA=; b=BrGMkA0ShGm+FAat0639Kit7m9GDiUjsdsyNzD0ggxfl8MG/4o6R0piRL+7R+1dT+Z i1Ot4b3xcTwaGoIsRSa/RB2/6gYbDy/bMkkP/57ZaPK3QmwmNKl9jwyHCWi1afNwrO6b +EzJig4+yCm8tMdjl6XIdUPiJVNzq7EmGV2g0tDahaxp9JQ77kSgICIkbn5y7OuRFry3 +XAinMUrBg3Kg8LIFmxN6HFBWoQm3JtlkQ+2qqC5Msf/Kyw+ikwWE0ElvXErO3IQA9ZD qpE8Db1f5xfCJ1Y8RDF7514HFKIKTP+26nP+E72fpe/9/5ffieCemVII6+qI06I9M3cv tLPA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729263720; x=1729868520; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=U1GVS96PrJ+97blLA+9kD2AB5N+wFHvX/h1C6kAjUKA=; b=ERKAP055stkiq2T991xfdhRlXYD06P2Vc0JFxFKLX8tejunggBWc5EdMiUbpGtkVLx d8H1qAp8GhZhWwaMySMVMMcHJVN3Z0FOmoXfudIOG0qllQYt/ApSKJsA9fQeEdQPNVmb nhu2yME1eaAnBF60zj+LjLfeHraS+VbtAezHHFem/NBqKXRI9PMoHPxlk/HZusQqt67k nLmJSs0wC6RYop+9YBqMw9RuZ++hRtjnuCXPqLREsQs3VM4jlul+e2XKvKW4AiQNEGxW uq3lZ4ciTqIG3t09ks6pBJ/+oBMakicz8critXROabqUWKz4JnB+P5yWbuustuNDMqXm gDYQ== X-Gm-Message-State: AOJu0YwRIYBeoZIEgGgR3EWmoVqo4FpAw/41tyIleoY9EzZRieew18/t rFwoQkjW2/CMut061nfc6N6ryUKKNIJsavhLWNZr8pFXLM0jWEJoJv5gzkJbUT+B+Xe/fxWbNwn 2k/sqFWx4bxR6lQL3TPURBpqCqyKZURZZYrI= X-Google-Smtp-Source: AGHT+IHpxb+hCrNHJXncNcJ6aN+0xFrnKwC4IWt88NYDfKdwxAmcJDpYTZIX4UgqFOPznJsmYeFAlG6ItlJYvNHqRUU= X-Received: by 2002:a2e:a585:0:b0:2fb:5d2c:7509 with SMTP id 38308e7fff4ca-2fb82ea304cmr16113261fa.14.1729263719725; Fri, 18 Oct 2024 08:01:59 -0700 (PDT) MIME-Version: 1.0 From: Mariam Arutunian Date: Fri, 18 Oct 2024 19:01:48 +0400 Message-ID: Subject: [RFC/RFA] [PATCH v5 10/12] Verify detected CRC loop with symbolic execution and LFSR matching. To: GCC Patches , Jeff Law X-Spam-Status: No, score=-7.6 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, HTML_MESSAGE, KAM_SHORT, RCVD_IN_DNSWL_NONE, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org Symbolically execute potential CRC loops and check whether the loop actually calculates CRC (uses LFSR matching). Calculated CRC and created LFSR are compared on each iteration of the potential CRC loop. gcc/ * Makefile.in (OBJS): Add crc-verification.o. * crc-verification.cc: New file. * crc-verification.h: New file. * gimple-crc-optimization.cc (loop_calculates_crc): New function. (is_output_crc): Likewise. (swap_crc_and_data_if_needed): Likewise. (validate_crc_and_data): Likewise. (optimize_crc_loop): Likewise. (get_output_phi): Likewise. (execute): Add check whether potential CRC loop calculates CRC. gcc/sym-exec/ * sym-exec-state.cc (create_reversed_lfsr): New function. (create_forward_lfsr): Likewise. (last_set_bit): Likewise. (create_lfsr): Likewise. * sym-exec-state.h (is_bit_vector): Reorder, make the function public and static. (create_reversed_lfsr): New static function declaration. (create_forward_lfsr): New static function declaration. Signed-off-by: Mariam Arutunian Mentored-by: Jeff Law --- gcc/Makefile.in | 1 + gcc/crc-verification.cc | 1298 ++++++++++++++++++++++++++++++++ gcc/crc-verification.h | 161 ++++ gcc/gimple-crc-optimization.cc | 327 +++++++- gcc/sym-exec/sym-exec-state.cc | 101 +++ gcc/sym-exec/sym-exec-state.h | 11 + 6 files changed, 1897 insertions(+), 2 deletions(-) create mode 100644 gcc/crc-verification.cc create mode 100644 gcc/crc-verification.h diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 6eab34d62bb..6b8a37a180c 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1717,6 +1717,7 @@ OBJS = \ tree-iterator.o \ tree-logical-location.o \ tree-loop-distribution.o \ + crc-verification.o \ gimple-crc-optimization.o \ sym-exec/sym-exec-expression.o \ sym-exec/sym-exec-state.o \ diff --git a/gcc/crc-verification.cc b/gcc/crc-verification.cc new file mode 100644 index 00000000000..a556bc92467 --- /dev/null +++ b/gcc/crc-verification.cc @@ -0,0 +1,1298 @@ +/* Execute symbolically all paths of the loop. + Calculate the value of the polynomial by executing loop with real values to + create LFSR state. + After each iteration check that final states of calculated CRC values match + determined LFSR. + Copyright (C) 2022-2024 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#include "crc-verification.h" +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "tree.h" +#include "gimple.h" +#include "ssa.h" +#include "gimple-iterator.h" +#include "tree-cfg.h" +#include "cfganal.h" +#include "tree-ssa-loop.h" + +/* Check whether defined variable is used outside the loop, only + CRC's definition is allowed to be used outside the loop. */ + +bool +crc_symbolic_execution::is_used_outside_the_loop (tree def) +{ + imm_use_iterator imm_iter; + gimple *use_stmt; + FOR_EACH_IMM_USE_STMT (use_stmt, imm_iter, def) + { + if (!flow_bb_inside_loop_p (m_crc_loop, use_stmt->bb)) + { + if (is_a (use_stmt) + && as_a (use_stmt) == m_output_crc) + return false; + if (dump_file) + fprintf (dump_file, "Defined variable is used outside the loop.\n"); + return true; + } + } + return false; +} + +/* Calculate value of the rhs operation of GS assigment statement + and assign it to lhs variable. */ + +bool +crc_symbolic_execution::execute_assign_statement (const gassign *gs) +{ + enum tree_code rhs_code = gimple_assign_rhs_code (gs); + tree lhs = gimple_assign_lhs (gs); + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "lhs type : %s \n", + get_tree_code_name (TREE_CODE (lhs))); + + /* This will filter some normal cases too. Ex. usage of array. */ + if (TREE_CODE (lhs) != SSA_NAME) + return false; + + /* Check uses only when m_output_crc is known. */ + if (m_output_crc) + if (is_used_outside_the_loop (lhs)) + return false; + + if (gimple_num_ops (gs) != 2 && gimple_num_ops (gs) != 3) + { + if (dump_file) + fprintf (dump_file, + "Warning, encountered unsupported operation, " + "with %s code while executing assign statement!\n", + get_tree_code_name (rhs_code)); + return false; + } + + tree op1 = gimple_assign_rhs1 (gs); + tree op2 = nullptr; + + if (gimple_num_ops (gs) == 3) + op2 = gimple_assign_rhs2 (gs); + + state *current_state = m_states.last (); + return current_state->do_operation (rhs_code, op1, op2, lhs); +} + +/* Add E edge into the STACK if it doesn't have an immediate + successor edge going to the loop header. + + When loop counter is checked in the if condition, + we mustn't continue on real path as we want to stop the execution before + the second iteration. */ + +bool +crc_symbolic_execution::add_edge (edge e, auto_vec &stack) +{ + if (EDGE_COUNT (e->dest->succs) == 0) + return false; + + edge next_bb_edge = EDGE_SUCC (e->dest, 0); + if (next_bb_edge && next_bb_edge->dest == m_crc_loop->header) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Completed one iteration. " + "Won't iterate loop once more, yet.\n"); + + return keep_states (); + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Adding the edge into the stack.\n"); + + /* If the result of the condition is true/false, + continue execution only by the true/false branch. */ + stack.quick_push (e); + } + return true; +} + +/* Add next basic blocks of the conditional block COND_BB + for the execution path into the STACK. + If the condition depends on symbolic values, keep both edges. + If the condition is true, keep true edge, else - false edge. + Returns true if addition succeeds. Otherwise - false. */ + +bool +crc_symbolic_execution::add_next_bbs (basic_block cond_bb, + state *new_branch_state, + auto_vec &stack) +{ + edge true_edge; + edge false_edge; + extract_true_false_edges_from_block (cond_bb, &true_edge, &false_edge); + + /* When the condition depends on symbolic values. */ + if (new_branch_state->get_last_cond_status () == CS_SYM) + { + /* Supported CRC cases may have only two states. */ + if (m_states.length () == 2) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Going to add a new state, " + "but there's already two states.\n"); + return false; + } + /* Add true branch's state into the states. + False branch's state will be kept in the current state. */ + m_states.quick_push (new_branch_state); + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Adding true and false edges into the stack.\n"); + + /* Add outgoing edges to the stack. */ + stack.quick_push (false_edge); + stack.quick_push (true_edge); + + return true; + } + /* When the condition evaluates to true. */ + else if (new_branch_state->get_last_cond_status () == CS_TRUE) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Condition is true.\n"); + add_edge (true_edge, stack); + } + /* When the condition evaluates to false. */ + else if (new_branch_state->get_last_cond_status () == CS_FALSE) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Condition is false.\n"); + add_edge (false_edge, stack); + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Something went wrong " + "during handling conditional statement.\n"); + return false; + } + + /* When we continue execution of only one path, + there's no need of new state. */ + delete new_branch_state; + return true; +} + +/* Add conditions depending on symbolic variables in the states. + + Keep conditions of each branch execution in its state. + Ex. + if (a == 0) // a's value is unknown + + new_branch_state.keep (a==0) + current_state.keep (a!=0) + + The condition is kept in the bit level. + For ex. + If a's size is 8 and its value is {symb_a, 0, 0, 0, 0, 0, 0, 0}, + then for a == 0 we'll have symb_a == 0 condition. */ + +bool +crc_symbolic_execution::add_condition (const gcond *cond, + state *current_state, + state *new_branch_state) +{ + tree lhs = gimple_cond_lhs (cond); + tree rhs = gimple_cond_rhs (cond); + switch (gimple_cond_code (cond)) + { + case EQ_EXPR: + { + new_branch_state->add_equal_cond (lhs, rhs); + if (new_branch_state->get_last_cond_status () == CS_SYM) + current_state->add_not_equal_cond (lhs, rhs); + return true; + } + case NE_EXPR: + { + new_branch_state->add_not_equal_cond (lhs, rhs); + if (new_branch_state->get_last_cond_status () == CS_SYM) + current_state->add_equal_cond (lhs, rhs); + return true; + } + case GT_EXPR: + { + new_branch_state->add_greater_than_cond (lhs, rhs); + if (new_branch_state->get_last_cond_status () == CS_SYM) + current_state->add_less_or_equal_cond (lhs, rhs); + return true; + } + case LT_EXPR: + { + new_branch_state->add_less_than_cond (lhs, rhs); + if (new_branch_state->get_last_cond_status () == CS_SYM) + current_state->add_greater_or_equal_cond (lhs, rhs); + return true; + } + case GE_EXPR: + { + new_branch_state->add_greater_or_equal_cond (lhs, rhs); + if (new_branch_state->get_last_cond_status () == CS_SYM) + current_state->add_less_than_cond (lhs, rhs); + return true; + } + case LE_EXPR: + { + new_branch_state->add_less_or_equal_cond (lhs, rhs); + if (new_branch_state->get_last_cond_status () == CS_SYM) + current_state->add_greater_than_cond (lhs, rhs); + return true; + } + default: + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Unsupported condition.\n"); + return false; + } + } +} + +/* Create new states for true and false branches. + Keep conditions in new created states. */ + +bool +crc_symbolic_execution::resolve_condition (const gcond *cond, + auto_vec &stack) +{ + state *current_state = m_states.last (); + state *new_branch_state = new state (*current_state); + + /* Create new states and for true and false branches keep corresponding + conditions. */ + if (!add_condition (cond, current_state, new_branch_state)) + return false; + + /* Add true and false edges to the stack. */ + return add_next_bbs (cond->bb, new_branch_state, stack); +} + +/* If final states are less than two, add new FINAL_STATE and return true. + Otherwise, return false. + Supported CRC cases may not have more than two final states. */ +bool crc_symbolic_execution::add_final_state (state *final_state) +{ + if (m_final_states.length () < 2) + m_final_states.quick_push (final_state); + else + { + if (dump_file) + fprintf (dump_file, + "There are already two final states\n"); + return false; + } + return true; +} + +/* Keep the state of the executed path in final states. */ + +bool crc_symbolic_execution::keep_states () +{ + if (m_states.is_empty ()) + return false; + + if (!add_final_state (m_states.last ())) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Couldn't add final state.\n"); + return false; + } + + m_states.pop (); + return true; +} + +/* Execute gimple statements of BB. + Keeping values of variables in the state. */ + +bool +crc_symbolic_execution::execute_bb_gimple_statements (basic_block bb, + auto_vec &stack) +{ + for (gimple_stmt_iterator bsi = gsi_start_bb (bb); + !gsi_end_p (bsi); gsi_next (&bsi)) + { + gimple *gs = gsi_stmt (bsi); + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Executing "); + print_gimple_stmt (dump_file, gs, dump_flags); + } + switch (gimple_code (gs)) + { + case GIMPLE_ASSIGN: + { + if (!execute_assign_statement (as_a (gs))) + return false; + break; + } + case GIMPLE_COND: + { + return resolve_condition (as_a (gs), stack); + } + /* Just skip debug statements. */ + case GIMPLE_DEBUG: + break; + default: + { + if (dump_file) + fprintf (dump_file, + "Warning, encountered unsupported statement, " + "while executing gimple statements!\n"); + return false; + } + } + } + + /* Add each outgoing edge of the current block to the stack, + despite the edges going to the loop header. + This code isn't reachable if the last statement of the basic block + is a conditional statement or return statement. + Those cases are handled separately. + We mustn't encounter edges going to the CRC loop header. */ + + edge out_edge; + edge_iterator ei; + FOR_EACH_EDGE (out_edge, ei, bb->succs) + if (out_edge->dest != m_crc_loop->header) + stack.quick_push (out_edge); + else + return false; + + return true; +} + +/* Assign values of phi instruction to its result. + Keep updated values in the state. */ + +bool +crc_symbolic_execution::execute_bb_phi_statements (basic_block bb, + edge incoming_edge) +{ + for (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gphi *phi = gsi.phi (); + tree lhs = gimple_phi_result (phi); + + /* Check uses only when m_output_crc is known. */ + if (m_output_crc) + if (is_used_outside_the_loop (lhs)) + return false; + + /* Don't consider virtual operands. */ + if (virtual_operand_p (lhs)) + continue; + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Determining the value " + "for the following phi.\n"); + print_gimple_stmt (dump_file, phi, dump_flags); + } + + tree rhs = PHI_ARG_DEF_FROM_EDGE (phi, incoming_edge); + + state *current_state = m_states.last (); + if (!current_state->do_operation (VAR_DECL, rhs, nullptr, lhs)) + return false; + } + return true; +} + +/* Execute all statements of BB. + Keeping values of variables in the state. */ + +bool +crc_symbolic_execution::execute_bb_statements (basic_block bb, + edge incoming_edge, + auto_vec &stack) +{ + if (!execute_bb_phi_statements (bb, incoming_edge)) + return false; + + return execute_bb_gimple_statements (bb, stack); +} + +/* If the phi statements' result variables have initial constant value in the + beginning of the loop, initialize those variables. */ + +void +assign_known_vals_to_header_phis (state *state, class loop *crc_loop) +{ + basic_block bb = crc_loop->header; + for (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + + gphi *phi = gsi.phi (); + tree lhs = gimple_phi_result (phi); + + /* Don't consider virtual operands. */ + if (virtual_operand_p (lhs)) + continue; + + tree inital_val = PHI_ARG_DEF_FROM_EDGE (phi, + loop_preheader_edge (crc_loop)); + if (TREE_CODE (inital_val) == INTEGER_CST) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "First value of phi is a constant, " + "assigning the number to "); + print_generic_expr (dump_file, lhs, dump_flags); + fprintf (dump_file, " variable.\n"); + } + state->do_operation (VAR_DECL, inital_val, + nullptr, lhs); + } + } +} + +/* If the phi statements' result variables have initial constant value in the + beginning of the loop, initialize those variables with + the value calculated during the previous iteration. */ + +bool +assign_calc_vals_to_header_phis (const vec &prev_states, + state *curr_state, + class loop *crc_loop) +{ + basic_block bb = crc_loop->header; + for (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + gphi *phi = gsi.phi (); + tree lhs = gimple_phi_result (phi); + + /* Don't consider virtual operands. */ + if (virtual_operand_p (lhs)) + continue; + tree inital_val = PHI_ARG_DEF_FROM_EDGE (phi, + loop_preheader_edge (crc_loop)); + if (TREE_CODE (inital_val) == INTEGER_CST) + { + tree input_tree = PHI_ARG_DEF_FROM_EDGE (phi, + loop_latch_edge (crc_loop)); + value * val_st1 = prev_states[0]->get_value (input_tree), + *val_st2 = prev_states[1]->get_value (input_tree); + if (!state::is_bit_vector (val_st1) + || !state::is_bit_vector (val_st2)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "The calculated values of "); + print_generic_expr (dump_file, input_tree, dump_flags); + fprintf (dump_file, " variable is not constant.\n"); + } + return false; + } + else if (!state::check_const_value_equality (val_st1, val_st2)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "The calculated values of "); + print_generic_expr (dump_file, input_tree, dump_flags); + fprintf (dump_file, " variable is different in the previous " + "iteration paths.\n"); + } + return false; + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Assigning calculated number to "); + print_generic_expr (dump_file, lhs, dump_flags); + fprintf (dump_file, " variable.\n"); + } + unsigned HOST_WIDE_INT calc_number + = state::make_number (val_st1); + tree calc_num_tree = build_int_cstu (TREE_TYPE (lhs), + calc_number); + curr_state->do_operation (VAR_DECL, calc_num_tree, nullptr, lhs); + } + } + } + return true; +} + +/* Create initial state of the CRC_LOOP's header BB variables which have + constant values. + If it is the first iteration of the loop, initialise variables with the + initial values, otherwise initialise the variable with the value calculated + during the previous execution. */ + +state * +crc_symbolic_execution::create_initial_state (class loop *crc_loop) +{ + state *curr_state = new state; + if (!m_final_states.is_empty ()) + { + if (!assign_calc_vals_to_header_phis (m_final_states, curr_state, + crc_loop)) + return nullptr; + state::remove_states (&m_final_states); + } + else + assign_known_vals_to_header_phis (curr_state, crc_loop); + return curr_state; +} + +/* Symbolically execute the CRC loop, doing one iteration. */ + +bool +crc_symbolic_execution::symb_execute_crc_loop () +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\n\nExecuting the loop with symbolic values.\n\n"); + + state *curr_state = create_initial_state (m_crc_loop); + if (!curr_state) + return false; + + m_states.quick_push (curr_state); + + auto_vec stack (m_crc_loop->num_nodes); + + basic_block header_bb = m_crc_loop->header; + if (!execute_bb_gimple_statements (header_bb, stack)) + return false; + + /* Successor BB's are added into the stack + from the execute_bb_gimple_statements function. */ + while (!stack.is_empty ()) + { + /* Look at the edge on the top of the stack. */ + edge e = stack.last (); + stack.pop (); + + /* Get destination block of the edge. */ + basic_block dest_bb = e->dest; + + /* Execute only basic blocks of the m_crc_loop. + At the end of the execution path save states in final states. */ + if (!flow_bb_inside_loop_p (m_crc_loop, dest_bb)) + { + m_is_last_iteration = true; + if (!keep_states ()) + return false; + continue; + } + + /* Execute statements. */ + if (!execute_bb_statements (dest_bb, e, stack)) + return false; + } + return true; +} + +/* Determine which bit of the DATA must be 1. + We assume that last bit must be 1. */ + +unsigned HOST_WIDE_INT +determine_index (tree data, bool is_shift_left) +{ + if (is_shift_left) + /* This won't work correctly in the case when data's size is larger, + but MSB is checked for the middle bit. */ + return tree_to_uhwi (TYPE_SIZE (TREE_TYPE (data))) - 1; + return 0; +} + +/* Assign appropriate values to data, CRC + and other phi results to calculate the polynomial. */ + +void +assign_vals_to_header_phis (state *polynomial_state, class loop *crc_loop, + gphi *crc_phi, gphi *data_phi, + bool is_shift_left) +{ + basic_block bb = crc_loop->header; + for (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + + gphi *phi = gsi.phi (); + tree lhs = gimple_phi_result (phi); + + /* Don't consider virtual operands. */ + if (virtual_operand_p (lhs)) + continue; + + if ((data_phi && phi == data_phi) || (!data_phi && phi == crc_phi)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Assigning the required value to "); + print_generic_expr (dump_file, lhs, dump_flags); + fprintf (dump_file, " variable.\n"); + } + polynomial_state->do_assign_pow2 (lhs, + determine_index (lhs, + is_shift_left)); + } + else if (phi == crc_phi) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Assigning 0 value to "); + print_generic_expr (dump_file, lhs, dump_flags); + fprintf (dump_file, " variable.\n"); + } + polynomial_state->do_operation (VAR_DECL, + build_zero_cst (TREE_TYPE (lhs)), + nullptr, lhs); + } + else + { + edge loop_preheader = loop_preheader_edge (crc_loop); + tree inital_val = PHI_ARG_DEF_FROM_EDGE (phi, loop_preheader); + if (TREE_CODE (inital_val) == INTEGER_CST) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "First value of phi is a constant, " + "assigning the number to "); + print_generic_expr (dump_file, lhs, dump_flags); + fprintf (dump_file, " variable.\n"); + } + polynomial_state->do_operation (VAR_DECL, inital_val, + nullptr, lhs); + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "First value of phi isn't constant, " + "assigning to "); + print_generic_expr (dump_file, lhs, dump_flags); + fprintf (dump_file, " variable.\n"); + } + polynomial_state->do_operation (VAR_DECL, + build_zero_cst (TREE_TYPE (lhs)), + nullptr, lhs); + } + } + } +} + +/* Execute the loop, which calculates CRC with initial values, + to calculate the polynomial. */ + +bool +crc_symbolic_execution::execute_crc_loop (gphi *crc_phi, + gphi *data_phi, + bool is_shift_left) +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "\n\nTrying to calculate the polynomial.\n\n"); + + m_states.quick_push (new state); + + basic_block bb = m_crc_loop->header; + assign_vals_to_header_phis (m_states.last (), m_crc_loop, crc_phi, data_phi, + is_shift_left); + + auto_vec stack (m_crc_loop->num_nodes); + + if (!execute_bb_gimple_statements (bb, stack)) + return false; + + /* stack may not be empty. Successor BB's are added into the stack + from the execute_bb_gimple_statements function. */ + while (!stack.is_empty ()) + { + /* Look at the edge on the top of the stack. */ + edge e = stack.last (); + stack.pop (); + + /* Get dest block of the edge. */ + basic_block bb = e->dest; + + /* Execute only basic blocks of the m_crc_loop. */ + if (!flow_bb_inside_loop_p (m_crc_loop, bb)) + continue; + + /* Execute statements. */ + if (!execute_bb_statements (bb, e, stack)) + return false; + } + + if (m_final_states.length () != 1) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "The number of states is not one when executed " + "the loop for calculating the polynomial.\n"); + return false; + } + return true; +} + +/* Return true if all bits of the POLYNOMIAL are constants (0 or 1). + Otherwise return false. */ + +bool +polynomial_is_known (const value *polynomial) +{ + for (size_t i = 0; i < polynomial->length (); i++) + { + if (!is_a ((*polynomial)[i])) + return false; + } + return true; +} + +/* Execute the loop, which is expected to calculate CRC, + to extract polynomial, assigning real numbers to CRC and data. + Returns a pair, first value of the pair is the tree containing + the value of the polynomial, second is the calculated polynomial. + The pair may contain nullptr. */ + +std::pair +crc_symbolic_execution::extract_polynomial (gphi *crc_phi, gphi *data_phi, + tree calculated_crc, + bool is_shift_left) +{ + if (!execute_crc_loop (crc_phi, data_phi, is_shift_left)) + return std::make_pair (nullptr, nullptr); + + if (m_final_states.length () != 1) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "The number of states isn't one " + "after executing the loop.\n"); + return std::make_pair (nullptr, nullptr); + } + state *polynomial_state = m_final_states.last (); + + /* CALCULATED_CRC contains the value of the polynomial + after one iteration of the loop. */ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Getting the value of "); + print_generic_expr (dump_file, calculated_crc, dump_flags); + fprintf (dump_file, " variable.\n"); + } + + /* Get the value (bit vector) of the tree (it may be the polynomial). */ + value *polynomial = polynomial_state->get_value (calculated_crc); + if (!polynomial) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Polynomial's value is null.\n"); + return std::make_pair (nullptr, nullptr); + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + /* Note: It may not be the real polynomial. + If it's a bit reflected CRC, + then to get a real polynomial, + it must be reflected and 1 bit added. */ + fprintf (dump_file, "Polynomial's value is "); + state::print_value (polynomial); + } + + /* Check that polynomial's all bits are constants. */ + if (!polynomial_is_known (polynomial)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Polynomial's value is not constant.\n"); + return std::make_pair (nullptr, nullptr); + } + + return std::make_pair (calculated_crc, polynomial); +} + + +/**************************** LFSR MATCHING *********************************/ + + +/* Return true if CONST_BIT value equals to 1. + Otherwise, return false. */ + +bool +is_one (value_bit *const_bit) +{ + return is_a (const_bit) + && (as_a (const_bit))->get_val () == 1; +} + +/* Return true if BIT is symbolic, + its index is same as LFSR bit's index (LFSR_BIT_INDEX) + and the origin is same as CRC_ORIGIN. */ + +bool +is_a_valid_symb (value_bit *bit, tree crc_origin, size_t lfsr_bit_index) +{ + if (!is_a (bit)) + return false; + + symbolic_bit *sym_bit = as_a (bit); + bool is_correct_index = (sym_bit->get_index () == lfsr_bit_index); + bool is_same_crc_origin = (sym_bit->get_origin () == crc_origin); + return is_correct_index && is_same_crc_origin; +} + +/* Return true if the BIT is a valid crc[LFSR_BIT_INDEX] ^ 1, + where i is a whole number and left part's origin is same as CRC_ORIGIN. + LFSR_BIT_INDEX is the index of the LFSR bit from the same position as in CRC. + + If there is lfsr[8] at LFSR value vectors' 9-th bit, + when in the CRC vectors' 9-th bit's value must be + crc[8]. + + Otherwise, return false. */ + +bool +is_a_valid_xor_one (value_bit *bit, tree crc_origin, size_t lfsr_bit_index) +{ + if (is_a (bit)) + { + bit_xor_expression *xor_exp = as_a (bit); + if (is_one (xor_exp->get_right ())) + return is_a_valid_symb (xor_exp->get_left (), crc_origin, + lfsr_bit_index); + return false; + } + return false; +} + +/* Return true, if CONDITION_EXP checks CRC's MSB/LSB value + (under which xor is/not done). + Otherwise, return false. */ + +bool +may_be_xors_condition (tree crc_origin, value_bit *condition_exp, + size_t sb_index) +{ + if (!crc_origin) + return false; + + if (!condition_exp) + return false; + + /* The CONDITION_EXP of CRC may be a symbolic bit, if CRC is xor-ed with + the data, and updated CRC's significant bit is checked. + So, the CONDITION_EXP will be CRC's condition if it's origin is the same as + CRC_ORIGIN, and it's index equals to checked significant bit's index. */ + if (is_a (condition_exp)) + { + symbolic_bit *condition_symbolic = as_a (condition_exp); + return crc_origin == condition_symbolic->get_origin () + && sb_index == condition_symbolic->get_index (); + } + /* The CONDITION_EXP of CRC may be a bit_xor_expression, + if CRC and data are xor-ed only for significant bit's check. + I.e. CONDITION_EXP in this case may be crc[]^data[]. + So, the CONDITION_EXP will be CRC's condition if it's left or right + part's origin is the same as CRC_ORIGIN, and it's index equals to checked + significant bit's index. */ + else if (is_a (condition_exp)) + { + bit_xor_expression *condition_xor_exp = as_a + (condition_exp); + if (!(is_a (condition_xor_exp->get_left ()) + && is_a (condition_xor_exp->get_right ()))) + return false; + + symbolic_bit *cond_left + = as_a (condition_xor_exp->get_left ()); + symbolic_bit *cond_right + = as_a (condition_xor_exp->get_right ()); + bool cond_left_is_crc = (crc_origin == cond_left->get_origin () + && sb_index == cond_left->get_index ()); + bool cond_right_is_crc = (crc_origin == cond_right->get_origin () + && sb_index == cond_right->get_index ()); + return cond_left_is_crc || cond_right_is_crc; + } + return false; +} + +/* Check whether the condition is checked for significant bit being 0 or 1. + If IS_ONE is 1, when check whether the significant bit is 1 (xor branch), + if 0, whether the significant bit is 0 (not xor branch). */ + +bool +is_crc_xor_condition (tree crc_origin, unsigned char is_one, + size_t sb_index, state *final_state) +{ + /* The CRC cases we detect must contain only one condition related to CRC. */ + if (final_state->get_conditions ().elements () != 1) + return false; + + auto condition_iter = final_state->get_conditions ().begin (); + if (!is_a (*condition_iter)) + return false; + + /* If the condition is for checking MSB/LSB, then + if is_one is 1 and the condition is for checking MSB/LSB being one, or + if is_one is 0 and condition is for checking MSB/LSB being 0 + return true, otherwise - false. */ + value_bit *cond_exp = (*condition_iter)->get_left (); + if (may_be_xors_condition (crc_origin, cond_exp, sb_index)) + { + if (!is_a ((*condition_iter)->get_right ())) + return false; + + bit_condition *condition = as_a (*condition_iter); + unsigned char comparison_val + = as_a ((*condition_iter)->get_right ())->get_val (); + if (condition->get_code () == EQ_EXPR) + return comparison_val == is_one; + if (condition->get_code () == NE_EXPR) + return comparison_val != is_one; + return false; + } + return false; +} + +/* Check whether LSB/MSB of LFSR and calculated (maybe)CRC match. + If MSB is checked in the CRC loop, then here we check LSB, or vice versa. + CHECKED_SB_VALUE indicates which state of CRC value is checked. + If the CHECKED_SB_VALUE is 1 - then xor-ed CRC value is checked, + otherwise, not xor-ed is checked. */ + +bool +given_sb_match (value_bit *crc, value_bit *lfsr, + unsigned short checked_sb_value) +{ + /* If LFSR's MSB/LSB value is a constant (0 or 1), + then CRC's MSB/LSB must have the same value. */ + if (is_a (lfsr)) + { + if (!((is_a (crc) + && as_a (crc)->get_val () + == as_a (lfsr)->get_val ()))) + return false; + return true; + } + /* If LFSR's MSB/LSB value is a symbolic_bit + (that means MSB/LSB of the polynomial is 1), + then CRC's MSB/LSB must be equal to CHECKED_SB_VALUE. */ + else if (is_a (lfsr)) + { + if (!(is_a (crc) + && (as_a (crc)->get_val () == checked_sb_value))) + return false; + return true; + } + return false; +} + +/* Check whether significant bit of LFSR and calculated (maybe)CRC match. */ + +bool +sb_match (const value *lfsr, const value *crc_value, size_t sb_index, + size_t it_end, unsigned short value) +{ + /* If it's bit-forward CRC, check 0 bit's value. */ + if (sb_index == it_end - 1) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Checking 0 bit.\n"); + + if (!given_sb_match ((*crc_value)[0], (*lfsr)[0], value)) + return false; + } + /* If it's bit-reversed CRC, check last bit's value. */ + else if (sb_index == 0) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Checking %zu bit.\n", it_end); + + if (!given_sb_match ((*crc_value)[it_end], (*lfsr)[it_end], value)) + return false; + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Significant bit index is incorrect.\n"); + } + return true; +} + +/* Match the CRC to the LFSR, where CRC's all bit values are + symbolic_bit or symbolic_bit ^ 1, besides MSB/LSB (it may be constant). */ + +bool +lfsr_and_crc_bits_match (const value *lfsr, const value *crc_state, + tree crc_origin, size_t i, size_t it_end, + size_t sb_index, unsigned short checked_sb_value) +{ + + /* Check whether significant bits of LFSR and CRC match. */ + if (!sb_match (lfsr, crc_state, sb_index, it_end, checked_sb_value)) + return false; + + for (; i < it_end; i++) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Checking %zu bit.\n", i); + + /* Check the case when in lfsr we have LFSR (i)^LFSR (SBi), + where 0 ((*lfsr)[i])) + { + size_t index = (as_a ((*lfsr)[i]))->get_left () + ->get_index (); + /* Check CRC value of xor branch. */ + if (checked_sb_value == 1) + { + if (!(is_a_valid_xor_one ((*crc_state)[i], crc_origin, index))) + return false; + } + else /* Check CRC value of not xor branch. */ + { + if (!(is_a_valid_symb ((*crc_state)[i], crc_origin, index))) + return false; + } + } + /* Check the case when in LFSR we have LFSR (i), where 0 ((*lfsr)[i])) + { + size_t index = (as_a ((*lfsr)[i]))->get_index (); + if (!(is_a_valid_symb ((*crc_state)[i], crc_origin, index))) + return false; + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Not expected expression in LFSR.\n"); + return false; + } + } + return true; +} + +/* Return origin of CRC_BIT. + The first tree in loop, from which CRC's calculation is started. */ + +tree +get_origin_of_crc_from_symb_bit (value_bit *crc_bit) +{ + if (is_a (crc_bit)) + return as_a (crc_bit)->get_origin (); + return nullptr; +} + +/* Return origin of CRC_BIT. The first tree in loop, from which CRC's + calculation is started. If the CRC_BIT is symbolic value, return its origin, + otherwise return its left part's origin (right must be 1 if its CRC's + value). */ + +tree +get_origin_of_crc (value_bit *crc_bit) +{ + tree origin = get_origin_of_crc_from_symb_bit (crc_bit); + if (origin) + return origin; + else if (is_a (crc_bit)) + { + value_bit *crc_bit_left + = as_a (crc_bit)->get_left (); + return get_origin_of_crc_from_symb_bit (crc_bit_left); + } + return nullptr; +} + +/* Determine and initialize significant bit index + (if MSB is checked for CRC, then it's LSB index, and vice versa) + and the remaining part's begin and end. + SB_INDEX is the significant bit index. + IT_BEG is the beginning of the remaining part. + IT_END is the end of the remaining part. */ + +void +init_sb_index_and_other_part_begin_end (size_t &it_beg, size_t &it_end, + size_t &sb_index, size_t crc_size, + bool is_bit_forward) +{ + it_end = crc_size; + if (is_bit_forward) + { + sb_index = it_end - 1; + it_beg = 1; + } + else + { + it_beg = 0; + sb_index = 0; + --it_end; + } +} + +/* Return true if CRC_STATE matches the LFSR, otherwise - false. + LFSR - is created LFSR value for the given polynomial and CRC size. + CRC_STATE - contains CRC's calculated value and execution path condition. + IT_BEG and IT_END - are the border indexes of the value to be matched. + SB_INDEX - is the significant bit index of the CRC value, + which will be checked separately. + IF MSB is checked for CRC, when sb_index will be the index of LSB. + Otherwise, will be the index of MSB. + CHECKED_SB_VALUE - is the significant bit's value (used for CRC's condition). + If CHECKED_SB_VALUE is 1, it indicates that CRC_STATE is + xor-ed path's state. + If CHECKED_SB_VALUE is 0, then CRC_STATE is the state of the + not xor branch. */ + +bool +lfsr_matches_crc_state (const value *lfsr, state *crc_state, value *crc_value, + size_t it_beg, size_t it_end, size_t sb_index, + unsigned short checked_sb_value) +{ + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Starting to match the following CRC value: "); + state::print_value (crc_value); + } + + /* Get the origin (name) of the calculated CRC value. + All bits must have the same origin. */ + tree crc_origin = get_origin_of_crc ((*crc_value)[it_beg]); + if (!crc_origin) + return false; + + if (!is_crc_xor_condition (crc_origin, checked_sb_value, sb_index, crc_state)) + return false; + + /* Check whether CRC_VALUE and LFSR bits match. */ + return lfsr_and_crc_bits_match (lfsr, crc_value, crc_origin, + it_beg, it_end, sb_index, checked_sb_value); +} + +/* Return true if in the CRC_VALUE exists xor expression. + Otherwise, return false. */ + +bool +is_xor_state (value *crc_value, size_t it_beg, size_t it_end) +{ + for (unsigned j = it_beg; j < it_end; ++j) + if ((*crc_value)[j]->get_type () == BIT_XOR_EXPRESSION) + return true; + return false; +} + +/* Keep the value of calculated CRC. */ + +value * +get_crc_val (tree calculated_crc, state *curr_state) +{ + if (!calculated_crc) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Couldn't get the potential CRC variable.\n"); + return nullptr; + } + + /* When the calculated CRC is constant, it's not possible to determine + whether the CRC has been calculated. */ + if (TREE_CODE (calculated_crc) == INTEGER_CST) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Calculated CRC is a constant.\n"); + return nullptr; + } + + /* Get calculated return value. */ + value * crc_value = curr_state->get_value (calculated_crc); + + if (!crc_value) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "CRC is not in the state.\n"); + return nullptr; + } + return crc_value; +} + +/* Return true if all states from the FINAL_STATES match the LFSR, + otherwise - false. */ + +bool +all_states_match_lfsr (value *lfsr, bool is_bit_forward, tree calculated_crc, + const vec &final_states) +{ + if (final_states.length () != 2) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "The final states count isn't two.\n"); + return false; + } + + value *crc_xor_value = get_crc_val (calculated_crc, final_states[0]); + value *crc_not_xor_value = get_crc_val (calculated_crc, final_states[1]); + + /* LFSR's size must be equal to CRC's size. */ + if ((crc_xor_value->length () != lfsr->length ()) + || (crc_not_xor_value->length () != lfsr->length ())) + return false; + + /* Depending on whether it is bit-forward or reversed CRC, + determine in which significant bit new value is added, + to examine that bit separately. + If in the CRC algorithm MSB (sb_index) is checked to be one for xor, + then here we check LSB separately (marginal bit). + If LSB (sb_index) is checked, then we separate MSB (marginal bit). */ + size_t it_beg, it_end, sb_index; + init_sb_index_and_other_part_begin_end (it_beg, it_end, sb_index, + crc_xor_value->length (), + is_bit_forward); + + size_t xor_st_index = 0, not_xor_st_index = 1; + /* If first is not xor's state, + then the second state is assumed to be xor's state. */ + if (!is_xor_state (crc_xor_value, it_beg, it_end)) + { + std::swap (crc_xor_value, crc_not_xor_value); + xor_st_index = 1; + not_xor_st_index = 0; + } + + /* If xor-ed CRC value doesn't match the LFSR value, return false. */ + if (!lfsr_matches_crc_state (lfsr, final_states[xor_st_index], crc_xor_value, + it_beg, it_end, sb_index, 1)) + return false; + + /* If not xor-ed CRC value doesn't match the LFSR value, return false. */ + if (!lfsr_matches_crc_state (lfsr, final_states[not_xor_st_index], + crc_not_xor_value, it_beg, it_end, sb_index, 0)) + return false; + + return true; +} \ No newline at end of file diff --git a/gcc/crc-verification.h b/gcc/crc-verification.h new file mode 100644 index 00000000000..4aa275d625e --- /dev/null +++ b/gcc/crc-verification.h @@ -0,0 +1,161 @@ +/* Execute symbolically all paths of the loop. + Copyright (C) 2022-2024 Free Software Foundation, Inc. + +This file is part of GCC. + +GCC is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free +Software Foundation; either version 3, or (at your option) any later +version. + +GCC is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +for more details. + +You should have received a copy of the GNU General Public License +along with GCC; see the file COPYING3. If not see +. */ + +#ifndef GCC_CRC_VERIFICATION +#define GCC_CRC_VERIFICATION + +#include "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "cfgloop.h" +#include "sym-exec/sym-exec-state.h" + +class crc_symbolic_execution { + + private: + /* A vector of states to keep the current state of each executed path. */ + vec m_states; + + /* A vector of final states + to keep the returned_value and path conditions. */ + vec m_final_states; + + /* Potential CRC loop, which must be executed symbolically, + to check whether it calculates CRC. */ + class loop *m_crc_loop; + + /* Output CRC from the last block of the loop. */ + gphi *m_output_crc; + + /* Indicates whether the loop execution brought to loop exit. + I.e. the condition of the loop is false. */ + bool m_is_last_iteration; + + /* Returns true if the variable is used outside the loop. + Otherwise, returns false. */ + bool is_used_outside_the_loop (tree); + + /* Add next basic blocks of the conditional block + for the execution path into the stack. + If the condition depends on symbolic values, keep both edges. + If the condition is true, keep true edge, else - false edge. + Returns true if addition succeed. Otherwise - false. */ + bool add_next_bbs (basic_block, state *, auto_vec &); + + /* Keep conditions depending on symbolic variables in the states. */ + static bool add_condition (const gcond *, state *, state *); + + /* The function adds E edge into the STACK if it doesn't have an immediate + successor back edge. + + When loop counter is checked in the if condition, + we mustn't continue on real path as we want to stop the execution before + the second iteration. */ + bool add_edge (edge, auto_vec &); + + /* Create new state for true and false branch. + Keep conditions in new created states. */ + bool resolve_condition (const gcond *, auto_vec &); + + /* If final states are less than two, adds new FINAL_STATE and returns true. + Otherwise, returns false. + In CRC cases we detect may not occur more than two final states. */ + bool add_final_state (state *); + + /* Keep the state of the executed path in final states. */ + bool keep_states (); + + bool execute_assign_statement (const gassign *); + + /* Execute gimple statements of the basic block. + Keeping values of variables in the state. */ + bool execute_bb_gimple_statements (basic_block, auto_vec &); + + /* Assign values of phi instruction to its result. + Keep updated values in the state. */ + bool execute_bb_phi_statements (basic_block, edge); + + /* Execute all statements of the basic block. + Keeping values of variables in the state. */ + bool execute_bb_statements (basic_block, edge, auto_vec &); + + /* Create initial state of the loop's header BB variables which have constant + values. + If it is the first iteration of the loop, initialise variables with the + initial values, otherwise initialise the variable with the value calculated + during the previous execution. */ + state *create_initial_state (class loop *); + +/* Traverse function fun's all paths from the first basic block to the last. + Each time iterate loops only once. + Symbolically execute statements of each path. */ + bool traverse_function (function *); + + /* Execute the loop, which calculates crc with initial values, + to calculate the polynomial. */ + bool execute_crc_loop (gphi *, gphi *, bool); + + public: + + /* Returns calculated polynomial by executing the loop + with concrete values. + First value of the pair is the tree containing the value of the polynomial, + second is the calculated polynomial. The pair may contain nullptr. */ + std::pair + extract_polynomial (gphi *, gphi *, tree, bool); + + /* Symbolically execute the CRC loop, doing one iteration. */ + bool symb_execute_crc_loop (); + + const vec &get_final_states () + { + return m_final_states; + } + + bool is_last_iteration () + { + return m_is_last_iteration; + } + + crc_symbolic_execution (class loop *loop, gphi * output_crc) : + m_crc_loop (loop), m_output_crc (output_crc), m_is_last_iteration (false) + { + /* Reserve memory for the vectors of states. */ + int max_states = 2; + m_states.create (max_states); + m_final_states.create (max_states); + } + + ~crc_symbolic_execution () + { + /* Free memory. */ + state::clear_states (&m_states); + state::clear_states (&m_final_states); + } +}; + + +/**************************** LFSR MATCHING *********************************/ + +/* Returns true if all states match the LFSR, otherwise - false. */ +bool all_states_match_lfsr (value *, bool, tree, const vec &); + + +#endif //GCC_CRC_VERIFICATION diff --git a/gcc/gimple-crc-optimization.cc b/gcc/gimple-crc-optimization.cc index c67b0fd38c3..a05aaf9f217 100644 --- a/gcc/gimple-crc-optimization.cc +++ b/gcc/gimple-crc-optimization.cc @@ -31,9 +31,19 @@ along with GCC; see the file COPYING3. If not see #include "tree-cfg.h" #include "cfgloop.h" #include "tree-scalar-evolution.h" +#include "crc-verification.h" class crc_optimization { private: + /* Record of statements already seen. */ + bitmap m_visited_stmts; + + /* Input CRC of the loop. */ + tree m_crc_arg; + + /* Input data of the loop. */ + tree m_data_arg; + /* The statement doing shift 1 operation before/after xor operation. */ gimple *m_shift_stmt; @@ -47,6 +57,9 @@ class crc_optimization { /* The loop, which probably calculates CRC. */ class loop *m_crc_loop; + /* Polynomial used in CRC calculation. */ + unsigned HOST_WIDE_INT m_polynomial; + /* Depending on the value of M_IS_BIT_FORWARD, may be forward or reversed CRC. If M_IS_BIT_FORWARD, then it is bit-forward implementation, otherwise bit-reversed. */ @@ -65,6 +78,15 @@ class crc_optimization { Xor must be done under the condition of MSB/LSB being 1. */ bool loop_may_calculate_crc (class loop *loop); + /* Symbolically executes the loop and checks that LFSR and resulting states + match. + Returns true if it is verified that the loop calculates CRC. + Otherwise, return false. + OUTPUT_CRC is the phi statement which will hold the calculated CRC value + after the loop exit. */ + bool loop_calculates_crc (gphi *output_crc, + std::pair calc_polynom); + /* Returns true if there is only two conditional blocks in the loop (one may be for the CRC bit check and the other for the loop counter). This may filter out some real CRCs, where more than one condition @@ -179,9 +201,33 @@ class crc_optimization { /* Prints extracted details of CRC calculation. */ void dump_crc_information (); + /* Returns true if OUTPUT_CRC's result is the input of m_phi_for_crc. + Otherwise, returns false. */ + bool is_output_crc (gphi *output_crc); + + /* Swaps m_phi_for_crc and m_phi_for_data if they are mixed. */ + void swap_crc_and_data_if_needed (gphi *output_crc); + + /* Validates CRC and data arguments and + sets them for potential CRC loop replacement. + + The function extracts the CRC and data arguments from PHI nodes and + performs several checks to ensure that the CRC and data are suitable for + replacing the CRC loop with a more efficient implementation. + + Returns true if the arguments are valid and the loop replacement is possible, + false otherwise. */ + bool validate_crc_and_data (); + + /* Convert polynomial to unsigned HOST_WIDE_INT. */ + void construct_constant_polynomial (value *polynomial); + + /* Returns phi statement which may hold the calculated CRC. */ + gphi *get_output_phi (); + public: crc_optimization () : m_visited_stmts (BITMAP_ALLOC (NULL)), - m_crc_loop (nullptr) + m_crc_loop (nullptr), m_polynomial (0) { set_initial_values (); } @@ -705,6 +751,8 @@ crc_optimization::cond_depends_on_crc (auto_vec& stmts) void crc_optimization::set_initial_values () { + m_crc_arg = nullptr; + m_data_arg = nullptr; m_shift_stmt = nullptr; m_phi_for_crc = nullptr; m_phi_for_data = nullptr; @@ -933,6 +981,240 @@ crc_optimization::loop_may_calculate_crc (class loop *loop) return false; } +/* Symbolically executes the loop and checks that LFSR and resulting states + match. + Returns true if it is verified that the loop calculates CRC. + Otherwise, return false. + OUTPUT_CRC is the phi statement which will hold the calculated CRC value + after the loop exit. */ + +bool +crc_optimization::loop_calculates_crc (gphi *output_crc, + std::pair calc_polynom) +{ + /* Create LFSR state using extracted polynomial. */ + value * lfsr = state::create_lfsr (calc_polynom.first, calc_polynom.second, + m_is_bit_forward); + if (!lfsr) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Couldn't create LFSR!\n"); + return false; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "\nLFSR value is \n"); + state::print_value (lfsr); + } + + /* Execute the loop with symbolic values + (symbolic value is assigned to the variable when its value isn't known) + to keep states, for further comparison. */ + bool is_crc = true; + crc_symbolic_execution loop_executor (m_crc_loop, output_crc); + while (!loop_executor.is_last_iteration ()) + { + if (!loop_executor.symb_execute_crc_loop ()) + { + if (dump_file) + fprintf (dump_file, "\nCRC verification didn't succeed " + "during symbolic execution!\n"); + is_crc = false; + break; + } + + /* Check whether LFSR and obtained states are same. */ + tree calculated_crc = PHI_ARG_DEF_FROM_EDGE (output_crc, + single_exit (m_crc_loop)); + if (!all_states_match_lfsr (lfsr, m_is_bit_forward, calculated_crc, + loop_executor.get_final_states ())) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Returned state and LFSR differ.\n"); + is_crc = false; + break; + } + } + delete lfsr; + return is_crc; +} + +/* Returns true if OUTPUT_CRC's result is the input of M_PHI_FOR_CRC. + Otherwise, returns false. */ + +bool +crc_optimization::is_output_crc (gphi *output_crc) +{ + tree crc_of_exit + = PHI_ARG_DEF_FROM_EDGE (output_crc, single_exit (m_crc_loop)); + tree crc_of_latch + = PHI_ARG_DEF_FROM_EDGE (m_phi_for_crc, loop_latch_edge (m_crc_loop)); + if (crc_of_exit == crc_of_latch) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + { + fprintf (dump_file, "Output CRC is "); + print_gimple_expr (dump_file, (gimple *) output_crc, dump_flags); + fprintf (dump_file, "\n"); + } + return true; + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Output CRC and determined input CRC " + "differ.\n"); + return false; + } +} + +/* Swaps M_PHI_FOR_CRC and M_PHI_FOR_DATA if they are mixed. */ + +void +crc_optimization::swap_crc_and_data_if_needed (gphi *output_crc) +{ + tree crc_of_exit + = PHI_ARG_DEF_FROM_EDGE (output_crc, single_exit (m_crc_loop)); + edge crc_loop_latch = loop_latch_edge (m_crc_loop); + if (crc_of_exit != PHI_ARG_DEF_FROM_EDGE (m_phi_for_crc, crc_loop_latch)) + { + if (m_phi_for_data + && crc_of_exit == PHI_ARG_DEF_FROM_EDGE (m_phi_for_data, + crc_loop_latch)) + { + std::swap (m_phi_for_crc, m_phi_for_data); + } + } +} + +/* Validates CRC and data arguments and + sets them for potential CRC loop replacement. + + The function extracts the CRC and data arguments from PHI nodes and + performs several checks to ensure that the CRC and data are suitable for + replacing the CRC loop with a more efficient implementation. + + Returns true if the arguments are valid and the loop replacement is possible, + false otherwise. */ + +bool crc_optimization::validate_crc_and_data () +{ + /* Set m_crc_arg and check if fits in word_mode. */ + gcc_assert (m_phi_for_crc); + m_crc_arg = PHI_ARG_DEF_FROM_EDGE (m_phi_for_crc, + loop_preheader_edge (m_crc_loop)); + gcc_assert (m_crc_arg); + + if (GET_MODE_SIZE (TYPE_MODE (TREE_TYPE (m_crc_arg))).to_constant () + > GET_MODE_SIZE (word_mode)) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "word_mode is less than CRC mode.\n"); + return false; + } + + unsigned HOST_WIDE_INT + data_size = tree_to_uhwi (m_crc_loop->nb_iterations) + 1; + /* We don't support the case where data is larger than the CRC. */ + if (TYPE_PRECISION (TREE_TYPE (m_crc_arg)) < data_size) + return false; + + /* Set m_data_arg if a PHI node for data exists, + and check its size against loop iterations. + This is the case when data and CRC are XOR-ed in the loop. */ + if (m_phi_for_data) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "Data and CRC are xor-ed in the for loop. Initializing data " + "with its value.\n"); + m_data_arg = PHI_ARG_DEF_FROM_EDGE (m_phi_for_data, + loop_preheader_edge (m_crc_loop)); + gcc_assert (m_data_arg); + if (TYPE_PRECISION (TREE_TYPE (m_data_arg)) != data_size) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "Loop iteration number and data's size differ.\n"); + return false; + } + return true; + } + return true; +} + +/* Convert polynomial to unsigned HOST_WIDE_INT. */ + +void +crc_optimization::construct_constant_polynomial (value *polynomial) +{ + m_polynomial = 0; + for (unsigned i = 0; i < (*polynomial).length (); i++) + { + value_bit *const_bit; + if (m_is_bit_forward) + const_bit = (*polynomial)[(*polynomial).length () - 1 - i]; + else + const_bit = (*polynomial)[i]; + m_polynomial <<= 1; + m_polynomial ^= (as_a (const_bit))->get_val () ? 1 : 0; + } +} + +/* Returns phi statement which may hold the calculated CRC. */ + +gphi * +crc_optimization::get_output_phi () +{ + edge loop_exit = single_exit (m_crc_loop); + if (!loop_exit) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "The loop doesn't have single exit.\n"); + return nullptr; + } + basic_block bb = loop_exit->dest; + gphi *output_crc = nullptr; + int phi_count = 0; + + /* We are only interested in cases when there is only one phi at the + loop exit, and that phi can potentially represent the CRC. + If there are other phis present, it indicates that additional values are + being calculated within the loop that are used outside it. */ + for (gphi_iterator gsi = gsi_start_phis (bb); !gsi_end_p (gsi); + gsi_next (&gsi)) + { + tree phi_result = gimple_phi_result (gsi.phi ()); + + /* Don't consider virtual operands. */ + if (!virtual_operand_p (phi_result)) + { + if (phi_count < 1) + { + output_crc = gsi.phi (); + phi_count++; + } + else + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "There is more than one output phi.\n"); + return nullptr; + } + } + } + + if (output_crc) + { + if (gimple_phi_num_args (output_crc) == 1) + return output_crc; + } + + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Couldn't determine output CRC.\n"); + return nullptr; +} + unsigned int crc_optimization::execute (function *fun) { @@ -948,7 +1230,48 @@ crc_optimization::execute (function *fun) for (auto loop: loop_list) { /* Perform initial checks to filter out non-CRCs. */ - loop_may_calculate_crc (loop); + if (loop_may_calculate_crc (loop)) + { + /* Get the phi which will hold the calculated CRC. */ + gphi *output_crc = get_output_phi (); + if (!output_crc) + break; + + swap_crc_and_data_if_needed (output_crc); + if (!is_output_crc (output_crc)) + break; + if (!validate_crc_and_data ()) + break; + + edge loop_latch = loop_latch_edge (m_crc_loop); + tree calced_crc = PHI_ARG_DEF_FROM_EDGE (m_phi_for_crc, loop_latch); + crc_symbolic_execution execute_loop (m_crc_loop, nullptr); + /* Execute the loop assigning specific values to CRC and data + for extracting the polynomial. */ + std::pair + calc_polynom = execute_loop.extract_polynomial (m_phi_for_crc, + m_phi_for_data, + calced_crc, + m_is_bit_forward); + + value *polynom_value = calc_polynom.second; + /* Stop analysis if we couldn't get the polynomial's value. */ + if (!polynom_value) + break; + + /* Stop analysis in case optimize_size is specified + and table-based would be generated. This check is only needed for + TARGET_CRC case, as polynomial's value isn't known in the + beginning. */ + construct_constant_polynomial (polynom_value); + + if (!loop_calculates_crc (output_crc, calc_polynom)) + break; + + if (dump_file) + fprintf (dump_file, "The loop with %d header BB index " + "calculates CRC!\n", m_crc_loop->header->index); + } } return 0; } diff --git a/gcc/sym-exec/sym-exec-state.cc b/gcc/sym-exec/sym-exec-state.cc index 15df4d6a87e..5962a3d0bf2 100644 --- a/gcc/sym-exec/sym-exec-state.cc +++ b/gcc/sym-exec/sym-exec-state.cc @@ -1894,6 +1894,55 @@ state::print_value (value *var) } +/* Create LFSR value for the reversed CRC. */ + +void +state::create_reversed_lfsr (value &lfsr, const value &crc, + const value &polynomial) +{ + size_t size = polynomial.length (); + + /* Determine values of all bits, except MSB. */ + for (size_t i = 0; i < size - 1; i++) + { + if (as_a (polynomial[i])->get_val ()) + lfsr.push (state::xor_two_bits (crc[i + 1], crc[0])); + else + lfsr.push (crc[i + 1]->copy ()); + } + + /* Determine value of MSB. */ + if (as_a (polynomial[size - 1])->get_val ()) + lfsr.push (crc[0]->copy ()); + else + lfsr.push (new bit (0)); +} + + +/* Create LFSR value for the forward CRC. */ + +void +state::create_forward_lfsr (value &lfsr, const value &crc, + const value &polynomial) +{ + size_t size = polynomial.length (); + /* Determine value of LSB. */ + if (as_a (polynomial[0])->get_val ()) + lfsr.push (crc[size - 1]->copy ()); + else + lfsr.push (new bit (0)); + + /* Determine values of remaining bits. */ + for (size_t i = 1; i < size; i++) + { + if (as_a (polynomial[i])->get_val ()) + lfsr.push (state::xor_two_bits (crc[i - 1], crc[size - 1])); + else + lfsr.push (crc[i - 1]->copy ()); + } +} + + /* Get the last 1 bit index. */ size_t @@ -1908,6 +1957,58 @@ last_set_bit (const value &polynomial) } +/* Create LFSR value. */ + +value * +state::create_lfsr (tree crc, value *polynomial, bool is_bit_forward) +{ + /* Check size compatibility․ */ + unsigned HOST_WIDE_INT polynomial_length = polynomial->length (); + unsigned HOST_WIDE_INT crc_size = tree_to_uhwi (TYPE_SIZE (TREE_TYPE (crc))); + if (crc_size < polynomial_length) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "LFSR state creation: " + "Polynomial doesn't fit into the crc.\n"); + + return nullptr; + } + + /* Get the minimal byte size to keep the polynomial. + Ie, if the last 1 bit of the polynomial is at 6 index, size will be 8. */ + size_t required_polynomial_size = ((last_set_bit (*polynomial)/8) + 1) * 8; + + /* Polynomial's length actually equals to the CRC variable's size. + Now we detect only those CRC calculation algorithms, where leading 1 of the + polynomial isn't kept. */ + if (required_polynomial_size == 0 + || required_polynomial_size != polynomial_length) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, "Polynomial's all bits are zeros " + "or the size of the polynomial is uncertain.\n"); + return nullptr; + } + + /* Create vector of symbolic bits for crc. */ + value crc_value (polynomial_length, TYPE_UNSIGNED (TREE_TYPE (crc))); + + for (unsigned HOST_WIDE_INT i = 0; i < polynomial_length; i++) + crc_value.push (new symbolic_bit (i, crc)); + + /* create LFSR vector. */ + value *lfsr = new value (polynomial_length, TYPE_UNSIGNED (TREE_TYPE (crc))); + + /* Calculate values for LFSR. */ + if (is_bit_forward) + create_forward_lfsr (*lfsr, crc_value, *polynomial); + else + create_reversed_lfsr (*lfsr, crc_value, *polynomial); + + return lfsr; +} + + /* Prints added conditions. */ void diff --git a/gcc/sym-exec/sym-exec-state.h b/gcc/sym-exec/sym-exec-state.h index 5a3f9ebbbff..2fd4805f46a 100644 --- a/gcc/sym-exec/sym-exec-state.h +++ b/gcc/sym-exec/sym-exec-state.h @@ -247,6 +247,14 @@ class state { /* Make a copy of given bits. */ static vec *make_copy (vec *bits); + /* Create LFSR value for the reversed CRC. */ + static void create_reversed_lfsr (value &lfsr, const value &crc, + const value &polynomial); + + /* Create LFSR value for the forward CRC. */ + static void create_forward_lfsr (value &lfsr, const value &crc, + const value &polynomial); + public: state () = default; @@ -374,6 +382,9 @@ class state { /* Returns status of last added condition. */ condition_status get_last_cond_status (); + + /* Create LFSR value. */ + static value *create_lfsr (tree crc, value *polynomial, bool is_bit_forward); }; -- 2.25.1 From patchwork Fri Oct 18 15:01:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mariam Arutunian X-Patchwork-Id: 1999256 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=In8GWW71; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XVSfS21dpz1xth for ; Sat, 19 Oct 2024 02:04:32 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 7843A3858294 for ; Fri, 18 Oct 2024 15:04:30 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-lj1-x22f.google.com (mail-lj1-x22f.google.com [IPv6:2a00:1450:4864:20::22f]) by sourceware.org (Postfix) with ESMTPS id C711A385840C for ; Fri, 18 Oct 2024 15:02:04 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org C711A385840C Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org C711A385840C Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::22f ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263729; cv=none; b=WrL87h1d8mxmH/z4eE4wvEZeb7HWGL5CP+uSOxibuIW2Meprvc6D2LHwJWmM/T/jDQYBOOqA4AZ4IPFqagZiYWY12Q0/0scqm6LWTHwkESNBh5L9DThXufY4gdw3ZUncUkTVMSNDg40BPJFcGa+8cx3yA6btGz6dR1DoAUxnOQo= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263729; c=relaxed/simple; bh=GmLmh/m3kfm7YYghBV32ND7xGy4oYMEqQSIYcB1CRy8=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=TM9lb5EpLEVammv7U7tup7EjL6K43hDd88rkHWhf6ZA0hVF/yODEnaIZDi6raLoGB7ZONR1Orc1govqSvnh3p7LJqpFYg4j62ChkyHvPScR5YR8gWr66Z8NQ653XPxUSyPTrX+U02vktf+RI9jJoYu5SQWNZPQZ4jxZfS56ULKg= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-lj1-x22f.google.com with SMTP id 38308e7fff4ca-2fb443746b8so26346301fa.0 for ; Fri, 18 Oct 2024 08:02:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1729263723; x=1729868523; darn=gcc.gnu.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=Myb00K/nUuAORjwZrUQawZdA2vzuPT67ho1OaL8j4Po=; b=In8GWW71/H4EOUZonKyflSsxocLH1Av3e3EwzsJSWBz+NJK59hMRAMZaQQZUhBa8TF I5W5FSd4rYkHBQbJR+gwpwSiF4DdO7v4yacVs9WIRKNQoBXkFFhnwyEJvoa1wM/QD724 tGOAX2rIxZgopZsbVuN7aAyg4W5dXa9k+IQZXUCfqZC8IG6FMmdVFy1H+mmTFYP7Uq0T lGz5HEfxMlX44Lyz22hHYcRoH9EuEoA4O9NXV+vv4MQobAm3D47+RHajercoAKLk3ndf w43AVQOPDViiMejQkHAF/PQmIiXzH/+V5jUKe8ZmzEVXnV4ECbhdlk5lBtFJqejNIWIK 4c4w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729263723; x=1729868523; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=Myb00K/nUuAORjwZrUQawZdA2vzuPT67ho1OaL8j4Po=; b=mZs0g2LysZ3FYj/YB53JRRg0XnjIg7tOhCH1ah6deqM2DnMQS1/h8Up0WE7nWRdj6L vmwHVsbpnRL2avcF/Sdzv1ayqtWSVGnOO57+PnO66SzNmjiV9VtEMoD7j8GqwQ11hIgW Nfv77yU7LMMrBijhOtZ8r1gcxRtwuz52CmqSZ8pdGxM2z244Q0Gi9KYxgGB1g1F49VtS sKcIMmGN0QLjSjlPustaclAJsi1XFEZndQR5SSuZuPYZeCT5eH0L4nfALAye9Kgct08i jKRbdBPqqqMXDt1lax/EgB6h3iBZI8ZoZQ7F2I4erzKohGYT5UmWmRKLquKGvu6fdvkT aszw== X-Gm-Message-State: AOJu0Ywagy+i40wRrOI2SVNnXy6ZxpKwbN9MsTeFdJzYzxw14Yi2OGf1 my2WVAd5DaUU7mDoWqw9Xb5tjz8kkyV7atTlhG+EoDpJf6pswT0Upc3Q5puddZNlighY7OyCiOy 4TDxebPfwT7HKQjF20tUvKjicNHDWC3U8xas= X-Google-Smtp-Source: AGHT+IFPL8keNKMmbk45s784JOVxv6o38medss28DQaZhn8Pa4+iPfH3XIrOuFfZtHn/h53WnVvmw8QbdQ++MVOMm4o= X-Received: by 2002:a05:651c:2115:b0:2fb:5f9d:c284 with SMTP id 38308e7fff4ca-2fb82ea2827mr16695281fa.16.1729263722537; Fri, 18 Oct 2024 08:02:02 -0700 (PDT) MIME-Version: 1.0 From: Mariam Arutunian Date: Fri, 18 Oct 2024 19:01:51 +0400 Message-ID: Subject: [RFC/RFA] [PATCH v5 11/12] Replace the original CRC loops with a faster CRC calculation. To: GCC Patches , Jeff Law X-Spam-Status: No, score=-7.7 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, HTML_MESSAGE, RCVD_IN_DNSWL_NONE, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org After the loop exit an internal function call (CRC, CRC_REV) is added, and its result is assigned to the output CRC variable (the variable where the calculated CRC is stored after the loop execution). The removal of the loop is left to CFG cleanup and DCE. gcc/ * gimple-crc-optimization.cc (optimize_crc_loop): New function. (execute): Add optimize_crc_loop function call. Signed-off-by: Mariam Arutunian Mentored-by: Jeff Law --- gcc/gimple-crc-optimization.cc | 78 ++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/gcc/gimple-crc-optimization.cc b/gcc/gimple-crc-optimization.cc index a05aaf9f217..9dee9be85d0 100644 --- a/gcc/gimple-crc-optimization.cc +++ b/gcc/gimple-crc-optimization.cc @@ -225,6 +225,11 @@ class crc_optimization { /* Returns phi statement which may hold the calculated CRC. */ gphi *get_output_phi (); + /* Attempts to optimize a CRC calculation loop by replacing it with a call to + an internal function (IFN_CRC or IFN_CRC_REV). + Returns true if replacement is succeeded, otherwise false. */ + bool optimize_crc_loop (gphi *output_crc); + public: crc_optimization () : m_visited_stmts (BITMAP_ALLOC (NULL)), m_crc_loop (nullptr), m_polynomial (0) @@ -1215,6 +1220,73 @@ crc_optimization::get_output_phi () return nullptr; } +/* Attempts to optimize a CRC calculation loop by replacing it with a call to + an internal function (IFN_CRC or IFN_CRC_REV). + Returns true if replacement is succeeded, otherwise false. */ + +bool +crc_optimization::optimize_crc_loop (gphi *output_crc) +{ + if (!output_crc) + { + if (dump_file) + fprintf (dump_file, "Couldn't determine output CRC.\n"); + return false; + } + + if (!m_data_arg) + { + if (dump_file && (dump_flags & TDF_DETAILS)) + fprintf (dump_file, + "Data and CRC are xor-ed before for loop. Initializing data " + "with 0.\n"); + /* Create a new variable for the data. + Determine the data's size with the loop iteration count. */ + unsigned HOST_WIDE_INT + data_size = tree_to_uhwi (m_crc_loop->nb_iterations) + 1; + tree type = build_nonstandard_integer_type (data_size, 1); + /* For the CRC calculation, it doesn't matter CRC is calculated for the + (CRC^data, 0) or (CRC, data). */ + m_data_arg = build_int_cstu (type, 0); + } + + /* Build tree node for the polynomial from its constant value. */ + tree polynomial_arg = build_int_cstu (TREE_TYPE (m_crc_arg), m_polynomial); + gcc_assert (polynomial_arg); + + internal_fn ifn; + if (m_is_bit_forward) + ifn = IFN_CRC; + else + ifn = IFN_CRC_REV; + + tree phi_result = gimple_phi_result (output_crc); + location_t loc; + loc = EXPR_LOCATION (phi_result); + + /* Add IFN call and write the return value in the phi_result. */ + gcall *call + = gimple_build_call_internal (ifn, 3, + m_crc_arg, + m_data_arg, + polynomial_arg); + + gimple_call_set_lhs (call, phi_result); + gimple_set_location (call, loc); + gimple_stmt_iterator si = gsi_start_bb (output_crc->bb); + gsi_insert_before (&si, call, GSI_SAME_STMT); + + /* Remove phi statement, which was holding CRC result. */ + gimple_stmt_iterator tmp_gsi = gsi_for_stmt (output_crc); + remove_phi_node (&tmp_gsi, false); + + /* Alter the exit condition of the loop to always exit. */ + gcond* loop_exit_cond = get_loop_exit_condition (m_crc_loop); + gimple_cond_make_false (loop_exit_cond); + update_stmt (loop_exit_cond); + return true; +} + unsigned int crc_optimization::execute (function *fun) { @@ -1271,6 +1343,12 @@ crc_optimization::execute (function *fun) if (dump_file) fprintf (dump_file, "The loop with %d header BB index " "calculates CRC!\n", m_crc_loop->header->index); + + if (!optimize_crc_loop (output_crc)) + { + if (dump_file) + fprintf (dump_file, "Couldn't generate faster CRC code.\n"); + } } } return 0; -- 2.25.1 From patchwork Fri Oct 18 15:01:56 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Mariam Arutunian X-Patchwork-Id: 1999254 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=fail reason="signature verification failed" (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=kAzGWQIo; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4XVSdk1HRxz1xth for ; Sat, 19 Oct 2024 02:03:54 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 61082385783B for ; Fri, 18 Oct 2024 15:03:52 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-lj1-x234.google.com (mail-lj1-x234.google.com [IPv6:2a00:1450:4864:20::234]) by sourceware.org (Postfix) with ESMTPS id 063C63858C3A for ; Fri, 18 Oct 2024 15:02:13 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 063C63858C3A Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 063C63858C3A Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2a00:1450:4864:20::234 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263744; cv=none; b=BIu3Y5C4sOveI3sqxxErQKmXjUWhWjpIdj2czbO+AfPH1GL7CAb5vGpfq7cnM/l/CfPDdycuMOXsQhM+Ia0kChCc+O88vitLo8D3TyKWfeaL0hTJ1X5GP17G4jZKAa5Q/XUci7abPEoSHpvrdycooEdgbMhE2LcLKEYSx/mnCxA= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1729263744; c=relaxed/simple; bh=SLpGCoBi4E5dS+w1XZXV5gCWWSjjrf07KTYGl53ZoKA=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=GLhh/eeTPUFz8bOygLMoqQlO3UnWY/Kmer60RyFSTZnAS3P8vc54VsQ/t/AeVWntraIH7cyl1Ps0+k8mgxDk3Db3Wkx8zH09JRf1c+6sXj4+GOcxZ5TBhk+0HKPZZ0t6HZjW1xaD2DoWOAj+UYeXd6pxCb/T9eUrgtNg2AwNTOU= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-lj1-x234.google.com with SMTP id 38308e7fff4ca-2fb50e84ec7so19839301fa.1 for ; Fri, 18 Oct 2024 08:02:12 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1729263731; x=1729868531; darn=gcc.gnu.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=yB0A6njQmGCP6clx4bIIh6OUcPe4/8eUR7Eg1B7dHZo=; b=kAzGWQIo5nVxJ7S8h+A8By6hCDV1G7zYZOr/QDeJWuwdOkXlmEirkNyCV6hSdFK/W3 En2AitOwxWKVEuqE1Be1h4GY2qZkUgYqvyc0y6D3wa3xi1+b5WqRI+dhr51/UYUU2c/c bWRUUlj3Nwd9Ganet7MGJC3ahUJYESyhjwZBTmkMw1s98CZ22cinMQ3O1/B4gIZrZP9S InbaVqJCDVFX3y9zBgvdpzrOaF+9v4IQMNXjS09/Qt5ekR5YMAqvz2KIWvujnzH1PeLy CsjB3WX5kYeY3+URSjS19uWh3zWeBLzBBzs1iVWu0HyP/lS2tgiixpx8gvQ3RG92gmfD B7ew== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1729263731; x=1729868531; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=yB0A6njQmGCP6clx4bIIh6OUcPe4/8eUR7Eg1B7dHZo=; b=l48RLf3MbKVKBKJho8kzM9RQ/19vpeYDnia6TEE/ur3oFRhUCMPjeVtKN5ERUPZWlu D9sl7Q4k5A/Sx1mikd7JGh+B5Svbsh2ZUdOUP0SRrMvT0adHpweIaSCUObjNl+wmgEUK PXvzesa2pMNQRFBfmgmCpV5rsVjByX5oxyr3cO+OPLZyziZUlscKb/SR8/vQTmkJyzpy RMH6TR9KEPy1j5utXHEi53ZgWdwWoGq9lxKpLbhFxxz87+RaGGhKRLqOj+1+nw5ejOm3 Fh/tkEK9mjudyNpDCD06KyyFSgOYYC+THHrtp0AndkPMlR1sRvo7lwobnfI4wWxD1333 vUhA== X-Gm-Message-State: AOJu0YxaRqWHf8oSA+3MAZiLiV3yWJ98u7GtPnc4zWnzkma5Zn3cNwZa lhQPgWpO74hu/n/g/db1tdomjbNgwPU8kqKHtn8V8uUMlSE3T9VPjGRkccTqBr44/oBeB0/lZ1q BNiERDdkcaRoZDDGEu7+u/Q57aOPEQo755iE= X-Google-Smtp-Source: AGHT+IEa+m3Ik7irPYgPzZFJK5y7d1Ged5J/VrehUugmDxIkxC88Wm/LxtPXRdKDfFFGnSfBWmMwzlZucoqNpx5uals= X-Received: by 2002:a2e:a546:0:b0:2f7:7d69:cb5d with SMTP id 38308e7fff4ca-2fb82da6bc3mr14538281fa.0.1729263729238; Fri, 18 Oct 2024 08:02:09 -0700 (PDT) MIME-Version: 1.0 From: Mariam Arutunian Date: Fri, 18 Oct 2024 19:01:56 +0400 Message-ID: Subject: [RFC/RFA][PATCH v5 12/12] Add tests for CRC detection and generation. To: GCC Patches , Jeff Law X-Spam-Status: No, score=-7.1 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, HTML_MESSAGE, RCVD_IN_DNSWL_NONE, SCC_5_SHORT_WORD_LINES, 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.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org gcc/testsuite/gcc.dg/ * crc-from-fedora-packages-(1-32).c New tests. * crc-linux-(1-5).c: Likewise. * crc-not-crc-(1-26).c: Likewise. * crc-side-instr-(1-17).c: Likewise. gcc/testsuite/gcc.dg/torture/ * crc-(1-29).c: New tests. * crc-CCIT-data16-xorOutside_InsideFor.c: Likewise. * crc-CCIT-data16.c: Likewise. * crc-CCIT-data8.c: Likewise. * crc-coremark16-data16.c: Likewise. * crc-coremark32-data16.c: Likewise. * crc-coremark32-data32.c: Likewise. * crc-coremark32-data8.c: Likewise. * crc-coremark64-data64.c: Likewise. * crc-coremark8-data8.c: Likewise. * crc-crc32-data16.c: Likewise. * crc-crc32-data24.c: Likewise. * crc-crc32-data8.c: Likewise. * crc-crc32.c: Likewise. * crc-crc64-data32.c: Likewise. * crc-crc64-data64.c: Likewise. * crc-crc8-data8-loop-xorInFor.c: Likewise. * crc-crc8-data8-loop-xorOutsideFor.c: Likewise. * crc-crc8-data8-xorOustideFor.c: Likewise. * crc-crc8.c: Likewise. Signed-off-by: Mariam Arutunian Mentored-by: Jeff Law --- .../gcc.dg/crc-from-fedora-packages-1.c | 27 +++++ .../gcc.dg/crc-from-fedora-packages-10.c | 26 ++++ .../gcc.dg/crc-from-fedora-packages-11.c | 31 +++++ .../gcc.dg/crc-from-fedora-packages-12.c | 74 ++++++++++++ .../gcc.dg/crc-from-fedora-packages-13.c | 26 ++++ .../gcc.dg/crc-from-fedora-packages-14.c | 35 ++++++ .../gcc.dg/crc-from-fedora-packages-15.c | 29 +++++ .../gcc.dg/crc-from-fedora-packages-16.c | 16 +++ .../gcc.dg/crc-from-fedora-packages-17.c | 52 ++++++++ .../gcc.dg/crc-from-fedora-packages-18.c | 31 +++++ .../gcc.dg/crc-from-fedora-packages-19.c | 58 +++++++++ .../gcc.dg/crc-from-fedora-packages-2.c | 25 ++++ .../gcc.dg/crc-from-fedora-packages-20.c | 47 ++++++++ .../gcc.dg/crc-from-fedora-packages-21.c | 58 +++++++++ .../gcc.dg/crc-from-fedora-packages-22.c | 26 ++++ .../gcc.dg/crc-from-fedora-packages-23.c | 27 +++++ .../gcc.dg/crc-from-fedora-packages-24.c | 29 +++++ .../gcc.dg/crc-from-fedora-packages-25.c | 33 +++++ .../gcc.dg/crc-from-fedora-packages-26.c | 22 ++++ .../gcc.dg/crc-from-fedora-packages-27.c | 23 ++++ .../gcc.dg/crc-from-fedora-packages-28.c | 30 +++++ .../gcc.dg/crc-from-fedora-packages-29.c | 35 ++++++ .../gcc.dg/crc-from-fedora-packages-3.c | 21 ++++ .../gcc.dg/crc-from-fedora-packages-30.c | 31 +++++ .../gcc.dg/crc-from-fedora-packages-31.c | 28 +++++ .../gcc.dg/crc-from-fedora-packages-32.c | 37 ++++++ .../gcc.dg/crc-from-fedora-packages-4.c | 21 ++++ .../gcc.dg/crc-from-fedora-packages-5.c | 53 ++++++++ .../gcc.dg/crc-from-fedora-packages-6.c | 40 ++++++ .../gcc.dg/crc-from-fedora-packages-7.c | 18 +++ .../gcc.dg/crc-from-fedora-packages-8.c | 29 +++++ .../gcc.dg/crc-from-fedora-packages-9.c | 48 ++++++++ gcc/testsuite/gcc.dg/crc-linux-1.c | 45 +++++++ gcc/testsuite/gcc.dg/crc-linux-2.c | 65 ++++++++++ gcc/testsuite/gcc.dg/crc-linux-3.c | 49 ++++++++ gcc/testsuite/gcc.dg/crc-linux-4.c | 30 +++++ gcc/testsuite/gcc.dg/crc-linux-5.c | 81 +++++++++++++ gcc/testsuite/gcc.dg/crc-not-crc-1.c | 27 +++++ gcc/testsuite/gcc.dg/crc-not-crc-10.c | 22 ++++ gcc/testsuite/gcc.dg/crc-not-crc-11.c | 25 ++++ gcc/testsuite/gcc.dg/crc-not-crc-12.c | 17 +++ gcc/testsuite/gcc.dg/crc-not-crc-13.c | 20 +++ gcc/testsuite/gcc.dg/crc-not-crc-14.c | 20 +++ gcc/testsuite/gcc.dg/crc-not-crc-15.c | 23 ++++ gcc/testsuite/gcc.dg/crc-not-crc-16.c | 18 +++ gcc/testsuite/gcc.dg/crc-not-crc-17.c | 18 +++ gcc/testsuite/gcc.dg/crc-not-crc-18.c | 18 +++ gcc/testsuite/gcc.dg/crc-not-crc-19.c | 18 +++ gcc/testsuite/gcc.dg/crc-not-crc-2.c | 27 +++++ gcc/testsuite/gcc.dg/crc-not-crc-20.c | 18 +++ gcc/testsuite/gcc.dg/crc-not-crc-21.c | 25 ++++ gcc/testsuite/gcc.dg/crc-not-crc-22.c | 25 ++++ gcc/testsuite/gcc.dg/crc-not-crc-23.c | 25 ++++ gcc/testsuite/gcc.dg/crc-not-crc-24.c | 25 ++++ gcc/testsuite/gcc.dg/crc-not-crc-25.c | 25 ++++ gcc/testsuite/gcc.dg/crc-not-crc-26.c | 25 ++++ gcc/testsuite/gcc.dg/crc-not-crc-3.c | 21 ++++ gcc/testsuite/gcc.dg/crc-not-crc-4.c | 18 +++ gcc/testsuite/gcc.dg/crc-not-crc-5.c | 26 ++++ gcc/testsuite/gcc.dg/crc-not-crc-6.c | 23 ++++ gcc/testsuite/gcc.dg/crc-not-crc-7.c | 19 +++ gcc/testsuite/gcc.dg/crc-not-crc-8.c | 20 +++ gcc/testsuite/gcc.dg/crc-not-crc-9.c | 16 +++ gcc/testsuite/gcc.dg/crc-side-instr-1.c | 27 +++++ gcc/testsuite/gcc.dg/crc-side-instr-10.c | 31 +++++ gcc/testsuite/gcc.dg/crc-side-instr-11.c | 31 +++++ gcc/testsuite/gcc.dg/crc-side-instr-12.c | 33 +++++ gcc/testsuite/gcc.dg/crc-side-instr-13.c | 31 +++++ gcc/testsuite/gcc.dg/crc-side-instr-14.c | 35 ++++++ gcc/testsuite/gcc.dg/crc-side-instr-15.c | 37 ++++++ gcc/testsuite/gcc.dg/crc-side-instr-16.c | 38 ++++++ gcc/testsuite/gcc.dg/crc-side-instr-17.c | 37 ++++++ gcc/testsuite/gcc.dg/crc-side-instr-2.c | 27 +++++ gcc/testsuite/gcc.dg/crc-side-instr-3.c | 26 ++++ gcc/testsuite/gcc.dg/crc-side-instr-4.c | 26 ++++ gcc/testsuite/gcc.dg/crc-side-instr-5.c | 26 ++++ gcc/testsuite/gcc.dg/crc-side-instr-6.c | 42 +++++++ gcc/testsuite/gcc.dg/crc-side-instr-7.c | 40 ++++++ gcc/testsuite/gcc.dg/crc-side-instr-8.c | 36 ++++++ gcc/testsuite/gcc.dg/crc-side-instr-9.c | 31 +++++ gcc/testsuite/gcc.dg/torture/crc-1.c | 114 ++++++++++++++++++ gcc/testsuite/gcc.dg/torture/crc-10.c | 48 ++++++++ gcc/testsuite/gcc.dg/torture/crc-11.c | 20 +++ gcc/testsuite/gcc.dg/torture/crc-12.c | 112 +++++++++++++++++ gcc/testsuite/gcc.dg/torture/crc-13.c | 58 +++++++++ gcc/testsuite/gcc.dg/torture/crc-14.c | 55 +++++++++ gcc/testsuite/gcc.dg/torture/crc-15.c | 32 +++++ gcc/testsuite/gcc.dg/torture/crc-16.c | 40 ++++++ gcc/testsuite/gcc.dg/torture/crc-17.c | 72 +++++++++++ gcc/testsuite/gcc.dg/torture/crc-18.c | 42 +++++++ gcc/testsuite/gcc.dg/torture/crc-19.c | 30 +++++ gcc/testsuite/gcc.dg/torture/crc-2.c | 28 +++++ gcc/testsuite/gcc.dg/torture/crc-20.c | 39 ++++++ gcc/testsuite/gcc.dg/torture/crc-21.c | 53 ++++++++ gcc/testsuite/gcc.dg/torture/crc-22.c | 66 ++++++++++ gcc/testsuite/gcc.dg/torture/crc-23.c | 74 ++++++++++++ gcc/testsuite/gcc.dg/torture/crc-24.c | 30 +++++ gcc/testsuite/gcc.dg/torture/crc-25.c | 78 ++++++++++++ gcc/testsuite/gcc.dg/torture/crc-26.c | 55 +++++++++ gcc/testsuite/gcc.dg/torture/crc-27.c | 27 +++++ gcc/testsuite/gcc.dg/torture/crc-28.c | 90 ++++++++++++++ gcc/testsuite/gcc.dg/torture/crc-29.c | 31 +++++ gcc/testsuite/gcc.dg/torture/crc-3.c | 37 ++++++ gcc/testsuite/gcc.dg/torture/crc-4.c | 51 ++++++++ gcc/testsuite/gcc.dg/torture/crc-5.c | 66 ++++++++++ gcc/testsuite/gcc.dg/torture/crc-6.c | 64 ++++++++++ gcc/testsuite/gcc.dg/torture/crc-7.c | 50 ++++++++ gcc/testsuite/gcc.dg/torture/crc-8.c | 50 ++++++++ gcc/testsuite/gcc.dg/torture/crc-9.c | 56 +++++++++ .../crc-CCIT-data16-xorOutside_InsideFor.c | 58 +++++++++ .../gcc.dg/torture/crc-CCIT-data16.c | 51 ++++++++ gcc/testsuite/gcc.dg/torture/crc-CCIT-data8.c | 47 ++++++++ .../gcc.dg/torture/crc-coremark16-data16.c | 65 ++++++++++ .../gcc.dg/torture/crc-coremark32-data16.c | 64 ++++++++++ .../gcc.dg/torture/crc-coremark32-data32.c | 63 ++++++++++ .../gcc.dg/torture/crc-coremark32-data8.c | 64 ++++++++++ .../gcc.dg/torture/crc-coremark64-data32.c | 64 ++++++++++ .../gcc.dg/torture/crc-coremark64-data64.c | 63 ++++++++++ .../gcc.dg/torture/crc-coremark8-data8.c | 63 ++++++++++ .../gcc.dg/torture/crc-crc32-data16.c | 51 ++++++++ .../gcc.dg/torture/crc-crc32-data24.c | 20 +++ .../gcc.dg/torture/crc-crc32-data8.c | 51 ++++++++ gcc/testsuite/gcc.dg/torture/crc-crc32.c | 51 ++++++++ .../gcc.dg/torture/crc-crc64-data32.c | 52 ++++++++ .../gcc.dg/torture/crc-crc64-data64.c | 52 ++++++++ .../torture/crc-crc8-data8-loop-xorInFor.c | 33 +++++ .../crc-crc8-data8-loop-xorOutsideFor.c | 33 +++++ .../torture/crc-crc8-data8-xorOustideFor.c | 51 ++++++++ gcc/testsuite/gcc.dg/torture/crc-crc8.c | 49 ++++++++ 129 files changed, 5087 insertions(+) create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-1.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-10.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-11.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-12.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-13.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-14.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-15.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-16.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-17.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-18.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-19.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-2.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-20.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-21.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-22.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-23.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-24.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-25.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-26.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-27.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-28.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-29.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-3.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-30.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-31.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-32.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-4.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-5.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-6.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-7.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-8.c create mode 100644 gcc/testsuite/gcc.dg/crc-from-fedora-packages-9.c create mode 100644 gcc/testsuite/gcc.dg/crc-linux-1.c create mode 100644 gcc/testsuite/gcc.dg/crc-linux-2.c create mode 100644 gcc/testsuite/gcc.dg/crc-linux-3.c create mode 100644 gcc/testsuite/gcc.dg/crc-linux-4.c create mode 100644 gcc/testsuite/gcc.dg/crc-linux-5.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-1.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-10.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-11.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-12.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-13.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-14.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-15.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-16.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-17.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-18.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-19.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-2.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-20.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-21.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-22.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-23.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-24.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-25.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-26.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-3.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-4.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-5.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-6.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-7.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-8.c create mode 100644 gcc/testsuite/gcc.dg/crc-not-crc-9.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-1.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-10.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-11.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-12.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-13.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-14.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-15.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-16.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-17.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-2.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-3.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-4.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-5.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-6.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-7.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-8.c create mode 100644 gcc/testsuite/gcc.dg/crc-side-instr-9.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-1.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-10.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-11.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-12.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-13.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-14.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-15.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-16.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-17.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-18.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-19.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-2.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-20.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-21.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-22.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-23.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-24.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-25.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-26.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-27.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-28.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-29.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-3.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-4.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-5.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-6.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-7.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-8.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-9.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-CCIT-data16-xorOutside_InsideFor.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-CCIT-data16.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-CCIT-data8.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-coremark16-data16.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-coremark32-data16.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-coremark32-data32.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-coremark32-data8.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-coremark64-data32.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-coremark64-data64.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-coremark8-data8.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-crc32-data16.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-crc32-data24.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-crc32-data8.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-crc32.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-crc64-data32.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-crc64-data64.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-crc8-data8-loop-xorInFor.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-crc8-data8-loop-xorOutsideFor.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-crc8-data8-xorOustideFor.c create mode 100644 gcc/testsuite/gcc.dg/torture/crc-crc8.c diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-1.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-1.c new file mode 100644 index 00000000000..41fe7a8f27e --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-1.c @@ -0,0 +1,27 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File_Ac3.cpp.ii +// We don't support non-constant polynomials. + +#include +typedef unsigned short int16u; +typedef unsigned char int8u; +int CRC16_Init(int16u *Table, int16u Polynomial) +{ + for (size_t Pos=0; Pos<256; Pos++) + { + Table[Pos]=(int16u)Pos<<8; + + for(int8u bit=0; bit<8; bit++) + { + if (Table[Pos]&0x8000) + Table[Pos]=(Table[Pos]<<1)^Polynomial; + else + Table[Pos]=Table[Pos]<<1; + } + } + return 0; +} + +/* { dg-final { scan-tree-dump "Second operand of the xor statement isn't an integer constant.\n" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-10.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-10.c new file mode 100644 index 00000000000..592b2d4bf9f --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-10.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - protocol +#include +#include +uint16_t crc16_mcrf4xx(uint16_t crc, uint8_t *data, size_t len) { + int i; + + if (!data || !len) + return crc; + + while (len--) { + crc ^= *data++; + for (i = 0; i < 8; i++) { + if (crc & 1) + crc = (crc >> 1) ^ 0x8408; + else + crc = (crc >> 1); + } + } + + return crc; +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-11.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-11.c new file mode 100644 index 00000000000..42e953b3c48 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-11.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - slz +#include +#include +uint32_t crc32_fast[4][256]; +void __slz_make_crc_table(void) { + uint32_t c; + int n, k; + + for (n = 0; n < 256; n++) { + c = (uint32_t) n ^ 255; + for (k = 0; k < 8; k++) { + if (c & 1) { + c = 0xedb88320 ^ (c >> 1); + } else { + c = c >> 1; + } + } + crc32_fast[0][n] = c ^ 0xff000000; + } +} + +int main () +{ + __slz_make_crc_table(); + printf ("%d", crc32_fast[0][0]); +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-12.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-12.c new file mode 100644 index 00000000000..bc6b21a84e4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-12.c @@ -0,0 +1,74 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - wifi. Sizeof check is added for the test. + +#include +#include +#include +typedef unsigned int u32; +typedef unsigned char u8; +u32 WIFI_CRC32Table[256]; +bool initialized; +u32 reflect (u32 ref, char ch) +{ + if (sizeof (u32) < 4) + exit (0); + + u32 value = 0; + + for (int i = 1; i < (ch + 1); i++) + { + if (ref & 1) + value |= 1 << (ch - i); + ref >>= 1; + } + + return value; +} + +u32 WIFI_calcCRC32 (u8 *data, int len) +{ + if (sizeof (u32) < 4) + exit (0); + + u32 crc = 0xFFFFFFFF; + + while (len--) + crc = (crc >> 8) ^ WIFI_CRC32Table[(crc & 0xFF) ^ *data++]; + + return (crc ^ 0xFFFFFFFF); +} + +void WIFI_initCRC32Table () +{ + if (sizeof (u32) < 4) + exit (0); + + initialized = false; + if (initialized) return; + initialized = true; + + u32 polynomial = 0x04C11DB7; + + for (int i = 0; i < 0x100; i++) + { + WIFI_CRC32Table[i] = reflect (i, 8) << 24; + for (int j = 0; j < 8; j++) + WIFI_CRC32Table[i] = (WIFI_CRC32Table[i] << 1) + ^ (WIFI_CRC32Table[i] & (1 << 31) ? polynomial + : 0); + WIFI_CRC32Table[i] = reflect (WIFI_CRC32Table[i], 32); + } +} + +int main () +{ + if (sizeof (u32) < 4) + exit (0); + + WIFI_initCRC32Table (); + WIFI_calcCRC32 ("dfsf", 4); +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-13.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-13.c new file mode 100644 index 00000000000..e8c0f77e96b --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-13.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// crc.ii, CRC16 +#include +typedef unsigned short u16; + +void GenerateCRC32Table(u16 polynomial, u16 *table) +{ + for (u16 i = 0; i <= 255; i++) { + u16 crc = i; + for (u16 j = 0; j < 8; j++) { // latch's next bb is loop header + crc = (crc >> 1) ^ ((crc & 1) ? 0x2342 : 0); + } + table[i] = crc; + } +} + +int main () +{ + u16 table [256]; + GenerateCRC32Table (0x4, table); +} + + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-14.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-14.c new file mode 100644 index 00000000000..9a9b5cb4133 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-14.c @@ -0,0 +1,35 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - base64, slightly modified +// We don't support the case when leading bit of the polynomial is kept. + +#include +#include + +uint32_t b64crc (const unsigned char* data, size_t ns) +{ + if (sizeof (unsigned int) < 4) + exit (0); + + const unsigned char *s = data; + uint32_t crc = 0xb704ceL; + + while (ns-- > 0) + { + int i; + crc ^= (*s++) << 16; + for (i = 0; i < 8; i++) + { + crc <<= 1; + if (crc & 0x1000000) + crc ^= 0x1864cfbL; + } + } + crc &= 0xffffff; + /* ... */ + return crc; +} + +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{\[0, \]*0\\\}" "crc" } } */ +/* { dg-final { scan-tree-dump "maybe contains CRC calculation." "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-15.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-15.c new file mode 100644 index 00000000000..41cc15c6a68 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-15.c @@ -0,0 +1,29 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - callerid + +unsigned short calc_crc(unsigned short crc, unsigned char data) +{ + unsigned int i, j, org, dst; + org = data; + dst = 0; + + for (i = 0; i < 8; i++) { + org <<= 1; + dst >>= 1; + if (org & 0x100) + dst |= 0x80; + } + data = (unsigned char) dst; + crc ^= (unsigned int) data << (16 - 8); + for (j = 0; j < 8; j++) { + if (crc & 0x8000U) + crc = (crc << 1) ^ 0x1021U ; + else + crc <<= 1 ; + } + return crc; +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-16.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-16.c new file mode 100644 index 00000000000..5d1b60458b2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-16.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - cc1541 + +unsigned char +crc8(unsigned char value) +{ + for (int i = 0; i < 8; ++i) { + value = (value & 0x80) ? ((value << 1) ^ 0x31) : (value << 1); + } + + return value; +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-17.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-17.c new file mode 100644 index 00000000000..ea1ca330379 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-17.c @@ -0,0 +1,52 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - crc.i. +// We don't support this case. + +#include +#include +uint32_t crc24_calculate(uint32_t preset, const uint8_t *data, uint8_t len) +{ + uint32_t state = preset; + uint8_t i; + + for (i = 0; i < len; i++) { + uint8_t n, cur = data[i]; + + for (n = 0; n < 8; n++) { + int next_bit = (state ^ cur) & 1; + + cur >>= 1; + state >>= 1; + if (next_bit) { + state |= 1 << 23; + state ^= 0x5a6000; + } + } + } + + return state; +} + +uint32_t crc24_reverse(uint32_t crc, const uint8_t *data, uint8_t len) +{ + uint32_t state = crc; + uint8_t i; + + for (i = 0; i < len; i++) { + uint8_t n, cur = data[len - i - 1]; + + for (n = 0; n < 8; n++) { + int top_bit = state >> 23; + + state = (state << 1) & 0xffffff; + state |= top_bit ^ ((cur >> (7 - n)) & 1); + if (top_bit) + state ^= 0xb4c000; + } + } + + return state; +} +/* { dg-final { scan-tree-dump "maybe contains CRC calculation." "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-18.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-18.c new file mode 100644 index 00000000000..895b82f8303 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-18.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - cryptobonrrxternd.i +// We don't support this case. + +unsigned char PGP_input[2*100]; +unsigned long crc24(int len) { + + if (sizeof (long) < 4) + __builtin_exit (0); + + long crc; + crc = 0xB704CE; + int i,j; + j = 0; + while (len--) { + crc ^= (PGP_input[j]) << 16; + j++; + for (i = 0; i < 8; i++) { + crc <<= 1; + if (crc & 0x1000000){ + crc ^= 0x1864CFB; + } + } + } + return crc & 0xFFFFFF; +} + +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{\[0, \]*0\\\}" "crc" } } */ +/* { dg-final { scan-tree-dump "maybe contains CRC calculation." "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-19.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-19.c new file mode 100644 index 00000000000..d243e0cd70e --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-19.c @@ -0,0 +1,58 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - liboctasic_la-oct6100_chip_open +// We don't support this case. + +typedef unsigned int UINT32; +typedef UINT32 * PUINT32; +typedef struct _OCT6100_INSTANCE_API_ +{ + + // tPOCT6100_SHARED_INFO pSharedInfo; + + //PVOID pProcessContext; + + //tOCT6100_USER_SERIAL_OBJECT ulApiSerObj; + + +} tOCT6100_INSTANCE_API, *tPOCT6100_INSTANCE_API; + +UINT32 Oct6100ApiProductionCrc( + tPOCT6100_INSTANCE_API f_pApiInstance, + PUINT32 f_pulMessage, + UINT32 f_ulMessageLength, + PUINT32 f_pulCrcResult ) +{ + UINT32 ulWidth = 32; + UINT32 ulKey, i, j; + UINT32 ulRemainder = 0; + + + ulRemainder = f_pulMessage[ f_ulMessageLength - 1 ]; + for ( j = f_ulMessageLength - 1; j != 0xFFFFFFFF ; j-- ) + { + for ( i = 0; i < ulWidth; i++ ) + { + if ( ( ( ulRemainder >> 0x1F ) & 0x1 ) == 0x1 ) + { + ulKey = 0x8765DCBA; + } + else + { + ulKey = 0; + } + ulRemainder = ulRemainder ^ ulKey; + + ulRemainder = ulRemainder << 1; + if ( j != 0 ) + { + ulRemainder = ulRemainder | ( ( f_pulMessage[ j - 1 ] ) >> ( 0x1F - i ) ); + } + } + } + + *f_pulCrcResult = ulRemainder; + + return 0x00000000; +} \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-2.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-2.c new file mode 100644 index 00000000000..d9c78f6d6be --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-2.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +//File - Ac3 from Fedora, polynomial made const. Original crc-side-instr-21.c test +#include +typedef unsigned short int16u; +typedef unsigned char int8u; +int CRC16_Init(int16u *Table) +{ + for (size_t Pos=0; Pos<256; Pos++) + { + Table[Pos]=(int16u)Pos<<8; + + for(int8u bit=0; bit<8; bit++) + { + if (Table[Pos]&0x8000) + Table[Pos]=(Table[Pos]<<1)^0x2101; + else + Table[Pos]=Table[Pos]<<1; + } + } + return 0; +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-20.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-20.c new file mode 100644 index 00000000000..1e60e57bfbb --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-20.c @@ -0,0 +1,47 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - scorelayout.i +// It is verified by the pass if we don't filter out the cases when data's size +// and loop iteration count differ. +#include +typedef unsigned char guchar; +typedef unsigned int guint32; +guint32 +bit_reverse (guint32 x) +{ + x = ((x & 0x55555555) << 1) | ((x >> 1) & 0x55555555); + x = ((x & 0x33333333) << 2) | ((x >> 2) & 0x33333333); + x = ((x & 0x0F0F0F0F) << 4) | ((x >> 4) & 0x0F0F0F0F); + x = (x << 24) | ((x & 0xFF00) << 8) | ((x >> 8) & 0xFF00) | (x >> 24); + return x; +} + +guint32 +crc32 (guchar * message) +{ + if (sizeof (int) < 4) + exit (0); + + int i, j; + guint32 byte, crc; + i = 0; + crc = 0xFFFFFFFF; + while (message[i] != 0) + { + byte = message[i]; + byte = bit_reverse (byte); + for (j = 0; j <= 7; j++) + { + if ((int) (crc ^ byte) < 0) + crc = (crc << 1) ^ 0x04C11DB7; + else + crc = crc << 1; + byte = byte << 1; + } + i = i + 1; + } + return bit_reverse (~crc); +} + +/* { dg-final { scan-tree-dump "Loop iteration number and data's size differ." "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-21.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-21.c new file mode 100644 index 00000000000..caa9dcaeef6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-21.c @@ -0,0 +1,58 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - Unsafe_crc16.i + +long BGl_crczd2valuezd2zz__crc16z00(long BgL_valz00_4, long BgL_crcz00_5) +{ + { + { + long BgL_iz00_1796; + long BgL_valuez00_1797; + long BgL_crcz00_1798; + + BgL_iz00_1796 = ((long) 0); + BgL_valuez00_1797 = (BgL_valz00_4 << (int) (((long) 8))); + BgL_crcz00_1798 = BgL_crcz00_5; + BgL_loopz00_1795: + if ((BgL_iz00_1796 == ((long) 8))) + { + return BgL_crcz00_1798; + } + else + { + long BgL_valuez00_1801; + long BgL_crcz00_1802; + + BgL_valuez00_1801 = (BgL_valuez00_1797 << (int) (((long) 1))); + BgL_crcz00_1802 = (BgL_crcz00_1798 << (int) (((long) 1))); + { + long BgL_crcz00_2209; + long BgL_valuez00_2208; + long BgL_iz00_2206; + + BgL_iz00_2206 = (BgL_iz00_1796 + ((long) 1)); + BgL_valuez00_2208 = BgL_valuez00_1801; + if ( + (((long) 0) == + (((long) 65536) & (BgL_crcz00_1802 ^ BgL_valuez00_1801)))) + { + BgL_crcz00_2209 = BgL_crcz00_1802; + } + else + { + BgL_crcz00_2209 = (BgL_crcz00_1802 ^ ((long) 32773)); + } + BgL_crcz00_1798 = BgL_crcz00_2209; + BgL_valuez00_1797 = BgL_valuez00_2208; + BgL_iz00_1796 = BgL_iz00_2206; + goto BgL_loopz00_1795; + } + } + } + } +} + +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0\\\} +" "crc" } } */ +/* { dg-final { scan-tree-dump "maybe contains CRC calculation." "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-22.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-22.c new file mode 100644 index 00000000000..7cdf64dc2d4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-22.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - crrcsim-LoggerReader_byte.ii +// We don't support this case. + +int crcReg; +enum { crcPoly = 49 }; +void crcByteSchritt(unsigned char data) +{ + for (int i=0; i<8; i++) + { + crcReg <<= 1; + + + if ((data & 0x80) != 0) + crcReg |= 1; + + data <<= 1; + + if ((crcReg & 0x100) != 0) + crcReg = crcReg ^ crcPoly; + } + + crcReg &= 0xFF; +} \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-23.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-23.c new file mode 100644 index 00000000000..1fcc1872b0d --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-23.c @@ -0,0 +1,27 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - ModbusComm.ii + +#include +uint16_t ModbusCrc(const uint8_t *data, unsigned int sz) +{ + static const uint16_t MODBUS_CRC_POLY = 0xA001; + uint16_t crc = 0xffff; + + while (sz--) + { + crc ^= *data++; + for (unsigned int i = 0; i < 8; ++i) + { + if (crc & 0x1) + crc = (crc >> 1) ^ MODBUS_CRC_POLY; + else + crc >>= 1; + } + } + + return crc; +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-24.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-24.c new file mode 100644 index 00000000000..842de6c12d2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-24.c @@ -0,0 +1,29 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - utils.ii +/* We don't support the case when CRC variable's size is different from the + calculated CRC (I.e. in this case CRC 16 is calculated.) */ + +void crc_byte(const char data, unsigned int *crc16) +{ + int k; + unsigned c,d ; + + c = data << 8 ; + d = c; + + for (k = 0; k < 8; k++) { + *crc16 = (c & 0x8000) ^ *crc16; + + if (*crc16 & 0x8000) { + *crc16 = *crc16 << 1; + *crc16 = *crc16 ^ 0x8005; + } else + *crc16 = *crc16 << 1; + + d = d << 1; + c = d; + } +} +/* { dg-final { scan-tree-dump "maybe contains CRC calculation." "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-25.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-25.c new file mode 100644 index 00000000000..93172fa65e1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-25.c @@ -0,0 +1,33 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - cold-flash.i +#include +uint32_t tab[256]; + +void crc32_gentab(void) { + + int i, j; + uint32_t crc; + uint32_t poly = 0xEDB88320L; + + for ( i = 0; i < 256; i++) { + + crc = i; + + for ( j = 8; j > 0; j-- ) { + + if ( crc & 1 ) + crc = (crc >> 1) ^ poly; + else + crc >>= 1; + + } + + tab[i] = crc; + + } + +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-26.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-26.c new file mode 100644 index 00000000000..7392f291fee --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-26.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - 3way.cpp. Modified +typedef unsigned int word32; +word32 ProcessAndXorBlock () +{ + word32 a0, a1, a2; + word32 START_D = 0xb1b1; + word32 rc = START_D; + unsigned int m_rounds = 32; + for(unsigned i=0; i +unsigned long long CRCTable[256]; +void XS_Digest__CRC__crc64() { + unsigned long long poly64rev = 0xd800000000000000ULL; + unsigned long long part; + int i, j; + for (i = 0; i < 256; i++) { + part = i; + for (j = 0; j < 8; j++) { + if (part & 1) + part = (part >> 1) ^ poly64rev; + else + part >>= 1; + } + CRCTable[i] = part; + } +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-28.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-28.c new file mode 100644 index 00000000000..24c7d38c4a8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-28.c @@ -0,0 +1,30 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - crc32-2.i + +#include +#include +void *xmalloc(size_t size) __attribute__ ((malloc)); +uint32_t* crc32_filltable(uint32_t *crc_table, int endian) +{ + uint32_t polynomial = endian ? 0x04c11db7 : 0xedb88320; + uint32_t c; + unsigned i, j; + + if (!crc_table) + crc_table = xmalloc(256 * sizeof(uint32_t)); + + for (i = 0; i < 256; i++) { + c = endian ? (i << 24) : i; + for (j = 8; j; j--) { + if (endian) + c = (c&0x80000000) ? ((c << 1) ^ polynomial) : (c << 1); + else + c = (c&1) ? ((c >> 1) ^ polynomial) : (c >> 1); + } + *crc_table++ = c; + } + + return crc_table - 256; +} \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-29.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-29.c new file mode 100644 index 00000000000..f34acfbcce4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-29.c @@ -0,0 +1,35 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - hash.c.i. +/* We don't support cases with multiple conditional branches in the loop. */ +#include +typedef struct _crc_calculator { + uint32_t remainder; + uint32_t trunc_poly; + uint32_t bits; + uint32_t table[256]; + uint32_t final_result_mask; +} CRC_CALCULATOR; + +void crc_calculator_init_table(CRC_CALCULATOR *p_crc_calculator) { + const uint32_t high_bit = 1 << (p_crc_calculator->bits - 1); + const uint32_t byte_high_bit = 1 << (8 - 1); + + for (uint32_t value = 0; value < 256; value++) { + uint32_t remainder = 0; + for (uint8_t mask = byte_high_bit; mask != 0; mask >>= 1) { + if (value & mask) { + remainder ^= high_bit; + } + + if (remainder & high_bit) { + remainder <<= 1; + remainder ^= p_crc_calculator->trunc_poly; + } else { + remainder <<= 1; + } + } + p_crc_calculator->table[value] = remainder; + } +} \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-3.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-3.c new file mode 100644 index 00000000000..96dd281671a --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-3.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +#include +//File - as_format +uint32_t crc32r(const uint8_t *data, uint32_t size) +{ + + uint32_t crc = 0xffffffff; + for(uint32_t i=0; i != size; i++) { + crc = crc ^ data[i]; + for(int j=0; j<8; j++) + if(crc & 1) + crc = (crc >> 1) ^ 0xedb88320; + else + crc = crc >> 1; + } + return ~crc; +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-30.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-30.c new file mode 100644 index 00000000000..8f74538ba0e --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-30.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - mkiss.i +// We don't verify as CRC's value is known. +#include +typedef unsigned short int u16; + +u16 crctab[256]; +void init_crc(void) +{ + short int i, j; + u16 accum, data; + + for (i = 0; i < 256; i++) { + accum = 0xffff; + data = i; + for (j = 0; j < 8; ++j) { + if ((data^accum) & 0x0001) + + + accum = (accum >> 1) ^ 0x8408; + else + + accum >>= 1; + data >>= 1; + } + crctab[i] = accum; + } +} +/* { dg-final { scan-tree-dump "maybe contains CRC calculation." "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-31.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-31.c new file mode 100644 index 00000000000..b67cf9661e5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-31.c @@ -0,0 +1,28 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - png_tools.i +#include + +uint32_t gen_grAb_crc(unsigned char *buf) +{ + uint32_t crc = 0xffffffff; + uint32_t crc_table[256]; + uint32_t c; + int32_t n, k; + for (n = 0; n < 256; n++) { + c = (uint32_t) n; + for (k = 0; k < 8; k++) { + if (c & 1) + c = ((uint32_t) 0xedb88320) ^ (c >> 1); + else + c = c >> 1; + } + crc_table[n] = c; + } + for (n = 0; n < 12; n++) + crc = crc_table[(crc ^ buf[n]) & 0xff] ^ (crc >> 8); + return crc ^ ((uint32_t) 0xffffffff); +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-32.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-32.c new file mode 100644 index 00000000000..11ed133200f --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-32.c @@ -0,0 +1,37 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - reliable_text.c +#include +char calculateCRC8_(char* input, int length) +{ + unsigned char generator = 0x1D; + unsigned char crc = 0x00; + + while (length > 0) + { + unsigned char ch = *input++; + length--; + + + if (ch == 0) break; + + crc ^= ch; + + for (int i = 0; i < 8; i++) + { + if ((crc & 0x80) != 0) + { + crc = (unsigned char)((crc << 1) ^ generator); + } + else + { + crc <<= 1; + } + } + } + + return crc; +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-4.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-4.c new file mode 100644 index 00000000000..5d116e68502 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-4.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// crc.ii +typedef unsigned int u32; + +void GenerateCRC32Table (u32 polynomial, u32 *table) +{ + for (u32 i = 0; i <= 255; i++) + { + u32 crc = i; + for (u32 j = 0; j < 8; j++) + { + crc = (crc >> 1) ^ ((crc & 1) ? 0x23428765 : 0); + } + table[i] = crc; + } +} + + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-5.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-5.c new file mode 100644 index 00000000000..71fda25d7da --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-5.c @@ -0,0 +1,53 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// crc32.i +// We don't support this case. + +#include +typedef unsigned int u32; +u32 gf2_multiply(u32 x, u32 y, u32 modulus) +{ + u32 product = x & 1 ? y : 0; + int i; + + for (i = 0; i < 31; i++) { + product = (product >> 1) ^ (product & 1 ? modulus : 0); + x >>= 1; + product ^= x & 1 ? y : 0; + } + + return product; +} + +u32 crc32_generic_shift(u32 crc, size_t len, + u32 polynomial) +{ + u32 power = 0x2101; + int i; + + for (i = 0; i < 8 * (int)(len & 3); i++) + crc = (crc >> 1) ^ (crc & 1 ? 0x2101 : 0); + + len >>= 2; + if (!len) + return crc; + + for (;;) { + + if (len & 1) + crc = gf2_multiply(crc, power, polynomial); + + len >>= 1; + if (!len) + break; + + + power = gf2_multiply(power, power, polynomial); + } + + return crc; +} + +/* { dg-final { scan-tree-dump "Loop iteration number isn't a constant." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 30" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-6.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-6.c new file mode 100644 index 00000000000..ea189a4845b --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-6.c @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// libmng_chunk_io +typedef unsigned int mng_uint32; +typedef signed int mng_int32; +typedef signed char mng_int8; +typedef mng_int8 mng_bool; +typedef struct mng_data_struct { + // ... + mng_uint32 aCRCtable [256]; + mng_bool bCRCcomputed; + // ... +} mng_data; + +typedef mng_data * mng_datap; +void make_crc_table (mng_datap pData) +{ + mng_uint32 iC; + mng_int32 iN, iK; + + for (iN = 0; iN < 256; iN++) + { + iC = (mng_uint32) iN; + + for (iK = 0; iK < 8; iK++) + { + if (iC & 1) + iC = 0xedb88320U ^ (iC >> 1); + else + iC = iC >> 1; + } + + pData->aCRCtable [iN] = iC; + } + + pData->bCRCcomputed = 1; +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-7.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-7.c new file mode 100644 index 00000000000..0f2e55f3100 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-7.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - lsf_decode, modified. +// We don't support this case. + +#include +#include + +uint16_t crc(uint8_t byte, uint16_t reg) { + for (size_t i = 0; i != 8; ++i) { + uint16_t msb = reg & 0x8000; + reg = ((reg << 1) & 0xFFFF) | ((byte >> (7 - i)) & 0x0001); + if (msb) reg ^= 0x5935; + } + return reg & 0xFFFF; +} +/* { dg-final { scan-tree-dump "maybe contains CRC calculation." "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-8.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-8.c new file mode 100644 index 00000000000..a30495b2b95 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-8.c @@ -0,0 +1,29 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +#include +#include +// File - misc +uint32_t _crc32(uint32_t crc, uint32_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 32; i++) { + if (crc & 0x80000000) + crc = (crc << 1) ^ 0x04C11DB7; + else + crc = (crc << 1); + } + + return crc; +} + +uint32_t stm_crc32(const uint8_t *data, size_t size) { + uint32_t crc = 0xffffffff; + const uint32_t *pend = (const uint32_t *) (data + size); + for (const uint32_t *p = (const uint32_t *) (data); p < pend; p++) + crc = _crc32(crc, *p); + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 2 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-from-fedora-packages-9.c b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-9.c new file mode 100644 index 00000000000..b070864ae25 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-from-fedora-packages-9.c @@ -0,0 +1,48 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -O2" } */ + +// File - nvme_mi +#include +typedef unsigned int __u32; +typedef unsigned char __u8; +struct nvme_mi_msg_hdr { + __u8 type; + __u8 nmp; + __u8 meb; + __u8 rsvd0; +} __attribute__((packed)); + +struct nvme_mi_req { + struct nvme_mi_msg_hdr *hdr; + size_t hdr_len; + void *data; + size_t data_len; + __u32 mic; +}; +__u32 nvme_mi_crc32_update (__u32 crc, void *data, size_t len) +{ + int i; + + while (len--) + { + crc ^= *(unsigned char *) (data++); + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? 0x82F63B78 : 0); + } + return crc; +} + +void nvme_mi_calc_req_mic (struct nvme_mi_req *req) +{ + if (sizeof (__u32) < 4) + exit (0); + + __u32 crc = 0xffffffff; + + crc = nvme_mi_crc32_update (crc, req->hdr, req->hdr_len); + crc = nvme_mi_crc32_update (crc, req->data, req->data_len); + + req->mic = ~crc; +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-linux-1.c b/gcc/testsuite/gcc.dg/crc-linux-1.c new file mode 100644 index 00000000000..918b423a583 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-linux-1.c @@ -0,0 +1,45 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-O3" "-flto" } } */ + +#include +#define CRC32_POLY_BE 0x04c11db7 +#define RETVAL_OUT_OF_MEMORY (-6) +#define RETVAL_NOT_BZIP_DATA (-2) +#define RETVAL_OK 0 + +struct bunzip_data { + unsigned int crc32Table[256]; +}; + + +int start_bunzip(struct bunzip_data **bdp, void *inbuf, long len, + long (*fill)(void*, unsigned long)) +{ + if (sizeof (unsigned int) <= 3) + exit (0); + + struct bunzip_data *bd; + unsigned int i, j, c; + + /* Figure out how much data to allocate */ + i = sizeof(struct bunzip_data); + + /* Allocate bunzip_data. Most fields initialize to zero. */ + bd = *bdp = malloc(i); + + /* ... */ + + /* Init the CRC32 table (big endian) */ + for (i = 0; i < 256; i++) { + c = i << 24; + for (j = 8; j; j--) + c = c&0x80000000 ? (c << 1)^(CRC32_POLY_BE) : (c << 1); + bd->crc32Table[i] = c; + } + + /* . . . */ + return RETVAL_OK; +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/crc-linux-2.c b/gcc/testsuite/gcc.dg/crc-linux-2.c new file mode 100644 index 00000000000..990a28cef6f --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-linux-2.c @@ -0,0 +1,65 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned char __u8; +typedef unsigned short __u16; +struct i2c_msg { + __u16 addr; + __u16 flags; +#define I2C_M_RD 0x0001 /* guaranteed to be 0x0001! */ + /* ... */ + __u16 len; + __u8 *buf; +}; + +#define POLY (0x1070U << 3) +static u8 crc8(u16 data) +{ + int i; + + for (i = 0; i < 8; i++) { + if (data & 0x8000) + data = data ^ POLY; + data = data << 1; + } + return (u8)(data >> 8); +} + +/** +* i2c_smbus_pec - Incremental CRC8 over the given input data array +* @crc: previous return crc8 value +* @p: pointer to data buffer. +* @count: number of bytes in data buffer. +* +* Incremental CRC8 over count bytes in the array pointed to by p +*/ +u8 i2c_smbus_pec(u8 crc, u8 *p, size_t count) +{ + int i; + + for (i = 0; i < count; i++) + crc = crc8((crc ^ p[i]) << 8); + return crc; +} +static inline u8 i2c_8bit_addr_from_msg(const struct i2c_msg *msg) +{ + return (msg->addr << 1) | (msg->flags & I2C_M_RD ? 1 : 0); +} + + +/* Assume a 7-bit address, which is reasonable for SMBus */ +u8 i2c_smbus_msg_pec(u8 pec, struct i2c_msg *msg) +{ + /* The address will be sent first */ + u8 addr = i2c_8bit_addr_from_msg(msg); + pec = i2c_smbus_pec(pec, &addr, 1); + + /* The data buffer follows */ + return i2c_smbus_pec(pec, msg->buf, msg->len); +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-linux-3.c b/gcc/testsuite/gcc.dg/crc-linux-3.c new file mode 100644 index 00000000000..301f6d22b65 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-linux-3.c @@ -0,0 +1,49 @@ +/* We don't find this case because in "crc32_generic_shift" function + loop's iteration number isn't a constant (i < 8 * (int)(len & 3)). */ + +#include +typedef unsigned int u32; +#define __attribute_const__ __attribute__((__const__)) +static u32 __attribute_const__ gf2_multiply (u32 x, u32 y, u32 modulus) +{ + u32 product = x & 1 ? y : 0; + int i; + + for (i = 0; i < 31; i++) + { + product = (product >> 1) ^ (product & 1 ? modulus : 0); + x >>= 1; + product ^= x & 1 ? y : 0; + } + return product; +} + +u32 __attribute_const__ crc32_generic_shift(u32 crc, size_t len, + u32 polynomial) +{ + u32 power = polynomial; /* CRC of x^32 */ + int i; + + /* Shift up to 32 bits in the simple linear way */ + for (i = 0; i < 8 * (int)(len & 3); i++) + crc = (crc >> 1) ^ (crc & 1 ? polynomial : 0); + + len >>= 2; + if (!len) + return crc; + + for (;;) { + /* "power" is x^(2^i), modulo the polynomial */ + if (len & 1) + crc = gf2_multiply(crc, power, polynomial); + + len >>= 1; + if (!len) + break; + + /* Square power, advancing to x^(2^(i+1)) */ + power = gf2_multiply(power, power, polynomial); + } + + return crc; +} \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-linux-4.c b/gcc/testsuite/gcc.dg/crc-linux-4.c new file mode 100644 index 00000000000..50cbbba49d2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-linux-4.c @@ -0,0 +1,30 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +/* We don't detect, it's optimized to branch-less CRC. */ +#define CRC32_POLY_LE 0xedb88320 +typedef unsigned int u32; +u32 calc_crc(unsigned char *buf, int len) +{ + u32 reg; + u32 tmp; + int j, k; + + reg = 0xffffffff; + + for (j = 0; j < len; j++) { + reg ^= buf[j]; + + for (k = 0; k < 8; k++) { + tmp = reg & 0x01; + + reg >>= 1; + + if (tmp) + reg ^= CRC32_POLY_LE; + } + } + + return ~reg; +} \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-linux-5.c b/gcc/testsuite/gcc.dg/crc-linux-5.c new file mode 100644 index 00000000000..ff3cc25fb66 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-linux-5.c @@ -0,0 +1,81 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include +#include +/* We don't find, as we can't replace the first loop, + and in the second loop polynomials leading 1 bit is kept too + (second function's iteration count is 4). */ +typedef unsigned char u8; +typedef unsigned short u16; +u8 drm_dp_msg_data_crc4(const uint8_t *data, u8 number_of_bytes) +{ + u8 bitmask = 0x80; + u8 bitshift = 7; + u8 array_index = 0; + int number_of_bits = number_of_bytes * 8; + u16 remainder = 0; + + while (number_of_bits != 0) { + number_of_bits--; + remainder <<= 1; + remainder |= (data[array_index] & bitmask) >> bitshift; + bitmask >>= 1; + bitshift--; + if (bitmask == 0) { + bitmask = 0x80; + bitshift = 7; + array_index++; + } + if ((remainder & 0x100) == 0x100) + remainder ^= 0xd5; + } + + number_of_bits = 8; + while (number_of_bits != 0) { + number_of_bits--; + remainder <<= 1; + if ((remainder & 0x100) != 0) + remainder ^= 0xd5; + } + + return remainder & 0xff; +} + +/* sideband msg handling */ +u8 drm_dp_msg_header_crc4(const uint8_t *data, size_t num_nibbles) +{ + u8 bitmask = 0x80; + u8 bitshift = 7; + u8 array_index = 0; + int number_of_bits = num_nibbles * 4; + u8 remainder = 0; + + while (number_of_bits != 0) { + number_of_bits--; + remainder <<= 1; + remainder |= (data[array_index] & bitmask) >> bitshift; + bitmask >>= 1; + bitshift--; + if (bitmask == 0) { + bitmask = 0x80; + bitshift = 7; + array_index++; + } + if ((remainder & 0x10) == 0x10) + remainder ^= 0x13; + } + + number_of_bits = 4; + while (number_of_bits != 0) { + number_of_bits--; + remainder <<= 1; + if ((remainder & 0x10) != 0) + remainder ^= 0x13; + } + + return remainder; +} + +/* { dg-final { scan-tree-dump "drm_dp_msg_data_crc4 function maybe contains CRC calculation." "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-1.c b/gcc/testsuite/gcc.dg/crc-not-crc-1.c new file mode 100644 index 00000000000..4c12fdd9125 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-1.c @@ -0,0 +1,27 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +//loop iteration number is 5 +ee_u16 not_crcu8 (ee_u8 data, ee_u16 crc) { + ee_u8 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 5; i++) { + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} +/* { dg-final { scan-tree-dump-times "crcu8 function maybe contains CRC calculation" 0 "crc" } } */ + diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-10.c b/gcc/testsuite/gcc.dg/crc-not-crc-10.c new file mode 100644 index 00000000000..5249419c2d3 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-10.c @@ -0,0 +1,22 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +//Spoiled crc value +uint16_t not_crc(uint16_t crc, uint8_t a) { + int i; + crc ^= a; + for (i = 0; i < 8; ++i) { + if (crc & 1) { + crc = 0; + crc = (crc << 1) ^ 0xA001; + } + else + crc = crc >> 1; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "not_crc function maybe contains CRC calculation" 0 "crc" } } */ + diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-11.c b/gcc/testsuite/gcc.dg/crc-not-crc-11.c new file mode 100644 index 00000000000..9e14732c061 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-11.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +ee_u16 not_crc(ee_u8 data, ee_u16 crc) { + ee_u8 i = 0, carry = 0; + for (i = 0; i < 8; i++) { + data >>= 1; + if (data == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "not_crc function maybe contains CRC calculation" 0 "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-12.c b/gcc/testsuite/gcc.dg/crc-not-crc-12.c new file mode 100644 index 00000000000..a2ccfde7fc8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-12.c @@ -0,0 +1,17 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +uint8_t not_crc(uint8_t crc, uint8_t data) { + uint8_t i; + crc = crc ^ data; + for (i = 0; i < 8; i++) { + if (data & 0x01) + crc = (crc >> 1) ^ 0x8C; + else + crc >>= 1; + } + return crc; +} +/* { dg-final { scan-tree-dump-times "not_crc function maybe contains CRC calculation" 0 "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-13.c b/gcc/testsuite/gcc.dg/crc-not-crc-13.c new file mode 100644 index 00000000000..8d1562f88ca --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-13.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +uint8_t not_crc(uint8_t crc, uint8_t data) { + uint8_t i; + crc = crc ^ data; + for (i = 0; i < 8; i++) { + if (crc & 0x01) + crc = (crc >> 1) ^ 0x8C; + else + crc >>= 1; + if (i > 1) + crc = 8; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-14.c b/gcc/testsuite/gcc.dg/crc-not-crc-14.c new file mode 100644 index 00000000000..4527be273ab --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-14.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +uint8_t not_crc(uint8_t crc, uint8_t data) { + crc = crc ^ data; + + for (uint8_t i = 0, n = 0; i < 8; i++, n++) { + if (data > i) + crc = 8; + if (crc & 0x01) + crc = (crc >> 1) ^ 0x8C; + else + crc >>= 1; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-15.c b/gcc/testsuite/gcc.dg/crc-not-crc-15.c new file mode 100644 index 00000000000..cf7993a2c49 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-15.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-O3" "-flto" } } */ + +/* With -O3 the cycles is split into 2, + and one of them calculates CRC (when data != 0). + Thus, when compiling with -O3 there is CRC. */ + +#include + +uint8_t not_crc(uint8_t crc, uint8_t data) { + crc = crc ^ data; + + for (uint8_t i = 0, n = 0; i < 8; i++, n++) { + if ((crc & 0x01) && data) + crc = (crc >> 1) ^ 0x8C; + else + crc >>= 1; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-16.c b/gcc/testsuite/gcc.dg/crc-not-crc-16.c new file mode 100644 index 00000000000..06c468057d6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-16.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +uint8_t not_crc(uint8_t crc, uint8_t data) { + uint8_t i; + crc = crc ^ data; + for (i = 0; i < 8; i++) { + if (!i && (crc & 0x01)) + crc = (crc >> 1) ^ 0x8C; + else + crc >>= 1; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-17.c b/gcc/testsuite/gcc.dg/crc-not-crc-17.c new file mode 100644 index 00000000000..778b209cb08 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-17.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +uint8_t not_crc(uint8_t crc, uint8_t data) { + uint8_t i; + crc = crc ^ data; + for (i = 0; i < 8; i++) { + if (i && (crc & 0x01)) + crc = (crc >> 1) ^ 0x8C; + else + crc >>= 1; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-18.c b/gcc/testsuite/gcc.dg/crc-not-crc-18.c new file mode 100644 index 00000000000..c7454bc9d5b --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-18.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +uint8_t not_crc(uint8_t crc, uint8_t data) { + uint8_t i; + crc = crc ^ data; + for (i = 0; i < 8; i++) { + if ((crc & 0x01) && i==0) + crc = (crc >> 1) ^ 0x8C; + else + crc >>= 1; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-19.c b/gcc/testsuite/gcc.dg/crc-not-crc-19.c new file mode 100644 index 00000000000..c072f72cbaf --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-19.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +uint8_t not_crc(uint8_t crc, uint8_t data) { + uint8_t i; + crc = crc ^ data; + for (i = 0; i < 8; i++) { + if ((crc & 0x01) || i==0) + crc = (crc >> 1) ^ 0x8C; + else + crc >>= 1; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-2.c b/gcc/testsuite/gcc.dg/crc-not-crc-2.c new file mode 100644 index 00000000000..ab2142ffa83 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-2.c @@ -0,0 +1,27 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +typedef uint8_t crc; +#define TOPBIT (1 << 7) + +//shift not by one +crc +notCrc(uint8_t const message[], int nBytes) { + crc remainder = 0; + for (int byte = 0; byte < nBytes; ++byte) { + remainder ^= message[byte] ; + for (uint8_t bit = 8; bit > 0; --bit) { + if (remainder & TOPBIT) { + remainder = (remainder << 3) ^ 1234; + } else { + remainder = (remainder << 9); + } + } + } + return (remainder); +} + +/* { dg-final { scan-tree-dump-times "notCrc function maybe contains CRC calculation" 0 "crc" } } */ + diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-20.c b/gcc/testsuite/gcc.dg/crc-not-crc-20.c new file mode 100644 index 00000000000..25360ea2d61 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-20.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +uint8_t not_crc(uint8_t crc, uint8_t data) { + uint8_t i; + crc = crc ^ data; + for (i = 0; i < 8; i++) { + if (i == 0 || (crc & 0x01)) + crc = (crc >> 1) ^ 0x8C; + else + crc >>= 1; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-21.c b/gcc/testsuite/gcc.dg/crc-not-crc-21.c new file mode 100644 index 00000000000..94465ae2f00 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-21.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +uint16_t not_crcu8 (uint8_t data, uint16_t crc) { + uint8_t i = 0, x16 = 0, carry = 0; + for (i = 0; i < 8; i++) { + x16 = (uint8_t)((data & 128) ^ ((uint8_t) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-22.c b/gcc/testsuite/gcc.dg/crc-not-crc-22.c new file mode 100644 index 00000000000..40a4f74bdb8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-22.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +uint16_t not_crcu8 (uint8_t data, uint16_t crc) { + uint8_t i = 0, x16 = 0, carry = 0; + for (i = 0; i < 8; i++) { + x16 = (uint8_t) ((data & 2) ^ ((uint8_t) crc & 2)); + data >>= 1; + if (x16 == 2) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-23.c b/gcc/testsuite/gcc.dg/crc-not-crc-23.c new file mode 100644 index 00000000000..06ebc96fb83 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-23.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +uint16_t not_crcu8 (uint8_t data, uint16_t crc) { + uint8_t i = 0, x16 = 0, carry = 0; + for (i = 0; i < 8; i++) { + x16 = (uint8_t) ((data & 1) ^ ((uint8_t) crc & 1) ^ (i & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-24.c b/gcc/testsuite/gcc.dg/crc-not-crc-24.c new file mode 100644 index 00000000000..603e436c6fb --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-24.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +uint16_t not_crcu8 (uint8_t data, uint16_t crc) { + uint8_t i = 0, x16 = 0, carry = 0; + for (i = 0; i < 8; i++) { + x16 = (uint8_t) (((uint8_t) crc & 1) ^ (i & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-25.c b/gcc/testsuite/gcc.dg/crc-not-crc-25.c new file mode 100644 index 00000000000..80e2e0419ce --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-25.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +uint16_t not_crcu8 (uint8_t *data, uint16_t crc) { + uint8_t x16 = 0, carry = 0; + for (uint8_t i = 0, j=4; i < 8; i++, j+=2) { + x16 = (uint8_t) (((uint8_t) crc & 1) ^ (j & 1)); + *data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-26.c b/gcc/testsuite/gcc.dg/crc-not-crc-26.c new file mode 100644 index 00000000000..39866ccb45d --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-26.c @@ -0,0 +1,25 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +uint16_t not_crcu8 (uint8_t data, uint16_t crc) { + uint8_t x16 = 0, carry = 0; + for (uint8_t i = 0, j=3; i < 8; i++, j+=2) { + x16 = (uint8_t) (((uint8_t) crc & 1) ^ (j & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-3.c b/gcc/testsuite/gcc.dg/crc-not-crc-3.c new file mode 100644 index 00000000000..a0424a5b330 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-3.c @@ -0,0 +1,21 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +//no shift in case of xor +uint16_t not_crc16_update(uint16_t crc, uint8_t a) { + + int i; + crc ^= a; + for (i = 0; i < 8; ++i) { + if (crc & 1) + crc = crc ^ 0xA001; + else + crc = (crc >> 1); + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "not_crc16_update function maybe contains CRC calculation" 0 "crc" } } */ + diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-4.c b/gcc/testsuite/gcc.dg/crc-not-crc-4.c new file mode 100644 index 00000000000..ed2ff7c98ed --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-4.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +//shift and xor only if lsb is 1 +uint16_t not_crc(uint16_t crc, uint8_t a) { + int i; + crc ^= a; + for (i = 0; i < 8; ++i) { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "not_crc function maybe contains CRC calculation" 0 "crc" } } */ + diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-5.c b/gcc/testsuite/gcc.dg/crc-not-crc-5.c new file mode 100644 index 00000000000..e54a293de61 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-5.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +typedef unsigned char uint8_t; + +//no xor +uint8_t not_crc(uint8_t *data, size_t len) { + uint8_t crc = 0xff; + size_t i, j; + for (i = 0; i < len; i++) { + crc ^= data[i]; + for (j = 0; j < 8; j++) { + if ((crc & 0x80) != 0) + crc = (uint8_t) ((crc << 1) | 0x31); + else + crc <<= 1; + } + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "not_crc function maybe contains CRC calculation" 0 "crc" } } */ + + diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-6.c b/gcc/testsuite/gcc.dg/crc-not-crc-6.c new file mode 100644 index 00000000000..20fa46c3103 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-6.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +#define POLY (0x1070U << 3) +#define u8 uint8_t +#define u16 uint16_t + +//xor in case 0 +u8 not_crc(u16 data) { + int i; + for (i = 0; i < 8; i++) { + if (data & 0x0000) + data = data ^ POLY; + data = data << 1; + } + return (u8)(data >> 8); +} + +/* { dg-final { scan-tree-dump-times "not_crc function maybe contains CRC calculation" 0 "crc" } } */ + + diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-7.c b/gcc/testsuite/gcc.dg/crc-not-crc-7.c new file mode 100644 index 00000000000..edfc103636d --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-7.c @@ -0,0 +1,19 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include +//in one case is called shift left, in another shift right +uint16_t not_crc(uint16_t crc, uint8_t a) { + int i; + crc ^= a; + for (i = 0; i < 8; ++i) { + if (crc & 1) + crc = (crc << 1) ^ 0xA001; + else + crc = crc >> 1; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "not_crc function maybe contains CRC calculation" 0 "crc" } } */ + diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-8.c b/gcc/testsuite/gcc.dg/crc-not-crc-8.c new file mode 100644 index 00000000000..fd6c6a02f18 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-8.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include + +//xor is done in case lsb is 0 +int16_t not_crc(int16_t crc, int8_t a) { + int i; + crc ^= a; + for (i = 0; i < 8; ++i) { + if (crc << 15 == 0) + crc = (crc >> 1) ^ 0xA001; + else + crc = crc >> 1; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "not_crc function maybe contains CRC calculation" 0 "crc" } } */ + diff --git a/gcc/testsuite/gcc.dg/crc-not-crc-9.c b/gcc/testsuite/gcc.dg/crc-not-crc-9.c new file mode 100644 index 00000000000..ce6cc746cb7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-not-crc-9.c @@ -0,0 +1,16 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc -O2" } */ + +#include +//no conditional xor +uint16_t not_crc(uint16_t crc, uint8_t a) { + int i; + crc ^= a; + for (i = 0; i < 8; ++i) { + crc = (crc << 1) ^ 0xA001; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "not_crc function maybe contains CRC calculation" 0 "crc" } } */ + diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-1.c b/gcc/testsuite/gcc.dg/crc-side-instr-1.c new file mode 100644 index 00000000000..69738d5c866 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-1.c @@ -0,0 +1,27 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include + +uint16_t crc16_update (uint16_t crc, uint8_t a) +{ + int i; + for (i = 0; i < 8; ++i) + { + int b; + if ((crc & 1) ^ (a & 1)) + crc = (crc >> 1) ^ 0xa001; + else + crc = (crc >> 1); + a >>= 1; + b = crc; // Unused instruction, this is safe to remove. + } + return crc; +} + +/* { dg-final { scan-tree-dump "crc16_update function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\\\}" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-10.c b/gcc/testsuite/gcc.dg/crc-side-instr-10.c new file mode 100644 index 00000000000..765572cf9b7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-10.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +ee_u16 crcu8 (ee_u8 data, ee_u16 crc) { + ee_u16 i = 0, x16 = 0, carry = 0; + int a = 1; + for (i = 0; i < 8; i++) { + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + a = a * (crc + 5); + if (carry) + { + crc |= 0x8000; + } + else + crc &= 0x7fff; + } + return a; +} + +/* { dg-final { scan-tree-dump "Output CRC and determined input CRC differ." "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-11.c b/gcc/testsuite/gcc.dg/crc-side-instr-11.c new file mode 100644 index 00000000000..d8a7a88de5d --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-11.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +ee_u16 crcu8 (ee_u8 data, ee_u16 crc) { + ee_u16 i = 0, x16 = 0, carry = 0; + int a = 1; + for (i = 0; i < 8; i++) { + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + a = a * (crc + 5); + if (carry) + { + crc |= 0x8000; + } + else + crc &= 0x7fff; + } + return a + crc; +} + +/* { dg-final { scan-tree-dump "There is more than one output phi." "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-12.c b/gcc/testsuite/gcc.dg/crc-side-instr-12.c new file mode 100644 index 00000000000..4511c08dc6b --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-12.c @@ -0,0 +1,33 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +int a; +ee_u16 crcu8 (ee_u8 data, ee_u16 crc) { + ee_u16 i = 0, x16 = 0, carry = 0; + int c; + for (i = 0; i < 8; i++) { + c += i*2; // In compiled code, it is moved outside of the loop. + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + a = c; + if (carry) + { + crc |= 0x8000; + } + else + crc &= 0x7fff; + } + return crc; +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-13.c b/gcc/testsuite/gcc.dg/crc-side-instr-13.c new file mode 100644 index 00000000000..968a35bf4df --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-13.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +int a; +ee_u16 crcu8 (ee_u8 data, ee_u16 crc) { + ee_u16 i = 0, x16 = 0, carry = 0; + int c; + for (i = 0; i < 8; i++) { + c += crc * 2; + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + a = c; + if (carry) { + crc |= 0x8000; + } else + crc &= 0x7fff; + } + return crc; +} + +/* { dg-final { scan-tree-dump "There is more than one output phi." "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-14.c b/gcc/testsuite/gcc.dg/crc-side-instr-14.c new file mode 100644 index 00000000000..c8b671f571e --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-14.c @@ -0,0 +1,35 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +int a; +ee_u16 crcu8 (ee_u8 data, ee_u16 crc) { + ee_u16 i = 0, x16 = 0, carry = 0; + int c; + for (i = 0; i < 8; i++) { + c += i*2; + printf ("%d", c); + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + a = c; + if (carry) + { + crc |= 0x8000; + } + else + crc &= 0x7fff; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-15.c b/gcc/testsuite/gcc.dg/crc-side-instr-15.c new file mode 100644 index 00000000000..ec3c879034d --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-15.c @@ -0,0 +1,37 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +int foo (int c) +{ + return c*c; +} + +ee_u16 crcu8 (ee_u8 data, ee_u16 crc) { + ee_u16 i = 0, x16 = 0, carry = 0; + int c; + for (i = 0; i < 8; i++) { + c += i*2; + c = foo (c); // All calculations related to c are removed. + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + { + crc |= 0x8000; + } + else + crc &= 0x7fff; + } + return crc; +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-16.c b/gcc/testsuite/gcc.dg/crc-side-instr-16.c new file mode 100644 index 00000000000..2f14c90ab11 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-16.c @@ -0,0 +1,38 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +__attribute__ ((noinline,noipa)) +int foo (int c) +{ + return c*c; +} + +ee_u16 crcu8 (ee_u8 data, ee_u16 crc) { + ee_u16 i = 0, x16 = 0, carry = 0; + int c; + for (i = 0; i < 8; i++) { + c += i*2; + c = foo (c); //Warning, encountered unsupported statement, while executing gimple statements! + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + { + crc |= 0x8000; + } + else + crc &= 0x7fff; + } + return crc; +} + +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-17.c b/gcc/testsuite/gcc.dg/crc-side-instr-17.c new file mode 100644 index 00000000000..8f98df87446 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-17.c @@ -0,0 +1,37 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +int foo (int c) +{ + return c*c; +} + +ee_u16 crcu8 (ee_u8 data, ee_u16 crc) { + ee_u16 i = 0, x16 = 0, carry = 0; + int c; + for (i = 0; i < 8; i++) { + c += i*2; + c = foo (c); + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + { + crc |= 0x8000; + } + else + crc &= 0x7fff; + } + return crc + c; +} + +/* { dg-final { scan-tree-dump "There is more than one output phi." "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-2.c b/gcc/testsuite/gcc.dg/crc-side-instr-2.c new file mode 100644 index 00000000000..23dfa3edc17 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-2.c @@ -0,0 +1,27 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include +#include + +uint16_t crc16_update(uint16_t crc, uint8_t a) { + int i; + int b; + for (i = 0; i < 8; ++i) { + if ((crc & 1) ^ (a & 1)) + crc = (crc >> 1) ^ 0xa001; + else + crc = (crc >> 1); + a >>= 1; + b = crc; // Unused instruction, this is safe to remove. + } + printf ("%d", b); + return crc; +} + +/* { dg-final { scan-tree-dump "crc16_update function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\\\}" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-3.c b/gcc/testsuite/gcc.dg/crc-side-instr-3.c new file mode 100644 index 00000000000..731c6624a2e --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-3.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include + +uint16_t crc16_update(uint16_t crc, uint8_t a) { + int i; + int b; + for (i = 0; i < 8; ++i) { + if ((crc & 1) ^ (a & 1)) + crc = (crc >> 1) ^ 0xa001; + else + crc = (crc >> 1); + a >>= 1; + b = crc; + } + int c = b; // This is removed from the compiled code. + return crc; +} + +/* { dg-final { scan-tree-dump "crc16_update function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\\\}" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-4.c b/gcc/testsuite/gcc.dg/crc-side-instr-4.c new file mode 100644 index 00000000000..7612c52c67e --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-4.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include + +uint16_t crc16_update(uint16_t crc, uint8_t a) { + int i; + uint16_t b; + for (i = 0; i < 8; ++i) { + if ((crc & 1) ^ (a & 1)) + crc = (crc >> 1) ^ 0xa001; + else + crc = (crc >> 1); + a >>= 1; + b = crc; + } + uint16_t c = b++; // This is removed from the compiled code. + return crc; +} + +/* { dg-final { scan-tree-dump "crc16_update function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\\\}" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-5.c b/gcc/testsuite/gcc.dg/crc-side-instr-5.c new file mode 100644 index 00000000000..ef97028e881 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-5.c @@ -0,0 +1,26 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include + +uint16_t crc16_update(uint16_t crc, uint8_t a) { + int i; + uint16_t b; + for (i = 0; i < 8; ++i) { + if ((crc & 1) ^ (a & 1)) + crc = (crc >> 1) ^ 0xa001; + else + crc = (crc >> 1); + a >>= 1; + b = crc; // In compiled version, b is outside of the loop. + } + uint16_t c = ++b; + return c; +} + +/* { dg-final { scan-tree-dump "crc16_update function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\\\}" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-6.c b/gcc/testsuite/gcc.dg/crc-side-instr-6.c new file mode 100644 index 00000000000..f559fa94a53 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-6.c @@ -0,0 +1,42 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include +#include +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +int a; +ee_u16 crcu8 (ee_u8 data, ee_u16 crc) { + ee_u8 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 8; i++) { + a++; // this is moved outside of the loop + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +int main() +{ + printf ("%04X\n", crcu8 (0, 0xaa)); + printf ("%d", a); +} + + +/* { dg-final { scan-tree-dump "crcu8 function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{\[0, \]*1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\\\}" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-7.c b/gcc/testsuite/gcc.dg/crc-side-instr-7.c new file mode 100644 index 00000000000..d80aadc6ccb --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-7.c @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +int a[10] = {}; +ee_u16 crcu8 (ee_u8 data, ee_u16 crc) { + ee_u8 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 8; i++) { + a[i] = crc; + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +int main() +{ + printf ("%04X\n", crcu8 (0, 0xaa)); + printf ("%d", a[0]); +} + + +/* { dg-final { scan-tree-dump "crcu8 function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump-times "calculates CRC!" 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-8.c b/gcc/testsuite/gcc.dg/crc-side-instr-8.c new file mode 100644 index 00000000000..23263e156a1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-8.c @@ -0,0 +1,36 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +int a; +ee_u16 crcu8 (ee_u8 data, ee_u16 crc) { + ee_u8 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 8; i++) { + a += crc; // Defined variable is used outside the loop. + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +int main() +{ + printf ("%04X\n", crcu8 (0, 0xaa)); + printf ("%d", a); +} + +/* { dg-final { scan-tree-dump "There is more than one output phi." "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/crc-side-instr-9.c b/gcc/testsuite/gcc.dg/crc-side-instr-9.c new file mode 100644 index 00000000000..477318e05e0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/crc-side-instr-9.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +int a; +ee_u16 crcu8 (ee_u8 data, ee_u16 crc) { + ee_u8 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 8; i++) { + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + { + a = crc; + crc |= 0x8000; + } + else + crc &= 0x7fff; + } + return crc; +} + +/* { dg-final { scan-tree-dump "There is more than one output phi." "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-1.c b/gcc/testsuite/gcc.dg/torture/crc-1.c new file mode 100644 index 00000000000..482fe04cc55 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-1.c @@ -0,0 +1,114 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details -fdisable-tree-phiopt2 -fdisable-tree-phiopt3" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include +#include +#include + +#define CRC16 0x8005 + +__attribute__ ((noinline,optimize(0))) +uint16_t gen_crc16_O0 (const uint8_t *data, uint16_t size) { + uint16_t out = 0; + int bits_read = 0, bit_flag; + + if (data == NULL) + return 0; + + while (size > 0) { + bit_flag = out >> 15; + + out <<= 1; + out |= (*data >> bits_read) & 1; + + bits_read++; + if (bits_read > 7) { + bits_read = 0; + data++; + size--; + } + + if (bit_flag) + out ^= CRC16; + } + + int i; + for (i = 0; i < 16; ++i) { + bit_flag = out >> 15; + out <<= 1; + if (bit_flag) + out ^= CRC16; + } + + uint16_t crc = 0; + i = 0x8000; + int j = 0x0001; + for (; i != 0; i >>= 1, j <<= 1) { + if (i & out) crc |= j; + } + + return crc; +} + +uint16_t gen_crc16 (const uint8_t *data, uint16_t size) { + uint16_t out = 0; + int bits_read = 0, bit_flag; + + if (data == NULL) + return 0; + + while (size > 0) { + bit_flag = out >> 15; + + out <<= 1; + out |= (*data >> bits_read) & 1; + + bits_read++; + if (bits_read > 7) { + bits_read = 0; + data++; + size--; + } + + if (bit_flag) + out ^= CRC16; + } + + int i; + for (i = 0; i < 16; ++i) { + bit_flag = out >> 15; + out <<= 1; + if (bit_flag) + out ^= CRC16; + } + + uint16_t crc = 0; + i = 0x8000; + int j = 0x0001; + for (; i != 0; i >>= 1, j <<= 1) { + if (i & out) crc |= j; + } + + return crc; +} + +int main () +{ + if (gen_crc16 ("hello", 5) != 13522) + abort (); + + for (uint8_t i = 0; i < 255; i++) + { + uint16_t res1 = gen_crc16_O0 (&i, 1); + uint16_t res2 = gen_crc16 (&i, 1); + if (res1 != res2) + abort (); + } +} +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 15" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{\[0, \]*1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1\\\}" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-10.c b/gcc/testsuite/gcc.dg/torture/crc-10.c new file mode 100644 index 00000000000..24bdda1e3b5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-10.c @@ -0,0 +1,48 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include +#include + +#define POLY (0x1070U << 3) +#define u8 uint8_t +#define u16 uint16_t + + __attribute__ ((noinline,optimize(0))) +u8 crc8_O0 (u16 data) { + int i; + for (i = 0; i < 8; i++) { + if (data & 0x8000) + data = data ^ POLY; + data = data << 1; + } + return (u8)(data >> 8); +} + +u8 crc8 (u16 data) { + int i; + for (i = 0; i < 8; i++) { + if (data & 0x8000) + data = data ^ POLY; + data = data << 1; + } + return (u8)(data >> 8); +} + +int main () +{ + for (u8 i = 0; i < 255; i++) + { + u8 res1 = crc8_O0 (i); + u8 res2 = crc8 (i); + if (res1 != res2) + abort (); + } +} +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0\\\}" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-11.c b/gcc/testsuite/gcc.dg/torture/crc-11.c new file mode 100644 index 00000000000..44da2b28cff --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-11.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +//We just don't check cases when the polynomial is a variable, as we can't replace it. +#include + +uint16_t crc16(uint16_t crc, uint8_t a, uint16_t polynom) { + int i; + crc ^= a; + for (i = 0; i < 8; ++i) { + if (crc & 1) + crc = (crc >> 1) ^ polynom; + else + crc = (crc >> 1); + } + return crc; +} + +/* { dg-final { scan-tree-dump "Second operand of the xor statement isn't an integer constant.\n" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-12.c b/gcc/testsuite/gcc.dg/torture/crc-12.c new file mode 100644 index 00000000000..a4fbc0268dd --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-12.c @@ -0,0 +1,112 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc -fdisable-tree-phiopt2 -fdisable-tree-phiopt3" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include +#include +#include + +#define CRC16 0x8005 + +__attribute__ ((noinline,optimize(0))) +uint16_t gen_crc16_O0 (const uint8_t *data, uint16_t size) { + uint16_t out = 0; + int bits_read = 0, bit_flag; + + if (data == NULL) + return 0; + + while (size > 0) { + bit_flag = out >> 15; + + out <<= 1; + out |= (*data >> bits_read) & 1; + + bits_read++; + if (bits_read > 7) { + bits_read = 0; + data++; + size--; + } + + if (bit_flag) + out ^= CRC16; + + } + + int i; + for (i = 0; i < 16; ++i) { + bit_flag = out >> 15; + out <<= 1; + if (bit_flag) + out ^= CRC16; + } + + uint16_t crc = 0; + i = 0x8000; + int j = 0x0001; + for (; i != 0; i >>= 1, j <<= 1) { + if (i & out) crc |= j; + } + + return crc; +} + +uint16_t gen_crc16 (const uint8_t *data, uint16_t size) { + uint16_t out = 0; + int bits_read = 0, bit_flag; + + if (data == NULL) + return 0; + + while (size > 0) { + bit_flag = out >> 15; + + out <<= 1; + out |= (*data >> bits_read) & 1; + + bits_read++; + if (bits_read > 7) { + bits_read = 0; + data++; + size--; + } + + if (bit_flag) + out ^= CRC16; + + } + + int i; + for (i = 0; i < 16; ++i) { + bit_flag = out >> 15; + out <<= 1; + if (bit_flag) + out ^= CRC16; + } + + uint16_t crc = 0; + i = 0x8000; + int j = 0x0001; + for (; i != 0; i >>= 1, j <<= 1) { + if (i & out) crc |= j; + } + + return crc; +} + +int main () +{ + for (uint8_t i = 0; i < 255; i++) + { + uint16_t res1 = gen_crc16_O0 (&i, 1); + uint16_t res2 = gen_crc16 (&i, 1); + if (res1 != res2) + abort (); + } +} +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc"} } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 15" "crc"} } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc"} } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-13.c b/gcc/testsuite/gcc.dg/torture/crc-13.c new file mode 100644 index 00000000000..9237056139f --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-13.c @@ -0,0 +1,58 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include + +__attribute__ ((noinline,optimize(0))) +unsigned short crc16_O0 (unsigned char newByte, unsigned short crcValue) { + unsigned char i; + + for (i = 0; i < 8; i++) { + + if (((crcValue & 0x8000) >> 8) ^ (newByte & 0x80)) { + crcValue = (crcValue << 1) ^ 0x102; + } else { + crcValue = (crcValue << 1); + } + + newByte <<= 1; + } + + return crcValue; +} + +unsigned short crc16 (unsigned char newByte, unsigned short crcValue) { + unsigned char i; + + for (i = 0; i < 8; i++) { + + if (((crcValue & 0x8000) >> 8) ^ (newByte & 0x80)) { + crcValue = (crcValue << 1) ^ 0x102; + } else { + crcValue = (crcValue << 1); + } + + newByte <<= 1; + } + + return crcValue; +} + +int main () +{ + unsigned short crc = 0x0D80; + for (unsigned char i = 0; i < 255; i++) + { + unsigned short res1 = crc16_O0 (crc, i); + unsigned short res2 = crc16 (crc, i); + if (res1 != res2) + abort (); + crc = res1; + } +} +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-14.c b/gcc/testsuite/gcc.dg/torture/crc-14.c new file mode 100644 index 00000000000..855695bf38f --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-14.c @@ -0,0 +1,55 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include + +typedef unsigned char uint8_t; + +__attribute__ ((noinline,optimize(0))) +uint8_t gencrc_O0 (uint8_t *data) +{ + uint8_t crc = 0xff; + size_t j; + crc ^= *data; + for (j = 0; j < 8; j++) + { + if ((crc & 0x80) != 0) + crc = (uint8_t) ((crc << 1) ^ 0x31); + else + crc <<= 1; + } + return crc; +} + +uint8_t gencrc (uint8_t *data) +{ + uint8_t crc = 0xff; + size_t j; + crc ^= *data; + for (j = 0; j < 8; j++) + { + if ((crc & 0x80) != 0) + crc = (uint8_t) ((crc << 1) ^ 0x31); + else + crc <<= 1; + } + return crc; +} + +int main () +{ + for (uint8_t i = 0; i < 255; i++) + { + uint8_t res1 = gencrc_O0 (&i); + uint8_t res2 = gencrc (&i); + if (res1 != res2) + abort (); + } +} + +/* { dg-final { scan-tree-dump "gencrc function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-15.c b/gcc/testsuite/gcc.dg/torture/crc-15.c new file mode 100644 index 00000000000..1a1fc6f7d17 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-15.c @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-options "-w -fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +/* Test from busybox, we don't verify as it depends on endian variable. */ +#include +#include + +uint32_t* crc32_filltable(uint32_t *crc_table, int endian) +{ + uint32_t polynomial = endian ? 0x04c11db7 : 0xedb88320; + uint32_t c; + unsigned i, j; + + if (!crc_table) + crc_table = malloc (256 * sizeof (uint32_t)); + + for (i = 0; i < 256; i++) + { + c = endian ? (i << 24) : i; + for (j = 8; j; j--) + { + if (endian) + c = (c & 0x80000000) ? ((c << 1) ^ polynomial) : (c << 1); + else + c = (c & 1) ? ((c >> 1) ^ polynomial) : (c >> 1); + } + *crc_table++ = c; + } + + return crc_table - 256; +} diff --git a/gcc/testsuite/gcc.dg/torture/crc-16.c b/gcc/testsuite/gcc.dg/torture/crc-16.c new file mode 100644 index 00000000000..c0e5b1faf40 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-16.c @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +/* A test from busybox - we don't verify, as unsigned is used for the + "expected" variable, but 16-bit CRC is calculated. We verify only those cases + when CRC variable's size and calculated CRC are equal. In the algorithm we don't + check whether "expected" variable's only low half is used. */ +int receive(/*int read_fd, */int file_fd) +{ + /* Initialization is not the same as in Busybox. */ + unsigned blockLength = 13; + unsigned char blockBuf[1024] = "sgdfsgdfsgdfs"; + int cksum_or_crc = 0x4561; + + unsigned expected; + int i, j; + /* ... */ + expected = 0; + for (i = 0; i < blockLength; i++) + { + expected = expected ^ blockBuf[i] << 8; + for (j = 0; j < 8; j++) + { + if (expected & 0x8000) + expected = (expected << 1) ^ 0x1021; + else + expected = (expected << 1); + } + } + expected &= 0xffff; + + if (cksum_or_crc != expected) { + /* ... */ + return 1; // was - goto timout + } + + /* ... */ + return -1; +} \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-17.c b/gcc/testsuite/gcc.dg/torture/crc-17.c new file mode 100644 index 00000000000..9ef5e49c001 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-17.c @@ -0,0 +1,72 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include + +__attribute__ ((noinline,optimize(0))) +unsigned short calc_crc_O0 (unsigned short crc, unsigned char data) +{ + unsigned int i, j, org, dst; + org = data; + dst = 0; + + for (i = 0; i < 8; i++) { + org <<= 1; + dst >>= 1; + if (org & 0x100) + dst |= 0x80; + } + data = (unsigned char) dst; + crc ^= (unsigned int) data << (16 - 8); + for (j = 0; j < 8; j++) { + if (crc & 0x8000U) + crc = (crc << 1) ^ 0x1021U ; + else + crc <<= 1 ; + } + return crc; +} + +unsigned short calc_crc (unsigned short crc, unsigned char data) +{ + unsigned int i, j, org, dst; + org = data; + dst = 0; + + for (i = 0; i < 8; i++) { + org <<= 1; + dst >>= 1; + if (org & 0x100) + dst |= 0x80; + } + data = (unsigned char) dst; + crc ^= (unsigned int) data << (16 - 8); + for (j = 0; j < 8; j++) { + if (crc & 0x8000U) + crc = (crc << 1) ^ 0x1021U ; + else + crc <<= 1 ; + } + return crc; +} + + +int main () +{ + unsigned short crc = 0x0D80; + for (unsigned char i = 0; i < 255; i++) + { + unsigned short res1 = calc_crc_O0 (crc, i); + unsigned short res2 = calc_crc (crc, i); + if (res1 != res2) + abort (); + crc = res1; + } +} +/* { dg-final { scan-tree-dump "calc_crc function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Polynomial's value is \\\{\[0, \]*1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1\\\}" 1 "crc"} } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-18.c b/gcc/testsuite/gcc.dg/torture/crc-18.c new file mode 100644 index 00000000000..9a9c7f9f3a4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-18.c @@ -0,0 +1,42 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include + +__attribute__ ((noinline,optimize(0))) +unsigned char crc8_O0 (unsigned char value) +{ + for (int i = 0; i < 8; ++i) { + value = (value & 0x80) ? ((value << 1) ^ 0x31) : (value << 1); + } + + return value; +} + +unsigned char crc8 (unsigned char value) +{ + for (int i = 0; i < 8; ++i) { + value = (value & 0x80) ? ((value << 1) ^ 0x31) : (value << 1); + } + + return value; +} + +int main () +{ + for (unsigned char i = 0; i < 255; i++) + { + unsigned char res1 = crc8_O0 (i); + unsigned char res2 = crc8 (i); + if (res1 != res2) + abort (); + } +} + +/* { dg-final { scan-tree-dump "crc8 function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc"} } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{\[0, \]*0, 0, 1, 1, 0, 0, 0, 1\\\}" "crc"} } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-19.c b/gcc/testsuite/gcc.dg/torture/crc-19.c new file mode 100644 index 00000000000..2cc4eaafffa --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-19.c @@ -0,0 +1,30 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-O3" "-flto" } } */ + + #include + + uint32_t crc24_reverse(uint32_t crc, const uint8_t *data, uint8_t len) + { + uint32_t state = crc; + uint8_t i; + + for (i = 0; i < len; i++) { + uint8_t n, cur = data[len - i - 1]; + + for (n = 0; n < 8; n++) { + int top_bit = state >> 23; + + state = (state << 1) & 0xffffff; + state |= top_bit ^ ((cur >> (7 - n)) & 1); + if (top_bit) + state ^= 0xb4c000; + } + } + + return state; + } + +/* { dg-final { scan-tree-dump "crc24_reverse function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/crc-2.c b/gcc/testsuite/gcc.dg/torture/crc-2.c new file mode 100644 index 00000000000..78cd88449d6 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-2.c @@ -0,0 +1,28 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#define CRC16_CCITT 0x102 +#define POLYNOM CRC16_CCITT + +unsigned int crc16 (unsigned int crcValue, unsigned char newByte) { + unsigned char i; + + for (i = 0; i < 8; i++) { + + if (((crcValue & 0x8000) >> 8) ^ (newByte & 0x80)) { + crcValue = (crcValue << 1) ^ POLYNOM; + } else { + crcValue = (crcValue << 1); + } + + newByte <<= 1; + } + + return crcValue; +} + +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{\[0, \]*1, 0, 0, 0, 0, 0, 0, 1, 0\\\}" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-20.c b/gcc/testsuite/gcc.dg/torture/crc-20.c new file mode 100644 index 00000000000..a12d4130632 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-20.c @@ -0,0 +1,39 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +/* We don't detect this case, because there are many conditional branches. + Even if we detected it won't be verified, + because we would get more than two execution paths (states). */ + +#include + +typedef uint8_t byte; +byte Compute_CRC8_Simple_OneByte_ShiftReg (byte byteVal) +{ + const byte generator = 0x1D; + byte crc = 0; /* init crc register with 0 */ + byte b = byteVal; + for (int i = 7; i >= 0; i--) + { + /* check if MSB is set */ + if ((crc & 0x80) != 0) + { /* MSB set, shift it out of the register */ + crc = (byte) (crc << 1); + /* shift in next bit of input stream: + * If it's 1, set LSB of crc to 1. + * If it's 0, set LSB of crc to 0. */ + crc = ((byte) (b & (1 << i)) != 0) ? (byte) (crc | 0x01) + : (byte) (crc & 0xFE); + /* Perform the 'division' by XORing the crc register with the generator polynomial */ + crc = (byte) (crc ^ generator); + } + else + { /* MSB not set, shift it out and shift in next bit of input stream. Same as above, just no division */ + crc = (byte) (crc << 1); + crc = ((byte) (b & (1 << i)) != 0) ? (byte) (crc | 0x01) + : (byte) (crc & 0xFE); + } + } + return crc; +} diff --git a/gcc/testsuite/gcc.dg/torture/crc-21.c b/gcc/testsuite/gcc.dg/torture/crc-21.c new file mode 100644 index 00000000000..49396a6fcd2 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-21.c @@ -0,0 +1,53 @@ +/* { dg-do run { target { lp64 } } } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint32_t _crc32_O0 (uint32_t crc, uint32_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 32; i++) { + if (crc & 0x80000000) + crc = (crc << 1) ^ 0x04C11DB7; + else + crc = (crc << 1); + } + + return crc; +} + +uint32_t _crc32 (uint32_t crc, uint32_t data) { + int i; + crc = crc ^ data; + + for (i = 0; i < 32; i++) { + if (crc & 0x80000000) + crc = (crc << 1) ^ 0x04C11DB7; + else + crc = (crc << 1); + } + + return crc; +} + +int main () +{ + uint32_t crc = 0x0D800D80; + for (uint32_t i = 0; i < 0xff; i++) + { + uint32_t res1 = _crc32_O0 (crc, i); + uint32_t res2 = _crc32 (crc, i); + if (res1 != res2) + abort (); + crc = res1; + } +} +/* { dg-final { scan-tree-dump "_crc32 function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 31" "crc"} } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc"} } */ +/* { dg-final { scan-tree-dump "calculates CRC." "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-22.c b/gcc/testsuite/gcc.dg/torture/crc-22.c new file mode 100644 index 00000000000..023e08aa98c --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-22.c @@ -0,0 +1,66 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include + +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +__attribute__ ((noinline,optimize(0))) +ee_u16 crcu16_O0 (ee_u16 data, ee_u16 crc) { + ee_u8 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 16; i++) { + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +ee_u16 crcu16 (ee_u16 data, ee_u16 crc) { + ee_u8 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 16; i++) { + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +int main () +{ + ee_u16 crc = 0x0D80; + for (ee_u16 i = 0; i < 255; i++) + { + ee_u16 res1 = crcu16_O0 (i, crc); + ee_u16 res2 = crcu16 (i, crc); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 15" "crc"} } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc"} } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-23.c b/gcc/testsuite/gcc.dg/torture/crc-23.c new file mode 100644 index 00000000000..e289e2f848c --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-23.c @@ -0,0 +1,74 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details -ftree-cselim" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +// Modified example from crc-from-fedora-packages-24.c + +#include +#include + +__attribute__ ((noinline,optimize(0))) +void crc_byte_O0 (const char data, uint16_t *crc16) +{ + int k; + uint16_t c,d ; + + c = data << 8 ; + d = c; + + for (k = 0; k < 16; k++) { + *crc16 = (c & 0x8000) ^ *crc16; + + if (*crc16 & 0x8000) { + *crc16 = *crc16 << 1; + *crc16 = *crc16 ^ 0x8005; + } else + *crc16 = *crc16 << 1; + + d = d << 1; + c = d; + } +} + +void crc_byte (const char data, uint16_t *crc16) +{ + int k; + uint16_t c,d ; + + c = data << 8 ; + d = c; + + for (k = 0; k < 16; k++) { + *crc16 = (c & 0x8000) ^ *crc16; + + if (*crc16 & 0x8000) { + *crc16 = *crc16 << 1; + *crc16 = *crc16 ^ 0x8005; + } else + *crc16 = *crc16 << 1; + + d = d << 1; + c = d; + } +} + +int main () +{ + uint16_t crc = 0x0D80; + for (char i = 0; i < 127; i++) + { + uint16_t res1 = crc, res2 = crc; + crc_byte_O0 (i, &res1); + crc_byte (i, &res2); + + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 15" "crc"} } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc"} } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-24.c b/gcc/testsuite/gcc.dg/torture/crc-24.c new file mode 100644 index 00000000000..863569a009a --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-24.c @@ -0,0 +1,30 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +// Modified Coremark test, we don't detect. + +#include + +uint16_t not_crcu8 (uint16_t data, uint16_t crc) +{ + uint16_t i = 0, carry = 0; + for (i = 0; i < 16; i++) + { + if ((((crc & 1) == 1) && ((data & 1) == 0)) + || (((crc & 1) == 0) && (data & 1) == 1)) + { + crc ^= 0x4002; + carry = 1; + } + else + carry = 0; + crc >>= 1; + data >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-25.c b/gcc/testsuite/gcc.dg/torture/crc-25.c new file mode 100644 index 00000000000..0e9207f6fad --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-25.c @@ -0,0 +1,78 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-funroll-loops" "-fpeel-loops" "-flto" } } */ + +//Test from roms/u-boot-sam460ex/drivers/net/mpc512x_fec.c(fsl_mcdmafec.c) + +#include +#include + +typedef uint8_t u8; +typedef uint32_t u32; + +__attribute__ ((noinline,optimize(0))) +u32 mpc512x_fec_set_hwaddr_O0 (unsigned char *mac) +{ + u8 currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + u32 crc = 0xffffffff; /* initial value */ + + for (byte = 0; byte < 6; byte++) { + currByte = mac[byte]; + for (bit = 0; bit < 8; bit++) { + if ((currByte & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + currByte >>= 1; + } + } + + crc = crc >> 26; + return crc; +} + +u32 mpc512x_fec_set_hwaddr (unsigned char *mac) +{ + u8 currByte; /* byte for which to compute the CRC */ + int byte; /* loop - counter */ + int bit; /* loop - counter */ + u32 crc = 0xffffffff; /* initial value */ + + for (byte = 0; byte < 6; byte++) { + currByte = mac[byte]; + for (bit = 0; bit < 8; bit++) { + if ((currByte & 0x01) ^ (crc & 0x01)) { + crc >>= 1; + crc = crc ^ 0xedb88320; + } else { + crc >>= 1; + } + currByte >>= 1; + } + } + + crc = crc >> 26; + return crc; +} + +int main () +{ + unsigned char st[6] = "Hello"; + for (unsigned char i = 0; i < 255; i++) + { + st[0] = i; + u32 res1 = mpc512x_fec_set_hwaddr_O0 (st); + u32 res2 = mpc512x_fec_set_hwaddr (st); + if (res1 != res2) + abort (); + } +} +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc"} } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc"} } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-26.c b/gcc/testsuite/gcc.dg/torture/crc-26.c new file mode 100644 index 00000000000..c5a4b47aff7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-26.c @@ -0,0 +1,55 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-funroll-loops" "-fpeel-loops" "-flto" } } */ + +//Test from roms/u-boot-sam460ex/drivers/mtd/ubi/crc32.c +#include +#include + +typedef uint8_t u8; +typedef uint32_t u32; +#define CRCPOLY_LE 0xedb88320 + +__attribute__ ((noinline,optimize(0))) +u32 crc32_le_O0 (u32 crc, unsigned char const *p, size_t len) +{ + int i; + while (len--) { + crc ^= *p++; + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + } + return crc; +} + +u32 crc32_le (u32 crc, unsigned char const *p, size_t len) +{ + int i; + while (len--) { + crc ^= *p++; + for (i = 0; i < 8; i++) + crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY_LE : 0); + } + return crc; +} + + +int main () +{ + u32 crc = 0x0D800D80; + unsigned char st[2] = {'H','i'}; + for (unsigned char i = 0; i < 255; i++) + { + st[0] = i; + u32 res1 = crc32_le_O0 (crc, st, 2); + u32 res2 = crc32_le (crc, st, 2); + if (res1 != res2) + abort (); + crc = res1; + } +} +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc"} } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc"} } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-27.c b/gcc/testsuite/gcc.dg/torture/crc-27.c new file mode 100644 index 00000000000..cd1ab9404c9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-27.c @@ -0,0 +1,27 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +// Test from roms/ipxe/src/util/zbin.c +// We don't detect this case, as second operand of the xor is a variable. + +#include +#include + +#define CRCPOLY 0xedb88320 +#define CRCSEED 0xffffffff + +uint32_t crc32_le ( uint32_t crc, const void *data, size_t len ) { + const uint8_t *src = data; + uint32_t mult; + unsigned int i; + + while ( len-- ) { + crc ^= *(src++); + for ( i = 0 ; i < 8 ; i++ ) { + mult = ( ( crc & 1 ) ? CRCPOLY : 0 ); + crc = ( ( crc >> 1 ) ^ mult ); + } + } + return crc; +} \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-28.c b/gcc/testsuite/gcc.dg/torture/crc-28.c new file mode 100644 index 00000000000..0ecd8842e08 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-28.c @@ -0,0 +1,90 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-funroll-loops" "-fpeel-loops" "-flto" } } */ + +// Test from roms/u-boot/drivers/ram/octeon/dimm_spd_eeprom.c + +#include +#include + +typedef uint8_t u8; +typedef uint16_t u16; + +// We don't verify as crc is of type int (32 bit), +// but the polynomial fits into 16 bit. +u16 ddr3_crc16_orig (u8 *ptr, int count) +{ + /* From DDR3 SPD specification */ + int crc, i; + + crc = 0; + while (--count >= 0) { + crc = crc ^ (int)*ptr++ << 8; + for (i = 0; i < 8; ++i) { + if (crc & 0x8000) + crc = crc << 1 ^ 0x1021; + else + crc = crc << 1; + } + } + + return (crc & 0xFFFF); +} + +__attribute__ ((noinline,optimize(0))) +u16 ddr3_crc16_modified_O0 (u8 *ptr, int count) +{ + /* From DDR3 SPD specification */ + u16 crc, i; + + crc = 0; + while (--count >= 0) { + crc = crc ^ (int)*ptr++ << 8; + for (i = 0; i < 8; ++i) { + if (crc & 0x8000) + crc = crc << 1 ^ 0x1021; + else + crc = crc << 1; + } + } + + return (crc & 0xFFFF); +} + +u16 ddr3_crc16_modified (u8 *ptr, int count) +{ + /* From DDR3 SPD specification */ + u16 crc, i; + + crc = 0; + while (--count >= 0) { + crc = crc ^ (int)*ptr++ << 8; + for (i = 0; i < 8; ++i) { + if (crc & 0x8000) + crc = crc << 1 ^ 0x1021; + else + crc = crc << 1; + } + } + + return (crc & 0xFFFF); +} + +int main () +{ + u8 st[2] = {'H', 'i'}; + for (u8 i = 0; i < 255; i++) + { + st[0] = i; + u16 res1 = ddr3_crc16_modified_O0 (st, 2); + u16 res2 = ddr3_crc16_modified (st, 2); + if (res1 != res2) + abort (); + } +} + +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc"} } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc"} } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc"} } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-29.c b/gcc/testsuite/gcc.dg/torture/crc-29.c new file mode 100644 index 00000000000..50833fb7a01 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-29.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +/* This is a CRC, even though it may seem it's not. + 'j' is initialized with 4 (100) and increased by 2 each time. + Thus, first bit value is never 1. For each iteration 'j & 1' yields 0, + and it doesn't affect the CRC result. */ + +#include + +uint16_t crc (uint8_t data, uint16_t crc) { + uint8_t x16 = 0, carry = 0; + for (uint8_t i = 0, j=4; i < 8; i++, j+=2) { + x16 = (uint8_t) (((uint8_t) crc & 1) ^ (j & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-3.c b/gcc/testsuite/gcc.dg/torture/crc-3.c new file mode 100644 index 00000000000..6297427320d --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-3.c @@ -0,0 +1,37 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-O3" "-flto" } } */ + +//We don't verify this case as for the CRC unsigned int (32 bit) is used and +// the polynomial fits into 16 bit. Also, as the loop's iteration number and +// data's size differ. + +unsigned short crc16 (char *data_p, unsigned short length) { + unsigned char i; + unsigned int data; + unsigned int crc = 0xffff; + + if (length == 0) + return (~crc); + + do { + for (i = 0, data = (unsigned int) 0xff & *data_p++; + i < 8; + i++, data >>= 1) { + if ((crc & 0x0001) ^ (data & 0x0001)) + crc = (crc >> 1) ^ 0x8408; + else crc >>= 1; + } + } while (--length); + + crc = ~crc; + data = crc; + crc = (crc << 8) | (data >> 8 & 0xff); + + return (crc); +} + +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number and data's size differ." "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-4.c b/gcc/testsuite/gcc.dg/torture/crc-4.c new file mode 100644 index 00000000000..e1771d9d6cf --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-4.c @@ -0,0 +1,51 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint16_t crc16_update_O0 (uint16_t crc, uint8_t a) { + int i; + crc ^= a; + for (i = 0; i < 8; ++i) { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + return crc; +} + +uint16_t crc16_update (uint16_t crc, uint8_t a) { + int i; + crc ^= a; + for (i = 0; i < 8; ++i) { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + return crc; +} + +int main () +{ + uint16_t crc = 0x0D80; + for (uint8_t i = 0; i < 255; i++) + { + uint16_t res1 = crc16_update (crc, i); + uint16_t res2 = crc16_update_O0 (crc, i); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\\\}" "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-5.c b/gcc/testsuite/gcc.dg/torture/crc-5.c new file mode 100644 index 00000000000..ca30f0ddbef --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-5.c @@ -0,0 +1,66 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include + +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +__attribute__ ((noinline,optimize(0))) +ee_u16 crcu8_O0 (ee_u8 data, ee_u16 crc) { + ee_u8 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 8; i++) { + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +ee_u16 crcu8 (ee_u8 data, ee_u16 crc) { + ee_u8 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 8; i++) { + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +int main () +{ + ee_u16 crc = 0x0D80; + for (ee_u8 i = 0; i < 255; i++) + { + ee_u16 res1 = crcu8_O0 (i, crc); + ee_u16 res2 = crcu8 (i, crc); + if (res1 != res2) + abort (); + crc = res1; + } +} +/* { dg-final { scan-tree-dump "crcu8 function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{\[0, \]*1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\\\}" "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-6.c b/gcc/testsuite/gcc.dg/torture/crc-6.c new file mode 100644 index 00000000000..a3705d683bc --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-6.c @@ -0,0 +1,64 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-O3" "-flto"} } */ + +#include +#include + +typedef uint8_t crc; +#define WIDTH (8 * sizeof(crc)) +#define TOPBIT (1 << (WIDTH - 1)) + +__attribute__ ((noinline,optimize(0))) +crc crcSlow_O0 (uint8_t const message[], int nBytes) { + crc remainder = 0; +/* +* Perform modulo-2 division, a byte at a time. +*/ + for (int byte = 0; byte < nBytes; ++byte) { + remainder ^= (message[byte] << (WIDTH - 8)); + for (uint8_t bit = 8; bit > 0; --bit) { + if (remainder & TOPBIT) { + remainder = (remainder << 1) ^ 1234; + } else { + remainder = (remainder << 1); + } + } + } + return (remainder); +} + +crc crcSlow (uint8_t const message[], int nBytes) { + crc remainder = 0; +/* +* Perform modulo-2 division, a byte at a time. +*/ + for (int byte = 0; byte < nBytes; ++byte) { + remainder ^= (message[byte] << (WIDTH - 8)); + for (uint8_t bit = 8; bit > 0; --bit) { + if (remainder & TOPBIT) { + remainder = (remainder << 1) ^ 1234; + } else { + remainder = (remainder << 1); + } + } + } + return (remainder); +} + +int main () +{ + for (crc i = 0; i < 255; i++) + { + crc res1 = crcSlow_O0 (&i, 1); + crc res2 = crcSlow (&i, 1); + if (res1 != res2) + abort (); + } +} +/* { dg-final { scan-tree-dump "crcSlow function maybe contains CRC calculation." "crc"} } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc"} } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc"} } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{1, 1, 0, 1, 0, 0, 1, 0\\\}" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-7.c b/gcc/testsuite/gcc.dg/torture/crc-7.c new file mode 100644 index 00000000000..53d88cb3310 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-7.c @@ -0,0 +1,50 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto"} } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint16_t crc_xmodem_update_O0 (uint16_t crc, uint8_t data) { + int i; + crc = crc ^ ((uint16_t) data << 8); + for (i = 0; i < 8; i++) { + if (crc & 0x8000) + crc = (crc << 1) ^ 0x1021; + else + crc <<= 1; + } + return crc; +} + +uint16_t crc_xmodem_update (uint16_t crc, uint8_t data) { + int i; + crc = crc ^ ((uint16_t) data << 8); + for (i = 0; i < 8; i++) { + if (crc & 0x8000) + crc = (crc << 1) ^ 0x1021; + else + crc <<= 1; + } + return crc; +} + +int main () +{ + uint16_t crc = 0x0D80; + for (uint8_t i = 0; i < 255; i++) + { + uint16_t res1 = crc_xmodem_update_O0 (i, crc); + uint16_t res2 = crc_xmodem_update (i, crc); + if (res1 != res2) + abort (); + crc = res1; + } +} +/* { dg-final { scan-tree-dump "crc_xmodem_update function maybe contains CRC calculation." "crc"} } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc"} } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc"} } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1\\\}" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-8.c b/gcc/testsuite/gcc.dg/torture/crc-8.c new file mode 100644 index 00000000000..41f8f5797d5 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-8.c @@ -0,0 +1,50 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto"} } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint8_t _crc_ibutton_update_O0 (uint8_t crc, uint8_t data) { + uint8_t i; + crc = crc ^ data; + for (i = 0; i < 8; i++) { + if (crc & 0x01) + crc = (crc >> 1) ^ 0x8C; + else + crc >>= 1; + } + return crc; +} + +uint8_t _crc_ibutton_update (uint8_t crc, uint8_t data) { + uint8_t i; + crc = crc ^ data; + for (i = 0; i < 8; i++) { + if (crc & 0x01) + crc = (crc >> 1) ^ 0x8C; + else + crc >>= 1; + } + return crc; +} + +int main () +{ + uint8_t crc = 0x0D; + for (uint8_t i = 0; i < 255; i++) + { + uint8_t res1 = _crc_ibutton_update_O0 (i, crc); + uint8_t res2 = _crc_ibutton_update (i, crc); + if (res1 != res2) + abort (); + crc = res1; + } +} +/* { dg-final { scan-tree-dump "_crc_ibutton_update function maybe contains CRC calculation." "crc"} } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc"} } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc"} } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{\[0, \]*1, 0, 0, 0, 1, 1, 0, 0\\\}" "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-9.c b/gcc/testsuite/gcc.dg/torture/crc-9.c new file mode 100644 index 00000000000..af7386b1bd8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-9.c @@ -0,0 +1,56 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-O3" "-flto"} } */ + +#include + +typedef unsigned char uint8_t; + +__attribute__ ((noinline,optimize(0))) +uint8_t gencrc_O0 (uint8_t *data, size_t len) { + uint8_t crc = 0xff; + size_t i, j; + for (i = 0; i < len; i++) { + crc ^= data[i]; + for (j = 0; j < 8; j++) { + if ((crc & 0x80) != 0) + crc = (uint8_t) ((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + return crc; +} + +uint8_t gencrc (uint8_t *data, size_t len) { + uint8_t crc = 0xff; + size_t i, j; + for (i = 0; i < len; i++) { + crc ^= data[i]; + for (j = 0; j < 8; j++) { + if ((crc & 0x80) != 0) + crc = (uint8_t) ((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + return crc; +} + +int main () +{ + for (uint8_t i = 0; i < 255; i++) + { + uint8_t res1 = gencrc_O0 (&i, 1); + uint8_t res2 = gencrc (&i, 1); + if (res1 != res2) + abort (); + } +} + +/* { dg-final { scan-tree-dump "gencrc function maybe contains CRC calculation." "crc"} } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc"} } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc"} } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{\[0, \]*0, 0, 1, 1, 0, 0, 0, 1\\\}" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc"} } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc"} } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-CCIT-data16-xorOutside_InsideFor.c b/gcc/testsuite/gcc.dg/torture/crc-CCIT-data16-xorOutside_InsideFor.c new file mode 100644 index 00000000000..edce64a55d1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-CCIT-data16-xorOutside_InsideFor.c @@ -0,0 +1,58 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include +#include + +__attribute__ ((noinline)) +uint16_t crc16_xor_outside (uint16_t data, uint16_t crc) +{ + int carry; + crc ^= data; + for (int i = 0; i < 16; ++i) { + carry = ((crc & 0x8000)); + crc <<= 1; + if (carry) crc ^= 0x1021; + } + return crc; +} + +__attribute__ ((noinline)) +uint16_t crc16_xor_inside (uint16_t data, uint16_t crc) +{ + int carry; + for (int i = 0; i < 16; ++i) { + carry = ((crc & 0x8000) ^ (data & 0x8000)); + crc <<= 1; + data <<= 1; + if (carry) crc ^= 0x1021; + } + return crc; +} + +int main () +{ + uint16_t crc = 0; + + for (uint16_t i = 0; i < 655; i++) + { + uint16_t res1 = crc16_xor_outside (i, crc); + uint16_t res2 = crc16_xor_inside (i, crc); + crc = res1; + if (res1 != res2) + abort (); + } + + if (crc16_xor_outside (0x1111, 0xFFFF) != 0x2f5d) + abort (); + + if (crc16_xor_inside (0x1111, 0xFFFF) != 0x2f5d) + abort (); +} + +/* { dg-final { scan-tree-dump "crc16_xor_outside function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "crc16_xor_inside function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 15" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ +/* { dg-final { scan-tree-dump-times "calculates CRC!" 2 "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/crc-CCIT-data16.c b/gcc/testsuite/gcc.dg/torture/crc-CCIT-data16.c new file mode 100644 index 00000000000..ff9b43eb7b9 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-CCIT-data16.c @@ -0,0 +1,51 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint16_t crc16_O0 (uint16_t data, uint16_t crc) +{ + int carry; + for (int i = 0; i < 16; ++i) { + carry = ((crc & 0x8000) ^ (data & 0x8000)); + crc <<= 1; + data <<= 1; + if (carry) crc ^= 0x1021; + } + return crc; +} + +uint16_t crc16 (uint16_t data, uint16_t crc) +{ + int carry; + for (int i = 0; i < 16; ++i) { + carry = ((crc & 0x8000) ^ (data & 0x8000)); + crc <<= 1; + data <<= 1; + if (carry) crc ^= 0x1021; + } + return crc; +} + +int main () +{ + uint16_t crc = 0; + + for (uint16_t i = 0; i < 655; i++) + { + uint16_t res1 = crc16_O0 (i, crc); + uint16_t res2 = crc16 (i, crc); + crc = res1; + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "crc16 function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 15" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/crc-CCIT-data8.c b/gcc/testsuite/gcc.dg/torture/crc-CCIT-data8.c new file mode 100644 index 00000000000..ebdd70fa745 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-CCIT-data8.c @@ -0,0 +1,47 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint16_t crc16_O0 (uint8_t data, uint16_t crc) { + int carry; + for (int i = 0; i < 8; ++i) { + carry = ((crc & 0x8000) >> 8 ^ (data & 0x80)); + crc <<= 1; + data <<= 1; + if (carry) crc ^= 0x1021; + } + return crc; +} + +uint16_t crc16 (uint8_t data, uint16_t crc) { + int carry; + for (int i = 0; i < 8; ++i) { + carry = ((crc & 0x8000) >> 8 ^ (data & 0x80)); + crc <<= 1; + data <<= 1; + if (carry) crc ^= 0x1021; + } + return crc; +} + +int main () +{ + uint16_t crc = 0x0D80; + for (uint8_t i = 0; i < 255; i++) + { + uint16_t res1 = crc16_O0 (i, crc); + uint16_t res2 = crc16 (i, crc); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "crc16 function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/crc-coremark16-data16.c b/gcc/testsuite/gcc.dg/torture/crc-coremark16-data16.c new file mode 100644 index 00000000000..6b4de1a9600 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-coremark16-data16.c @@ -0,0 +1,65 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include +typedef unsigned short ee_u16; +typedef unsigned char ee_u8; + +__attribute__ ((noinline,optimize(0))) +ee_u16 crcu16_O0 (ee_u16 data, ee_u16 crc) { + ee_u8 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 16; i++) { + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +ee_u16 crcu16 (ee_u16 data, ee_u16 crc) { + ee_u8 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 16; i++) { + x16 = (ee_u8) ((data & 1) ^ ((ee_u8) crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000; + else + crc &= 0x7fff; + } + return crc; +} + +int main () +{ + ee_u16 crc = 0x0D80; + for (ee_u16 i = 0; i < 65535; i++) + { + ee_u16 res1 = crcu16_O0 (i, crc); + ee_u16 res2 = crcu16 (i, crc); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 15" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump "Polynomial's value is \\\{\[0, \]*1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1\\\}" "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/crc-coremark32-data16.c b/gcc/testsuite/gcc.dg/torture/crc-coremark32-data16.c new file mode 100644 index 00000000000..c53edfbda9e --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-coremark32-data16.c @@ -0,0 +1,64 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include + +typedef unsigned int ee_u32; +typedef unsigned short ee_u16; + +__attribute__ ((noinline,optimize(0))) +ee_u32 crcu32_O0 (ee_u16 data, ee_u32 crc) { + ee_u32 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 16; i++) { + x16 = ((data & 1) ^ (crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002123; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x80000000; + else + crc &= 0x7fffffff; + } + return crc; +} + +ee_u32 crcu32 (ee_u16 data, ee_u32 crc) { + ee_u32 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 16; i++) { + x16 = ((data & 1) ^ (crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002123; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x80000000; + else + crc &= 0x7fffffff; + } + return crc; +} + +int main () +{ + ee_u32 crc = 0; + for (ee_u16 i = 0; i < 0xff; i++) + { + ee_u32 res1 = crcu32_O0 (i, crc); + ee_u32 res2 = crcu32 (i, crc); + if (res1 != res2) + abort (); + crc = res1; + } +} +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 15" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/crc-coremark32-data32.c b/gcc/testsuite/gcc.dg/torture/crc-coremark32-data32.c new file mode 100644 index 00000000000..a4486413d35 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-coremark32-data32.c @@ -0,0 +1,63 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include + +typedef unsigned int ee_u32; + +__attribute__ ((noinline,optimize(0))) +ee_u32 crcu32_O0 (ee_u32 data, ee_u32 crc) { + ee_u32 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 32; i++) { + x16 = ((data & 1) ^ (crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002123; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x80000000; + else + crc &= 0x7fffffff; + } + return crc; +} + +ee_u32 crcu32 (ee_u32 data, ee_u32 crc) { + ee_u32 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 32; i++) { + x16 = ((data & 1) ^ (crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002123; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x80000000; + else + crc &= 0x7fffffff; + } + return crc; +} + +int main () +{ + ee_u32 crc = 0; + for (ee_u32 i = 0; i < 0xff; i++) + { + ee_u32 res1 = crcu32_O0 (i, crc); + ee_u32 res2 = crcu32 (i, crc); + if (res1 != res2) + abort (); + crc = res1; + } +} +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 31" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/crc-coremark32-data8.c b/gcc/testsuite/gcc.dg/torture/crc-coremark32-data8.c new file mode 100644 index 00000000000..783b0c7daa4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-coremark32-data8.c @@ -0,0 +1,64 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include + +typedef unsigned int ee_u32; +typedef unsigned char ee_u8; + +__attribute__ ((noinline,optimize(0))) +ee_u32 crcu32_O0 (ee_u8 data, ee_u32 crc) { + ee_u32 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 8; i++) { + x16 = ((data & 1) ^ (crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002123; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x80000000; + else + crc &= 0x7fffffff; + } + return crc; +} + +ee_u32 crcu32 (ee_u8 data, ee_u32 crc) { + ee_u32 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 8; i++) { + x16 = ((data & 1) ^ (crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002123; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x80000000; + else + crc &= 0x7fffffff; + } + return crc; +} + +int main () +{ + ee_u32 crc = 0; + for (ee_u8 i = 0; i < 0xff; i++) + { + ee_u32 res1 = crcu32_O0 (i, crc); + ee_u32 res2 = crcu32 (i, crc); + if (res1 != res2) + abort (); + crc = res1; + } +} +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/crc-coremark64-data32.c b/gcc/testsuite/gcc.dg/torture/crc-coremark64-data32.c new file mode 100644 index 00000000000..db3f69ce0ec --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-coremark64-data32.c @@ -0,0 +1,64 @@ +/* { dg-do run { target lp64 } } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include + +typedef unsigned int ee_u32; +typedef unsigned long long int ee_u64; + +__attribute__ ((noinline,optimize(0))) +ee_u64 crcu64_O0 (ee_u32 data, ee_u64 crc) { + ee_u64 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 32; i++) { + x16 = ((data & 1) ^ (crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002123f4002123f; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000000000000000; + else + crc &= 0x7fffffffffffffff; + } + return crc; +} + +ee_u64 crcu64 (ee_u32 data, ee_u64 crc) { + ee_u64 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 32; i++) { + x16 = ((data & 1) ^ (crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002123f4002123f; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000000000000000; + else + crc &= 0x7fffffffffffffff; + } + return crc; +} + +int main () +{ + ee_u64 crc = 0; + for (ee_u32 i = 0; i < 0xff; i++) + { + ee_u64 res1 = crcu64_O0 (i, crc); + ee_u64 res2 = crcu64 (i, crc); + if (res1 != res2) + abort (); + crc = res1; + } +} +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 31" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/crc-coremark64-data64.c b/gcc/testsuite/gcc.dg/torture/crc-coremark64-data64.c new file mode 100644 index 00000000000..41d06aae6db --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-coremark64-data64.c @@ -0,0 +1,63 @@ +/* { dg-do run { target lp64 } } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include + +typedef unsigned long long int ee_u64; + +__attribute__ ((noinline,optimize(0))) +ee_u64 crcu64_O0 (ee_u64 data, ee_u64 crc) { + ee_u64 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 64; i++) { + x16 = ((data & 1) ^ (crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002123f4002123f; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000000000000000; + else + crc &= 0x7fffffffffffffff; + } + return crc; +} + +ee_u64 crcu64 (ee_u64 data, ee_u64 crc) { + ee_u64 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 64; i++) { + x16 = ((data & 1) ^ (crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x4002123f4002123f; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x8000000000000000; + else + crc &= 0x7fffffffffffffff; + } + return crc; +} + +int main () +{ + ee_u64 crc = 0; + for (ee_u64 i = 0; i < 0xff; i++) + { + ee_u64 res1 = crcu64_O0 (i, crc); + ee_u64 res2 = crcu64 (i, crc); + if (res1 != res2) + abort (); + crc = res1; + } +} +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 63" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/crc-coremark8-data8.c b/gcc/testsuite/gcc.dg/torture/crc-coremark8-data8.c new file mode 100644 index 00000000000..10e5408496c --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-coremark8-data8.c @@ -0,0 +1,63 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include + +typedef unsigned char ee_u8; + +__attribute__ ((noinline,optimize(0))) +ee_u8 crcu8_O0(ee_u8 data, ee_u8 crc) { + ee_u8 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 8; i++) { + x16 = ((data & 1) ^ (crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x40; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x80; + else + crc &= 0x7f; + } + return crc; +} + +ee_u8 crcu8(ee_u8 data, ee_u8 crc) { + ee_u8 i = 0, x16 = 0, carry = 0; + for (i = 0; i < 8; i++) { + x16 = ((data & 1) ^ (crc & 1)); + data >>= 1; + if (x16 == 1) { + crc ^= 0x40; + carry = 1; + } else + carry = 0; + crc >>= 1; + if (carry) + crc |= 0x80; + else + crc &= 0x7f; + } + return crc; +} + +int main () +{ + ee_u8 crc = 0; + for (ee_u8 i = 0; i < 0xff; i++) + { + ee_u8 res1 = crcu8_O0 (i, crc); + ee_u8 res2 = crcu8 (i, crc); + if (res1 != res2) + abort (); + crc = res1; + } +} +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit reversed" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/crc-crc32-data16.c b/gcc/testsuite/gcc.dg/torture/crc-crc32-data16.c new file mode 100644 index 00000000000..7f3692c4b4a --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-crc32-data16.c @@ -0,0 +1,51 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint32_t _crc32_O0 (uint16_t data, uint32_t crc) { + int i; + for (i = 0; i < 16; i++) { + if ((crc & 0x80000000) >> 16 ^ (data & 0x8000)) + crc = (crc << 1) ^ 0x04C11DB7; + else + crc = (crc << 1); + data <<= 1; + } + return crc; +} + +uint32_t _crc32 (uint16_t data, uint32_t crc) { + int i; + for (i = 0; i < 16; i++) { + if ((crc & 0x80000000) >> 16 ^ (data & 0x8000)) + crc = (crc << 1) ^ 0x04C11DB7; + else + crc = (crc << 1); + data <<= 1; + } + return crc; +} + +int main () +{ + uint32_t crc = 0; + + for (uint16_t i = 0; i < 0xff; i++) + { + uint32_t res1 = _crc32_O0 (i, crc); + uint32_t res2 = _crc32 (i, crc); + crc = res2; + if (res1 != res2) + abort (); + } +} + +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 15" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC." "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-crc32-data24.c b/gcc/testsuite/gcc.dg/torture/crc-crc32-data24.c new file mode 100644 index 00000000000..86ecde2461d --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-crc32-data24.c @@ -0,0 +1,20 @@ +/* { dg-do compile } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-Os" "-flto" } } */ + +#include +uint32_t _crc32 (uint32_t data, uint32_t crc) { + int i; + for (i = 0; i < 24; i++) { + if ((crc & 0x80000000) >> 8 ^ (data & 0x800000)) + crc = (crc << 1) ^ 0x04C11DB7; + else + crc = (crc << 1); + data <<= 1; + } + return crc; +} + +/* { dg-final { scan-tree-dump "_crc32 function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 23" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/crc-crc32-data8.c b/gcc/testsuite/gcc.dg/torture/crc-crc32-data8.c new file mode 100644 index 00000000000..8b040f17c00 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-crc32-data8.c @@ -0,0 +1,51 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint32_t _crc32_O0 (uint8_t data, uint32_t crc) { + int i; + for (i = 0; i < 8; i++) { + if ((crc & 0x80000000) >> 24 ^ (data & 0x80)) + crc = (crc << 1) ^ 0x04C11DB7; + else + crc = (crc << 1); + data <<= 1; + } + return crc; +} + +uint32_t _crc32 (uint8_t data, uint32_t crc) { + int i; + for (i = 0; i < 8; i++) { + if ((crc & 0x80000000) >> 24 ^ (data & 0x80)) + crc = (crc << 1) ^ 0x04C11DB7; + else + crc = (crc << 1); + data <<= 1; + } + return crc; +} + +int main () +{ + uint32_t crc = 0; + + for (uint8_t i = 0; i < 0xff; i++) + { + uint32_t res1 = _crc32_O0 (i, crc); + uint32_t res2 = _crc32 (i, crc); + crc = res2; + if (res1 != res2) + abort (); + } +} + +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC." "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-crc32.c b/gcc/testsuite/gcc.dg/torture/crc-crc32.c new file mode 100644 index 00000000000..e1e85a73923 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-crc32.c @@ -0,0 +1,51 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint32_t _crc32_O0 (uint32_t data, uint32_t crc) { + int i; + for (i = 0; i < 32; i++) { + if ((crc & 0x80000000) ^ (data & 0x80000000)) + crc = (crc << 1) ^ 0x04C11DB7; + else + crc = (crc << 1); + data <<= 1; + } + return crc; +} + +uint32_t _crc32 (uint32_t data, uint32_t crc) { + int i; + for (i = 0; i < 32; i++) { + if ((crc & 0x80000000) ^ (data & 0x80000000)) + crc = (crc << 1) ^ 0x04C11DB7; + else + crc = (crc << 1); + data <<= 1; + } + return crc; +} + +int main () +{ + uint32_t crc = 0; + + for (uint32_t i = 0; i < 0xffff; i++) + { + uint32_t res1 = _crc32_O0 (i, crc); + uint32_t res2 = _crc32 (i, crc); + if (res1 != res2) + abort (); + crc = res2; + } +} + +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 31" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC." "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-crc64-data32.c b/gcc/testsuite/gcc.dg/torture/crc-crc64-data32.c new file mode 100644 index 00000000000..4a870c1cc7c --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-crc64-data32.c @@ -0,0 +1,52 @@ +/* { dg-do run { target lp64 } } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include +#include + +// CRC64_ECMA_182 +__attribute__ ((noinline,optimize(0))) +uint64_t _crc64_O0 (uint32_t data, uint64_t crc) { + int i; + for (i = 0; i < 32; i++) { + if (((crc & 0x8000000000000000) >> 32 ^ (data & 0x80000000))) + crc = (crc << 1) ^ 0x42F0E1EBA9EA3693; + else + crc = (crc << 1); + data <<= 1; + } + return crc; +} + +uint64_t _crc64 (uint32_t data, uint64_t crc) { + int i; + for (i = 0; i < 32; i++) { + if (((crc & 0x8000000000000000) >> 32 ^ (data & 0x80000000))) + crc = (crc << 1) ^ 0x42F0E1EBA9EA3693; + else + crc = (crc << 1); + data <<= 1; + } + return crc; +} + +int main () +{ + uint64_t crc = 0; + + for (uint32_t i = 0; i < 0xff; i++) + { + uint64_t res1 = _crc64_O0 (i, crc); + uint64_t res2 = _crc64 (i, crc); + crc = res2; + if (res1 != res2) + abort (); + } +} + +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 31" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/crc-crc64-data64.c b/gcc/testsuite/gcc.dg/torture/crc-crc64-data64.c new file mode 100644 index 00000000000..0c0df0f8f10 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-crc64-data64.c @@ -0,0 +1,52 @@ +/* { dg-do run { target lp64 } } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include +#include + +// CRC64_ECMA_182 +__attribute__ ((noinline,optimize(0))) +uint64_t _crc64_O0 (uint64_t crc, uint64_t data) { + int i; + for (i = 0; i < 64; i++) { + if (((crc & 0x8000000000000000) ^ (data & 0x8000000000000000))) + crc = (crc << 1) ^ 0x42F0E1EBA9EA3693; + else + crc = (crc << 1); + data <<= 1; + } + return crc; +} + +uint64_t _crc64 (uint64_t crc, uint64_t data) { + int i; + for (i = 0; i < 64; i++) { + if (((crc & 0x8000000000000000) ^ (data & 0x8000000000000000))) + crc = (crc << 1) ^ 0x42F0E1EBA9EA3693; + else + crc = (crc << 1); + data <<= 1; + } + return crc; +} + +int main () +{ + uint64_t crc = 0; + + for (uint64_t i = 0; i < 0xff; i++) + { + uint64_t res1 = _crc64_O0 (i, crc); + uint64_t res2 = _crc64 (i, crc); + crc = res2; + if (res1 != res2) + abort (); + } +} + +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 63" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-crc8-data8-loop-xorInFor.c b/gcc/testsuite/gcc.dg/torture/crc-crc8-data8-loop-xorInFor.c new file mode 100644 index 00000000000..79e46d6057d --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-crc8-data8-loop-xorInFor.c @@ -0,0 +1,33 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-funroll-loops" "-fpeel-loops" "-flto" } } */ + +#include +#include + +typedef unsigned char uint8_t; + +uint8_t gencrc (uint8_t *message, size_t len) { + uint8_t crc = 0; + size_t i, j; + for (i = 0; i < len; i++) { + uint8_t data = message[i]; + for (j = 0; j < 8; j++) { + if (((crc & 0x80) ^ (data & 0x80)) != 0) + crc = (uint8_t) ((crc << 1) ^ 0x31); + else + crc <<= 1; + data <<=1; + } + } + return crc; +} + +int main() +{ + uint8_t message[] = "Hello world!"; + assert (gencrc(message, 12) == 0x24); +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc" } } */ diff --git a/gcc/testsuite/gcc.dg/torture/crc-crc8-data8-loop-xorOutsideFor.c b/gcc/testsuite/gcc.dg/torture/crc-crc8-data8-loop-xorOutsideFor.c new file mode 100644 index 00000000000..e1749fef607 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-crc8-data8-loop-xorOutsideFor.c @@ -0,0 +1,33 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-funroll-loops" "-fpeel-loops" "-flto" } } */ + +#include +#include + +typedef unsigned char uint8_t; + +uint8_t gencrc (uint8_t *message, size_t len) { + uint8_t crc = 0; + size_t i, j; + for (i = 0; i < len; i++) { + uint8_t data = message[i]; + crc ^= data; + for (j = 0; j < 8; j++) { + if ((crc & 0x80)!= 0) + crc = (uint8_t) ((crc << 1) ^ 0x31); + else + crc <<= 1; + } + } + return crc; +} + +int main() +{ + uint8_t message[] = "Hello world!"; + assert (gencrc(message, 12) == 0x24); +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-crc8-data8-xorOustideFor.c b/gcc/testsuite/gcc.dg/torture/crc-crc8-data8-xorOustideFor.c new file mode 100644 index 00000000000..56a1c3da366 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-crc8-data8-xorOustideFor.c @@ -0,0 +1,51 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint8_t gencrc_O0 (uint8_t crc, uint8_t data) +{ + size_t j; + crc ^= data; + for (j = 0; j < 8; j++) + { + if ((crc & 0x80) != 0) + crc = (uint8_t) ((crc << 1) ^ 0x31); + else + crc <<= 1; + } + return crc; +} + +uint8_t gencrc (uint8_t crc, uint8_t data) +{ + size_t j; + crc ^= data; + for (j = 0; j < 8; j++) + { + if ((crc & 0x80) != 0) + crc = (uint8_t) ((crc << 1) ^ 0x31); + else + crc <<= 1; + } + return crc; +} + +int main () +{ + uint8_t crc = 0x0D; + for (uint8_t i = 0; i < 255; i++) + { + uint8_t res1 = gencrc_O0 (crc, i); + uint8_t res2 = gencrc (crc, i); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ +/* { dg-final { scan-tree-dump-times "Couldn't generate faster CRC code." 0 "crc" } } */ \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/torture/crc-crc8.c b/gcc/testsuite/gcc.dg/torture/crc-crc8.c new file mode 100644 index 00000000000..f1a8dd08362 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/crc-crc8.c @@ -0,0 +1,49 @@ +/* { dg-do run } */ +/* { dg-options "-fdump-tree-crc-details -w" } */ +/* { dg-skip-if "" { *-*-* } { "-O0" "-O1" "-g" "-Os" "-flto" } } */ + +#include +#include + +__attribute__ ((noinline,optimize(0))) +uint8_t crc8_O0 (uint8_t data, uint8_t crc) +{ + int carry; + for (int i = 0; i < 8; ++i) { + carry = ((crc & 0x80) ^ (data & 0x80)); + crc <<= 1; + data <<= 1; + if (carry) crc ^= 0x21; + } + return crc; +} + +uint8_t crc8 (uint8_t data, uint8_t crc) +{ + int carry; + for (int i = 0; i < 8; ++i) { + carry = ((crc & 0x80) ^ (data & 0x80)); + crc <<= 1; + data <<= 1; + if (carry) crc ^= 0x21; + } + return crc; +} + +int main () +{ + uint8_t crc = 0x0D; + for (uint8_t i = 0; i < 255; i++) + { + uint8_t res1 = crc8_O0 (i, crc); + uint8_t res2 = crc8 (i, crc); + if (res1 != res2) + abort (); + crc = res1; + } +} + +/* { dg-final { scan-tree-dump "function maybe contains CRC calculation." "crc" } } */ +/* { dg-final { scan-tree-dump "Loop iteration number is 7" "crc" } } */ +/* { dg-final { scan-tree-dump "Bit forward" "crc" } } */ +/* { dg-final { scan-tree-dump "calculates CRC!" "crc" } } */ \ No newline at end of file -- 2.25.1