From eb966d393dfdfd2c80994e4bfcc0dddf85828a73 Mon Sep 17 00:00:00 2001 From: Andre Simoes Dias Vieira Date: Mon, 25 Jul 2022 10:27:13 +0100 Subject: aarch64: Implement ACLE Data Intrinsics This patch adds support for the ACLE Data Intrinsics to the AArch64 port. gcc/ChangeLog: 2022-07-25 Andre Vieira * config/aarch64/aarch64.md (rbit2): Rename this ... (@aarch64_rbit): ... to this and change it in... (ffs2,ctz2): ... here. (@aarch64_rev16): New. * config/aarch64/aarch64-builtins.cc: (aarch64_builtins): Define the following enum AARCH64_REV16, AARCH64_REV16L, AARCH64_REV16LL, AARCH64_RBIT, AARCH64_RBITL, AARCH64_RBITLL. (aarch64_init_data_intrinsics): New. (aarch64_general_init_builtins): Add call to aarch64_init_data_intrinsics. (aarch64_expand_builtin_data_intrinsic): New. (aarch64_general_expand_builtin): Add call to aarch64_expand_builtin_data_intrinsic. * config/aarch64/arm_acle.h (__clz, __clzl, __clzll, __cls, __clsl, __clsll, __rbit, __rbitl, __rbitll, __rev, __revl, __revll, __rev16, __rev16l, __rev16ll, __ror, __rorl, __rorll, __revsh): New. gcc/testsuite/ChangeLog: 2022-07-25 Andre Vieira * gcc.target/aarch64/acle/data-intrinsics.c: New test. --- gcc/config/aarch64/aarch64-builtins.cc | 71 ++++ gcc/config/aarch64/aarch64.md | 13 +- gcc/config/aarch64/arm_acle.h | 53 +++ .../gcc.target/aarch64/acle/data-intrinsics.c | 468 +++++++++++++++++++++ 4 files changed, 602 insertions(+), 3 deletions(-) create mode 100644 gcc/testsuite/gcc.target/aarch64/acle/data-intrinsics.c (limited to 'gcc') diff --git a/gcc/config/aarch64/aarch64-builtins.cc b/gcc/config/aarch64/aarch64-builtins.cc index 69f1e4e..2cacb4d 100644 --- a/gcc/config/aarch64/aarch64-builtins.cc +++ b/gcc/config/aarch64/aarch64-builtins.cc @@ -612,6 +612,12 @@ enum aarch64_builtins AARCH64_LS64_BUILTIN_ST64B, AARCH64_LS64_BUILTIN_ST64BV, AARCH64_LS64_BUILTIN_ST64BV0, + AARCH64_REV16, + AARCH64_REV16L, + AARCH64_REV16LL, + AARCH64_RBIT, + AARCH64_RBITL, + AARCH64_RBITLL, AARCH64_BUILTIN_MAX }; @@ -1659,6 +1665,36 @@ aarch64_init_ls64_builtins (void) = aarch64_general_add_builtin (data[i].name, data[i].type, data[i].code); } +static void +aarch64_init_data_intrinsics (void) +{ + tree uint32_fntype = build_function_type_list (uint32_type_node, + uint32_type_node, NULL_TREE); + tree ulong_fntype = build_function_type_list (long_unsigned_type_node, + long_unsigned_type_node, + NULL_TREE); + tree uint64_fntype = build_function_type_list (uint64_type_node, + uint64_type_node, NULL_TREE); + aarch64_builtin_decls[AARCH64_REV16] + = aarch64_general_add_builtin ("__builtin_aarch64_rev16", uint32_fntype, + AARCH64_REV16); + aarch64_builtin_decls[AARCH64_REV16L] + = aarch64_general_add_builtin ("__builtin_aarch64_rev16l", ulong_fntype, + AARCH64_REV16L); + aarch64_builtin_decls[AARCH64_REV16LL] + = aarch64_general_add_builtin ("__builtin_aarch64_rev16ll", uint64_fntype, + AARCH64_REV16LL); + aarch64_builtin_decls[AARCH64_RBIT] + = aarch64_general_add_builtin ("__builtin_aarch64_rbit", uint32_fntype, + AARCH64_RBIT); + aarch64_builtin_decls[AARCH64_RBITL] + = aarch64_general_add_builtin ("__builtin_aarch64_rbitl", ulong_fntype, + AARCH64_RBITL); + aarch64_builtin_decls[AARCH64_RBITLL] + = aarch64_general_add_builtin ("__builtin_aarch64_rbitll", uint64_fntype, + AARCH64_RBITLL); +} + /* Implement #pragma GCC aarch64 "arm_acle.h". */ void handle_arm_acle_h (void) @@ -1737,6 +1773,7 @@ aarch64_general_init_builtins (void) aarch64_init_crc32_builtins (); aarch64_init_builtin_rsqrt (); aarch64_init_rng_builtins (); + aarch64_init_data_intrinsics (); tree ftype_jcvt = build_function_type_list (intSI_type_node, double_type_node, NULL); @@ -2389,6 +2426,37 @@ aarch64_expand_builtin_memtag (int fcode, tree exp, rtx target) return target; } +/* Function to expand an expression EXP which calls one of the ACLE Data + Intrinsic builtins FCODE with the result going to TARGET. */ +static rtx +aarch64_expand_builtin_data_intrinsic (unsigned int fcode, tree exp, rtx target) +{ + expand_operand ops[2]; + machine_mode mode = GET_MODE (target); + create_output_operand (&ops[0], target, mode); + create_input_operand (&ops[1], expand_normal (CALL_EXPR_ARG (exp, 0)), mode); + enum insn_code icode; + + switch (fcode) + { + case AARCH64_REV16: + case AARCH64_REV16L: + case AARCH64_REV16LL: + icode = code_for_aarch64_rev16 (mode); + break; + case AARCH64_RBIT: + case AARCH64_RBITL: + case AARCH64_RBITLL: + icode = code_for_aarch64_rbit (mode); + break; + default: + gcc_unreachable (); + } + + expand_insn (icode, 2, ops); + return ops[0].value; +} + /* Expand an expression EXP as fpsr or fpcr setter (depending on UNSPEC) using MODE. */ static void @@ -2546,6 +2614,9 @@ aarch64_general_expand_builtin (unsigned int fcode, tree exp, rtx target, if (fcode >= AARCH64_MEMTAG_BUILTIN_START && fcode <= AARCH64_MEMTAG_BUILTIN_END) return aarch64_expand_builtin_memtag (fcode, exp, target); + if (fcode >= AARCH64_REV16 + && fcode <= AARCH64_RBITLL) + return aarch64_expand_builtin_data_intrinsic (fcode, exp, target); gcc_unreachable (); } diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md index acec8c1..ef0aed2 100644 --- a/gcc/config/aarch64/aarch64.md +++ b/gcc/config/aarch64/aarch64.md @@ -4950,7 +4950,7 @@ rtx ccreg = aarch64_gen_compare_reg (EQ, operands[1], const0_rtx); rtx x = gen_rtx_NE (VOIDmode, ccreg, const0_rtx); - emit_insn (gen_rbit2 (operands[0], operands[1])); + emit_insn (gen_aarch64_rbit (mode, operands[0], operands[1])); emit_insn (gen_clz2 (operands[0], operands[0])); emit_insn (gen_csinc3_insn (operands[0], x, operands[0], const0_rtx)); DONE; @@ -4996,7 +4996,7 @@ [(set_attr "type" "clz")] ) -(define_insn "rbit2" +(define_insn "@aarch64_rbit" [(set (match_operand:GPI 0 "register_operand" "=r") (unspec:GPI [(match_operand:GPI 1 "register_operand" "r")] UNSPEC_RBIT))] "" @@ -5017,7 +5017,7 @@ "reload_completed" [(const_int 0)] " - emit_insn (gen_rbit2 (operands[0], operands[1])); + emit_insn (gen_aarch64_rbit (mode, operands[0], operands[1])); emit_insn (gen_clz2 (operands[0], operands[0])); DONE; ") @@ -6022,6 +6022,13 @@ [(set_attr "type" "rev")] ) +(define_insn "@aarch64_rev16" + [(set (match_operand:GPI 0 "register_operand" "=r") + (unspec:GPI [(match_operand:GPI 1 "register_operand" "r")] UNSPEC_REV))] + "" + "rev16\\t%0, %1" + [(set_attr "type" "rev")]) + (define_insn "*aarch64_bfxil" [(set (match_operand:GPI 0 "register_operand" "=r,r") (ior:GPI (and:GPI (match_operand:GPI 1 "register_operand" "r,0") diff --git a/gcc/config/aarch64/arm_acle.h b/gcc/config/aarch64/arm_acle.h index 9775a48..d26e269 100644 --- a/gcc/config/aarch64/arm_acle.h +++ b/gcc/config/aarch64/arm_acle.h @@ -28,6 +28,7 @@ #define _GCC_ARM_ACLE_H #include +#include #pragma GCC aarch64 "arm_acle.h" @@ -35,6 +36,58 @@ extern "C" { #endif +#define _GCC_ARM_ACLE_ROR_FN(NAME, TYPE) \ +__extension__ extern __inline TYPE \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +NAME (TYPE __value, uint32_t __rotate) \ +{ \ + size_t __size = sizeof (TYPE) * __CHAR_BIT__; \ + __rotate = __rotate % __size; \ + return __value >> __rotate | __value << ((__size - __rotate) % __size); \ +} + +_GCC_ARM_ACLE_ROR_FN (__ror, uint32_t) +_GCC_ARM_ACLE_ROR_FN (__rorl, unsigned long) +_GCC_ARM_ACLE_ROR_FN (__rorll, uint64_t) + +#undef _GCC_ARM_ACLE_ROR_FN + +#define _GCC_ARM_ACLE_DATA_FN(NAME, BUILTIN, ITYPE, RTYPE) \ +__extension__ extern __inline RTYPE \ +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) \ +__##NAME (ITYPE __value) \ +{ \ + return __builtin_##BUILTIN (__value); \ +} + +_GCC_ARM_ACLE_DATA_FN (clz, clz, uint32_t, unsigned int) +_GCC_ARM_ACLE_DATA_FN (clzl, clzl, unsigned long, unsigned int) +_GCC_ARM_ACLE_DATA_FN (clzll, clzll, uint64_t, unsigned int) +_GCC_ARM_ACLE_DATA_FN (cls, clrsb, uint32_t, unsigned int) +_GCC_ARM_ACLE_DATA_FN (clsl, clrsbl, unsigned long, unsigned int) +_GCC_ARM_ACLE_DATA_FN (clsll, clrsbll, uint64_t, unsigned int) +_GCC_ARM_ACLE_DATA_FN (rev16, aarch64_rev16, uint32_t, uint32_t) +_GCC_ARM_ACLE_DATA_FN (rev16l, aarch64_rev16l, unsigned long, unsigned long) +_GCC_ARM_ACLE_DATA_FN (rev16ll, aarch64_rev16ll, uint64_t, uint64_t) +_GCC_ARM_ACLE_DATA_FN (rbit, aarch64_rbit, uint32_t, uint32_t) +_GCC_ARM_ACLE_DATA_FN (rbitl, aarch64_rbitl, unsigned long, unsigned long) +_GCC_ARM_ACLE_DATA_FN (rbitll, aarch64_rbitll, uint64_t, uint64_t) +_GCC_ARM_ACLE_DATA_FN (revsh, bswap16, int16_t, int16_t) +_GCC_ARM_ACLE_DATA_FN (rev, bswap32, uint32_t, uint32_t) +_GCC_ARM_ACLE_DATA_FN (revll, bswap64, uint64_t, uint64_t) + +#undef _GCC_ARM_ACLE_DATA_FN + +__extension__ extern __inline unsigned long +__attribute__ ((__always_inline__, __gnu_inline__, __artificial__)) +__revl (unsigned long __value) +{ + if (sizeof (unsigned long) == 8) + return __revll (__value); + else + return __rev (__value); +} + #pragma GCC push_options #pragma GCC target ("arch=armv8.3-a") __extension__ extern __inline int32_t diff --git a/gcc/testsuite/gcc.target/aarch64/acle/data-intrinsics.c b/gcc/testsuite/gcc.target/aarch64/acle/data-intrinsics.c new file mode 100644 index 0000000..e067ef2 --- /dev/null +++ b/gcc/testsuite/gcc.target/aarch64/acle/data-intrinsics.c @@ -0,0 +1,468 @@ +/* Test the ACLE data intrinsics. */ +/* { dg-do assemble } */ +/* { dg-additional-options "--save-temps -O1" } */ +/* { dg-final { check-function-bodies "**" "" "" } } */ + +#include "arm_acle.h" + +/* +** test_clz: +** clz w0, w0 +** ret +*/ + +unsigned int test_clz (uint32_t a) +{ + return __clz (a); +} + +/* +** test_clzl: +** clz [wx]0, [wx]0 +** ret +*/ + +unsigned int test_clzl (unsigned long a) +{ + return __clzl (a); +} + +/* +** test_clzll: +** clz x0, x0 +** ret +*/ + +unsigned int test_clzll (uint64_t a) +{ + return __clzll (a); +} + +/* +** test_cls: +** cls w0, w0 +** ret +*/ + +unsigned int test_cls (uint32_t a) +{ + return __cls (a); +} + +/* +** test_clsl: +** cls [wx]0, [wx]0 +** ret +*/ + +unsigned int test_clsl (unsigned long a) +{ + return __clsl (a); +} + +/* +** test_clsll: +** cls x0, x0 +** ret +*/ + +unsigned int test_clsll (uint64_t a) +{ + return __clsll (a); +} + +/* +** test_rbit: +** rbit w0, w0 +** ret +*/ + +uint32_t test_rbit (uint32_t a) +{ + return __rbit (a); +} + +/* +** test_rbitl: +** rbit [wx]0, [wx]0 +** ret +*/ + +unsigned long test_rbitl (unsigned long a) +{ + return __rbitl (a); +} + +/* +** test_rbitll: +** rbit x0, x0 +** ret +*/ + +uint64_t test_rbitll (uint64_t a) +{ + return __rbitll (a); +} + +/* +** test_rev: +** rev w0, w0 +** ret +*/ + +uint32_t test_rev (uint32_t a) +{ + return __rev (a); +} + +/* +** test_revl: +** rev [wx]0, [wx]0 +** ret +*/ + +unsigned long test_revl (unsigned long a) +{ + return __revl (a); +} + +/* +** test_revll: +** rev x0, x0 +** ret +*/ + +uint64_t test_revll (uint64_t a) +{ + return __revll (a); +} + +/* +** test_rev16: +** rev16 w0, w0 +** ret +*/ + +uint32_t test_rev16 (uint32_t a) +{ + return __rev16 (a); +} + +/* +** test_rev16l: +** rev16 [wx]0, [wx]0 +** ret +*/ + +unsigned long test_rev16l (unsigned long a) +{ + return __rev16l (a); +} + +/* +** test_rev16ll: +** rev16 x0, x0 +** ret +*/ + +uint64_t test_rev16ll (uint64_t a) +{ + return __rev16ll (a); +} + +/* +** test_ror: +** ror w0, w0, w1 +** ret +*/ + +uint32_t test_ror (uint32_t a, uint32_t r) +{ + return __ror (a, r); +} + +/* +** test_rorl: +** ror [wx]0, [wx]0, [wx]1 +** ret +*/ + +unsigned long test_rorl (unsigned long a, uint32_t r) +{ + return __rorl (a, r); +} + +/* +** test_rorll: +** ror x0, x0, x1 +** ret +*/ + +uint64_t test_rorll (uint64_t a, uint32_t r) +{ + return __rorll (a, r); +} + +/* +** test_revsh: +** rev16 w0, w0 +** ret +*/ + +int16_t test_revsh (int16_t a) +{ + return __revsh (a); +} + +uint32_t *g32; +unsigned long *gul; +uint64_t *g64; +unsigned int *gui; +int16_t *g16; + +/* +** test_clz_mem: +** ... +** clz w[0-9]+, w[0-9]+ +** ... +** ret +*/ + +void test_clz_mem (uint32_t *a) +{ + *gui = __clz (*a); +} + +/* +** test_clzl_mem: +** ... +** clz [wx][0-9]+, [wx][0-9]+ +** ... +** ret +*/ + +void test_clzl_mem (unsigned long *a) +{ + *gui = __clzl (*a); +} + +/* +** test_clzll_mem: +** ... +** clz x[0-9]+, x[0-9]+ +** ... +** ret +*/ + +void test_clzll_mem (uint64_t *a) +{ + *gui = __clzll (*a); +} + +/* +** test_cls_mem: +** ... +** cls w[0-9]+, w[0-9]+ +** ... +** ret +*/ + +void test_cls_mem (uint32_t *a) +{ + *gui = __cls (*a); +} + +/* +** test_clsl_mem: +** ... +** cls [wx][0-9]+, [wx][0-9]+ +** ... +** ret +*/ + +void test_clsl_mem (unsigned long *a) +{ + *gui = __clsl (*a); +} + +/* +** test_clsll_mem: +** ... +** cls x[0-9]+, x[0-9]+ +** ... +** ret +*/ + +void test_clsll_mem (uint64_t *a) +{ + *gui = __clsll (*a); +} + +/* +** test_rbit_mem: +** ... +** rbit w[0-9]+, w[0-9]+ +** ... +** ret +*/ + +void test_rbit_mem (uint32_t *a) +{ + *g32 = __rbit (*a); +} + +/* +** test_rbitl_mem: +** ... +** rbit [wx][0-9]+, [wx][0-9]+ +** ... +** ret +*/ + +void test_rbitl_mem (unsigned long *a) +{ + *gul = __rbitl (*a); +} + +/* +** test_rbitll_mem: +** ... +** rbit x[0-9]+, x[0-9]+ +** ... +** ret +*/ + +void test_rbitll_mem (uint64_t *a) +{ + *g64 = __rbitll (*a); +} + +/* +** test_rev_mem: +** ... +** rev w[0-9]+, w[0-9]+ +** ... +** ret +*/ + +void test_rev_mem (uint32_t *a) +{ + *g32 = __rev (*a); +} + +/* +** test_revl_mem: +** ... +** rev [wx][0-9]+, [wx][0-9]+ +** ... +** ret +*/ + +void test_revl_mem (unsigned long *a) +{ + *gul = __revl (*a); +} + +/* +** test_revll_mem: +** ... +** rev x[0-9]+, x[0-9]+ +** ... +** ret +*/ + +void test_revll_mem (uint64_t *a) +{ + *g64 = __revll (*a); +} + +/* +** test_rev16_mem: +** ... +** rev16 w[0-9]+, w[0-9]+ +** ... +** ret +*/ + +void test_rev16_mem (uint32_t *a) +{ + *g32 = __rev16 (*a); +} + +/* +** test_rev16l_mem: +** ... +** rev16 [wx][0-9]+, [wx][0-9]+ +** ... +** ret +*/ + +void test_rev16l_mem (unsigned long *a) +{ + *gul = __rev16l (*a); +} + +/* +** test_rev16ll_mem: +** ... +** rev16 x[0-9]+, x[0-9]+ +** ... +** ret +*/ + +void test_rev16ll_mem (uint64_t *a) +{ + *g64 = __rev16ll (*a); +} + +/* +** test_ror_mem: +** ... +** ror w[0-9]+, w[0-9]+, w[0-9]+ +** ... +** ret +*/ + +void test_ror_mem (uint32_t *a, uint32_t *r) +{ + *g32 = __ror (*a, *r); +} + +/* +** test_rorl_mem: +** ... +** ror [wx][0-9]+, [wx][0-9]+, [wx][0-9]+ +** ... +** ret +*/ + +void test_rorl_mem (unsigned long *a, uint32_t *r) +{ + *gul = __rorl (*a, *r); +} + +/* +** test_rorll_mem: +** ... +** ror x[0-9]+, x[0-9]+, x[0-9]+ +** ... +** ret +*/ + +void test_rorll_mem (uint64_t *a, uint32_t *r) +{ + *g64 = __rorll (*a, *r); +} + +/* +** test_revsh_mem: +** ... +** rev16 w[0-9]+, w[0-9]+ +** ... +** ret +*/ + +void test_revsh_mem (int16_t *a) +{ + *g16 = __revsh (*a); +} -- cgit v1.1 From 613e3b86955ea3b4977490625328708e82bea6fa Mon Sep 17 00:00:00 2001 From: Richard Biener Date: Mon, 25 Jul 2022 12:10:48 +0200 Subject: middle-end/106414 - fix mistake in ~(x ^ y) -> x == y pattern When compares are integer typed the inversion with ~ isn't properly preserved by the equality comparison even when converting the result properly. The following fixes this by restricting the input precisions accordingly. PR middle-end/106414 * match.pd (~(x ^ y) -> x == y): Restrict to single bit precision types. * gcc.dg/torture/pr106414-1.c: New testcase. * gcc.dg/torture/pr106414-2.c: Likewise. --- gcc/match.pd | 3 ++- gcc/testsuite/gcc.dg/torture/pr106414-1.c | 12 ++++++++++++ gcc/testsuite/gcc.dg/torture/pr106414-2.c | 12 ++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/gcc.dg/torture/pr106414-1.c create mode 100644 gcc/testsuite/gcc.dg/torture/pr106414-2.c (limited to 'gcc') diff --git a/gcc/match.pd b/gcc/match.pd index 9736393..330c1db 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -1946,7 +1946,8 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) /* ~(a ^ b) is a == b for truth valued a and b. */ (simplify (bit_not (bit_xor:s truth_valued_p@0 truth_valued_p@1)) - (if (!VECTOR_TYPE_P (type)) + (if (INTEGRAL_TYPE_P (TREE_TYPE (@0)) + && TYPE_PRECISION (TREE_TYPE (@0)) == 1) (convert (eq @0 @1)))) /* (x & ~m) | (y & m) -> ((x ^ y) & m) ^ x */ diff --git a/gcc/testsuite/gcc.dg/torture/pr106414-1.c b/gcc/testsuite/gcc.dg/torture/pr106414-1.c new file mode 100644 index 0000000..0974716 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr106414-1.c @@ -0,0 +1,12 @@ +/* { dg-do run } */ + +int a, c, e; +const int b = 1; +char d; +int main() { + a = ~(e || 0) ^ b & ~d; + d = ~(a | ~2); + if (d) + __builtin_abort(); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/torture/pr106414-2.c b/gcc/testsuite/gcc.dg/torture/pr106414-2.c new file mode 100644 index 0000000..bed6a40 --- /dev/null +++ b/gcc/testsuite/gcc.dg/torture/pr106414-2.c @@ -0,0 +1,12 @@ +/* { dg-do run } */ + +int a, b, c, d; +unsigned e; +int main() { + c = e = -((a && 1) ^ ~(b || 0)); + if (e < -1) + d = c; + if (!d) + __builtin_abort(); + return 0; +} -- cgit v1.1 From 556b816b820f6749910784cbaca8bb0bb822a970 Mon Sep 17 00:00:00 2001 From: Sebastian Huber Date: Fri, 22 Jul 2022 14:09:20 +0200 Subject: RTEMS: Do not define _GNU_SOURCE by default gcc/ChangeLog: * config/rs6000/rtems.h (CPLUSPLUS_CPP_SPEC): Undef. --- gcc/config/rs6000/rtems.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'gcc') diff --git a/gcc/config/rs6000/rtems.h b/gcc/config/rs6000/rtems.h index 8aa41d5..d529e22 100644 --- a/gcc/config/rs6000/rtems.h +++ b/gcc/config/rs6000/rtems.h @@ -23,6 +23,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see . */ +/* Undef gnu-user.h macro we don't want. */ +#undef CPLUSPLUS_CPP_SPEC + /* Copy and paste from linux64.h and freebsd64.h */ #ifdef IN_LIBGCC2 #undef TARGET_64BIT -- cgit v1.1 From 4c6567b4090d9363f90b5c68e238e7d0964b6e89 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Sun, 24 Jul 2022 23:26:59 -0400 Subject: c++: -Woverloaded-virtual false positive [PR87729] My attempt to shortcut unnecessary checking after finding a match was also wrong for multiple inheritance, so let's give up on it. PR c++/87729 gcc/cp/ChangeLog: * class.cc (warn_hidden): Remove shortcut. gcc/testsuite/ChangeLog: * g++.dg/warn/Woverloaded-virt4.C: New test. --- gcc/cp/class.cc | 11 +++++++---- gcc/testsuite/g++.dg/warn/Woverloaded-virt4.C | 7 +++++++ 2 files changed, 14 insertions(+), 4 deletions(-) create mode 100644 gcc/testsuite/g++.dg/warn/Woverloaded-virt4.C (limited to 'gcc') diff --git a/gcc/cp/class.cc b/gcc/cp/class.cc index eb69e7f..a12d367 100644 --- a/gcc/cp/class.cc +++ b/gcc/cp/class.cc @@ -3040,22 +3040,25 @@ warn_hidden (tree t) bool seen_non_override = false; for (tree fndecl : ovl_range (fns)) { + bool any_override = false; if (TREE_CODE (fndecl) == FUNCTION_DECL && DECL_VINDEX (fndecl)) { /* If the method from the base class has the same signature as the method from the derived class, it - has been overridden. */ + has been overridden. Note that we can't move on + after finding one match: fndecl might override + multiple base fns. */ for (size_t k = 0; k < base_fndecls.length (); k++) if (base_fndecls[k] && same_signature_p (fndecl, base_fndecls[k])) { base_fndecls[k] = NULL_TREE; - goto next; + any_override = true; } } - seen_non_override = true; - next:; + if (!any_override) + seen_non_override = true; } if (!seen_non_override && warn_overloaded_virtual == 1) diff --git a/gcc/testsuite/g++.dg/warn/Woverloaded-virt4.C b/gcc/testsuite/g++.dg/warn/Woverloaded-virt4.C new file mode 100644 index 0000000..b4d8668 --- /dev/null +++ b/gcc/testsuite/g++.dg/warn/Woverloaded-virt4.C @@ -0,0 +1,7 @@ +// PR c++/87729 +// { dg-additional-options -Woverloaded-virtual } + +struct S1 { virtual void f(); }; +struct S2: S1 {}; +struct S3: S1 {}; +struct S4: S2, S3 { void f(); }; -- cgit v1.1 From ca1e4b26c19351077db1d8e6ff1cb97f05bca79f Mon Sep 17 00:00:00 2001 From: Aldy Hernandez Date: Mon, 25 Jul 2022 15:58:04 +0200 Subject: [PR middle-end/106432] Gracefully handle unsupported type in range_on_edge A cleaner approach to fix this PR has been suggested by Andrew, which is to just return false on range_on_edge for unsupported range types. Tested on x86-64 Linux. PR middle-end/106432 gcc/ChangeLog: * gimple-range.cc (gimple_ranger::range_on_edge): Return false when the result range type is unsupported. --- gcc/gimple-range.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'gcc') diff --git a/gcc/gimple-range.cc b/gcc/gimple-range.cc index 7ac4830..eb347ee 100644 --- a/gcc/gimple-range.cc +++ b/gcc/gimple-range.cc @@ -201,7 +201,9 @@ bool gimple_ranger::range_on_edge (vrange &r, edge e, tree name) { Value_Range edge_range (TREE_TYPE (name)); - gcc_checking_assert (r.supports_type_p (TREE_TYPE (name))); + + if (!r.supports_type_p (TREE_TYPE (name))) + return false; // Do not process values along abnormal edges. if (e->flags & EDGE_ABNORMAL) -- cgit v1.1 From 16aafa3194d4851a07cc204f56a5f0618f77e5d7 Mon Sep 17 00:00:00 2001 From: Roger Sayle Date: Mon, 25 Jul 2022 17:33:48 +0100 Subject: PR target/91681: zero_extendditi2 pattern for more optimizations on x86. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Technically, PR target/91681 has already been resolved; we now recognize the highpart multiplication at the tree-level, we no longer use the stack, and we currently generate the same number of instructions as LLVM. However, it is still possible to do better, the current x86_64 code to generate a double word addition of a zero extended operand, looks like: xorl %r11d, %r11d addq %r10, %rax adcq %r11, %rdx when it's possible (as LLVM does) to use an immediate constant: addq %r10, %rax adcq $0, %rdx This is implemented by introducing a zero_extendditi2 pattern, for zero extension from DImode to TImode on TARGET_64BIT that is split after reload. With zero extension now visible to combine, we add two new define_insn_and_split that add/subtract a zero extended operand in double word mode. These apply to both 32-bit and 64-bit code generation, to produce adc $0 and sbb $0. One consequence of this is that these new patterns interfere with the optimization that recognizes DW:DI = (HI:SI<<32)+LO:SI as a pair of register moves, or more accurately the combine splitter no longer triggers as we're now converting two instructions into two instructions (not three instructions into two instructions). This is easily repaired (and extended to handle TImode) by changing from a pair of define_split (that handle operand commutativity) to a set of four define_insn_and_split (again to handle operand commutativity). 2022-07-25 Roger Sayle Uroš Bizjak gcc/ChangeLog PR target/91681 * config/i386/i386-expand.cc (split_double_concat): A new helper function for setting a double word value from two word values. * config/i386/i386-protos.h (split_double_concat): Prototype here. * config/i386/i386.md (zero_extendditi2): New define_insn_and_split. (*add3_doubleword_zext): New define_insn_and_split. (*sub3_doubleword_zext): New define_insn_and_split. (*concat3_1): New define_insn_and_split replacing previous define_split for implementing DST = (HI<<32)|LO as pair of move instructions, setting lopart and hipart. (*concat3_2): Likewise. (*concat3_3): Likewise, where HI is zero_extended. (*concat3_4): Likewise, where HI is zero_extended. gcc/testsuite/ChangeLog PR target/91681 * g++.target/i386/pr91681.C: New test case (from the PR). * gcc.target/i386/pr91681-1.c: New int128 test case. * gcc.target/i386/pr91681-2.c: Likewise. * gcc.target/i386/pr91681-3.c: Likewise, but for ia32. --- gcc/config/i386/i386-expand.cc | 40 +++++++++ gcc/config/i386/i386-protos.h | 1 + gcc/config/i386/i386.md | 140 +++++++++++++++++++++++++----- gcc/testsuite/g++.target/i386/pr91681.C | 20 +++++ gcc/testsuite/gcc.target/i386/pr91681-1.c | 20 +++++ gcc/testsuite/gcc.target/i386/pr91681-2.c | 20 +++++ gcc/testsuite/gcc.target/i386/pr91681-3.c | 16 ++++ 7 files changed, 233 insertions(+), 24 deletions(-) create mode 100644 gcc/testsuite/g++.target/i386/pr91681.C create mode 100644 gcc/testsuite/gcc.target/i386/pr91681-1.c create mode 100644 gcc/testsuite/gcc.target/i386/pr91681-2.c create mode 100644 gcc/testsuite/gcc.target/i386/pr91681-3.c (limited to 'gcc') diff --git a/gcc/config/i386/i386-expand.cc b/gcc/config/i386/i386-expand.cc index 40f821e..66d8f28 100644 --- a/gcc/config/i386/i386-expand.cc +++ b/gcc/config/i386/i386-expand.cc @@ -165,6 +165,46 @@ split_double_mode (machine_mode mode, rtx operands[], } } +/* Emit the double word assignment DST = { LO, HI }. */ + +void +split_double_concat (machine_mode mode, rtx dst, rtx lo, rtx hi) +{ + rtx dlo, dhi; + int deleted_move_count = 0; + split_double_mode (mode, &dst, 1, &dlo, &dhi); + if (!rtx_equal_p (dlo, hi)) + { + if (!rtx_equal_p (dlo, lo)) + emit_move_insn (dlo, lo); + else + deleted_move_count++; + if (!rtx_equal_p (dhi, hi)) + emit_move_insn (dhi, hi); + else + deleted_move_count++; + } + else if (!rtx_equal_p (lo, dhi)) + { + if (!rtx_equal_p (dhi, hi)) + emit_move_insn (dhi, hi); + else + deleted_move_count++; + if (!rtx_equal_p (dlo, lo)) + emit_move_insn (dlo, lo); + else + deleted_move_count++; + } + else if (mode == TImode) + emit_insn (gen_swapdi (dlo, dhi)); + else + emit_insn (gen_swapsi (dlo, dhi)); + + if (deleted_move_count == 2) + emit_note (NOTE_INSN_DELETED); +} + + /* Generate either "mov $0, reg" or "xor reg, reg", as appropriate for the target. */ diff --git a/gcc/config/i386/i386-protos.h b/gcc/config/i386/i386-protos.h index cf84775..e27c14f 100644 --- a/gcc/config/i386/i386-protos.h +++ b/gcc/config/i386/i386-protos.h @@ -85,6 +85,7 @@ extern void print_reg (rtx, int, FILE*); extern void ix86_print_operand (FILE *, rtx, int); extern void split_double_mode (machine_mode, rtx[], int, rtx[], rtx[]); +extern void split_double_concat (machine_mode, rtx, rtx lo, rtx); extern const char *output_set_got (rtx, rtx); extern const char *output_387_binary_op (rtx_insn *, rtx*); diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 9aaeb69..fab6aed 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -4116,6 +4116,16 @@ ;; Zero extension instructions +(define_insn_and_split "zero_extendditi2" + [(set (match_operand:TI 0 "nonimmediate_operand" "=r,o") + (zero_extend:TI (match_operand:DI 1 "nonimmediate_operand" "rm,r")))] + "TARGET_64BIT" + "#" + "&& reload_completed" + [(set (match_dup 3) (match_dup 1)) + (set (match_dup 4) (const_int 0))] + "split_double_mode (TImode, &operands[0], 1, &operands[3], &operands[4]);") + (define_expand "zero_extendsidi2" [(set (match_operand:DI 0 "nonimmediate_operand") (zero_extend:DI (match_operand:SI 1 "nonimmediate_operand")))]) @@ -5814,6 +5824,31 @@ } }) +(define_insn_and_split "*add3_doubleword_zext" + [(set (match_operand: 0 "nonimmediate_operand" "=r,o") + (plus: + (zero_extend: + (match_operand:DWIH 2 "nonimmediate_operand" "rm,r")) + (match_operand: 1 "nonimmediate_operand" "0,0"))) + (clobber (reg:CC FLAGS_REG))] + "ix86_binary_operator_ok (UNKNOWN, mode, operands)" + "#" + "&& reload_completed" + [(parallel [(set (reg:CCC FLAGS_REG) + (compare:CCC + (plus:DWIH (match_dup 1) (match_dup 2)) + (match_dup 1))) + (set (match_dup 0) + (plus:DWIH (match_dup 1) (match_dup 2)))]) + (parallel [(set (match_dup 3) + (plus:DWIH + (plus:DWIH + (ltu:DWIH (reg:CC FLAGS_REG) (const_int 0)) + (match_dup 4)) + (const_int 0))) + (clobber (reg:CC FLAGS_REG))])] + "split_double_mode (mode, &operands[0], 2, &operands[0], &operands[3]);") + (define_insn "*add_1" [(set (match_operand:SWI48 0 "nonimmediate_operand" "=rm,r,r,r") (plus:SWI48 @@ -6962,6 +6997,29 @@ } }) +(define_insn_and_split "*sub3_doubleword_zext" + [(set (match_operand: 0 "nonimmediate_operand" "=r,o") + (minus: + (match_operand: 1 "nonimmediate_operand" "0,0") + (zero_extend: + (match_operand:DWIH 2 "nonimmediate_operand" "rm,r")))) + (clobber (reg:CC FLAGS_REG))] + "ix86_binary_operator_ok (UNKNOWN, mode, operands)" + "#" + "&& reload_completed" + [(parallel [(set (reg:CC FLAGS_REG) + (compare:CC (match_dup 1) (match_dup 2))) + (set (match_dup 0) + (minus:DWIH (match_dup 1) (match_dup 2)))]) + (parallel [(set (match_dup 3) + (minus:DWIH + (minus:DWIH + (match_dup 4) + (ltu:DWIH (reg:CC FLAGS_REG) (const_int 0))) + (const_int 0))) + (clobber (reg:CC FLAGS_REG))])] + "split_double_mode (mode, &operands[0], 2, &operands[0], &operands[3]);") + (define_insn "*sub_1" [(set (match_operand:SWI 0 "nonimmediate_operand" "=m,") (minus:SWI @@ -11111,34 +11169,68 @@ ;; Split DST = (HI<<32)|LO early to minimize register usage. (define_code_iterator any_or_plus [plus ior xor]) -(define_split - [(set (match_operand:DI 0 "register_operand") - (any_or_plus:DI - (ashift:DI (match_operand:DI 1 "register_operand") - (const_int 32)) - (zero_extend:DI (match_operand:SI 2 "register_operand"))))] - "!TARGET_64BIT" - [(set (match_dup 3) (match_dup 4)) - (set (match_dup 5) (match_dup 2))] +(define_insn_and_split "*concat3_1" + [(set (match_operand: 0 "nonimmediate_operand" "=ro") + (any_or_plus: + (ashift: (match_operand: 1 "register_operand" "r") + (match_operand: 2 "const_int_operand")) + (zero_extend: (match_operand:DWIH 3 "register_operand" "r"))))] + "INTVAL (operands[2]) == * BITS_PER_UNIT" + "#" + "&& reload_completed" + [(clobber (const_int 0))] { - operands[3] = gen_highpart (SImode, operands[0]); - operands[4] = gen_lowpart (SImode, operands[1]); - operands[5] = gen_lowpart (SImode, operands[0]); + split_double_concat (mode, operands[0], operands[3], + gen_lowpart (mode, operands[1])); + DONE; }) -(define_split - [(set (match_operand:DI 0 "register_operand") - (any_or_plus:DI - (zero_extend:DI (match_operand:SI 1 "register_operand")) - (ashift:DI (match_operand:DI 2 "register_operand") - (const_int 32))))] - "!TARGET_64BIT" - [(set (match_dup 3) (match_dup 4)) - (set (match_dup 5) (match_dup 1))] +(define_insn_and_split "*concat3_2" + [(set (match_operand: 0 "nonimmediate_operand" "=ro") + (any_or_plus: + (zero_extend: (match_operand:DWIH 1 "register_operand" "r")) + (ashift: (match_operand: 2 "register_operand" "r") + (match_operand: 3 "const_int_operand"))))] + "INTVAL (operands[3]) == * BITS_PER_UNIT" + "#" + "&& reload_completed" + [(clobber (const_int 0))] +{ + split_double_concat (mode, operands[0], operands[1], + gen_lowpart (mode, operands[2])); + DONE; +}) + +(define_insn_and_split "*concat3_3" + [(set (match_operand: 0 "nonimmediate_operand" "=ro") + (any_or_plus: + (ashift: + (zero_extend: (match_operand:DWIH 1 "register_operand" "r")) + (match_operand: 2 "const_int_operand")) + (zero_extend: (match_operand:DWIH 3 "register_operand" "r"))))] + "INTVAL (operands[2]) == * BITS_PER_UNIT" + "#" + "&& reload_completed" + [(clobber (const_int 0))] { - operands[3] = gen_highpart (SImode, operands[0]); - operands[4] = gen_lowpart (SImode, operands[2]); - operands[5] = gen_lowpart (SImode, operands[0]); + split_double_concat (mode, operands[0], operands[3], operands[1]); + DONE; +}) + +(define_insn_and_split "*concat3_4" + [(set (match_operand: 0 "nonimmediate_operand" "=ro") + (any_or_plus: + (zero_extend: (match_operand:DWIH 1 "register_operand" "r")) + (ashift: + (zero_extend: (match_operand:DWIH 2 "register_operand" "r")) + (match_operand: 3 "const_int_operand"))))] + "INTVAL (operands[3]) == * BITS_PER_UNIT" + "#" + "&& reload_completed" + [(clobber (const_int 0))] +{ + split_double_concat (mode, operands[0], operands[1], operands[2]); + DONE; }) ;; Negation instructions diff --git a/gcc/testsuite/g++.target/i386/pr91681.C b/gcc/testsuite/g++.target/i386/pr91681.C new file mode 100644 index 0000000..0271e43 --- /dev/null +++ b/gcc/testsuite/g++.target/i386/pr91681.C @@ -0,0 +1,20 @@ +/* { dg-do compile { target int128 } } */ +/* { dg-options "-O2" } */ + +void multiply128x64x2_3 ( + const unsigned long a, + const unsigned long b, + const unsigned long c, + const unsigned long d, + __uint128_t o[2]) +{ + __uint128_t B0 = (__uint128_t) b * c; + __uint128_t B2 = (__uint128_t) a * c; + __uint128_t B1 = (__uint128_t) b * d; + __uint128_t B3 = (__uint128_t) a * d; + + o[0] = B2 + (B0 >> 64); + o[1] = B3 + (B1 >> 64); +} + +/* { dg-final { scan-assembler-not "xor" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr91681-1.c b/gcc/testsuite/gcc.target/i386/pr91681-1.c new file mode 100644 index 0000000..ab83cc4 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr91681-1.c @@ -0,0 +1,20 @@ +/* { dg-do compile { target int128 } } */ +/* { dg-options "-O2" } */ +unsigned __int128 m; + +unsigned __int128 foo(unsigned __int128 x, unsigned long long y) +{ + return x + y; +} + +void bar(unsigned __int128 x, unsigned long long y) +{ + m = x + y; +} + +void baz(unsigned long long y) +{ + m += y; +} + +/* { dg-final { scan-assembler-not "xor" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr91681-2.c b/gcc/testsuite/gcc.target/i386/pr91681-2.c new file mode 100644 index 0000000..ea52c72 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr91681-2.c @@ -0,0 +1,20 @@ +/* { dg-do compile { target int128 } } */ +/* { dg-options "-O2" } */ +unsigned __int128 m; + +unsigned __int128 foo(unsigned __int128 x, unsigned long long y) +{ + return x - y; +} + +void bar(unsigned __int128 x, unsigned long long y) +{ + m = x - y; +} + +void baz(unsigned long long y) +{ + m -= y; +} + +/* { dg-final { scan-assembler-not "xor" } } */ diff --git a/gcc/testsuite/gcc.target/i386/pr91681-3.c b/gcc/testsuite/gcc.target/i386/pr91681-3.c new file mode 100644 index 0000000..22a03c2 --- /dev/null +++ b/gcc/testsuite/gcc.target/i386/pr91681-3.c @@ -0,0 +1,16 @@ +/* { dg-do compile { target ia32 } } */ +/* { dg-options "-O2" } */ + +unsigned long long m; + +unsigned long long foo(unsigned long long x, unsigned int y) +{ + return x - y; +} + +void bar(unsigned long long x, unsigned int y) +{ + m = x - y; +} + +/* { dg-final { scan-assembler-not "xor" } } */ -- cgit v1.1 From 9365b2bf2f310780d80193b196204e0b8ce7767f Mon Sep 17 00:00:00 2001 From: Martin Liska Date: Mon, 25 Jul 2022 08:10:01 +0200 Subject: analyzer: fix coding style in sm-fd.cc gcc/analyzer/ChangeLog: * sm-fd.cc: Run dos2unix and fix coding style issues. --- gcc/analyzer/sm-fd.cc | 2114 ++++++++++++++++++++++++------------------------- 1 file changed, 1057 insertions(+), 1057 deletions(-) (limited to 'gcc') diff --git a/gcc/analyzer/sm-fd.cc b/gcc/analyzer/sm-fd.cc index c3dac48..56b0063 100644 --- a/gcc/analyzer/sm-fd.cc +++ b/gcc/analyzer/sm-fd.cc @@ -1,1057 +1,1057 @@ -/* A state machine for detecting misuses of POSIX file descriptor APIs. - Copyright (C) 2019-2022 Free Software Foundation, Inc. - Contributed by Immad Mir . - -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 "config.h" -#include "system.h" -#include "coretypes.h" -#include "tree.h" -#include "function.h" -#include "basic-block.h" -#include "gimple.h" -#include "options.h" -#include "diagnostic-path.h" -#include "diagnostic-metadata.h" -#include "function.h" -#include "json.h" -#include "analyzer/analyzer.h" -#include "diagnostic-event-id.h" -#include "analyzer/analyzer-logging.h" -#include "analyzer/sm.h" -#include "analyzer/pending-diagnostic.h" -#include "analyzer/function-set.h" -#include "analyzer/analyzer-selftests.h" -#include "tristate.h" -#include "selftest.h" -#include "stringpool.h" -#include "attribs.h" -#include "analyzer/call-string.h" -#include "analyzer/program-point.h" -#include "analyzer/store.h" -#include "analyzer/region-model.h" -#include "bitmap.h" - -#if ENABLE_ANALYZER - -namespace ana { - -namespace { - -/* An enum for distinguishing between three different access modes. */ - -enum access_mode -{ - READ_WRITE, - READ_ONLY, - WRITE_ONLY -}; - -enum access_directions -{ - DIRS_READ_WRITE, - DIRS_READ, - DIRS_WRITE -}; - -class fd_state_machine : public state_machine -{ -public: - fd_state_machine (logger *logger); - - bool - inherited_state_p () const final override - { - return false; - } - - state_machine::state_t - get_default_state (const svalue *sval) const final override - { - if (tree cst = sval->maybe_get_constant ()) - { - if (TREE_CODE (cst) == INTEGER_CST) - { - int val = TREE_INT_CST_LOW (cst); - if (val >= 0) - return m_constant_fd; - else - return m_invalid; - } - } - return m_start; - } - - bool on_stmt (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt) const final override; - - void on_condition (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt, const svalue *lhs, const tree_code op, - const svalue *rhs) const final override; - - bool can_purge_p (state_t s) const final override; - pending_diagnostic *on_leak (tree var) const final override; - - bool is_unchecked_fd_p (state_t s) const; - bool is_valid_fd_p (state_t s) const; - bool is_closed_fd_p (state_t s) const; - bool is_constant_fd_p (state_t s) const; - bool is_readonly_fd_p (state_t s) const; - bool is_writeonly_fd_p (state_t s) const; - enum access_mode get_access_mode_from_flag (int flag) const; - - /* State for a constant file descriptor (>= 0) */ - state_t m_constant_fd; - - /* States representing a file descriptor that hasn't yet been - checked for validity after opening, for three different - access modes. */ - state_t m_unchecked_read_write; - - state_t m_unchecked_read_only; - - state_t m_unchecked_write_only; - - /* States for representing a file descriptor that is known to be valid (>= - 0), for three different access modes.*/ - state_t m_valid_read_write; - - state_t m_valid_read_only; - - state_t m_valid_write_only; - - /* State for a file descriptor that is known to be invalid (< 0). */ - state_t m_invalid; - - /* State for a file descriptor that has been closed.*/ - state_t m_closed; - - /* State for a file descriptor that we do not want to track anymore . */ - state_t m_stop; - -private: - void on_open (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, - const gcall *call) const; - void on_close (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, - const gcall *call) const; - void on_read (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, - const gcall *call, const tree callee_fndecl) const; - void on_write (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, - const gcall *call, const tree callee_fndecl) const; - void check_for_open_fd (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call, - const tree callee_fndecl, - enum access_directions access_fn) const; - - void make_valid_transitions_on_condition (sm_context *sm_ctxt, - const supernode *node, - const gimple *stmt, - const svalue *lhs) const; - void make_invalid_transitions_on_condition (sm_context *sm_ctxt, - const supernode *node, - const gimple *stmt, - const svalue *lhs) const; - void check_for_fd_attrs (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call, - const tree callee_fndecl, const char *attr_name, - access_directions fd_attr_access_dir) const; -}; - -/* Base diagnostic class relative to fd_state_machine. */ -class fd_diagnostic : public pending_diagnostic -{ -public: - fd_diagnostic (const fd_state_machine &sm, tree arg) : m_sm (sm), m_arg (arg) - { - } - - bool - subclass_equal_p (const pending_diagnostic &base_other) const override - { - return same_tree_p (m_arg, ((const fd_diagnostic &)base_other).m_arg); - } - - label_text - describe_state_change (const evdesc::state_change &change) override - { - if (change.m_old_state == m_sm.get_start_state () - && m_sm.is_unchecked_fd_p (change.m_new_state)) - { - if (change.m_new_state == m_sm.m_unchecked_read_write) - return change.formatted_print ("opened here as read-write"); - - if (change.m_new_state == m_sm.m_unchecked_read_only) - return change.formatted_print ("opened here as read-only"); - - if (change.m_new_state == m_sm.m_unchecked_write_only) - return change.formatted_print ("opened here as write-only"); - } - - if (change.m_new_state == m_sm.m_closed) - return change.formatted_print ("closed here"); - - if (m_sm.is_unchecked_fd_p (change.m_old_state) - && m_sm.is_valid_fd_p (change.m_new_state)) - { - if (change.m_expr) - return change.formatted_print ( - "assuming %qE is a valid file descriptor (>= 0)", change.m_expr); - else - return change.formatted_print ("assuming a valid file descriptor"); - } - - if (m_sm.is_unchecked_fd_p (change.m_old_state) - && change.m_new_state == m_sm.m_invalid) - { - if (change.m_expr) - return change.formatted_print ( - "assuming %qE is an invalid file descriptor (< 0)", - change.m_expr); - else - return change.formatted_print ("assuming an invalid file descriptor"); - } - - return label_text (); - } - -protected: - const fd_state_machine &m_sm; - tree m_arg; -}; - -class fd_param_diagnostic : public fd_diagnostic -{ -public: - fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl, - const char *attr_name, int arg_idx) - : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl), - m_attr_name (attr_name), m_arg_idx (arg_idx) - { - } - - fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl) - : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl), - m_attr_name (NULL), m_arg_idx (-1) - { - } - - bool - subclass_equal_p (const pending_diagnostic &base_other) const override - { - const fd_param_diagnostic &sub_other - = (const fd_param_diagnostic &)base_other; - return (same_tree_p (m_arg, sub_other.m_arg) - && same_tree_p (m_callee_fndecl, sub_other.m_callee_fndecl) - && m_arg_idx == sub_other.m_arg_idx - && ((m_attr_name) - ? (strcmp (m_attr_name, sub_other.m_attr_name) == 0) - : true)); - } - - void - inform_filedescriptor_attribute (access_directions fd_dir) - { - - if (m_attr_name) - switch (fd_dir) - { - case DIRS_READ_WRITE: - inform (DECL_SOURCE_LOCATION (m_callee_fndecl), - "argument %d of %qD must be an open file descriptor, due to " - "%<__attribute__((%s(%d)))%>", - m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1); - break; - case DIRS_WRITE: - inform (DECL_SOURCE_LOCATION (m_callee_fndecl), - "argument %d of %qD must be a readable file descriptor, due " - "to %<__attribute__((%s(%d)))%>", - m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1); - break; - case DIRS_READ: - inform (DECL_SOURCE_LOCATION (m_callee_fndecl), - "argument %d of %qD must be a writable file descriptor, due " - "to %<__attribute__((%s(%d)))%>", - m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1); - break; - } - } - -protected: - tree m_callee_fndecl; - const char *m_attr_name; - /* ARG_IDX is 0-based. */ - int m_arg_idx; -}; - -class fd_leak : public fd_diagnostic -{ -public: - fd_leak (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg) {} - - const char * - get_kind () const final override - { - return "fd_leak"; - } - - int - get_controlling_option () const final override - { - return OPT_Wanalyzer_fd_leak; - } - - bool - emit (rich_location *rich_loc) final override - { - /*CWE-775: Missing Release of File Descriptor or Handle after Effective - Lifetime - */ - diagnostic_metadata m; - m.add_cwe (775); - if (m_arg) - return warning_meta (rich_loc, m, get_controlling_option (), - "leak of file descriptor %qE", m_arg); - else - return warning_meta (rich_loc, m, get_controlling_option (), - "leak of file descriptor"); - } - - label_text - describe_state_change (const evdesc::state_change &change) final override - { - if (m_sm.is_unchecked_fd_p (change.m_new_state)) - { - m_open_event = change.m_event_id; - return label_text::borrow ("opened here"); - } - - return fd_diagnostic::describe_state_change (change); - } - - label_text - describe_final_event (const evdesc::final_event &ev) final override - { - if (m_open_event.known_p ()) - { - if (ev.m_expr) - return ev.formatted_print ("%qE leaks here; was opened at %@", - ev.m_expr, &m_open_event); - else - return ev.formatted_print ("leaks here; was opened at %@", - &m_open_event); - } - else - { - if (ev.m_expr) - return ev.formatted_print ("%qE leaks here", ev.m_expr); - else - return ev.formatted_print ("leaks here"); - } - } - -private: - diagnostic_event_id_t m_open_event; -}; - -class fd_access_mode_mismatch : public fd_param_diagnostic -{ -public: - fd_access_mode_mismatch (const fd_state_machine &sm, tree arg, - enum access_directions fd_dir, - const tree callee_fndecl, const char *attr_name, - int arg_idx) - : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx), - m_fd_dir (fd_dir) - - { - } - - fd_access_mode_mismatch (const fd_state_machine &sm, tree arg, - enum access_directions fd_dir, - const tree callee_fndecl) - : fd_param_diagnostic (sm, arg, callee_fndecl), m_fd_dir (fd_dir) - { - } - - const char * - get_kind () const final override - { - return "fd_access_mode_mismatch"; - } - - int - get_controlling_option () const final override - { - return OPT_Wanalyzer_fd_access_mode_mismatch; - } - - bool - emit (rich_location *rich_loc) final override - { - bool warned; - switch (m_fd_dir) - { - case DIRS_READ: - warned = warning_at (rich_loc, get_controlling_option (), - "%qE on read-only file descriptor %qE", - m_callee_fndecl, m_arg); - break; - case DIRS_WRITE: - warned = warning_at (rich_loc, get_controlling_option (), - "%qE on write-only file descriptor %qE", - m_callee_fndecl, m_arg); - break; - default: - gcc_unreachable (); - } - if (warned) - inform_filedescriptor_attribute (m_fd_dir); - return warned; - } - - label_text - describe_final_event (const evdesc::final_event &ev) final override - { - switch (m_fd_dir) - { - case DIRS_READ: - return ev.formatted_print ("%qE on read-only file descriptor %qE", - m_callee_fndecl, m_arg); - case DIRS_WRITE: - return ev.formatted_print ("%qE on write-only file descriptor %qE", - m_callee_fndecl, m_arg); - default: - gcc_unreachable (); - } - } - -private: - enum access_directions m_fd_dir; -}; - -class fd_double_close : public fd_diagnostic -{ -public: - fd_double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg) - { - } - - const char * - get_kind () const final override - { - return "fd_double_close"; - } - - int - get_controlling_option () const final override - { - return OPT_Wanalyzer_fd_double_close; - } - bool - emit (rich_location *rich_loc) final override - { - diagnostic_metadata m; - // CWE-1341: Multiple Releases of Same Resource or Handle - m.add_cwe (1341); - return warning_meta (rich_loc, m, get_controlling_option (), - "double % of file descriptor %qE", m_arg); - } - - label_text - describe_state_change (const evdesc::state_change &change) override - { - if (m_sm.is_unchecked_fd_p (change.m_new_state)) - return label_text::borrow ("opened here"); - - if (change.m_new_state == m_sm.m_closed) - { - m_first_close_event = change.m_event_id; - return change.formatted_print ("first %qs here", "close"); - } - return fd_diagnostic::describe_state_change (change); - } - - label_text - describe_final_event (const evdesc::final_event &ev) final override - { - if (m_first_close_event.known_p ()) - return ev.formatted_print ("second %qs here; first %qs was at %@", - "close", "close", &m_first_close_event); - return ev.formatted_print ("second %qs here", "close"); - } - -private: - diagnostic_event_id_t m_first_close_event; -}; - -class fd_use_after_close : public fd_param_diagnostic -{ -public: - fd_use_after_close (const fd_state_machine &sm, tree arg, - const tree callee_fndecl, const char *attr_name, - int arg_idx) - : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx) - { - } - - fd_use_after_close (const fd_state_machine &sm, tree arg, - const tree callee_fndecl) - : fd_param_diagnostic (sm, arg, callee_fndecl) - { - } - - const char * - get_kind () const final override - { - return "fd_use_after_close"; - } - - int - get_controlling_option () const final override - { - return OPT_Wanalyzer_fd_use_after_close; - } - - bool - emit (rich_location *rich_loc) final override - { - bool warned; - warned = warning_at (rich_loc, get_controlling_option (), - "%qE on closed file descriptor %qE", m_callee_fndecl, - m_arg); - if (warned) - inform_filedescriptor_attribute (DIRS_READ_WRITE); - return warned; - } - - label_text - describe_state_change (const evdesc::state_change &change) override - { - if (m_sm.is_unchecked_fd_p (change.m_new_state)) - return label_text::borrow ("opened here"); - - if (change.m_new_state == m_sm.m_closed) - { - m_first_close_event = change.m_event_id; - return change.formatted_print ("closed here"); - } - - return fd_diagnostic::describe_state_change (change); - } - - label_text - describe_final_event (const evdesc::final_event &ev) final override - { - if (m_first_close_event.known_p ()) - return ev.formatted_print ( - "%qE on closed file descriptor %qE; %qs was at %@", m_callee_fndecl, - m_arg, "close", &m_first_close_event); - else - return ev.formatted_print ("%qE on closed file descriptor %qE", - m_callee_fndecl, m_arg); - } - -private: - diagnostic_event_id_t m_first_close_event; -}; - -class fd_use_without_check : public fd_param_diagnostic -{ -public: - fd_use_without_check (const fd_state_machine &sm, tree arg, - const tree callee_fndecl, const char *attr_name, - int arg_idx) - : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx) - { - } - - fd_use_without_check (const fd_state_machine &sm, tree arg, - const tree callee_fndecl) - : fd_param_diagnostic (sm, arg, callee_fndecl) - { - } - - const char * - get_kind () const final override - { - return "fd_use_without_check"; - } - - int - get_controlling_option () const final override - { - return OPT_Wanalyzer_fd_use_without_check; - } - - bool - emit (rich_location *rich_loc) final override - { - bool warned; - warned = warning_at (rich_loc, get_controlling_option (), - "%qE on possibly invalid file descriptor %qE", - m_callee_fndecl, m_arg); - if (warned) - inform_filedescriptor_attribute (DIRS_READ_WRITE); - return warned; - } - - label_text - describe_state_change (const evdesc::state_change &change) override - { - if (m_sm.is_unchecked_fd_p (change.m_new_state)) - { - m_first_open_event = change.m_event_id; - return label_text::borrow ("opened here"); - } - - return fd_diagnostic::describe_state_change (change); - } - - label_text - describe_final_event (const evdesc::final_event &ev) final override - { - if (m_first_open_event.known_p ()) - return ev.formatted_print ( - "%qE could be invalid: unchecked value from %@", m_arg, - &m_first_open_event); - else - return ev.formatted_print ("%qE could be invalid", m_arg); - } - -private: - diagnostic_event_id_t m_first_open_event; -}; - -fd_state_machine::fd_state_machine (logger *logger) - : state_machine ("file-descriptor", logger), - m_constant_fd (add_state ("fd-constant")), - m_unchecked_read_write (add_state ("fd-unchecked-read-write")), - m_unchecked_read_only (add_state ("fd-unchecked-read-only")), - m_unchecked_write_only (add_state ("fd-unchecked-write-only")), - m_valid_read_write (add_state ("fd-valid-read-write")), - m_valid_read_only (add_state ("fd-valid-read-only")), - m_valid_write_only (add_state ("fd-valid-write-only")), - m_invalid (add_state ("fd-invalid")), - m_closed (add_state ("fd-closed")), - m_stop (add_state ("fd-stop")) -{ -} - -bool -fd_state_machine::is_unchecked_fd_p (state_t s) const -{ - return (s == m_unchecked_read_write - || s == m_unchecked_read_only - || s == m_unchecked_write_only); -} - -bool -fd_state_machine::is_valid_fd_p (state_t s) const -{ - return (s == m_valid_read_write - || s == m_valid_read_only - || s == m_valid_write_only); -} - -enum access_mode -fd_state_machine::get_access_mode_from_flag (int flag) const -{ - /* FIXME: this code assumes the access modes on the host and - target are the same, which in practice might not be the case. */ - - if ((flag & O_ACCMODE) == O_RDONLY) - { - return READ_ONLY; - } - else if ((flag & O_ACCMODE) == O_WRONLY) - { - return WRITE_ONLY; - } - return READ_WRITE; -} - -bool -fd_state_machine::is_readonly_fd_p (state_t state) const -{ - return (state == m_unchecked_read_only || state == m_valid_read_only); -} - -bool -fd_state_machine::is_writeonly_fd_p (state_t state) const -{ - return (state == m_unchecked_write_only || state == m_valid_write_only); -} - -bool -fd_state_machine::is_closed_fd_p (state_t state) const -{ - return (state == m_closed); -} - -bool -fd_state_machine::is_constant_fd_p (state_t state) const -{ - return (state == m_constant_fd); -} - -bool -fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt) const -{ - if (const gcall *call = dyn_cast (stmt)) - if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call)) - { - if (is_named_call_p (callee_fndecl, "open", call, 2)) - { - on_open (sm_ctxt, node, stmt, call); - return true; - } // "open" - - if (is_named_call_p (callee_fndecl, "close", call, 1)) - { - on_close (sm_ctxt, node, stmt, call); - return true; - } // "close" - - if (is_named_call_p (callee_fndecl, "write", call, 3)) - { - on_write (sm_ctxt, node, stmt, call, callee_fndecl); - return true; - } // "write" - - if (is_named_call_p (callee_fndecl, "read", call, 3)) - { - on_read (sm_ctxt, node, stmt, call, callee_fndecl); - return true; - } // "read" - - - { - // Handle __attribute__((fd_arg)) - - check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, - "fd_arg", DIRS_READ_WRITE); - - // Handle __attribute__((fd_arg_read)) - - check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, - "fd_arg_read", DIRS_READ); - - // Handle __attribute__((fd_arg_write)) - - check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, - "fd_arg_write", DIRS_WRITE); - } - } - - return false; -} - -void -fd_state_machine::check_for_fd_attrs ( - sm_context *sm_ctxt, const supernode *node, const gimple *stmt, - const gcall *call, const tree callee_fndecl, const char *attr_name, - access_directions fd_attr_access_dir) const -{ - - tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl)); - attrs = lookup_attribute (attr_name, attrs); - if (!attrs) - return; - - if (!TREE_VALUE (attrs)) - return; - - auto_bitmap argmap; - - for (tree idx = TREE_VALUE (attrs); idx; idx = TREE_CHAIN (idx)) - { - unsigned int val = TREE_INT_CST_LOW (TREE_VALUE (idx)) - 1; - bitmap_set_bit (argmap, val); - } - if (bitmap_empty_p (argmap)) - return; - - for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++) - { - tree arg = gimple_call_arg (call, arg_idx); - tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); - state_t state = sm_ctxt->get_state (stmt, arg); - bool bit_set = bitmap_bit_p (argmap, arg_idx); - if (TREE_CODE (TREE_TYPE (arg)) != INTEGER_TYPE) - continue; - if (bit_set) // Check if arg_idx is marked by any of the file descriptor - // attributes - { - - if (is_closed_fd_p (state)) - { - - sm_ctxt->warn (node, stmt, arg, - new fd_use_after_close (*this, diag_arg, - callee_fndecl, attr_name, - arg_idx)); - continue; - } - - if (!(is_valid_fd_p (state) || (state == m_stop))) - { - if (!is_constant_fd_p (state)) - sm_ctxt->warn (node, stmt, arg, - new fd_use_without_check (*this, diag_arg, - callee_fndecl, attr_name, - arg_idx)); - } - - switch (fd_attr_access_dir) - { - case DIRS_READ_WRITE: - break; - case DIRS_READ: - - if (is_writeonly_fd_p (state)) - { - sm_ctxt->warn ( - node, stmt, arg, - new fd_access_mode_mismatch (*this, diag_arg, DIRS_WRITE, - callee_fndecl, attr_name, arg_idx)); - } - - break; - case DIRS_WRITE: - - if (is_readonly_fd_p (state)) - { - sm_ctxt->warn ( - node, stmt, arg, - new fd_access_mode_mismatch (*this, diag_arg, DIRS_READ, - callee_fndecl, attr_name, arg_idx)); - } - - break; - } - } - } -} - - -void -fd_state_machine::on_open (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call) const -{ - tree lhs = gimple_call_lhs (call); - if (lhs) - { - tree arg = gimple_call_arg (call, 1); - if (TREE_CODE (arg) == INTEGER_CST) - { - int flag = TREE_INT_CST_LOW (arg); - enum access_mode mode = get_access_mode_from_flag (flag); - - switch (mode) - { - case READ_ONLY: - sm_ctxt->on_transition (node, stmt, lhs, m_start, - m_unchecked_read_only); - break; - case WRITE_ONLY: - sm_ctxt->on_transition (node, stmt, lhs, m_start, - m_unchecked_write_only); - break; - default: - sm_ctxt->on_transition (node, stmt, lhs, m_start, - m_unchecked_read_write); - } - } - } - else - { - sm_ctxt->warn (node, stmt, NULL_TREE, new fd_leak (*this, NULL_TREE)); - } -} - -void -fd_state_machine::on_close (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call) const -{ - tree arg = gimple_call_arg (call, 0); - state_t state = sm_ctxt->get_state (stmt, arg); - tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); - - sm_ctxt->on_transition (node, stmt, arg, m_start, m_closed); - sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_write, m_closed); - sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_only, m_closed); - sm_ctxt->on_transition (node, stmt, arg, m_unchecked_write_only, m_closed); - sm_ctxt->on_transition (node, stmt, arg, m_valid_read_write, m_closed); - sm_ctxt->on_transition (node, stmt, arg, m_valid_read_only, m_closed); - sm_ctxt->on_transition (node, stmt, arg, m_valid_write_only, m_closed); - sm_ctxt->on_transition (node, stmt, arg, m_constant_fd, m_closed); - - if (is_closed_fd_p (state)) - { - sm_ctxt->warn (node, stmt, arg, new fd_double_close (*this, diag_arg)); - sm_ctxt->set_next_state (stmt, arg, m_stop); - } -} -void -fd_state_machine::on_read (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call, - const tree callee_fndecl) const -{ - check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_READ); -} -void -fd_state_machine::on_write (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt, const gcall *call, - const tree callee_fndecl) const -{ - check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_WRITE); -} - -void -fd_state_machine::check_for_open_fd ( - sm_context *sm_ctxt, const supernode *node, const gimple *stmt, - const gcall *call, const tree callee_fndecl, - enum access_directions callee_fndecl_dir) const -{ - tree arg = gimple_call_arg (call, 0); - tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); - state_t state = sm_ctxt->get_state (stmt, arg); - - if (is_closed_fd_p (state)) - { - sm_ctxt->warn (node, stmt, arg, - new fd_use_after_close (*this, diag_arg, callee_fndecl)); - } - - else - { - if (!(is_valid_fd_p (state) || (state == m_stop))) - { - if (!is_constant_fd_p (state)) - sm_ctxt->warn ( - node, stmt, arg, - new fd_use_without_check (*this, diag_arg, callee_fndecl)); - } - switch (callee_fndecl_dir) - { - case DIRS_READ: - if (is_writeonly_fd_p (state)) - { - tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); - sm_ctxt->warn (node, stmt, arg, - new fd_access_mode_mismatch ( - *this, diag_arg, DIRS_WRITE, callee_fndecl)); - } - - break; - case DIRS_WRITE: - - if (is_readonly_fd_p (state)) - { - tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); - sm_ctxt->warn (node, stmt, arg, - new fd_access_mode_mismatch ( - *this, diag_arg, DIRS_READ, callee_fndecl)); - } - break; - default: - gcc_unreachable (); - } - } -} - -void -fd_state_machine::on_condition (sm_context *sm_ctxt, const supernode *node, - const gimple *stmt, const svalue *lhs, - enum tree_code op, const svalue *rhs) const -{ - if (tree cst = rhs->maybe_get_constant ()) - { - if (TREE_CODE (cst) == INTEGER_CST) - { - int val = TREE_INT_CST_LOW (cst); - if (val == -1) - { - if (op == NE_EXPR) - make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs); - - else if (op == EQ_EXPR) - make_invalid_transitions_on_condition (sm_ctxt, node, stmt, - lhs); - } - } - } - - if (rhs->all_zeroes_p ()) - { - if (op == GE_EXPR) - make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs); - else if (op == LT_EXPR) - make_invalid_transitions_on_condition (sm_ctxt, node, stmt, lhs); - } -} - -void -fd_state_machine::make_valid_transitions_on_condition (sm_context *sm_ctxt, - const supernode *node, - const gimple *stmt, - const svalue *lhs) const -{ - sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write, - m_valid_read_write); - sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, - m_valid_read_only); - sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, - m_valid_write_only); -} - -void -fd_state_machine::make_invalid_transitions_on_condition ( - sm_context *sm_ctxt, const supernode *node, const gimple *stmt, - const svalue *lhs) const -{ - sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write, m_invalid); - sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, m_invalid); - sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, m_invalid); -} - -bool -fd_state_machine::can_purge_p (state_t s) const -{ - if (is_unchecked_fd_p (s) || is_valid_fd_p (s)) - return false; - else - return true; -} - -pending_diagnostic * -fd_state_machine::on_leak (tree var) const -{ - return new fd_leak (*this, var); -} -} // namespace - -state_machine * -make_fd_state_machine (logger *logger) -{ - return new fd_state_machine (logger); -} -} // namespace ana - -#endif // ENABLE_ANALYZER \ No newline at end of file +/* A state machine for detecting misuses of POSIX file descriptor APIs. + Copyright (C) 2019-2022 Free Software Foundation, Inc. + Contributed by Immad Mir . + +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 "config.h" +#include "system.h" +#include "coretypes.h" +#include "tree.h" +#include "function.h" +#include "basic-block.h" +#include "gimple.h" +#include "options.h" +#include "diagnostic-path.h" +#include "diagnostic-metadata.h" +#include "function.h" +#include "json.h" +#include "analyzer/analyzer.h" +#include "diagnostic-event-id.h" +#include "analyzer/analyzer-logging.h" +#include "analyzer/sm.h" +#include "analyzer/pending-diagnostic.h" +#include "analyzer/function-set.h" +#include "analyzer/analyzer-selftests.h" +#include "tristate.h" +#include "selftest.h" +#include "stringpool.h" +#include "attribs.h" +#include "analyzer/call-string.h" +#include "analyzer/program-point.h" +#include "analyzer/store.h" +#include "analyzer/region-model.h" +#include "bitmap.h" + +#if ENABLE_ANALYZER + +namespace ana { + +namespace { + +/* An enum for distinguishing between three different access modes. */ + +enum access_mode +{ + READ_WRITE, + READ_ONLY, + WRITE_ONLY +}; + +enum access_directions +{ + DIRS_READ_WRITE, + DIRS_READ, + DIRS_WRITE +}; + +class fd_state_machine : public state_machine +{ +public: + fd_state_machine (logger *logger); + + bool + inherited_state_p () const final override + { + return false; + } + + state_machine::state_t + get_default_state (const svalue *sval) const final override + { + if (tree cst = sval->maybe_get_constant ()) + { + if (TREE_CODE (cst) == INTEGER_CST) + { + int val = TREE_INT_CST_LOW (cst); + if (val >= 0) + return m_constant_fd; + else + return m_invalid; + } + } + return m_start; + } + + bool on_stmt (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt) const final override; + + void on_condition (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const svalue *lhs, const tree_code op, + const svalue *rhs) const final override; + + bool can_purge_p (state_t s) const final override; + pending_diagnostic *on_leak (tree var) const final override; + + bool is_unchecked_fd_p (state_t s) const; + bool is_valid_fd_p (state_t s) const; + bool is_closed_fd_p (state_t s) const; + bool is_constant_fd_p (state_t s) const; + bool is_readonly_fd_p (state_t s) const; + bool is_writeonly_fd_p (state_t s) const; + enum access_mode get_access_mode_from_flag (int flag) const; + + /* State for a constant file descriptor (>= 0) */ + state_t m_constant_fd; + + /* States representing a file descriptor that hasn't yet been + checked for validity after opening, for three different + access modes. */ + state_t m_unchecked_read_write; + + state_t m_unchecked_read_only; + + state_t m_unchecked_write_only; + + /* States for representing a file descriptor that is known to be valid (>= + 0), for three different access modes. */ + state_t m_valid_read_write; + + state_t m_valid_read_only; + + state_t m_valid_write_only; + + /* State for a file descriptor that is known to be invalid (< 0). */ + state_t m_invalid; + + /* State for a file descriptor that has been closed. */ + state_t m_closed; + + /* State for a file descriptor that we do not want to track anymore . */ + state_t m_stop; + +private: + void on_open (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, + const gcall *call) const; + void on_close (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, + const gcall *call) const; + void on_read (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, + const gcall *call, const tree callee_fndecl) const; + void on_write (sm_context *sm_ctxt, const supernode *node, const gimple *stmt, + const gcall *call, const tree callee_fndecl) const; + void check_for_open_fd (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const gcall *call, + const tree callee_fndecl, + enum access_directions access_fn) const; + + void make_valid_transitions_on_condition (sm_context *sm_ctxt, + const supernode *node, + const gimple *stmt, + const svalue *lhs) const; + void make_invalid_transitions_on_condition (sm_context *sm_ctxt, + const supernode *node, + const gimple *stmt, + const svalue *lhs) const; + void check_for_fd_attrs (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const gcall *call, + const tree callee_fndecl, const char *attr_name, + access_directions fd_attr_access_dir) const; +}; + +/* Base diagnostic class relative to fd_state_machine. */ +class fd_diagnostic : public pending_diagnostic +{ +public: + fd_diagnostic (const fd_state_machine &sm, tree arg) : m_sm (sm), m_arg (arg) + { + } + + bool + subclass_equal_p (const pending_diagnostic &base_other) const override + { + return same_tree_p (m_arg, ((const fd_diagnostic &)base_other).m_arg); + } + + label_text + describe_state_change (const evdesc::state_change &change) override + { + if (change.m_old_state == m_sm.get_start_state () + && m_sm.is_unchecked_fd_p (change.m_new_state)) + { + if (change.m_new_state == m_sm.m_unchecked_read_write) + return change.formatted_print ("opened here as read-write"); + + if (change.m_new_state == m_sm.m_unchecked_read_only) + return change.formatted_print ("opened here as read-only"); + + if (change.m_new_state == m_sm.m_unchecked_write_only) + return change.formatted_print ("opened here as write-only"); + } + + if (change.m_new_state == m_sm.m_closed) + return change.formatted_print ("closed here"); + + if (m_sm.is_unchecked_fd_p (change.m_old_state) + && m_sm.is_valid_fd_p (change.m_new_state)) + { + if (change.m_expr) + return change.formatted_print ( + "assuming %qE is a valid file descriptor (>= 0)", change.m_expr); + else + return change.formatted_print ("assuming a valid file descriptor"); + } + + if (m_sm.is_unchecked_fd_p (change.m_old_state) + && change.m_new_state == m_sm.m_invalid) + { + if (change.m_expr) + return change.formatted_print ( + "assuming %qE is an invalid file descriptor (< 0)", + change.m_expr); + else + return change.formatted_print ("assuming an invalid file descriptor"); + } + + return label_text (); + } + +protected: + const fd_state_machine &m_sm; + tree m_arg; +}; + +class fd_param_diagnostic : public fd_diagnostic +{ +public: + fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl, + const char *attr_name, int arg_idx) + : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl), + m_attr_name (attr_name), m_arg_idx (arg_idx) + { + } + + fd_param_diagnostic (const fd_state_machine &sm, tree arg, tree callee_fndecl) + : fd_diagnostic (sm, arg), m_callee_fndecl (callee_fndecl), + m_attr_name (NULL), m_arg_idx (-1) + { + } + + bool + subclass_equal_p (const pending_diagnostic &base_other) const override + { + const fd_param_diagnostic &sub_other + = (const fd_param_diagnostic &)base_other; + return (same_tree_p (m_arg, sub_other.m_arg) + && same_tree_p (m_callee_fndecl, sub_other.m_callee_fndecl) + && m_arg_idx == sub_other.m_arg_idx + && ((m_attr_name) + ? (strcmp (m_attr_name, sub_other.m_attr_name) == 0) + : true)); + } + + void + inform_filedescriptor_attribute (access_directions fd_dir) + { + + if (m_attr_name) + switch (fd_dir) + { + case DIRS_READ_WRITE: + inform (DECL_SOURCE_LOCATION (m_callee_fndecl), + "argument %d of %qD must be an open file descriptor, due to " + "%<__attribute__((%s(%d)))%>", + m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1); + break; + case DIRS_WRITE: + inform (DECL_SOURCE_LOCATION (m_callee_fndecl), + "argument %d of %qD must be a readable file descriptor, due " + "to %<__attribute__((%s(%d)))%>", + m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1); + break; + case DIRS_READ: + inform (DECL_SOURCE_LOCATION (m_callee_fndecl), + "argument %d of %qD must be a writable file descriptor, due " + "to %<__attribute__((%s(%d)))%>", + m_arg_idx + 1, m_callee_fndecl, m_attr_name, m_arg_idx + 1); + break; + } + } + +protected: + tree m_callee_fndecl; + const char *m_attr_name; + /* ARG_IDX is 0-based. */ + int m_arg_idx; +}; + +class fd_leak : public fd_diagnostic +{ +public: + fd_leak (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg) {} + + const char * + get_kind () const final override + { + return "fd_leak"; + } + + int + get_controlling_option () const final override + { + return OPT_Wanalyzer_fd_leak; + } + + bool + emit (rich_location *rich_loc) final override + { + /*CWE-775: Missing Release of File Descriptor or Handle after Effective + Lifetime + */ + diagnostic_metadata m; + m.add_cwe (775); + if (m_arg) + return warning_meta (rich_loc, m, get_controlling_option (), + "leak of file descriptor %qE", m_arg); + else + return warning_meta (rich_loc, m, get_controlling_option (), + "leak of file descriptor"); + } + + label_text + describe_state_change (const evdesc::state_change &change) final override + { + if (m_sm.is_unchecked_fd_p (change.m_new_state)) + { + m_open_event = change.m_event_id; + return label_text::borrow ("opened here"); + } + + return fd_diagnostic::describe_state_change (change); + } + + label_text + describe_final_event (const evdesc::final_event &ev) final override + { + if (m_open_event.known_p ()) + { + if (ev.m_expr) + return ev.formatted_print ("%qE leaks here; was opened at %@", + ev.m_expr, &m_open_event); + else + return ev.formatted_print ("leaks here; was opened at %@", + &m_open_event); + } + else + { + if (ev.m_expr) + return ev.formatted_print ("%qE leaks here", ev.m_expr); + else + return ev.formatted_print ("leaks here"); + } + } + +private: + diagnostic_event_id_t m_open_event; +}; + +class fd_access_mode_mismatch : public fd_param_diagnostic +{ +public: + fd_access_mode_mismatch (const fd_state_machine &sm, tree arg, + enum access_directions fd_dir, + const tree callee_fndecl, const char *attr_name, + int arg_idx) + : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx), + m_fd_dir (fd_dir) + + { + } + + fd_access_mode_mismatch (const fd_state_machine &sm, tree arg, + enum access_directions fd_dir, + const tree callee_fndecl) + : fd_param_diagnostic (sm, arg, callee_fndecl), m_fd_dir (fd_dir) + { + } + + const char * + get_kind () const final override + { + return "fd_access_mode_mismatch"; + } + + int + get_controlling_option () const final override + { + return OPT_Wanalyzer_fd_access_mode_mismatch; + } + + bool + emit (rich_location *rich_loc) final override + { + bool warned; + switch (m_fd_dir) + { + case DIRS_READ: + warned = warning_at (rich_loc, get_controlling_option (), + "%qE on read-only file descriptor %qE", + m_callee_fndecl, m_arg); + break; + case DIRS_WRITE: + warned = warning_at (rich_loc, get_controlling_option (), + "%qE on write-only file descriptor %qE", + m_callee_fndecl, m_arg); + break; + default: + gcc_unreachable (); + } + if (warned) + inform_filedescriptor_attribute (m_fd_dir); + return warned; + } + + label_text + describe_final_event (const evdesc::final_event &ev) final override + { + switch (m_fd_dir) + { + case DIRS_READ: + return ev.formatted_print ("%qE on read-only file descriptor %qE", + m_callee_fndecl, m_arg); + case DIRS_WRITE: + return ev.formatted_print ("%qE on write-only file descriptor %qE", + m_callee_fndecl, m_arg); + default: + gcc_unreachable (); + } + } + +private: + enum access_directions m_fd_dir; +}; + +class fd_double_close : public fd_diagnostic +{ +public: + fd_double_close (const fd_state_machine &sm, tree arg) : fd_diagnostic (sm, arg) + { + } + + const char * + get_kind () const final override + { + return "fd_double_close"; + } + + int + get_controlling_option () const final override + { + return OPT_Wanalyzer_fd_double_close; + } + bool + emit (rich_location *rich_loc) final override + { + diagnostic_metadata m; + // CWE-1341: Multiple Releases of Same Resource or Handle + m.add_cwe (1341); + return warning_meta (rich_loc, m, get_controlling_option (), + "double % of file descriptor %qE", m_arg); + } + + label_text + describe_state_change (const evdesc::state_change &change) override + { + if (m_sm.is_unchecked_fd_p (change.m_new_state)) + return label_text::borrow ("opened here"); + + if (change.m_new_state == m_sm.m_closed) + { + m_first_close_event = change.m_event_id; + return change.formatted_print ("first %qs here", "close"); + } + return fd_diagnostic::describe_state_change (change); + } + + label_text + describe_final_event (const evdesc::final_event &ev) final override + { + if (m_first_close_event.known_p ()) + return ev.formatted_print ("second %qs here; first %qs was at %@", + "close", "close", &m_first_close_event); + return ev.formatted_print ("second %qs here", "close"); + } + +private: + diagnostic_event_id_t m_first_close_event; +}; + +class fd_use_after_close : public fd_param_diagnostic +{ +public: + fd_use_after_close (const fd_state_machine &sm, tree arg, + const tree callee_fndecl, const char *attr_name, + int arg_idx) + : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx) + { + } + + fd_use_after_close (const fd_state_machine &sm, tree arg, + const tree callee_fndecl) + : fd_param_diagnostic (sm, arg, callee_fndecl) + { + } + + const char * + get_kind () const final override + { + return "fd_use_after_close"; + } + + int + get_controlling_option () const final override + { + return OPT_Wanalyzer_fd_use_after_close; + } + + bool + emit (rich_location *rich_loc) final override + { + bool warned; + warned = warning_at (rich_loc, get_controlling_option (), + "%qE on closed file descriptor %qE", m_callee_fndecl, + m_arg); + if (warned) + inform_filedescriptor_attribute (DIRS_READ_WRITE); + return warned; + } + + label_text + describe_state_change (const evdesc::state_change &change) override + { + if (m_sm.is_unchecked_fd_p (change.m_new_state)) + return label_text::borrow ("opened here"); + + if (change.m_new_state == m_sm.m_closed) + { + m_first_close_event = change.m_event_id; + return change.formatted_print ("closed here"); + } + + return fd_diagnostic::describe_state_change (change); + } + + label_text + describe_final_event (const evdesc::final_event &ev) final override + { + if (m_first_close_event.known_p ()) + return ev.formatted_print ( + "%qE on closed file descriptor %qE; %qs was at %@", m_callee_fndecl, + m_arg, "close", &m_first_close_event); + else + return ev.formatted_print ("%qE on closed file descriptor %qE", + m_callee_fndecl, m_arg); + } + +private: + diagnostic_event_id_t m_first_close_event; +}; + +class fd_use_without_check : public fd_param_diagnostic +{ +public: + fd_use_without_check (const fd_state_machine &sm, tree arg, + const tree callee_fndecl, const char *attr_name, + int arg_idx) + : fd_param_diagnostic (sm, arg, callee_fndecl, attr_name, arg_idx) + { + } + + fd_use_without_check (const fd_state_machine &sm, tree arg, + const tree callee_fndecl) + : fd_param_diagnostic (sm, arg, callee_fndecl) + { + } + + const char * + get_kind () const final override + { + return "fd_use_without_check"; + } + + int + get_controlling_option () const final override + { + return OPT_Wanalyzer_fd_use_without_check; + } + + bool + emit (rich_location *rich_loc) final override + { + bool warned; + warned = warning_at (rich_loc, get_controlling_option (), + "%qE on possibly invalid file descriptor %qE", + m_callee_fndecl, m_arg); + if (warned) + inform_filedescriptor_attribute (DIRS_READ_WRITE); + return warned; + } + + label_text + describe_state_change (const evdesc::state_change &change) override + { + if (m_sm.is_unchecked_fd_p (change.m_new_state)) + { + m_first_open_event = change.m_event_id; + return label_text::borrow ("opened here"); + } + + return fd_diagnostic::describe_state_change (change); + } + + label_text + describe_final_event (const evdesc::final_event &ev) final override + { + if (m_first_open_event.known_p ()) + return ev.formatted_print ( + "%qE could be invalid: unchecked value from %@", m_arg, + &m_first_open_event); + else + return ev.formatted_print ("%qE could be invalid", m_arg); + } + +private: + diagnostic_event_id_t m_first_open_event; +}; + +fd_state_machine::fd_state_machine (logger *logger) + : state_machine ("file-descriptor", logger), + m_constant_fd (add_state ("fd-constant")), + m_unchecked_read_write (add_state ("fd-unchecked-read-write")), + m_unchecked_read_only (add_state ("fd-unchecked-read-only")), + m_unchecked_write_only (add_state ("fd-unchecked-write-only")), + m_valid_read_write (add_state ("fd-valid-read-write")), + m_valid_read_only (add_state ("fd-valid-read-only")), + m_valid_write_only (add_state ("fd-valid-write-only")), + m_invalid (add_state ("fd-invalid")), + m_closed (add_state ("fd-closed")), + m_stop (add_state ("fd-stop")) +{ +} + +bool +fd_state_machine::is_unchecked_fd_p (state_t s) const +{ + return (s == m_unchecked_read_write + || s == m_unchecked_read_only + || s == m_unchecked_write_only); +} + +bool +fd_state_machine::is_valid_fd_p (state_t s) const +{ + return (s == m_valid_read_write + || s == m_valid_read_only + || s == m_valid_write_only); +} + +enum access_mode +fd_state_machine::get_access_mode_from_flag (int flag) const +{ + /* FIXME: this code assumes the access modes on the host and + target are the same, which in practice might not be the case. */ + + if ((flag & O_ACCMODE) == O_RDONLY) + { + return READ_ONLY; + } + else if ((flag & O_ACCMODE) == O_WRONLY) + { + return WRITE_ONLY; + } + return READ_WRITE; +} + +bool +fd_state_machine::is_readonly_fd_p (state_t state) const +{ + return (state == m_unchecked_read_only || state == m_valid_read_only); +} + +bool +fd_state_machine::is_writeonly_fd_p (state_t state) const +{ + return (state == m_unchecked_write_only || state == m_valid_write_only); +} + +bool +fd_state_machine::is_closed_fd_p (state_t state) const +{ + return (state == m_closed); +} + +bool +fd_state_machine::is_constant_fd_p (state_t state) const +{ + return (state == m_constant_fd); +} + +bool +fd_state_machine::on_stmt (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt) const +{ + if (const gcall *call = dyn_cast (stmt)) + if (tree callee_fndecl = sm_ctxt->get_fndecl_for_call (call)) + { + if (is_named_call_p (callee_fndecl, "open", call, 2)) + { + on_open (sm_ctxt, node, stmt, call); + return true; + } // "open" + + if (is_named_call_p (callee_fndecl, "close", call, 1)) + { + on_close (sm_ctxt, node, stmt, call); + return true; + } // "close" + + if (is_named_call_p (callee_fndecl, "write", call, 3)) + { + on_write (sm_ctxt, node, stmt, call, callee_fndecl); + return true; + } // "write" + + if (is_named_call_p (callee_fndecl, "read", call, 3)) + { + on_read (sm_ctxt, node, stmt, call, callee_fndecl); + return true; + } // "read" + + + { + // Handle __attribute__((fd_arg)) + + check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, + "fd_arg", DIRS_READ_WRITE); + + // Handle __attribute__((fd_arg_read)) + + check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, + "fd_arg_read", DIRS_READ); + + // Handle __attribute__((fd_arg_write)) + + check_for_fd_attrs (sm_ctxt, node, stmt, call, callee_fndecl, + "fd_arg_write", DIRS_WRITE); + } + } + + return false; +} + +void +fd_state_machine::check_for_fd_attrs ( + sm_context *sm_ctxt, const supernode *node, const gimple *stmt, + const gcall *call, const tree callee_fndecl, const char *attr_name, + access_directions fd_attr_access_dir) const +{ + + tree attrs = TYPE_ATTRIBUTES (TREE_TYPE (callee_fndecl)); + attrs = lookup_attribute (attr_name, attrs); + if (!attrs) + return; + + if (!TREE_VALUE (attrs)) + return; + + auto_bitmap argmap; + + for (tree idx = TREE_VALUE (attrs); idx; idx = TREE_CHAIN (idx)) + { + unsigned int val = TREE_INT_CST_LOW (TREE_VALUE (idx)) - 1; + bitmap_set_bit (argmap, val); + } + if (bitmap_empty_p (argmap)) + return; + + for (unsigned arg_idx = 0; arg_idx < gimple_call_num_args (call); arg_idx++) + { + tree arg = gimple_call_arg (call, arg_idx); + tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); + state_t state = sm_ctxt->get_state (stmt, arg); + bool bit_set = bitmap_bit_p (argmap, arg_idx); + if (TREE_CODE (TREE_TYPE (arg)) != INTEGER_TYPE) + continue; + if (bit_set) // Check if arg_idx is marked by any of the file descriptor + // attributes + { + + if (is_closed_fd_p (state)) + { + + sm_ctxt->warn (node, stmt, arg, + new fd_use_after_close (*this, diag_arg, + callee_fndecl, attr_name, + arg_idx)); + continue; + } + + if (!(is_valid_fd_p (state) || (state == m_stop))) + { + if (!is_constant_fd_p (state)) + sm_ctxt->warn (node, stmt, arg, + new fd_use_without_check (*this, diag_arg, + callee_fndecl, attr_name, + arg_idx)); + } + + switch (fd_attr_access_dir) + { + case DIRS_READ_WRITE: + break; + case DIRS_READ: + + if (is_writeonly_fd_p (state)) + { + sm_ctxt->warn ( + node, stmt, arg, + new fd_access_mode_mismatch (*this, diag_arg, DIRS_WRITE, + callee_fndecl, attr_name, arg_idx)); + } + + break; + case DIRS_WRITE: + + if (is_readonly_fd_p (state)) + { + sm_ctxt->warn ( + node, stmt, arg, + new fd_access_mode_mismatch (*this, diag_arg, DIRS_READ, + callee_fndecl, attr_name, arg_idx)); + } + + break; + } + } + } +} + + +void +fd_state_machine::on_open (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const gcall *call) const +{ + tree lhs = gimple_call_lhs (call); + if (lhs) + { + tree arg = gimple_call_arg (call, 1); + if (TREE_CODE (arg) == INTEGER_CST) + { + int flag = TREE_INT_CST_LOW (arg); + enum access_mode mode = get_access_mode_from_flag (flag); + + switch (mode) + { + case READ_ONLY: + sm_ctxt->on_transition (node, stmt, lhs, m_start, + m_unchecked_read_only); + break; + case WRITE_ONLY: + sm_ctxt->on_transition (node, stmt, lhs, m_start, + m_unchecked_write_only); + break; + default: + sm_ctxt->on_transition (node, stmt, lhs, m_start, + m_unchecked_read_write); + } + } + } + else + { + sm_ctxt->warn (node, stmt, NULL_TREE, new fd_leak (*this, NULL_TREE)); + } +} + +void +fd_state_machine::on_close (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const gcall *call) const +{ + tree arg = gimple_call_arg (call, 0); + state_t state = sm_ctxt->get_state (stmt, arg); + tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); + + sm_ctxt->on_transition (node, stmt, arg, m_start, m_closed); + sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_write, m_closed); + sm_ctxt->on_transition (node, stmt, arg, m_unchecked_read_only, m_closed); + sm_ctxt->on_transition (node, stmt, arg, m_unchecked_write_only, m_closed); + sm_ctxt->on_transition (node, stmt, arg, m_valid_read_write, m_closed); + sm_ctxt->on_transition (node, stmt, arg, m_valid_read_only, m_closed); + sm_ctxt->on_transition (node, stmt, arg, m_valid_write_only, m_closed); + sm_ctxt->on_transition (node, stmt, arg, m_constant_fd, m_closed); + + if (is_closed_fd_p (state)) + { + sm_ctxt->warn (node, stmt, arg, new fd_double_close (*this, diag_arg)); + sm_ctxt->set_next_state (stmt, arg, m_stop); + } +} +void +fd_state_machine::on_read (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const gcall *call, + const tree callee_fndecl) const +{ + check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_READ); +} +void +fd_state_machine::on_write (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const gcall *call, + const tree callee_fndecl) const +{ + check_for_open_fd (sm_ctxt, node, stmt, call, callee_fndecl, DIRS_WRITE); +} + +void +fd_state_machine::check_for_open_fd ( + sm_context *sm_ctxt, const supernode *node, const gimple *stmt, + const gcall *call, const tree callee_fndecl, + enum access_directions callee_fndecl_dir) const +{ + tree arg = gimple_call_arg (call, 0); + tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); + state_t state = sm_ctxt->get_state (stmt, arg); + + if (is_closed_fd_p (state)) + { + sm_ctxt->warn (node, stmt, arg, + new fd_use_after_close (*this, diag_arg, callee_fndecl)); + } + + else + { + if (!(is_valid_fd_p (state) || (state == m_stop))) + { + if (!is_constant_fd_p (state)) + sm_ctxt->warn ( + node, stmt, arg, + new fd_use_without_check (*this, diag_arg, callee_fndecl)); + } + switch (callee_fndecl_dir) + { + case DIRS_READ: + if (is_writeonly_fd_p (state)) + { + tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); + sm_ctxt->warn (node, stmt, arg, + new fd_access_mode_mismatch ( + *this, diag_arg, DIRS_WRITE, callee_fndecl)); + } + + break; + case DIRS_WRITE: + + if (is_readonly_fd_p (state)) + { + tree diag_arg = sm_ctxt->get_diagnostic_tree (arg); + sm_ctxt->warn (node, stmt, arg, + new fd_access_mode_mismatch ( + *this, diag_arg, DIRS_READ, callee_fndecl)); + } + break; + default: + gcc_unreachable (); + } + } +} + +void +fd_state_machine::on_condition (sm_context *sm_ctxt, const supernode *node, + const gimple *stmt, const svalue *lhs, + enum tree_code op, const svalue *rhs) const +{ + if (tree cst = rhs->maybe_get_constant ()) + { + if (TREE_CODE (cst) == INTEGER_CST) + { + int val = TREE_INT_CST_LOW (cst); + if (val == -1) + { + if (op == NE_EXPR) + make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs); + + else if (op == EQ_EXPR) + make_invalid_transitions_on_condition (sm_ctxt, node, stmt, + lhs); + } + } + } + + if (rhs->all_zeroes_p ()) + { + if (op == GE_EXPR) + make_valid_transitions_on_condition (sm_ctxt, node, stmt, lhs); + else if (op == LT_EXPR) + make_invalid_transitions_on_condition (sm_ctxt, node, stmt, lhs); + } +} + +void +fd_state_machine::make_valid_transitions_on_condition (sm_context *sm_ctxt, + const supernode *node, + const gimple *stmt, + const svalue *lhs) const +{ + sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write, + m_valid_read_write); + sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, + m_valid_read_only); + sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, + m_valid_write_only); +} + +void +fd_state_machine::make_invalid_transitions_on_condition ( + sm_context *sm_ctxt, const supernode *node, const gimple *stmt, + const svalue *lhs) const +{ + sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_write, m_invalid); + sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_read_only, m_invalid); + sm_ctxt->on_transition (node, stmt, lhs, m_unchecked_write_only, m_invalid); +} + +bool +fd_state_machine::can_purge_p (state_t s) const +{ + if (is_unchecked_fd_p (s) || is_valid_fd_p (s)) + return false; + else + return true; +} + +pending_diagnostic * +fd_state_machine::on_leak (tree var) const +{ + return new fd_leak (*this, var); +} +} // namespace + +state_machine * +make_fd_state_machine (logger *logger) +{ + return new fd_state_machine (logger); +} +} // namespace ana + +#endif // ENABLE_ANALYZER -- cgit v1.1 From 838da6cf8d2c8574f5b626823b742032e656619d Mon Sep 17 00:00:00 2001 From: Martin Liska Date: Mon, 25 Jul 2022 08:38:37 +0200 Subject: analyzer: convert tests with dos2unix gcc/testsuite/ChangeLog: * gcc.dg/analyzer/fd-2.c: Convert Windows endlines to Unix style. * gcc.dg/analyzer/fd-3.c: Likewise. * gcc.dg/analyzer/fd-4.c: Likewise. * gcc.dg/analyzer/fd-5.c: Likewise. * c-c++-common/attr-fd.c: Likewise. --- gcc/testsuite/c-c++-common/attr-fd.c | 36 ++++---- gcc/testsuite/gcc.dg/analyzer/fd-2.c | 96 ++++++++++---------- gcc/testsuite/gcc.dg/analyzer/fd-3.c | 168 +++++++++++++++++------------------ gcc/testsuite/gcc.dg/analyzer/fd-4.c | 142 ++++++++++++++--------------- gcc/testsuite/gcc.dg/analyzer/fd-5.c | 104 +++++++++++----------- 5 files changed, 273 insertions(+), 273 deletions(-) (limited to 'gcc') diff --git a/gcc/testsuite/c-c++-common/attr-fd.c b/gcc/testsuite/c-c++-common/attr-fd.c index e4bb4ed..9f12093 100644 --- a/gcc/testsuite/c-c++-common/attr-fd.c +++ b/gcc/testsuite/c-c++-common/attr-fd.c @@ -1,18 +1,18 @@ - -int not_a_fn __attribute__ ((fd_arg(1))); /* { dg-warning "'fd_arg' attribute only applies to function types" } */ - -void f (char *p) __attribute__ ((fd_arg(1))); /* { dg-warning "'fd_arg' attribute argument value '1' refers to parameter type 'char ?\\\*'" } */ - - -int not_a_fn_b __attribute__ ((fd_arg_read(1))); /* { dg-warning "'fd_arg_read' attribute only applies to function types" } */ - -void g (char *p) __attribute__ ((fd_arg_read(1))); /* { dg-warning "'fd_arg_read' attribute argument value '1' refers to parameter type 'char ?\\\*'" } */ - - -int not_a_fn_c __attribute__ ((fd_arg_write(1))); /* { dg-warning "'fd_arg_write' attribute only applies to function types" } */ - -void f (char *p) __attribute__ ((fd_arg_write(1))); /* { dg-warning "'fd_arg_write' attribute argument value '1' refers to parameter type 'char ?\\\*'" } */ - - -void fn_a (int fd) __attribute__ ((fd_arg(0))); /* { dg-warning "'fd_arg' attribute argument value '0' does not refer to a function parameter" } */ -void fd_a_1 (int fd) __attribute__ ((fd_arg("notint"))); /* { dg-warning "'fd_arg' attribute argument has type ('char\\\[7\\\]'|'const char\\\*')" } */ + +int not_a_fn __attribute__ ((fd_arg(1))); /* { dg-warning "'fd_arg' attribute only applies to function types" } */ + +void f (char *p) __attribute__ ((fd_arg(1))); /* { dg-warning "'fd_arg' attribute argument value '1' refers to parameter type 'char ?\\\*'" } */ + + +int not_a_fn_b __attribute__ ((fd_arg_read(1))); /* { dg-warning "'fd_arg_read' attribute only applies to function types" } */ + +void g (char *p) __attribute__ ((fd_arg_read(1))); /* { dg-warning "'fd_arg_read' attribute argument value '1' refers to parameter type 'char ?\\\*'" } */ + + +int not_a_fn_c __attribute__ ((fd_arg_write(1))); /* { dg-warning "'fd_arg_write' attribute only applies to function types" } */ + +void f (char *p) __attribute__ ((fd_arg_write(1))); /* { dg-warning "'fd_arg_write' attribute argument value '1' refers to parameter type 'char ?\\\*'" } */ + + +void fn_a (int fd) __attribute__ ((fd_arg(0))); /* { dg-warning "'fd_arg' attribute argument value '0' does not refer to a function parameter" } */ +void fd_a_1 (int fd) __attribute__ ((fd_arg("notint"))); /* { dg-warning "'fd_arg' attribute argument has type ('char\\\[7\\\]'|'const char\\\*')" } */ diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-2.c b/gcc/testsuite/gcc.dg/analyzer/fd-2.c index 96ccf2f..d794b46 100644 --- a/gcc/testsuite/gcc.dg/analyzer/fd-2.c +++ b/gcc/testsuite/gcc.dg/analyzer/fd-2.c @@ -1,49 +1,49 @@ -int open(const char *, int mode); -void close(int fd); -#define O_RDONLY 0 -#define O_WRONLY 1 -#define O_RDWR 2 -#define STDIN 0 - -void -test_1 (const char *path) -{ - int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */ - close (fd); /* { dg-message "\\(2\\) first 'close' here" "event1" } */ - close (fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" "warning" } */ - /* { dg-message "\\(3\\) second 'close' here; first 'close' was at \\(2\\)" "event2" { target *-*-* } .-1 } */ -} - -void -test_2 (const char *path) -{ - int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */ - if (fd < 0) /* { dg-message "\\(2\\) assuming 'fd' is a valid file descriptor \\(>= 0\\)" "event1" } */ - /* { dg-message "\\(3\\) following 'false' branch \\(when 'fd >= 0'\\)..." "event2" { target *-*-* } .-1 } */ - return; - close (fd); /* { dg-message "\\(4\\) ...to here" "event1" } */ - /* { dg-message "\\(5\\) first 'close' here" "event2" { target *-*-* } .-1 } */ - close (fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" "warning" } */ - /* {dg-message "\\(6\\) second 'close' here; first was at \\(5\\)" "" { target *-*-* } .-1 } */ -} - -void -test_3 () -{ - /* FD 0 is stdin at the entry to "main" and thus read-only, but we have no - guarantees here that it hasn't been closed and then reopened for - writing, so we can't issue a warning */ - - int fd = STDIN; - close(fd); /* { dg-message "\\(1\\) first 'close' here" } */ - close(fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" "warning" } */ - /* { dg-message "\\(2\\) second 'close' here; first 'close' was at \\(1\\)" "event2" { target *-*-* } .-1 } */ -} - -void -test_4 () -{ - int fd = -1; - close(fd); - close(fd); +int open(const char *, int mode); +void close(int fd); +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#define STDIN 0 + +void +test_1 (const char *path) +{ + int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */ + close (fd); /* { dg-message "\\(2\\) first 'close' here" "event1" } */ + close (fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" "warning" } */ + /* { dg-message "\\(3\\) second 'close' here; first 'close' was at \\(2\\)" "event2" { target *-*-* } .-1 } */ +} + +void +test_2 (const char *path) +{ + int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */ + if (fd < 0) /* { dg-message "\\(2\\) assuming 'fd' is a valid file descriptor \\(>= 0\\)" "event1" } */ + /* { dg-message "\\(3\\) following 'false' branch \\(when 'fd >= 0'\\)..." "event2" { target *-*-* } .-1 } */ + return; + close (fd); /* { dg-message "\\(4\\) ...to here" "event1" } */ + /* { dg-message "\\(5\\) first 'close' here" "event2" { target *-*-* } .-1 } */ + close (fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" "warning" } */ + /* {dg-message "\\(6\\) second 'close' here; first was at \\(5\\)" "" { target *-*-* } .-1 } */ +} + +void +test_3 () +{ + /* FD 0 is stdin at the entry to "main" and thus read-only, but we have no + guarantees here that it hasn't been closed and then reopened for + writing, so we can't issue a warning */ + + int fd = STDIN; + close(fd); /* { dg-message "\\(1\\) first 'close' here" } */ + close(fd); /* { dg-warning "double 'close' of file descriptor 'fd' \\\[CWE-1341\\\]" "warning" } */ + /* { dg-message "\\(2\\) second 'close' here; first 'close' was at \\(1\\)" "event2" { target *-*-* } .-1 } */ +} + +void +test_4 () +{ + int fd = -1; + close(fd); + close(fd); } \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-3.c b/gcc/testsuite/gcc.dg/analyzer/fd-3.c index 40fc8af..55e84e3 100644 --- a/gcc/testsuite/gcc.dg/analyzer/fd-3.c +++ b/gcc/testsuite/gcc.dg/analyzer/fd-3.c @@ -1,85 +1,85 @@ -int open(const char *, int mode); -void close(int fd); -int write (int fd, void *buf, int nbytes); -int read (int fd, void *buf, int nbytes); -int some_condition(); - -#define O_RDONLY 0 -#define O_WRONLY 1 -#define O_RDWR 2 -#define STDIN 0 -#define O_NOATIME 262144 - -void -test_1 (const char *path, void *buf) -{ - int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */ - write (fd, buf, 1); /* { dg-message "\\(2\\) 'fd' could be invalid: unchecked value from \\(1\\)" } */ - /* { dg-warning "'write' on possibly invalid file descriptor 'fd'" "warning" { target *-*-* } .-1 } */ - close(fd); -} - -void -test_2 (const char *path, void *buf) -{ - int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */ - read (fd, buf, 1); /* { dg-message "\\(2\\) 'fd' could be invalid: unchecked value from \\(1\\)" } */ - /* { dg-warning "'read' on possibly invalid file descriptor 'fd'" "warning" { target *-*-* } .-1 } */ - close (fd); -} - -void -test_3 (void *buf) -{ - int fd = -1; - read (fd, buf, 1); /* { dg-warning "'read' on possibly invalid file descriptor 'fd'" } */ - /* { dg-message "\\(1\\) 'fd' could be invalid" "" { target *-*-* } .-1 } */ -} - -void -test_4 (void *buf) -{ - int fd = STDIN; - read (fd, buf, 1); - close(fd); -} - -void -test_5 (char *path, void *buf) -{ - int flags = O_RDONLY; - if (some_condition()) - flags |= O_NOATIME; - int fd = open (path, flags); - read (fd, buf, 1); /* { dg-warning "'read' on possibly invalid file descriptor 'fd'" } */ - /* { dg-message "\\(1\\) 'fd' could be invalid" "" { target *-*-* } .-1 } */ - close (fd); -} - - -void -test_6 (char *path, void *buf) -{ - int fd = open (path, O_RDONLY); - if (fd != -1) - { - read (fd, buf, 1); - } - close (fd); -} - - -void -test_7 (char *path, void *buf) -{ - int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */ - if (fd != -1) /* { dg-message "\\(2\\) assuming 'fd' is an invalid file descriptor \\(< 0\\)" } */ - { - read (fd, buf, 1); - } else - { - write (fd, buf, 1); /* { dg-warning "'write' on possibly invalid file descriptor 'fd'" } */ - - } - close(fd); +int open(const char *, int mode); +void close(int fd); +int write (int fd, void *buf, int nbytes); +int read (int fd, void *buf, int nbytes); +int some_condition(); + +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 +#define STDIN 0 +#define O_NOATIME 262144 + +void +test_1 (const char *path, void *buf) +{ + int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */ + write (fd, buf, 1); /* { dg-message "\\(2\\) 'fd' could be invalid: unchecked value from \\(1\\)" } */ + /* { dg-warning "'write' on possibly invalid file descriptor 'fd'" "warning" { target *-*-* } .-1 } */ + close(fd); +} + +void +test_2 (const char *path, void *buf) +{ + int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */ + read (fd, buf, 1); /* { dg-message "\\(2\\) 'fd' could be invalid: unchecked value from \\(1\\)" } */ + /* { dg-warning "'read' on possibly invalid file descriptor 'fd'" "warning" { target *-*-* } .-1 } */ + close (fd); +} + +void +test_3 (void *buf) +{ + int fd = -1; + read (fd, buf, 1); /* { dg-warning "'read' on possibly invalid file descriptor 'fd'" } */ + /* { dg-message "\\(1\\) 'fd' could be invalid" "" { target *-*-* } .-1 } */ +} + +void +test_4 (void *buf) +{ + int fd = STDIN; + read (fd, buf, 1); + close(fd); +} + +void +test_5 (char *path, void *buf) +{ + int flags = O_RDONLY; + if (some_condition()) + flags |= O_NOATIME; + int fd = open (path, flags); + read (fd, buf, 1); /* { dg-warning "'read' on possibly invalid file descriptor 'fd'" } */ + /* { dg-message "\\(1\\) 'fd' could be invalid" "" { target *-*-* } .-1 } */ + close (fd); +} + + +void +test_6 (char *path, void *buf) +{ + int fd = open (path, O_RDONLY); + if (fd != -1) + { + read (fd, buf, 1); + } + close (fd); +} + + +void +test_7 (char *path, void *buf) +{ + int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */ + if (fd != -1) /* { dg-message "\\(2\\) assuming 'fd' is an invalid file descriptor \\(< 0\\)" } */ + { + read (fd, buf, 1); + } else + { + write (fd, buf, 1); /* { dg-warning "'write' on possibly invalid file descriptor 'fd'" } */ + + } + close(fd); } \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-4.c b/gcc/testsuite/gcc.dg/analyzer/fd-4.c index 4126346..ecd787c 100644 --- a/gcc/testsuite/gcc.dg/analyzer/fd-4.c +++ b/gcc/testsuite/gcc.dg/analyzer/fd-4.c @@ -1,72 +1,72 @@ -#include - -int open(const char *, int mode); -void close(int fd); -int write (int fd, void *buf, int nbytes); -int read (int fd, void *buf, int nbytes); - -#define O_RDONLY 0 -#define O_WRONLY 1 -#define O_RDWR 2 - - -void -test_1 (const char *path, void *buf) -{ - int fd = open (path, O_RDONLY); /* { dg-message "opened here as read-only" } */ - if (fd >= 0) /* { dg-message "assuming 'fd' is a valid file descriptor \\(>= 0\\)" "event1" } */ - /* { dg-message "following 'true' branch \\(when 'fd >= 0'\\)..." "event2" { target *-*-* } .-1 } */ - { - write (fd, buf, 1); /* { dg-warning "'write' on read-only file descriptor 'fd'" "warning" } */ - /* { dg-message "\\(4\\) ...to here" "event1" { target *-*-* } .-1 } */ - /* { dg-message "\\(5\\) 'write' on read-only file descriptor 'fd'" "event2" { target *-*-* } .-2 } */ - close (fd); - } -} - -void -test_2 (const char *path, void *buf) -{ - int fd = open (path, O_WRONLY); /* { dg-message "opened here as write-only" } */ - if (fd >= 0) /* { dg-message "assuming 'fd' is a valid file descriptor \\(>= 0\\)" "event1" } */ - /* { dg-message "following 'true' branch \\(when 'fd >= 0'\\)..." "event2" { target *-*-* } .-1 } */ - { - read (fd, buf, 1); /* { dg-warning "'read' on write-only file descriptor 'fd'" "warning" } */ - /* { dg-message "\\(4\\) ...to here" "event1" { target *-*-* } .-1 } */ - /* { dg-message "\\(5\\) 'read' on write-only file descriptor 'fd'" "event2" { target *-*-* } .-2 } */ - close (fd); - } -} - - -void -test_3 (const char *path, void *buf) -{ - int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */ - if (fd >= 0) - { - close(fd); /* {dg-message "\\(2\\) closed here"} */ - read(fd, buf, 1); /* { dg-warning "'read' on closed file descriptor 'fd'" } */ - /* {dg-message "\\(3\\) 'read' on closed file descriptor 'fd'; 'close' was at \\(2\\)" "" {target *-*-*} .-1 } */ - } -} - -void -test_4 (const char *path, void *buf) -{ - int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */ - if (fd >= 0) - { - close(fd); /* {dg-message "\\(2\\) closed here"} */ - write(fd, buf, 1); /* { dg-warning "'write' on closed file descriptor 'fd'" } */ - /* {dg-message "\\(3\\) 'write' on closed file descriptor 'fd'; 'close' was at \\(2\\)" "" {target *-*-*} .-1 } */ - } -} - -void -test_5 (const char *path) -{ - int fd = open (path, O_RDWR); - close(fd); - printf("%d", fd); /* { dg-bogus "'printf' on a closed file descriptor 'fd'" } */ +#include + +int open(const char *, int mode); +void close(int fd); +int write (int fd, void *buf, int nbytes); +int read (int fd, void *buf, int nbytes); + +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 + + +void +test_1 (const char *path, void *buf) +{ + int fd = open (path, O_RDONLY); /* { dg-message "opened here as read-only" } */ + if (fd >= 0) /* { dg-message "assuming 'fd' is a valid file descriptor \\(>= 0\\)" "event1" } */ + /* { dg-message "following 'true' branch \\(when 'fd >= 0'\\)..." "event2" { target *-*-* } .-1 } */ + { + write (fd, buf, 1); /* { dg-warning "'write' on read-only file descriptor 'fd'" "warning" } */ + /* { dg-message "\\(4\\) ...to here" "event1" { target *-*-* } .-1 } */ + /* { dg-message "\\(5\\) 'write' on read-only file descriptor 'fd'" "event2" { target *-*-* } .-2 } */ + close (fd); + } +} + +void +test_2 (const char *path, void *buf) +{ + int fd = open (path, O_WRONLY); /* { dg-message "opened here as write-only" } */ + if (fd >= 0) /* { dg-message "assuming 'fd' is a valid file descriptor \\(>= 0\\)" "event1" } */ + /* { dg-message "following 'true' branch \\(when 'fd >= 0'\\)..." "event2" { target *-*-* } .-1 } */ + { + read (fd, buf, 1); /* { dg-warning "'read' on write-only file descriptor 'fd'" "warning" } */ + /* { dg-message "\\(4\\) ...to here" "event1" { target *-*-* } .-1 } */ + /* { dg-message "\\(5\\) 'read' on write-only file descriptor 'fd'" "event2" { target *-*-* } .-2 } */ + close (fd); + } +} + + +void +test_3 (const char *path, void *buf) +{ + int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */ + if (fd >= 0) + { + close(fd); /* {dg-message "\\(2\\) closed here"} */ + read(fd, buf, 1); /* { dg-warning "'read' on closed file descriptor 'fd'" } */ + /* {dg-message "\\(3\\) 'read' on closed file descriptor 'fd'; 'close' was at \\(2\\)" "" {target *-*-*} .-1 } */ + } +} + +void +test_4 (const char *path, void *buf) +{ + int fd = open (path, O_RDWR); /* { dg-message "\\(1\\) opened here" } */ + if (fd >= 0) + { + close(fd); /* {dg-message "\\(2\\) closed here"} */ + write(fd, buf, 1); /* { dg-warning "'write' on closed file descriptor 'fd'" } */ + /* {dg-message "\\(3\\) 'write' on closed file descriptor 'fd'; 'close' was at \\(2\\)" "" {target *-*-*} .-1 } */ + } +} + +void +test_5 (const char *path) +{ + int fd = open (path, O_RDWR); + close(fd); + printf("%d", fd); /* { dg-bogus "'printf' on a closed file descriptor 'fd'" } */ } \ No newline at end of file diff --git a/gcc/testsuite/gcc.dg/analyzer/fd-5.c b/gcc/testsuite/gcc.dg/analyzer/fd-5.c index 8f29c11..c18b2ad 100644 --- a/gcc/testsuite/gcc.dg/analyzer/fd-5.c +++ b/gcc/testsuite/gcc.dg/analyzer/fd-5.c @@ -1,53 +1,53 @@ -int open(const char *, int mode); -void close(int fd); -int write (int fd, void *buf, int nbytes); -int read (int fd, void *buf, int nbytes); - -#define O_RDONLY 0 -#define O_WRONLY 1 -#define O_RDWR 2 - -void f (int fd) __attribute__((fd_arg(1))); /* { dg-message "argument 1 of 'f' must be an open file descriptor, due to '__attribute__\\(\\(fd_arg\\(1\\)\\)\\)'" } */ - -void -test_1 (const char *path) -{ - int fd = open (path, O_RDWR); - close(fd); - f(fd); /* { dg-warning "'f' on closed file descriptor 'fd'" } */ - /* { dg-message "\\(3\\) 'f' on closed file descriptor 'fd'; 'close' was at \\(2\\)" "" { target *-*-* } .-1 } */ -} - -void g (int fd) __attribute__((fd_arg_read(1))); /* { dg-message "argument 1 of 'g' must be a readable file descriptor, due to '__attribute__\\(\\(fd_arg_read\\(1\\)\\)\\)'" } */ - -void -test_2 (const char *path) -{ - int fd = open (path, O_WRONLY); - if (fd != -1) - { - g (fd); /* { dg-warning "'g' on write-only file descriptor 'fd'" } */ - } - close (fd); -} - -void h (int fd) __attribute__((fd_arg_write(1))); /* { dg-message "argument 1 of 'h' must be a writable file descriptor, due to '__attribute__\\(\\(fd_arg_write\\(1\\)\\)\\)'" } */ -void -test_3 (const char *path) -{ - int fd = open (path, O_RDONLY); - if (fd != -1) - { - h (fd); /* { dg-warning "'h' on read-only file descriptor 'fd'" } */ - } - close(fd); -} - -void ff (int fd) __attribute__((fd_arg(1))); /* { dg-message "argument 1 of 'ff' must be an open file descriptor, due to '__attribute__\\(\\(fd_arg\\(1\\)\\)\\)'" } */ - -void test_4 (const char *path) -{ - int fd = open (path, O_RDWR); - ff (fd); /* { dg-warning "'ff' on possibly invalid file descriptor 'fd'" } */ - close(fd); +int open(const char *, int mode); +void close(int fd); +int write (int fd, void *buf, int nbytes); +int read (int fd, void *buf, int nbytes); + +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 + +void f (int fd) __attribute__((fd_arg(1))); /* { dg-message "argument 1 of 'f' must be an open file descriptor, due to '__attribute__\\(\\(fd_arg\\(1\\)\\)\\)'" } */ + +void +test_1 (const char *path) +{ + int fd = open (path, O_RDWR); + close(fd); + f(fd); /* { dg-warning "'f' on closed file descriptor 'fd'" } */ + /* { dg-message "\\(3\\) 'f' on closed file descriptor 'fd'; 'close' was at \\(2\\)" "" { target *-*-* } .-1 } */ +} + +void g (int fd) __attribute__((fd_arg_read(1))); /* { dg-message "argument 1 of 'g' must be a readable file descriptor, due to '__attribute__\\(\\(fd_arg_read\\(1\\)\\)\\)'" } */ + +void +test_2 (const char *path) +{ + int fd = open (path, O_WRONLY); + if (fd != -1) + { + g (fd); /* { dg-warning "'g' on write-only file descriptor 'fd'" } */ + } + close (fd); +} + +void h (int fd) __attribute__((fd_arg_write(1))); /* { dg-message "argument 1 of 'h' must be a writable file descriptor, due to '__attribute__\\(\\(fd_arg_write\\(1\\)\\)\\)'" } */ +void +test_3 (const char *path) +{ + int fd = open (path, O_RDONLY); + if (fd != -1) + { + h (fd); /* { dg-warning "'h' on read-only file descriptor 'fd'" } */ + } + close(fd); +} + +void ff (int fd) __attribute__((fd_arg(1))); /* { dg-message "argument 1 of 'ff' must be an open file descriptor, due to '__attribute__\\(\\(fd_arg\\(1\\)\\)\\)'" } */ + +void test_4 (const char *path) +{ + int fd = open (path, O_RDWR); + ff (fd); /* { dg-warning "'ff' on possibly invalid file descriptor 'fd'" } */ + close(fd); } \ No newline at end of file -- cgit v1.1 From a6efab5fbc468b6f98a7522295b7991d2036588b Mon Sep 17 00:00:00 2001 From: Aldy Hernandez Date: Mon, 25 Jul 2022 16:42:00 +0200 Subject: Dispatch code for floating point range ops. This modifies the range-op dispatch code to handle floats. Also provided are the stub routines for the floating point range-ops, as we need something to dispatch to ;-). I am not ecstatic about the dispatch code, but there's no getting around having to switch on the tree code and type in some manner. All the other alternatives I played with ended up being slower, or harder to maintain. At least, this one is self-contained in the range_op_handler API, and less than 0.16% slower for VRP in our benchmarks. Tested on x86-64 Linux. gcc/ChangeLog: * Makefile.in (OBJS): Add range-op-float.o. * range-op.cc (get_float_handler): New. (range_op_handler::range_op_handler): Save code and type for delayed querying. (range_op_handler::oeprator bool): Move from header file, and add support for floats. (range_op_handler::fold_range): Add support for floats. (range_op_handler::op1_range): Same. (range_op_handler::op2_range): Same. (range_op_handler::lhs_op1_relation): Same. (range_op_handler::lhs_op2_relation): Same. (range_op_handler::op1_op2_relation): Same. * range-op.h (class range_operator_float): New. (class floating_op_table): New. * value-query.cc (range_query::get_tree_range): Add case for REAL_CST. * range-op-float.cc: New file. --- gcc/Makefile.in | 1 + gcc/range-op-float.cc | 206 ++++++++++++++++++++++++++++++++++++++++++++++++++ gcc/range-op.cc | 153 ++++++++++++++++++++++++++++++++----- gcc/range-op.h | 69 ++++++++++++++++- gcc/value-query.cc | 1 + 5 files changed, 407 insertions(+), 23 deletions(-) create mode 100644 gcc/range-op-float.cc (limited to 'gcc') diff --git a/gcc/Makefile.in b/gcc/Makefile.in index 001506f..203f0a1 100644 --- a/gcc/Makefile.in +++ b/gcc/Makefile.in @@ -1548,6 +1548,7 @@ OBJS = \ profile-count.o \ range.o \ range-op.o \ + range-op-float.o \ read-md.o \ read-rtl.o \ read-rtl-function.o \ diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc new file mode 100644 index 0000000..8e9d83e --- /dev/null +++ b/gcc/range-op-float.cc @@ -0,0 +1,206 @@ +/* Floating point range operators. + Copyright (C) 2022 Free Software Foundation, Inc. + Contributed by Aldy Hernandez . + +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 "config.h" +#include "system.h" +#include "coretypes.h" +#include "backend.h" +#include "insn-codes.h" +#include "rtl.h" +#include "tree.h" +#include "gimple.h" +#include "cfghooks.h" +#include "tree-pass.h" +#include "ssa.h" +#include "optabs-tree.h" +#include "gimple-pretty-print.h" +#include "diagnostic-core.h" +#include "flags.h" +#include "fold-const.h" +#include "stor-layout.h" +#include "calls.h" +#include "cfganal.h" +#include "gimple-iterator.h" +#include "gimple-fold.h" +#include "tree-eh.h" +#include "gimple-walk.h" +#include "tree-cfg.h" +#include "wide-int.h" +#include "value-relation.h" +#include "range-op.h" + +// Default definitions for floating point operators. + +bool +range_operator_float::fold_range (frange &r ATTRIBUTE_UNUSED, + tree type ATTRIBUTE_UNUSED, + const frange &lh ATTRIBUTE_UNUSED, + const frange &rh ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const +{ + return false; +} + +bool +range_operator_float::fold_range (irange &r ATTRIBUTE_UNUSED, + tree type ATTRIBUTE_UNUSED, + const frange &lh ATTRIBUTE_UNUSED, + const frange &rh ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const +{ + return false; +} + +bool +range_operator_float::op1_range (frange &r ATTRIBUTE_UNUSED, + tree type ATTRIBUTE_UNUSED, + const frange &lhs ATTRIBUTE_UNUSED, + const frange &op2 ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const +{ + return false; +} + +bool +range_operator_float::op1_range (frange &r ATTRIBUTE_UNUSED, + tree type ATTRIBUTE_UNUSED, + const irange &lhs ATTRIBUTE_UNUSED, + const frange &op2 ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const +{ + return false; +} + +bool +range_operator_float::op2_range (frange &r ATTRIBUTE_UNUSED, + tree type ATTRIBUTE_UNUSED, + const frange &lhs ATTRIBUTE_UNUSED, + const frange &op1 ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const +{ + return false; +} + +bool +range_operator_float::op2_range (frange &r ATTRIBUTE_UNUSED, + tree type ATTRIBUTE_UNUSED, + const irange &lhs ATTRIBUTE_UNUSED, + const frange &op1 ATTRIBUTE_UNUSED, + relation_kind rel ATTRIBUTE_UNUSED) const +{ + return false; +} + +relation_kind +range_operator_float::lhs_op1_relation (const frange &lhs ATTRIBUTE_UNUSED, + const frange &op1 ATTRIBUTE_UNUSED, + const frange &op2 ATTRIBUTE_UNUSED, + relation_kind) const +{ + return VREL_VARYING; +} + +relation_kind +range_operator_float::lhs_op1_relation (const irange &lhs ATTRIBUTE_UNUSED, + const frange &op1 ATTRIBUTE_UNUSED, + const frange &op2 ATTRIBUTE_UNUSED, + relation_kind) const +{ + return VREL_VARYING; +} + +relation_kind +range_operator_float::lhs_op2_relation (const irange &lhs ATTRIBUTE_UNUSED, + const frange &op1 ATTRIBUTE_UNUSED, + const frange &op2 ATTRIBUTE_UNUSED, + relation_kind) const +{ + return VREL_VARYING; +} + +relation_kind +range_operator_float::lhs_op2_relation (const frange &lhs ATTRIBUTE_UNUSED, + const frange &op1 ATTRIBUTE_UNUSED, + const frange &op2 ATTRIBUTE_UNUSED, + relation_kind) const +{ + return VREL_VARYING; +} + +relation_kind +range_operator_float::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) const +{ + return VREL_VARYING; +} + +class foperator_identity : public range_operator_float +{ + using range_operator_float::fold_range; + using range_operator_float::op1_range; + + bool fold_range (frange &r, tree type ATTRIBUTE_UNUSED, + const frange &op1, const frange &op2 ATTRIBUTE_UNUSED, + relation_kind) const final override + { + r = op1; + return true; + } + bool op1_range (frange &r, tree type ATTRIBUTE_UNUSED, + const frange &lhs, const frange &op2 ATTRIBUTE_UNUSED, + relation_kind) const final override + { + r = lhs; + return true; + } +public: +} fop_identity; + + +// Instantiate a range_op_table for floating point operations. +static floating_op_table global_floating_table; + +// Pointer to the float table so the dispatch code can access it. +floating_op_table *floating_tree_table = &global_floating_table; + +floating_op_table::floating_op_table () +{ + set (SSA_NAME, fop_identity); + set (PAREN_EXPR, fop_identity); + set (OBJ_TYPE_REF, fop_identity); + set (REAL_CST, fop_identity); +} + +// Return a pointer to the range_operator_float instance, if there is +// one associated with tree_code CODE. + +range_operator_float * +floating_op_table::operator[] (enum tree_code code) +{ + return m_range_tree[code]; +} + +// Add OP to the handler table for CODE. + +void +floating_op_table::set (enum tree_code code, range_operator_float &op) +{ + gcc_checking_assert (m_range_tree[code] == NULL); + m_range_tree[code] = &op; +} diff --git a/gcc/range-op.cc b/gcc/range-op.cc index e184129..dfdd971 100644 --- a/gcc/range-op.cc +++ b/gcc/range-op.cc @@ -4152,28 +4152,56 @@ get_handler (enum tree_code code, tree type) return NULL; } +// Return the floating point operator for CODE or NULL if none available. + +static inline range_operator_float * +get_float_handler (enum tree_code code, tree) +{ + return (*floating_tree_table)[code]; +} + range_op_handler::range_op_handler (tree_code code, tree type) + : m_code (code), m_type (type) { - m_op = get_handler (code, type); } range_op_handler::range_op_handler (const gimple *s) { if (const gassign *ass = dyn_cast (s)) { - enum tree_code code = gimple_assign_rhs_code (ass); + m_code = gimple_assign_rhs_code (ass); // The LHS of a comparison is always an int, so we must look at // the operands. - if (TREE_CODE_CLASS (code) == tcc_comparison) - m_op = get_handler (code, TREE_TYPE (gimple_assign_rhs1 (ass))); + if (TREE_CODE_CLASS (m_code) == tcc_comparison) + m_type = TREE_TYPE (gimple_assign_rhs1 (ass)); else - m_op = get_handler (code, TREE_TYPE (gimple_assign_lhs (ass))); + m_type = TREE_TYPE (gimple_assign_lhs (ass)); } else if (const gcond *cond = dyn_cast (s)) - m_op = get_handler (gimple_cond_code (cond), - TREE_TYPE (gimple_cond_lhs (cond))); + { + m_code = gimple_cond_code (cond); + m_type = TREE_TYPE (gimple_cond_lhs (cond)); + } else - m_op = NULL; + { + // A null type means there is no handler for this combination, + // but the decision whether there is one or not, is delayed + // until operator bool below is queried. + m_code = NOP_EXPR; + m_type = nullptr; + } +} + +// Return TRUE if there is a handler available for the current +// combination of tree_code and type. + +range_op_handler::operator bool () const +{ + if (!m_type) + return false; + if (frange::supports_p (m_type)) + return get_float_handler (m_code, m_type); + return get_handler (m_code, m_type); } bool @@ -4182,10 +4210,24 @@ range_op_handler::fold_range (vrange &r, tree type, const vrange &rh, relation_kind rel) const { - if (is_a (lh)) - return m_op->fold_range (as_a (r), type, + if (irange::supports_p (m_type)) + { + range_operator *op = get_handler (m_code, m_type); + return op->fold_range (as_a (r), type, as_a (lh), as_a (rh), rel); + } + if (frange::supports_p (m_type)) + { + range_operator_float *op = get_float_handler (m_code, m_type); + if (is_a (r)) + return op->fold_range (as_a (r), type, + as_a (lh), + as_a (rh), rel); + return op->fold_range (as_a (r), type, + as_a (lh), + as_a (rh), rel); + } gcc_unreachable (); return false; } @@ -4196,10 +4238,24 @@ range_op_handler::op1_range (vrange &r, tree type, const vrange &op2, relation_kind rel) const { - if (is_a (r)) - return m_op->op1_range (as_a (r), type, + if (irange::supports_p (m_type)) + { + range_operator *op = get_handler (m_code, m_type); + return op->op1_range (as_a (r), type, as_a (lhs), as_a (op2), rel); + } + if (frange::supports_p (m_type)) + { + range_operator_float *op = get_float_handler (m_code, m_type); + if (is_a (lhs)) + return op->op1_range (as_a (r), type, + as_a (lhs), + as_a (op2), rel); + return op->op1_range (as_a (r), type, + as_a (lhs), + as_a (op2), rel); + } gcc_unreachable (); return false; } @@ -4210,10 +4266,24 @@ range_op_handler::op2_range (vrange &r, tree type, const vrange &op1, relation_kind rel) const { - if (is_a (r)) - return m_op->op2_range (as_a (r), type, + if (irange::supports_p (m_type)) + { + range_operator *op = get_handler (m_code, m_type); + return op->op2_range (as_a (r), type, as_a (lhs), as_a (op1), rel); + } + if (frange::supports_p (m_type)) + { + range_operator_float *op = get_float_handler (m_code, m_type); + if (is_a (lhs)) + return op->op2_range (as_a (r), type, + as_a (lhs), + as_a (op1), rel); + return op->op2_range (as_a (r), type, + as_a (lhs), + as_a (op1), rel); + } gcc_unreachable (); return false; } @@ -4224,9 +4294,24 @@ range_op_handler::lhs_op1_relation (const vrange &lhs, const vrange &op2, relation_kind rel) const { - if (is_a (op1)) - return m_op->lhs_op1_relation (as_a (lhs), - as_a (op1), as_a (op2), rel); + if (irange::supports_p (m_type)) + { + range_operator *op = get_handler (m_code, m_type); + return op->lhs_op1_relation (as_a (lhs), + as_a (op1), + as_a (op2), rel); + } + if (frange::supports_p (m_type)) + { + range_operator_float *op = get_float_handler (m_code, m_type); + if (is_a (lhs)) + return op->lhs_op1_relation (as_a (lhs), + as_a (op1), + as_a (op2), rel); + return op->lhs_op1_relation (as_a (lhs), + as_a (op1), + as_a (op2), rel); + } gcc_unreachable (); return VREL_VARYING; } @@ -4237,9 +4322,24 @@ range_op_handler::lhs_op2_relation (const vrange &lhs, const vrange &op2, relation_kind rel) const { - if (is_a (op1)) - return m_op->lhs_op2_relation (as_a (lhs), - as_a (op1), as_a (op2), rel); + if (irange::supports_p (m_type)) + { + range_operator *op = get_handler (m_code, m_type); + return op->lhs_op2_relation (as_a (lhs), + as_a (op1), + as_a (op2), rel); + } + if (frange::supports_p (m_type)) + { + range_operator_float *op = get_float_handler (m_code, m_type); + if (is_a (lhs)) + return op->lhs_op2_relation (as_a (lhs), + as_a (op1), + as_a (op2), rel); + return op->lhs_op2_relation (as_a (lhs), + as_a (op1), + as_a (op2), rel); + } gcc_unreachable (); return VREL_VARYING; } @@ -4247,7 +4347,18 @@ range_op_handler::lhs_op2_relation (const vrange &lhs, relation_kind range_op_handler::op1_op2_relation (const vrange &lhs) const { - return m_op->op1_op2_relation (as_a (lhs)); + if (irange::supports_p (m_type)) + { + range_operator *op = get_handler (m_code, m_type); + return op->op1_op2_relation (as_a (lhs)); + } + if (frange::supports_p (m_type)) + { + range_operator_float *op = get_float_handler (m_code, m_type); + return op->op1_op2_relation (as_a (lhs)); + } + gcc_unreachable (); + return VREL_VARYING; } // Cast the range in R to TYPE. diff --git a/gcc/range-op.h b/gcc/range-op.h index 262c796..37d9aa9 100644 --- a/gcc/range-op.h +++ b/gcc/range-op.h @@ -108,12 +108,61 @@ protected: const wide_int &rh_ub) const; }; +// Like range_operator above, but for floating point operators. + +class range_operator_float +{ +public: + virtual bool fold_range (frange &r, tree type, + const frange &lh, + const frange &rh, + relation_kind rel = VREL_VARYING) const; + virtual bool fold_range (irange &r, tree type, + const frange &lh, + const frange &rh, + relation_kind rel = VREL_VARYING) const; + virtual bool op1_range (frange &r, tree type, + const frange &lhs, + const frange &op2, + relation_kind rel = VREL_VARYING) const; + virtual bool op1_range (frange &r, tree type, + const irange &lhs, + const frange &op2, + relation_kind rel = VREL_VARYING) const; + virtual bool op2_range (frange &r, tree type, + const frange &lhs, + const frange &op1, + relation_kind rel = VREL_VARYING) const; + virtual bool op2_range (frange &r, tree type, + const irange &lhs, + const frange &op1, + relation_kind rel = VREL_VARYING) const; + + virtual relation_kind lhs_op1_relation (const frange &lhs, + const frange &op1, + const frange &op2, + relation_kind = VREL_VARYING) const; + virtual relation_kind lhs_op1_relation (const irange &lhs, + const frange &op1, + const frange &op2, + relation_kind = VREL_VARYING) const; + virtual relation_kind lhs_op2_relation (const frange &lhs, + const frange &op1, + const frange &op2, + relation_kind = VREL_VARYING) const; + virtual relation_kind lhs_op2_relation (const irange &lhs, + const frange &op1, + const frange &op2, + relation_kind = VREL_VARYING) const; + virtual relation_kind op1_op2_relation (const irange &lhs) const; +}; + class range_op_handler { public: range_op_handler (enum tree_code code, tree type); range_op_handler (const gimple *s); - operator bool () const { return m_op; } + operator bool () const; bool fold_range (vrange &r, tree type, const vrange &lh, @@ -137,7 +186,8 @@ public: relation_kind = VREL_VARYING) const; relation_kind op1_op2_relation (const vrange &lhs) const; private: - range_operator *m_op; + enum tree_code m_code; + tree m_type; }; extern bool range_cast (vrange &, tree type); @@ -218,4 +268,19 @@ private: range_operator *m_range_tree[MAX_TREE_CODES]; }; +// Like above, but for floating point operators. + +class floating_op_table +{ +public: + floating_op_table (); + range_operator_float *operator[] (enum tree_code code); +private: + void set (enum tree_code code, range_operator_float &op); + range_operator_float *m_range_tree[MAX_TREE_CODES]; +}; + +// This holds the range op table for floating point operations. +extern floating_op_table *floating_tree_table; + #endif // GCC_RANGE_OP_H diff --git a/gcc/value-query.cc b/gcc/value-query.cc index decf5aa..4af8eca 100644 --- a/gcc/value-query.cc +++ b/gcc/value-query.cc @@ -211,6 +211,7 @@ range_query::get_tree_range (vrange &r, tree expr, gimple *stmt) switch (TREE_CODE (expr)) { case INTEGER_CST: + case REAL_CST: if (TREE_OVERFLOW_P (expr)) expr = drop_tree_overflow (expr); r.set (expr, expr); -- cgit v1.1 From 789c4b9bb015c361bc1a6adfcd0abadce555e562 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Mon, 25 Jul 2022 11:13:31 -0400 Subject: c++: aggregate prvalue as for range [PR106230] Since my PR94041 work on temporary lifetime in aggregate initialization, we end up calling build_vec_init to initialize the reference-extended temporary for the artificial __for_range variable. And build_vec_init uses finish_for_stmt to implement its loop. That function assumes that if __for_range is in current_binding_level, we're finishing a range-for, and we should fix up the variable as it goes out of scope. But when called from build_vec_init we aren't finishing a range-for, and do_poplevel doesn't remove the variable from scope because stmts_are_full_exprs_p is false. So let's check that here as well, and leave the DECL_NAME alone. PR c++/106230 gcc/cp/ChangeLog: * semantics.cc (finish_for_stmt): Check stmts_are_full_exprs_p. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/range-for38.C: New test. --- gcc/cp/semantics.cc | 5 +++++ gcc/testsuite/g++.dg/cpp0x/range-for38.C | 16 ++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 gcc/testsuite/g++.dg/cpp0x/range-for38.C (limited to 'gcc') diff --git a/gcc/cp/semantics.cc b/gcc/cp/semantics.cc index 96037c2..16dea05 100644 --- a/gcc/cp/semantics.cc +++ b/gcc/cp/semantics.cc @@ -1411,6 +1411,11 @@ finish_for_stmt (tree for_stmt) add_stmt (do_poplevel (scope)); + /* If we're being called from build_vec_init, don't mess with the names of + the variables for an enclosing range-for. */ + if (!stmts_are_full_exprs_p ()) + return; + for (int i = 0; i < 3; i++) if (range_for_decl[i]) DECL_NAME (range_for_decl[i]) diff --git a/gcc/testsuite/g++.dg/cpp0x/range-for38.C b/gcc/testsuite/g++.dg/cpp0x/range-for38.C new file mode 100644 index 0000000..39845b9 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/range-for38.C @@ -0,0 +1,16 @@ +// PR c++/106230 +// { dg-do compile { target c++11 } } + +struct A { + A(); + operator int(); +}; +template struct array { + A elts[N]; + A *begin(); + A *end(); +}; +void fn() { + for (int i : array<4>{}) + ; +} -- cgit v1.1 From a5271b144985bd65b8fcbb14984deecd139e90ca Mon Sep 17 00:00:00 2001 From: GCC Administrator Date: Tue, 26 Jul 2022 00:16:29 +0000 Subject: Daily bump. --- gcc/ChangeLog | 120 ++++++++++++++++++++++++++++++++++++++++++++++++ gcc/DATESTAMP | 2 +- gcc/analyzer/ChangeLog | 4 ++ gcc/cp/ChangeLog | 10 ++++ gcc/testsuite/ChangeLog | 42 +++++++++++++++++ 5 files changed, 177 insertions(+), 1 deletion(-) (limited to 'gcc') diff --git a/gcc/ChangeLog b/gcc/ChangeLog index b090352..258cc48 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,123 @@ +2022-07-25 Aldy Hernandez + + * Makefile.in (OBJS): Add range-op-float.o. + * range-op.cc (get_float_handler): New. + (range_op_handler::range_op_handler): Save code and type for + delayed querying. + (range_op_handler::oeprator bool): Move from header file, and + add support for floats. + (range_op_handler::fold_range): Add support for floats. + (range_op_handler::op1_range): Same. + (range_op_handler::op2_range): Same. + (range_op_handler::lhs_op1_relation): Same. + (range_op_handler::lhs_op2_relation): Same. + (range_op_handler::op1_op2_relation): Same. + * range-op.h (class range_operator_float): New. + (class floating_op_table): New. + * value-query.cc (range_query::get_tree_range): Add case for + REAL_CST. + * range-op-float.cc: New file. + +2022-07-25 Roger Sayle + Uroš Bizjak + + PR target/91681 + * config/i386/i386-expand.cc (split_double_concat): A new helper + function for setting a double word value from two word values. + * config/i386/i386-protos.h (split_double_concat): Prototype here. + * config/i386/i386.md (zero_extendditi2): New define_insn_and_split. + (*add3_doubleword_zext): New define_insn_and_split. + (*sub3_doubleword_zext): New define_insn_and_split. + (*concat3_1): New define_insn_and_split replacing + previous define_split for implementing DST = (HI<<32)|LO as + pair of move instructions, setting lopart and hipart. + (*concat3_2): Likewise. + (*concat3_3): Likewise, where HI is zero_extended. + (*concat3_4): Likewise, where HI is zero_extended. + +2022-07-25 Aldy Hernandez + + PR middle-end/106432 + * gimple-range.cc (gimple_ranger::range_on_edge): Return false + when the result range type is unsupported. + +2022-07-25 Sebastian Huber + + * config/rs6000/rtems.h (CPLUSPLUS_CPP_SPEC): Undef. + +2022-07-25 Richard Biener + + PR middle-end/106414 + * match.pd (~(x ^ y) -> x == y): Restrict to single bit + precision types. + +2022-07-25 Andre Vieira + + * config/aarch64/aarch64.md (rbit2): Rename this ... + (@aarch64_rbit): ... to this and change it in... + (ffs2,ctz2): ... here. + (@aarch64_rev16): New. + * config/aarch64/aarch64-builtins.cc: (aarch64_builtins): + Define the following enum AARCH64_REV16, AARCH64_REV16L, + AARCH64_REV16LL, AARCH64_RBIT, AARCH64_RBITL, AARCH64_RBITLL. + (aarch64_init_data_intrinsics): New. + (aarch64_general_init_builtins): Add call to + aarch64_init_data_intrinsics. + (aarch64_expand_builtin_data_intrinsic): New. + (aarch64_general_expand_builtin): Add call to + aarch64_expand_builtin_data_intrinsic. + * config/aarch64/arm_acle.h (__clz, __clzl, __clzll, __cls, __clsl, + __clsll, __rbit, __rbitl, __rbitll, __rev, __revl, __revll, __rev16, + __rev16l, __rev16ll, __ror, __rorl, __rorll, __revsh): New. + +2022-07-25 Martin Liska + + * doc/extend.texi: Remove trailing whitespaces. + * doc/invoke.texi: Likewise. + +2022-07-25 Aldy Hernandez + + * value-range-pretty-print.cc (vrange_printer::visit): New. + (vrange_printer::print_frange_prop): New. + * value-range-pretty-print.h (class vrange_printer): Add visit and + print_frange_prop. + * value-range-storage.h (vrange_allocator::alloc_vrange): Handle frange. + (vrange_allocator::alloc_frange): New. + * value-range.cc (vrange::operator=): Handle frange. + (vrange::operator==): Same. + (frange::accept): New. + (frange::set): New. + (frange::normalize_kind): New. + (frange::union_): New. + (frange::intersect): New. + (frange::operator=): New. + (frange::operator==): New. + (frange::supports_type_p): New. + (frange::verify_range): New. + * value-range.h (enum value_range_discriminator): Handle frange. + (class fp_prop): New. + (FP_PROP_ACCESSOR): New. + (class frange_props): New. + (FRANGE_PROP_ACCESSOR): New. + (class frange): New. + (Value_Range::init): Handle frange. + (Value_Range::operator=): Same. + (Value_Range::supports_type_p): Same. + (frange_props::operator==): New. + (frange_props::union_): New. + (frange_props::intersect): New + (frange::frange): New. + (frange::type): New. + (frange::set_varying): New. + (frange::set_undefined): New. + +2022-07-25 Peter Bergner + Kewen Lin + + PR testsuite/106345 + * config/rs6000/rs6000.h (DRIVER_SELF_SPECS): Adjust -mdejagnu-cpu + to filter out all -mtune options. + 2022-07-24 Aldy Hernandez * value-query.cc (range_query::get_value_range): Add assert. diff --git a/gcc/DATESTAMP b/gcc/DATESTAMP index c35b552..5d98481 100644 --- a/gcc/DATESTAMP +++ b/gcc/DATESTAMP @@ -1 +1 @@ -20220725 +20220726 diff --git a/gcc/analyzer/ChangeLog b/gcc/analyzer/ChangeLog index 59642a6..00905b2 100644 --- a/gcc/analyzer/ChangeLog +++ b/gcc/analyzer/ChangeLog @@ -1,3 +1,7 @@ +2022-07-25 Martin Liska + + * sm-fd.cc: Run dos2unix and fix coding style issues. + 2022-07-23 Immad Mir * sm-fd.cc (fd_param_diagnostic): New diagnostic class. diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index f5ff8de..208275e 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,13 @@ +2022-07-26 Jason Merrill + + PR c++/106230 + * semantics.cc (finish_for_stmt): Check stmts_are_full_exprs_p. + +2022-07-25 Jason Merrill + + PR c++/87729 + * class.cc (warn_hidden): Remove shortcut. + 2022-07-22 Patrick Palka PR c++/106366 diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 054c92f..18e136c 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,45 @@ +2022-07-26 Jason Merrill + + PR c++/106230 + * g++.dg/cpp0x/range-for38.C: New test. + +2022-07-25 Martin Liska + + * gcc.dg/analyzer/fd-2.c: Convert Windows endlines to Unix + style. + * gcc.dg/analyzer/fd-3.c: Likewise. + * gcc.dg/analyzer/fd-4.c: Likewise. + * gcc.dg/analyzer/fd-5.c: Likewise. + * c-c++-common/attr-fd.c: Likewise. + +2022-07-25 Roger Sayle + Uroš Bizjak + + PR target/91681 + * g++.target/i386/pr91681.C: New test case (from the PR). + * gcc.target/i386/pr91681-1.c: New int128 test case. + * gcc.target/i386/pr91681-2.c: Likewise. + * gcc.target/i386/pr91681-3.c: Likewise, but for ia32. + +2022-07-25 Jason Merrill + + PR c++/87729 + * g++.dg/warn/Woverloaded-virt4.C: New test. + +2022-07-25 Richard Biener + + PR middle-end/106414 + * gcc.dg/torture/pr106414-1.c: New testcase. + * gcc.dg/torture/pr106414-2.c: Likewise. + +2022-07-25 Andre Vieira + + * gcc.target/aarch64/acle/data-intrinsics.c: New test. + +2022-07-25 Jason Merrill + + * g++.dg/cpp0x/nsdmi-union7.C: Fix PR number. + 2022-07-24 Roger Sayle H.J. Lu -- cgit v1.1 From f4286601933406142b46693660f7f4b682cb50a5 Mon Sep 17 00:00:00 2001 From: Kewen Lin Date: Mon, 25 Jul 2022 21:29:14 -0500 Subject: rs6000: Preserve REG_EH_REGION when replacing load/store [PR106091] As test case in PR106091 shows, rs6000 specific pass swaps doesn't preserve the reg_note REG_EH_REGION when replacing some load insn at the end of basic block, it causes the flow info verification to fail unexpectedly. Since memory reference rtx may trap, this patch is to ensure we copy REG_EH_REGION reg_note while replacing swapped aligned load or store. PR target/106091 gcc/ChangeLog: * config/rs6000/rs6000-p8swap.cc (replace_swapped_aligned_store): Copy REG_EH_REGION when replacing one store insn having it. (replace_swapped_aligned_load): Likewise. gcc/testsuite/ChangeLog: * gcc.target/powerpc/pr106091.c: New test. --- gcc/config/rs6000/rs6000-p8swap.cc | 20 ++++++++++++++++++-- gcc/testsuite/gcc.target/powerpc/pr106091.c | 15 +++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) create mode 100644 gcc/testsuite/gcc.target/powerpc/pr106091.c (limited to 'gcc') diff --git a/gcc/config/rs6000/rs6000-p8swap.cc b/gcc/config/rs6000/rs6000-p8swap.cc index 275702f..19fbbfb 100644 --- a/gcc/config/rs6000/rs6000-p8swap.cc +++ b/gcc/config/rs6000/rs6000-p8swap.cc @@ -1690,7 +1690,15 @@ replace_swapped_aligned_store (swap_web_entry *insn_entry, gcc_assert ((GET_CODE (new_body) == SET) && MEM_P (SET_DEST (new_body))); - set_block_for_insn (new_insn, BLOCK_FOR_INSN (store_insn)); + basic_block bb = BLOCK_FOR_INSN (store_insn); + set_block_for_insn (new_insn, bb); + /* Handle REG_EH_REGION note. */ + if (cfun->can_throw_non_call_exceptions && BB_END (bb) == store_insn) + { + rtx note = find_reg_note (store_insn, REG_EH_REGION, NULL_RTX); + if (note) + add_reg_note (new_insn, REG_EH_REGION, XEXP (note, 0)); + } df_insn_rescan (new_insn); df_insn_delete (store_insn); @@ -1784,7 +1792,15 @@ replace_swapped_aligned_load (swap_web_entry *insn_entry, rtx swap_insn) gcc_assert ((GET_CODE (new_body) == SET) && MEM_P (SET_SRC (new_body))); - set_block_for_insn (new_insn, BLOCK_FOR_INSN (def_insn)); + basic_block bb = BLOCK_FOR_INSN (def_insn); + set_block_for_insn (new_insn, bb); + /* Handle REG_EH_REGION note. */ + if (cfun->can_throw_non_call_exceptions && BB_END (bb) == def_insn) + { + rtx note = find_reg_note (def_insn, REG_EH_REGION, NULL_RTX); + if (note) + add_reg_note (new_insn, REG_EH_REGION, XEXP (note, 0)); + } df_insn_rescan (new_insn); df_insn_delete (def_insn); diff --git a/gcc/testsuite/gcc.target/powerpc/pr106091.c b/gcc/testsuite/gcc.target/powerpc/pr106091.c new file mode 100644 index 0000000..61ce8cf --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/pr106091.c @@ -0,0 +1,15 @@ +/* { dg-options "-O -fnon-call-exceptions -fno-tree-dce -fno-tree-forwprop -w" } */ + +/* Verify there is no ICE. */ + +typedef short __attribute__ ((__vector_size__ (64))) V; +V v, w; + +inline V foo (V a, V b); + +V +foo (V a, V b) +{ + b &= v < b; + return (V){foo (b, w)[3], (V){}[3]}; +} -- cgit v1.1 From d5e401fb1452d6a9504e23d76d072a89fd2ba379 Mon Sep 17 00:00:00 2001 From: Lulu Cheng Date: Thu, 21 Jul 2022 10:32:51 +0800 Subject: LoongArch: Subdivision symbol type, add SYMBOL_PCREL support. 1. Remove cModel type support other than normal. 2. The method for calling global functions changed from 'la.global + jirl' to 'bl' when complied add '-fplt'. gcc/ChangeLog: * config/loongarch/constraints.md (a): Delete the constraint. (b): A constant call not local address. (h): Delete the constraint. (t): Delete the constraint. * config/loongarch/loongarch-opts.cc (loongarch_config_target): Remove cModel type support other than normal. * config/loongarch/loongarch-protos.h (enum loongarch_symbol_type): Add new symbol type 'SYMBOL_PCREL', 'SYMBOL_TLS_IE' and 'SYMBOL_TLS_LE'. (loongarch_split_symbol): Delete useless function declarations. (loongarch_split_symbol_type): Delete useless function declarations. * config/loongarch/loongarch.cc (enum loongarch_address_type): Delete unnecessary comment information. (loongarch_symbol_binds_local_p): Modified the judgment order of label and symbol. (loongarch_classify_symbol): Return symbol type. If symbol is a label, or symbol is a local symbol return SYMBOL_PCREL. If is a tls symbol, return SYMBOL_TLS. If is a not local symbol return SYMBOL_GOT_DISP. (loongarch_symbolic_constant_p): Add handling of 'SYMBOL_TLS_IE' 'SYMBOL_TLS_LE' and 'SYMBOL_PCREL'. (loongarch_symbol_insns): Add handling of 'SYMBOL_TLS_IE' 'SYMBOL_TLS_LE' and 'SYMBOL_PCREL'. (loongarch_address_insns): Sort code. (loongarch_12bit_offset_address_p): Sort code. (loongarch_14bit_shifted_offset_address_p): Sort code. (loongarch_call_tls_get_addr): Sort code. (loongarch_legitimize_tls_address): Sort code. (loongarch_output_move): Remove schema support for cmodel other than normal. (loongarch_memmodel_needs_release_fence): Sort code. (loongarch_print_operand): Sort code. * config/loongarch/loongarch.h (LARCH_U12BIT_OFFSET_P): Rename to LARCH_12BIT_OFFSET_P. (LARCH_12BIT_OFFSET_P): New macro. * config/loongarch/loongarch.md: Reimplement the function call. Remove schema support for cmodel other than normal. * config/loongarch/predicates.md (is_const_call_weak_symbol): Delete this predicate. (is_const_call_plt_symbol): Delete this predicate. (is_const_call_global_noplt_symbol): Delete this predicate. (is_const_call_no_local_symbol): New predicate, determines whether it is a local symbol or label. gcc/testsuite/ChangeLog: * gcc.target/loongarch/func-call-1.c: New test. * gcc.target/loongarch/func-call-2.c: New test. * gcc.target/loongarch/func-call-3.c: New test. * gcc.target/loongarch/func-call-4.c: New test. --- gcc/config/loongarch/constraints.md | 24 +- gcc/config/loongarch/loongarch-opts.cc | 7 + gcc/config/loongarch/loongarch-protos.h | 9 +- gcc/config/loongarch/loongarch.cc | 256 +++++++++------------ gcc/config/loongarch/loongarch.h | 2 +- gcc/config/loongarch/loongarch.md | 279 +++-------------------- gcc/config/loongarch/predicates.md | 40 ++-- gcc/testsuite/gcc.target/loongarch/func-call-1.c | 32 +++ gcc/testsuite/gcc.target/loongarch/func-call-2.c | 32 +++ gcc/testsuite/gcc.target/loongarch/func-call-3.c | 32 +++ gcc/testsuite/gcc.target/loongarch/func-call-4.c | 32 +++ 11 files changed, 312 insertions(+), 433 deletions(-) create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-1.c create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-2.c create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-3.c create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-4.c (limited to 'gcc') diff --git a/gcc/config/loongarch/constraints.md b/gcc/config/loongarch/constraints.md index d0bfddb..43cb7b5 100644 --- a/gcc/config/loongarch/constraints.md +++ b/gcc/config/loongarch/constraints.md @@ -20,14 +20,14 @@ ;; Register constraints -;; "a" "A constant call global and noplt address." -;; "b" <-----unused +;; "a" <-----unused +;; "b" "A constant call not local address." ;; "c" "A constant call local address." ;; "d" <-----unused ;; "e" JIRL_REGS ;; "f" FP_REGS ;; "g" <-----unused -;; "h" "A constant call plt address." +;; "h" <-----unused ;; "i" "Matches a general integer constant." (Global non-architectural) ;; "j" SIBCALL_REGS ;; "k" "A memory operand whose address is formed by a base register and @@ -42,7 +42,7 @@ ;; "q" CSR_REGS ;; "r" GENERAL_REGS (Global non-architectural) ;; "s" "Matches a symbolic integer constant." (Global non-architectural) -;; "t" "A constant call weak address" +;; "t" <-----unused ;; "u" "A signed 52bit constant and low 32-bit is zero (for logic instructions)" ;; "v" "A signed 64-bit constant and low 44-bit is zero (for logic instructions)." ;; "w" "Matches any valid memory." @@ -89,10 +89,10 @@ ;; "<" "Matches a pre-dec or post-dec operand." (Global non-architectural) ;; ">" "Matches a pre-inc or post-inc operand." (Global non-architectural) -(define_constraint "a" +(define_constraint "b" "@internal - A constant call global and noplt address." - (match_operand 0 "is_const_call_global_noplt_symbol")) + A constant call no local address." + (match_operand 0 "is_const_call_no_local_symbol")) (define_constraint "c" "@internal @@ -105,11 +105,6 @@ (define_register_constraint "f" "TARGET_HARD_FLOAT ? FP_REGS : NO_REGS" "A floating-point register (if available).") -(define_constraint "h" - "@internal - A constant call plt address." - (match_operand 0 "is_const_call_plt_symbol")) - (define_register_constraint "j" "SIBCALL_REGS" "@internal") @@ -134,11 +129,6 @@ (define_register_constraint "q" "CSR_REGS" "A general-purpose register except for $r0 and $r1 for lcsr.") -(define_constraint "t" - "@internal - A constant call weak address." - (match_operand 0 "is_const_call_weak_symbol")) - (define_constraint "u" "A signed 52bit constant and low 32-bit is zero (for logic instructions)." (and (match_code "const_int") diff --git a/gcc/config/loongarch/loongarch-opts.cc b/gcc/config/loongarch/loongarch-opts.cc index eb9c2a5..fc477bf 100644 --- a/gcc/config/loongarch/loongarch-opts.cc +++ b/gcc/config/loongarch/loongarch-opts.cc @@ -376,6 +376,13 @@ fallback: /* 5. Target code model */ t.cmodel = constrained.cmodel ? opt_cmodel : CMODEL_NORMAL; + if (t.cmodel != CMODEL_NORMAL) + { + warning (0, "%qs is not supported, now cmodel is set to 'normal'.", + loongarch_cmodel_strings[t.cmodel]); + t.cmodel = CMODEL_NORMAL; + } + /* Cleanup and return. */ obstack_free (&msg_obstack, NULL); diff --git a/gcc/config/loongarch/loongarch-protos.h b/gcc/config/loongarch/loongarch-protos.h index 2287fd37..0807662 100644 --- a/gcc/config/loongarch/loongarch-protos.h +++ b/gcc/config/loongarch/loongarch-protos.h @@ -27,9 +27,13 @@ along with GCC; see the file COPYING3. If not see SYMBOL_GOT_DISP The symbol's value will be loaded directly from the GOT. + SYMBOL_PCREL + The symbol's value will be loaded directly from data section. + SYMBOL_TLS A thread-local symbol. + SYMBOL_TLS_IE SYMBOL_TLSGD SYMBOL_TLSLDM UNSPEC wrappers around SYMBOL_TLS, corresponding to the @@ -37,7 +41,10 @@ along with GCC; see the file COPYING3. If not see */ enum loongarch_symbol_type { SYMBOL_GOT_DISP, + SYMBOL_PCREL, SYMBOL_TLS, + SYMBOL_TLS_IE, + SYMBOL_TLS_LE, SYMBOL_TLSGD, SYMBOL_TLSLDM, }; @@ -61,7 +68,6 @@ extern int loongarch_idiv_insns (machine_mode); #ifdef RTX_CODE extern void loongarch_emit_binary (enum rtx_code, rtx, rtx, rtx); #endif -extern bool loongarch_split_symbol (rtx, rtx, machine_mode, rtx *); extern rtx loongarch_unspec_address (rtx, enum loongarch_symbol_type); extern rtx loongarch_strip_unspec_address (rtx); extern void loongarch_move_integer (rtx, rtx, unsigned HOST_WIDE_INT); @@ -154,7 +160,6 @@ extern rtx loongarch_expand_thread_pointer (rtx); extern bool loongarch_eh_uses (unsigned int); extern bool loongarch_epilogue_uses (unsigned int); extern bool loongarch_load_store_bonding_p (rtx *, machine_mode, bool); -extern bool loongarch_split_symbol_type (enum loongarch_symbol_type); typedef rtx (*mulsidi3_gen_fn) (rtx, rtx, rtx); diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc index 8b0d7f4..1cb5742 100644 --- a/gcc/config/loongarch/loongarch.cc +++ b/gcc/config/loongarch/loongarch.cc @@ -114,19 +114,7 @@ enum loongarch_address_type }; -/* Information about an address described by loongarch_address_type. - - ADDRESS_CONST_INT - No fields are used. - - ADDRESS_REG - REG is the base register and OFFSET is the constant offset. - - ADDRESS_REG_REG - A base register indexed by (optionally scaled) register. - - ADDRESS_SYMBOLIC - SYMBOL_TYPE is the type of symbol that the address references. */ +/* Information about an address described by loongarch_address_type. */ struct loongarch_address_info { enum loongarch_address_type type; @@ -1617,11 +1605,12 @@ loongarch_weak_symbol_p (const_rtx x) bool loongarch_symbol_binds_local_p (const_rtx x) { - if (LABEL_REF_P (x)) + if (SYMBOL_REF_P (x)) + return (SYMBOL_REF_DECL (x) + ? targetm.binds_local_p (SYMBOL_REF_DECL (x)) + : SYMBOL_REF_LOCAL_P (x)); + else return false; - - return (SYMBOL_REF_DECL (x) ? targetm.binds_local_p (SYMBOL_REF_DECL (x)) - : SYMBOL_REF_LOCAL_P (x)); } /* Return true if rtx constants of mode MODE should be put into a small @@ -1640,17 +1629,16 @@ static enum loongarch_symbol_type loongarch_classify_symbol (const_rtx x) { if (LABEL_REF_P (x)) - return SYMBOL_GOT_DISP; - - gcc_assert (SYMBOL_REF_P (x)); + return SYMBOL_PCREL; if (SYMBOL_REF_TLS_MODEL (x)) return SYMBOL_TLS; - if (SYMBOL_REF_P (x)) + if (SYMBOL_REF_P (x) + && !loongarch_symbol_binds_local_p (x)) return SYMBOL_GOT_DISP; - return SYMBOL_GOT_DISP; + return SYMBOL_PCREL; } /* Return true if X is a symbolic constant. If it is, @@ -1683,9 +1671,15 @@ loongarch_symbolic_constant_p (rtx x, enum loongarch_symbol_type *symbol_type) relocations. */ switch (*symbol_type) { - case SYMBOL_GOT_DISP: + case SYMBOL_TLS_IE: + case SYMBOL_TLS_LE: case SYMBOL_TLSGD: case SYMBOL_TLSLDM: + case SYMBOL_PCREL: + /* GAS rejects offsets outside the range [-2^31, 2^31-1]. */ + return sext_hwi (INTVAL (offset), 32) == INTVAL (offset); + + case SYMBOL_GOT_DISP: case SYMBOL_TLS: return false; } @@ -1707,9 +1701,14 @@ loongarch_symbol_insns (enum loongarch_symbol_type type, machine_mode mode) return 3; + case SYMBOL_PCREL: + case SYMBOL_TLS_IE: + case SYMBOL_TLS_LE: + return 2; + case SYMBOL_TLSGD: case SYMBOL_TLSLDM: - return 1; + return 3; case SYMBOL_TLS: /* We don't treat a bare TLS symbol as a constant. */ @@ -1937,11 +1936,7 @@ loongarch_address_insns (rtx x, machine_mode mode, bool might_split_p) switch (addr.type) { case ADDRESS_REG: - return factor; - case ADDRESS_REG_REG: - return factor; - case ADDRESS_CONST_INT: return factor; @@ -1983,7 +1978,7 @@ loongarch_12bit_offset_address_p (rtx x, machine_mode mode) return (loongarch_classify_address (&addr, x, mode, false) && addr.type == ADDRESS_REG && CONST_INT_P (addr.offset) - && LARCH_U12BIT_OFFSET_P (INTVAL (addr.offset))); + && LARCH_12BIT_OFFSET_P (INTVAL (addr.offset))); } /* Return true if X is a legitimate address with a 14-bit offset shifted 2. @@ -2001,6 +1996,9 @@ loongarch_14bit_shifted_offset_address_p (rtx x, machine_mode mode) && LARCH_SHIFT_2_OFFSET_P (INTVAL (addr.offset))); } +/* Return true if X is a legitimate address with base and index. + MODE is the mode of the value being accessed. */ + bool loongarch_base_index_address_p (rtx x, machine_mode mode) { @@ -2310,7 +2308,7 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0) /* Generate the code to access LOC, a thread-local SYMBOL_REF, and return its address. The return value will be both a valid address and a valid - SET_SRC (either a REG or a LO_SUM). */ + SET_SRC. */ static rtx loongarch_legitimize_tls_address (rtx loc) @@ -2336,7 +2334,7 @@ loongarch_legitimize_tls_address (rtx loc) break; case TLS_MODEL_INITIAL_EXEC: - /* la.tls.ie; tp-relative add */ + /* la.tls.ie; tp-relative add. */ tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); tmp = gen_reg_rtx (Pmode); emit_insn (loongarch_got_load_tls_ie (tmp, loc)); @@ -2345,7 +2343,7 @@ loongarch_legitimize_tls_address (rtx loc) break; case TLS_MODEL_LOCAL_EXEC: - /* la.tls.le; tp-relative add */ + /* la.tls.le; tp-relative add. */ tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); tmp = gen_reg_rtx (Pmode); emit_insn (loongarch_got_load_tls_le (tmp, loc)); @@ -3371,6 +3369,7 @@ loongarch_output_move (rtx dest, rtx src) case 2: return "st.h\t%z1,%0"; case 4: + /* Matching address type with a 12bit offset. */ if (const_arith_operand (offset, Pmode)) return "st.w\t%z1,%0"; else @@ -3409,6 +3408,7 @@ loongarch_output_move (rtx dest, rtx src) case 2: return "ld.hu\t%0,%1"; case 4: + /* Matching address type with a 12bit offset. */ if (const_arith_operand (offset, Pmode)) return "ld.w\t%0,%1"; else @@ -3436,56 +3436,16 @@ loongarch_output_move (rtx dest, rtx src) else gcc_unreachable (); } + } - if (symbolic_operand (src, VOIDmode)) - { - if ((TARGET_CMODEL_TINY && (!loongarch_global_symbol_p (src) - || loongarch_symbol_binds_local_p (src))) - || (TARGET_CMODEL_TINY_STATIC && !loongarch_weak_symbol_p (src))) - { - /* The symbol must be aligned to 4 byte. */ - unsigned int align; - - if (LABEL_REF_P (src)) - align = 32 /* Whatever. */; - else if (CONSTANT_POOL_ADDRESS_P (src)) - align = GET_MODE_ALIGNMENT (get_pool_mode (src)); - else if (TREE_CONSTANT_POOL_ADDRESS_P (src)) - { - tree exp = SYMBOL_REF_DECL (src); - align = TYPE_ALIGN (TREE_TYPE (exp)); - align = loongarch_constant_alignment (exp, align); - } - else if (SYMBOL_REF_DECL (src)) - align = DECL_ALIGN (SYMBOL_REF_DECL (src)); - else if (SYMBOL_REF_HAS_BLOCK_INFO_P (src) - && SYMBOL_REF_BLOCK (src) != NULL) - align = SYMBOL_REF_BLOCK (src)->alignment; - else - align = BITS_PER_UNIT; - - if (align % (4 * 8) == 0) - return "pcaddi\t%0,%%pcrel(%1)>>2"; - } - if (TARGET_CMODEL_TINY - || TARGET_CMODEL_TINY_STATIC - || TARGET_CMODEL_NORMAL - || TARGET_CMODEL_LARGE) - { - if (!loongarch_global_symbol_p (src) - || loongarch_symbol_binds_local_p (src)) - return "la.local\t%0,%1"; - else - return "la.global\t%0,%1"; - } - if (TARGET_CMODEL_EXTREME) - { - sorry ("Normal symbol loading not implemented in extreme mode."); - gcc_unreachable (); - } - - } + if (dest_code == REG && symbolic_operand (src, VOIDmode)) + { + if (loongarch_classify_symbol (src) == SYMBOL_PCREL) + return "la.local\t%0,%1"; + else + return "la.global\t%0,%1"; } + if (src_code == REG && FP_REG_P (REGNO (src))) { if (dest_code == REG && FP_REG_P (REGNO (dest))) @@ -3503,6 +3463,7 @@ loongarch_output_move (rtx dest, rtx src) return dbl_p ? "fst.d\t%1,%0" : "fst.s\t%1,%0"; } } + if (dest_code == REG && FP_REG_P (REGNO (dest))) { if (src_code == MEM) @@ -3517,6 +3478,7 @@ loongarch_output_move (rtx dest, rtx src) return dbl_p ? "fld.d\t%0,%1" : "fld.s\t%0,%1"; } } + gcc_unreachable (); } @@ -4347,27 +4309,27 @@ loongarch_memmodel_needs_release_fence (enum memmodel model) /* Implement TARGET_PRINT_OPERAND. The LoongArch-specific operand codes are: - 'X' Print CONST_INT OP in hexadecimal format. - 'x' Print the low 16 bits of CONST_INT OP in hexadecimal format. + 'A' Print a _DB suffix if the memory model requires a release. + 'b' Print the address of a memory operand, without offset. + 'C' Print the integer branch condition for comparison OP. 'd' Print CONST_INT OP in decimal. + 'F' Print the FPU branch condition for comparison OP. + 'G' Print a DBAR insn if the memory model requires a release. + 'i' Print i if the operand is not a register. 'm' Print one less than CONST_INT OP in decimal. - 'y' Print exact log2 of CONST_INT OP in decimal. - 'C' Print the integer branch condition for comparison OP. 'N' Print the inverse of the integer branch condition for comparison OP. - 'F' Print the FPU branch condition for comparison OP. - 'W' Print the inverse of the FPU branch condition for comparison OP. 'T' Print 'f' for (eq:CC ...), 't' for (ne:CC ...), 'z' for (eq:?I ...), 'n' for (ne:?I ...). 't' Like 'T', but with the EQ/NE cases reversed - 'Y' Print loongarch_fp_conditions[INTVAL (OP)] - 'Z' Print OP and a comma for 8CC, otherwise print nothing. - 'z' Print $0 if OP is zero, otherwise print OP normally. - 'b' Print the address of a memory operand, without offset. 'V' Print exact log2 of CONST_INT OP element 0 of a replicated CONST_VECTOR in decimal. - 'A' Print a _DB suffix if the memory model requires a release. - 'G' Print a DBAR insn if the memory model requires a release. - 'i' Print i if the operand is not a register. */ + 'W' Print the inverse of the FPU branch condition for comparison OP. + 'X' Print CONST_INT OP in hexadecimal format. + 'x' Print the low 16 bits of CONST_INT OP in hexadecimal format. + 'Y' Print loongarch_fp_conditions[INTVAL (OP)] + 'y' Print exact log2 of CONST_INT OP in decimal. + 'Z' Print OP and a comma for 8CC, otherwise print nothing. + 'z' Print $0 if OP is zero, otherwise print OP normally. */ static void loongarch_print_operand (FILE *file, rtx op, int letter) @@ -4385,18 +4347,13 @@ loongarch_print_operand (FILE *file, rtx op, int letter) switch (letter) { - case 'X': - if (CONST_INT_P (op)) - fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op)); - else - output_operand_lossage ("invalid use of '%%%c'", letter); + case 'A': + if (loongarch_memmodel_needs_rel_acq_fence ((enum memmodel) INTVAL (op))) + fputs ("_db", file); break; - case 'x': - if (CONST_INT_P (op)) - fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op) & 0xffff); - else - output_operand_lossage ("invalid use of '%%%c'", letter); + case 'C': + loongarch_print_int_branch_condition (file, code, letter); break; case 'd': @@ -4406,6 +4363,20 @@ loongarch_print_operand (FILE *file, rtx op, int letter) output_operand_lossage ("invalid use of '%%%c'", letter); break; + case 'F': + loongarch_print_float_branch_condition (file, code, letter); + break; + + case 'G': + if (loongarch_memmodel_needs_release_fence ((enum memmodel) INTVAL (op))) + fputs ("dbar\t0", file); + break; + + case 'i': + if (code != REG) + fputs ("i", file); + break; + case 'm': if (CONST_INT_P (op)) fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op) - 1); @@ -4413,17 +4384,17 @@ loongarch_print_operand (FILE *file, rtx op, int letter) output_operand_lossage ("invalid use of '%%%c'", letter); break; - case 'y': - if (CONST_INT_P (op)) - { - int val = exact_log2 (INTVAL (op)); - if (val != -1) - fprintf (file, "%d", val); - else - output_operand_lossage ("invalid use of '%%%c'", letter); - } - else - output_operand_lossage ("invalid use of '%%%c'", letter); + case 'N': + loongarch_print_int_branch_condition (file, reverse_condition (code), + letter); + break; + + case 't': + case 'T': + { + int truth = (code == NE) == (letter == 'T'); + fputc ("zfnt"[truth * 2 + FCC_REG_P (REGNO (XEXP (op, 0)))], file); + } break; case 'V': @@ -4441,30 +4412,36 @@ loongarch_print_operand (FILE *file, rtx op, int letter) output_operand_lossage ("invalid use of '%%%c'", letter); break; - case 'C': - loongarch_print_int_branch_condition (file, code, letter); - break; - - case 'N': - loongarch_print_int_branch_condition (file, reverse_condition (code), - letter); + case 'W': + loongarch_print_float_branch_condition (file, reverse_condition (code), + letter); break; - case 'F': - loongarch_print_float_branch_condition (file, code, letter); + case 'x': + if (CONST_INT_P (op)) + fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op) & 0xffff); + else + output_operand_lossage ("invalid use of '%%%c'", letter); break; - case 'W': - loongarch_print_float_branch_condition (file, reverse_condition (code), - letter); + case 'X': + if (CONST_INT_P (op)) + fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op)); + else + output_operand_lossage ("invalid use of '%%%c'", letter); break; - case 'T': - case 't': - { - int truth = (code == NE) == (letter == 'T'); - fputc ("zfnt"[truth * 2 + FCC_REG_P (REGNO (XEXP (op, 0)))], file); - } + case 'y': + if (CONST_INT_P (op)) + { + int val = exact_log2 (INTVAL (op)); + if (val != -1) + fprintf (file, "%d", val); + else + output_operand_lossage ("invalid use of '%%%c'", letter); + } + else + output_operand_lossage ("invalid use of '%%%c'", letter); break; case 'Y': @@ -4481,21 +4458,6 @@ loongarch_print_operand (FILE *file, rtx op, int letter) fputc (',', file); break; - case 'A': - if (loongarch_memmodel_needs_rel_acq_fence ((enum memmodel) INTVAL (op))) - fputs ("_db", file); - break; - - case 'G': - if (loongarch_memmodel_needs_release_fence ((enum memmodel) INTVAL (op))) - fputs ("dbar\t0", file); - break; - - case 'i': - if (code != REG) - fputs ("i", file); - break; - default: switch (code) { diff --git a/gcc/config/loongarch/loongarch.h b/gcc/config/loongarch/loongarch.h index f9de9a6..89a5bd7 100644 --- a/gcc/config/loongarch/loongarch.h +++ b/gcc/config/loongarch/loongarch.h @@ -614,7 +614,7 @@ enum reg_class #define LU12I_INT(X) LU12I_OPERAND (INTVAL (X)) #define LU32I_INT(X) LU32I_OPERAND (INTVAL (X)) #define LU52I_INT(X) LU52I_OPERAND (INTVAL (X)) -#define LARCH_U12BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -2048, 2047)) +#define LARCH_12BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -2048, 2047)) #define LARCH_9BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -256, 255)) #define LARCH_16BIT_OFFSET_P(OFFSET) (IN_RANGE (OFFSET, -32768, 32767)) #define LARCH_SHIFT_2_OFFSET_P(OFFSET) (((OFFSET) & 0x3) == 0) diff --git a/gcc/config/loongarch/loongarch.md b/gcc/config/loongarch/loongarch.md index 5c0445d..376879f 100644 --- a/gcc/config/loongarch/loongarch.md +++ b/gcc/config/loongarch/loongarch.md @@ -2844,48 +2844,14 @@ }) (define_insn "sibcall_internal" - [(call (mem:SI (match_operand 0 "call_insn_operand" "j,c,a,t,h")) + [(call (mem:SI (match_operand 0 "call_insn_operand" "j,c,b")) (match_operand 1 "" ""))] "SIBLING_CALL_P (insn)" -{ - switch (which_alternative) - { - case 0: - return "jr\t%0"; - case 1: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r12,(%%pcrel(%0+0x20000))>>18\n\t" - "jirl\t$r0,$r12,%%pcrel(%0+4)-(%%pcrel(%0+4+0x20000)>>18<<18)"; - else if (TARGET_CMODEL_EXTREME) - return "la.local\t$r12,$r13,%0\n\tjr\t$r12"; - else - return "b\t%0"; - case 2: - if (TARGET_CMODEL_TINY_STATIC) - return "b\t%0"; - else if (TARGET_CMODEL_EXTREME) - return "la.global\t$r12,$r13,%0\n\tjr\t$r12"; - else - return "la.global\t$r12,%0\n\tjr\t$r12"; - case 3: - if (TARGET_CMODEL_EXTREME) - return "la.global\t$r12,$r13,%0\n\tjr\t$r12"; - else - return "la.global\t$r12,%0\n\tjr\t$r12"; - case 4: - if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY) - return "b\t%%plt(%0)"; - else if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r12,(%%plt(%0)+0x20000)>>18\n\t" - "jirl\t$r0,$r12,%%plt(%0)+4-((%%plt(%0)+(4+0x20000))>>18<<18)"; - else - /* Cmodel extreme and tiny static not support plt. */ - gcc_unreachable (); - default: - gcc_unreachable (); - } -} - [(set_attr "jirl" "indirect,direct,direct,direct,direct")]) + "@ + jr\t%0 + b\t%0 + b\t%%plt(%0)" + [(set_attr "jirl" "indirect,direct,direct")]) (define_expand "sibcall_value" [(parallel [(set (match_operand 0 "") @@ -2920,96 +2886,28 @@ (define_insn "sibcall_value_internal" [(set (match_operand 0 "register_operand" "") - (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,a,t,h")) + (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,b")) (match_operand 2 "" "")))] "SIBLING_CALL_P (insn)" -{ - switch (which_alternative) - { - case 0: - return "jr\t%1"; - case 1: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r12,%%pcrel(%1+0x20000)>>18\n\t" - "jirl\t$r0,$r12,%%pcrel(%1+4)-((%%pcrel(%1+4+0x20000))>>18<<18)"; - else if (TARGET_CMODEL_EXTREME) - return "la.local\t$r12,$r13,%1\n\tjr\t$r12"; - else - return "b\t%1"; - case 2: - if (TARGET_CMODEL_TINY_STATIC) - return "b\t%1"; - else if (TARGET_CMODEL_EXTREME) - return "la.global\t$r12,$r13,%1\n\tjr\t$r12"; - else - return "la.global\t$r12,%1\n\tjr\t$r12"; - case 3: - if (TARGET_CMODEL_EXTREME) - return "la.global\t$r12,$r13,%1\n\tjr\t$r12"; - else - return "la.global\t$r12,%1\n\tjr\t$r12"; - case 4: - if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY) - return " b\t%%plt(%1)"; - else if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r12,(%%plt(%1)+0x20000)>>18\n\t" - "jirl\t$r0,$r12,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)"; - else - /* Cmodel extreme and tiny static not support plt. */ - gcc_unreachable (); - default: - gcc_unreachable (); - } -} - [(set_attr "jirl" "indirect,direct,direct,direct,direct")]) + "@ + jr\t%1 + b\t%1 + b\t%%plt(%1)" + [(set_attr "jirl" "indirect,direct,direct")]) (define_insn "sibcall_value_multiple_internal" [(set (match_operand 0 "register_operand" "") - (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,a,t,h")) + (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,b")) (match_operand 2 "" ""))) (set (match_operand 3 "register_operand" "") (call (mem:SI (match_dup 1)) (match_dup 2)))] "SIBLING_CALL_P (insn)" -{ - switch (which_alternative) - { - case 0: - return "jr\t%1"; - case 1: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r12,%%pcrel(%1+0x20000)>>18\n\t" - "jirl\t$r0,$r12,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)"; - else if (TARGET_CMODEL_EXTREME) - return "la.local\t$r12,$r13,%1\n\tjr\t$r12"; - else - return "b\t%1"; - case 2: - if (TARGET_CMODEL_TINY_STATIC) - return "b\t%1"; - else if (TARGET_CMODEL_EXTREME) - return "la.global\t$r12,$r13,%1\n\tjr\t$r12"; - else - return "la.global\t$r12,%1\n\tjr\t$r12"; - case 3: - if (TARGET_CMODEL_EXTREME) - return "la.global\t$r12,$r13,%1\n\tjr\t$r12"; - else - return "la.global\t$r12,%1\n\tjr\t$r12"; - case 4: - if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY) - return "b\t%%plt(%1)"; - else if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r12,(%%plt(%1)+0x20000)>>18\n\t" - "jirl\t$r0,$r12,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)"; - else - /* Cmodel extreme and tiny static not support plt. */ - gcc_unreachable (); - default: - gcc_unreachable (); - } -} - [(set_attr "jirl" "indirect,direct,direct,direct,direct")]) + "@ + jr\t%1 + b\t%1 + b\t%%plt(%1)" + [(set_attr "jirl" "indirect,direct,direct")]) (define_expand "call" [(parallel [(call (match_operand 0 "") @@ -3025,50 +2923,15 @@ }) (define_insn "call_internal" - [(call (mem:SI (match_operand 0 "call_insn_operand" "e,c,a,t,h")) + [(call (mem:SI (match_operand 0 "call_insn_operand" "e,c,b")) (match_operand 1 "" "")) (clobber (reg:SI RETURN_ADDR_REGNUM))] "" -{ - switch (which_alternative) - { - case 0: - return "jirl\t$r1,%0,0"; - case 1: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r1,%%pcrel(%0+0x20000)>>18\n\t" - "jirl\t$r1,$r1,%%pcrel(%0+4)-(%%pcrel(%0+4+0x20000)>>18<<18)"; - else if (TARGET_CMODEL_EXTREME) - return "la.local\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0"; - else - return "bl\t%0"; - case 2: - if (TARGET_CMODEL_TINY_STATIC) - return "bl\t%0"; - else if (TARGET_CMODEL_EXTREME) - return "la.global\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0"; - else - return "la.global\t$r1,%0\n\tjirl\t$r1,$r1,0"; - case 3: - if (TARGET_CMODEL_EXTREME) - return "la.global\t$r1,$r12,%0\n\tjirl\t$r1,$r1,0"; - else - return "la.global\t$r1,%0\n\tjirl\t$r1,$r1,0"; - case 4: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r1,(%%plt(%0)+0x20000)>>18\n\t" - "jirl\t$r1,$r1,%%plt(%0)+4-((%%plt(%0)+(4+0x20000))>>18<<18)"; - else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY) - return "bl\t%%plt(%0)"; - else - /* Cmodel extreme and tiny static not support plt. */ - gcc_unreachable (); - default: - gcc_unreachable (); - } -} - [(set_attr "jirl" "indirect,direct,direct,direct,direct") - (set_attr "insn_count" "1,2,3,3,2")]) + "@ + jirl\t$r1,%0,0 + bl\t%0 + bl\t%%plt(%0)" + [(set_attr "jirl" "indirect,direct,direct")]) (define_expand "call_value" [(parallel [(set (match_operand 0 "") @@ -3101,100 +2964,30 @@ (define_insn "call_value_internal" [(set (match_operand 0 "register_operand" "") - (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,a,t,h")) + (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,b")) (match_operand 2 "" ""))) (clobber (reg:SI RETURN_ADDR_REGNUM))] "" -{ - switch (which_alternative) - { - case 0: - return "jirl\t$r1,%1,0"; - case 1: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r1,%%pcrel(%1+0x20000)>>18\n\t" - "jirl\t$r1,$r1,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)"; - else if (TARGET_CMODEL_EXTREME) - return "la.local\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0"; - else - return "bl\t%1"; - case 2: - if (TARGET_CMODEL_TINY_STATIC) - return "bl\t%1"; - else if (TARGET_CMODEL_EXTREME) - return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0"; - else - return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0"; - case 3: - if (TARGET_CMODEL_EXTREME) - return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0"; - else - return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0"; - case 4: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r1,(%%plt(%1)+0x20000)>>18\n\t" - "jirl\t$r1,$r1,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)"; - else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY) - return "bl\t%%plt(%1)"; - else - /* Cmodel extreme and tiny static not support plt. */ - gcc_unreachable (); - default: - gcc_unreachable (); - } -} - [(set_attr "jirl" "indirect,direct,direct,direct,direct") - (set_attr "insn_count" "1,2,3,3,2")]) + "@ + jirl\t$r1,%1,0 + bl\t%1 + bl\t%%plt(%1)" + [(set_attr "jirl" "indirect,direct,direct")]) (define_insn "call_value_multiple_internal" [(set (match_operand 0 "register_operand" "") - (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,a,t,h")) + (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,b")) (match_operand 2 "" ""))) (set (match_operand 3 "register_operand" "") (call (mem:SI (match_dup 1)) (match_dup 2))) (clobber (reg:SI RETURN_ADDR_REGNUM))] "" -{ - switch (which_alternative) - { - case 0: - return "jirl\t$r1,%1,0"; - case 1: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r1,%%pcrel(%1+0x20000)>>18\n\t" - "jirl\t$r1,$r1,%%pcrel(%1+4)-(%%pcrel(%1+4+0x20000)>>18<<18)"; - else if (TARGET_CMODEL_EXTREME) - return "la.local\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0"; - else - return "bl\t%1"; - case 2: - if (TARGET_CMODEL_TINY_STATIC) - return "bl\t%1"; - else if (TARGET_CMODEL_EXTREME) - return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0 "; - else - return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0"; - case 3: - if (TARGET_CMODEL_EXTREME) - return "la.global\t$r1,$r12,%1\n\tjirl\t$r1,$r1,0"; - else - return "la.global\t$r1,%1\n\tjirl\t$r1,$r1,0"; - case 4: - if (TARGET_CMODEL_LARGE) - return "pcaddu18i\t$r1,(%%plt(%1)+0x20000)>>18\n\t" - "jirl\t$r1,$r1,%%plt(%1)+4-((%%plt(%1)+(4+0x20000))>>18<<18)"; - else if (TARGET_CMODEL_NORMAL || TARGET_CMODEL_TINY) - return "bl\t%%plt(%1)"; - else - /* Cmodel extreme and tiny static not support plt. */ - gcc_unreachable (); - default: - gcc_unreachable (); - } -} - [(set_attr "jirl" "indirect,direct,direct,direct,direct") - (set_attr "insn_count" "1,2,3,3,2")]) + "@ + jirl\t$r1,%1,0 + bl\t%1 + bl\t%%plt(%1)" + [(set_attr "jirl" "indirect,direct,direct")]) ;; Call subroutine returning any type. diff --git a/gcc/config/loongarch/predicates.md b/gcc/config/loongarch/predicates.md index edd74d4..2243ef7 100644 --- a/gcc/config/loongarch/predicates.md +++ b/gcc/config/loongarch/predicates.md @@ -111,20 +111,25 @@ (match_code "const,symbol_ref,label_ref") { enum loongarch_symbol_type symbol_type; + loongarch_symbolic_constant_p (op, &symbol_type); - if (!loongarch_symbolic_constant_p (op, &symbol_type)) + rtx offset, x = op; + split_const (x, &x, &offset); + + if (offset != const0_rtx) return false; switch (symbol_type) { - case SYMBOL_GOT_DISP: - /* Without explicit relocs, there is no special syntax for - loading the address of a call destination into a register. - Using "la.global JIRL_REGS,foo; jirl JIRL_REGS" would prevent the lazy - binding of "foo", so keep the address of global symbols with the jirl - macro. */ + case SYMBOL_PCREL: return 1; + case SYMBOL_GOT_DISP: + if (!flag_plt) + return false; + else + return 1; + default: return false; } @@ -140,22 +145,11 @@ (match_test "loongarch_symbol_binds_local_p (op) != 0")) (match_test "CONSTANT_P (op)"))) -(define_predicate "is_const_call_weak_symbol" - (and (match_operand 0 "const_call_insn_operand") - (not (match_operand 0 "is_const_call_local_symbol")) - (match_test "loongarch_weak_symbol_p (op) != 0") - (match_test "CONSTANT_P (op)"))) - -(define_predicate "is_const_call_plt_symbol" - (and (match_operand 0 "const_call_insn_operand") - (match_test "flag_plt != 0") - (match_test "loongarch_global_symbol_noweak_p (op) != 0") - (match_test "CONSTANT_P (op)"))) - -(define_predicate "is_const_call_global_noplt_symbol" +(define_predicate "is_const_call_no_local_symbol" (and (match_operand 0 "const_call_insn_operand") - (match_test "flag_plt == 0") - (match_test "loongarch_global_symbol_noweak_p (op) != 0") + (ior (match_test "loongarch_global_symbol_p (op) != 0") + (match_test "loongarch_symbol_binds_local_p (op) == 0") + (match_test "loongarch_weak_symbol_p (op) != 0")) (match_test "CONSTANT_P (op)"))) ;; A legitimate CONST_INT operand that takes more than one instruction @@ -219,7 +213,7 @@ case CONST: case SYMBOL_REF: case LABEL_REF: - return (loongarch_symbolic_constant_p (op, &symbol_type)); + return loongarch_symbolic_constant_p (op, &symbol_type); default: return true; } diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-1.c b/gcc/testsuite/gcc.target/loongarch/func-call-1.c new file mode 100644 index 0000000..b048276 --- /dev/null +++ b/gcc/testsuite/gcc.target/loongarch/func-call-1.c @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-options "-mabi=lp64d -O0 -fpic -fplt" } */ +/* { dg-final { scan-assembler "test:.*bl\t%plt\\(g\\)\n" } } */ +/* { dg-final { scan-assembler "test1:.*bl\t%plt\\(f\\)\n" } } */ +/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ + +extern void g (void); +void +f (void) +{} + +static void +l (void) +{} + +void +test (void) +{ + g (); +} + +void +test1 (void) +{ + f (); +} + +void +test2 (void) +{ + l (); +} diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-2.c b/gcc/testsuite/gcc.target/loongarch/func-call-2.c new file mode 100644 index 0000000..f5e061c --- /dev/null +++ b/gcc/testsuite/gcc.target/loongarch/func-call-2.c @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt" } */ +/* { dg-final { scan-assembler "test:.*bl\t%plt\\(g\\)\n" } } */ +/* { dg-final { scan-assembler "test1:.*bl\tf\n" } } */ +/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ + +extern void g (void); +void +f (void) +{} + +static void +l (void) +{} + +void +test (void) +{ + g (); +} + +void +test1 (void) +{ + f (); +} + +void +test2 (void) +{ + l (); +} diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-3.c b/gcc/testsuite/gcc.target/loongarch/func-call-3.c new file mode 100644 index 0000000..75082c5 --- /dev/null +++ b/gcc/testsuite/gcc.target/loongarch/func-call-3.c @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt" } */ +/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */ +/* { dg-final { scan-assembler "test1:.*la\.global\t.*f\n\tjirl" } } */ +/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ + +extern void g (void); +void +f (void) +{} + +static void +l (void) +{} + +void +test (void) +{ + g (); +} + +void +test1 (void) +{ + f (); +} + +void +test2 (void) +{ + l (); +} diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-4.c b/gcc/testsuite/gcc.target/loongarch/func-call-4.c new file mode 100644 index 0000000..e8a8395 --- /dev/null +++ b/gcc/testsuite/gcc.target/loongarch/func-call-4.c @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt" } */ +/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */ +/* { dg-final { scan-assembler "test1:.*bl\tf\n" } } */ +/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ + +extern void g (void); +void +f (void) +{} + +static void +l (void) +{} + +void +test (void) +{ + g (); +} + +void +test1 (void) +{ + f (); +} + +void +test2 (void) +{ + l (); +} -- cgit v1.1 From 16fc26d4e7a4a7618d8d231f9b4cb7bd487fb7b8 Mon Sep 17 00:00:00 2001 From: Lulu Cheng Date: Thu, 21 Jul 2022 11:04:08 +0800 Subject: LoongArch: Support split symbol. Add compilation option '-mexplicit-relocs', and if enable '-mexplicit-relocs' the symbolic address load instruction 'la.*' will be split into two instructions. This compilation option enabled by default. gcc/ChangeLog: * common/config/loongarch/loongarch-common.cc: Enable '-fsection-anchors' when O1 and more advanced optimization. * config/loongarch/genopts/loongarch.opt.in: Add new option '-mexplicit-relocs', and enable by default. * config/loongarch/loongarch-protos.h (loongarch_split_move_insn_p): Delete function declaration. (loongarch_split_move_insn): Delete function declaration. (loongarch_split_symbol_type): Add function declaration. * config/loongarch/loongarch.cc (enum loongarch_address_type): Add new address type 'ADDRESS_LO_SUM'. (loongarch_classify_symbolic_expression): New function definitions. Classify the base of symbolic expression X, given that X appears in context CONTEXT. (loongarch_symbol_insns): Add a judgment condition TARGET_EXPLICIT_RELOCS. (loongarch_split_symbol_type): New function definitions. Determines whether the symbol load should be split into two instructions. (loongarch_valid_lo_sum_p): New function definitions. Return true if a LO_SUM can address a value of mode MODE when the LO_SUM symbol has type SYMBOL_TYPE. (loongarch_classify_address): Add handling of 'LO_SUM'. (loongarch_address_insns): Add handling of 'ADDRESS_LO_SUM'. (loongarch_signed_immediate_p): Sort code. (loongarch_12bit_offset_address_p): Return true if address type is ADDRESS_LO_SUM. (loongarch_const_insns): Add handling of 'HIGH'. (loongarch_split_move_insn_p): Add the static attribute to the function. (loongarch_emit_set): New function definitions. (loongarch_call_tls_get_addr): Add symbol handling when defining TARGET_EXPLICIT_RELOCS. (loongarch_legitimize_tls_address): Add symbol handling when defining the TARGET_EXPLICIT_RELOCS macro. (loongarch_split_symbol): New function definitions. Split symbol. (loongarch_legitimize_address): Add codes see if the address can split into a high part and a LO_SUM. (loongarch_legitimize_const_move): Add codes split moves of symbolic constants into high and low. (loongarch_split_move_insn): Delete function definitions. (loongarch_output_move): Add support for HIGH and LO_SUM. (loongarch_print_operand_reloc): New function definitions. Print symbolic operand OP, which is part of a HIGH or LO_SUM in context CONTEXT. (loongarch_memmodel_needs_release_fence): Sort code. (loongarch_print_operand): Rearrange alphabetical order and add H and L to support HIGH and LOW output. (loongarch_print_operand_address): Add handling of 'ADDRESS_LO_SUM'. (TARGET_MIN_ANCHOR_OFFSET): Define macro to -IMM_REACH/2. (TARGET_MAX_ANCHOR_OFFSET): Define macro to IMM_REACH/2-1. * config/loongarch/loongarch.md (movti): Delete the template. (*movti): Delete the template. (movtf): Delete the template. (*movtf): Delete the template. (*low): New template of normal symbol low address. (@tls_low): New template of tls symbol low address. (@ld_from_got): New template load address from got table. (@ori_l_lo12): New template. * config/loongarch/loongarch.opt: Update from loongarch.opt.in. * config/loongarch/predicates.md: Add support for symbol_type HIGH. gcc/testsuite/ChangeLog: * gcc.target/loongarch/func-call-1.c: Add build option '-mno-explicit-relocs'. * gcc.target/loongarch/func-call-2.c: Add build option '-mno-explicit-relocs'. * gcc.target/loongarch/func-call-3.c: Add build option '-mno-explicit-relocs'. * gcc.target/loongarch/func-call-4.c: Add build option '-mno-explicit-relocs'. * gcc.target/loongarch/func-call-5.c: New test. * gcc.target/loongarch/func-call-6.c: New test. * gcc.target/loongarch/func-call-7.c: New test. * gcc.target/loongarch/func-call-8.c: New test. * gcc.target/loongarch/relocs-symbol-noaddend.c: New test. --- gcc/common/config/loongarch/loongarch-common.cc | 1 + gcc/config/loongarch/genopts/loongarch.opt.in | 4 + gcc/config/loongarch/loongarch-protos.h | 3 +- gcc/config/loongarch/loongarch.cc | 412 +++++++++++++++++++-- gcc/config/loongarch/loongarch.md | 122 +++--- gcc/config/loongarch/loongarch.opt | 4 + gcc/config/loongarch/predicates.md | 20 +- gcc/testsuite/gcc.target/loongarch/func-call-1.c | 2 +- gcc/testsuite/gcc.target/loongarch/func-call-2.c | 2 +- gcc/testsuite/gcc.target/loongarch/func-call-3.c | 2 +- gcc/testsuite/gcc.target/loongarch/func-call-4.c | 2 +- gcc/testsuite/gcc.target/loongarch/func-call-5.c | 33 ++ gcc/testsuite/gcc.target/loongarch/func-call-6.c | 33 ++ gcc/testsuite/gcc.target/loongarch/func-call-7.c | 34 ++ gcc/testsuite/gcc.target/loongarch/func-call-8.c | 33 ++ .../gcc.target/loongarch/relocs-symbol-noaddend.c | 23 ++ 16 files changed, 614 insertions(+), 116 deletions(-) create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-5.c create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-6.c create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-7.c create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-8.c create mode 100644 gcc/testsuite/gcc.target/loongarch/relocs-symbol-noaddend.c (limited to 'gcc') diff --git a/gcc/common/config/loongarch/loongarch-common.cc b/gcc/common/config/loongarch/loongarch-common.cc index ed3730f..f8b4660 100644 --- a/gcc/common/config/loongarch/loongarch-common.cc +++ b/gcc/common/config/loongarch/loongarch-common.cc @@ -34,6 +34,7 @@ along with GCC; see the file COPYING3. If not see static const struct default_options loongarch_option_optimization_table[] = { { OPT_LEVELS_ALL, OPT_fasynchronous_unwind_tables, NULL, 1 }, + { OPT_LEVELS_1_PLUS, OPT_fsection_anchors, NULL, 1 }, { OPT_LEVELS_NONE, 0, NULL, 0 } }; diff --git a/gcc/config/loongarch/genopts/loongarch.opt.in b/gcc/config/loongarch/genopts/loongarch.opt.in index 61e7d72..6f39500 100644 --- a/gcc/config/loongarch/genopts/loongarch.opt.in +++ b/gcc/config/loongarch/genopts/loongarch.opt.in @@ -154,6 +154,10 @@ mmax-inline-memcpy-size= Target Joined RejectNegative UInteger Var(loongarch_max_inline_memcpy_size) Init(1024) -mmax-inline-memcpy-size=SIZE Set the max size of memcpy to inline, default is 1024. +mexplicit-relocs +Target Var(TARGET_EXPLICIT_RELOCS) Init(1) +Use %reloc() assembly operators. + ; The code model option names for -mcmodel. Enum Name(cmodel) Type(int) diff --git a/gcc/config/loongarch/loongarch-protos.h b/gcc/config/loongarch/loongarch-protos.h index 0807662..cadaad7 100644 --- a/gcc/config/loongarch/loongarch-protos.h +++ b/gcc/config/loongarch/loongarch-protos.h @@ -77,8 +77,6 @@ extern rtx loongarch_legitimize_call_address (rtx); extern rtx loongarch_subword (rtx, bool); extern bool loongarch_split_move_p (rtx, rtx); extern void loongarch_split_move (rtx, rtx, rtx); -extern bool loongarch_split_move_insn_p (rtx, rtx); -extern void loongarch_split_move_insn (rtx, rtx, rtx); extern const char *loongarch_output_move (rtx, rtx); extern bool loongarch_cfun_has_cprestore_slot_p (void); #ifdef RTX_CODE @@ -160,6 +158,7 @@ extern rtx loongarch_expand_thread_pointer (rtx); extern bool loongarch_eh_uses (unsigned int); extern bool loongarch_epilogue_uses (unsigned int); extern bool loongarch_load_store_bonding_p (rtx *, machine_mode, bool); +extern bool loongarch_split_symbol_type (enum loongarch_symbol_type); typedef rtx (*mulsidi3_gen_fn) (rtx, rtx, rtx); diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc index 1cb5742..7968734 100644 --- a/gcc/config/loongarch/loongarch.cc +++ b/gcc/config/loongarch/loongarch.cc @@ -100,6 +100,10 @@ along with GCC; see the file COPYING3. If not see ADDRESS_REG_REG A base register indexed by (optionally scaled) register. + ADDRESS_LO_SUM + A LO_SUM rtx. The first operand is a valid base register and the second + operand is a symbolic address. + ADDRESS_CONST_INT A signed 16-bit constant address. @@ -109,6 +113,7 @@ enum loongarch_address_type { ADDRESS_REG, ADDRESS_REG_REG, + ADDRESS_LO_SUM, ADDRESS_CONST_INT, ADDRESS_SYMBOLIC }; @@ -1641,6 +1646,21 @@ loongarch_classify_symbol (const_rtx x) return SYMBOL_PCREL; } +/* Classify the base of symbolic expression X, given that X appears in + context CONTEXT. */ + +static enum loongarch_symbol_type +loongarch_classify_symbolic_expression (rtx x) +{ + rtx offset; + + split_const (x, &x, &offset); + if (UNSPEC_ADDRESS_P (x)) + return UNSPEC_ADDRESS_TYPE (x); + + return loongarch_classify_symbol (x); +} + /* Return true if X is a symbolic constant. If it is, store the type of the symbol in *SYMBOL_TYPE. */ @@ -1696,7 +1716,7 @@ loongarch_symbol_insns (enum loongarch_symbol_type type, machine_mode mode) case SYMBOL_GOT_DISP: /* The constant will have to be loaded from the GOT before it is used in an address. */ - if (mode != MAX_MACHINE_MODE) + if (!TARGET_EXPLICIT_RELOCS && mode != MAX_MACHINE_MODE) return 0; return 3; @@ -1814,6 +1834,84 @@ loongarch_valid_offset_p (rtx x, machine_mode mode) return true; } +/* Should a symbol of type SYMBOL_TYPE should be split in two? */ + +bool +loongarch_split_symbol_type (enum loongarch_symbol_type symbol_type) +{ + switch (symbol_type) + { + case SYMBOL_PCREL: + case SYMBOL_GOT_DISP: + case SYMBOL_TLS_IE: + case SYMBOL_TLS_LE: + case SYMBOL_TLSGD: + case SYMBOL_TLSLDM: + return true; + + case SYMBOL_TLS: + return false; + + default: + gcc_unreachable (); + } +} + +/* Return true if a LO_SUM can address a value of mode MODE when the + LO_SUM symbol has type SYMBOL_TYPE. */ + +static bool +loongarch_valid_lo_sum_p (enum loongarch_symbol_type symbol_type, + machine_mode mode, rtx x) +{ + int align, size; + + /* Check that symbols of type SYMBOL_TYPE can be used to access values + of mode MODE. */ + if (loongarch_symbol_insns (symbol_type, mode) == 0) + return false; + + /* Check that there is a known low-part relocation. */ + if (!loongarch_split_symbol_type (symbol_type)) + return false; + + /* We can't tell size or alignment when we have BLKmode, so try extracing a + decl from the symbol if possible. */ + if (mode == BLKmode) + { + rtx offset; + + /* Extract the symbol from the LO_SUM operand, if any. */ + split_const (x, &x, &offset); + + /* Might be a CODE_LABEL. We can compute align but not size for that, + so don't bother trying to handle it. */ + if (!SYMBOL_REF_P (x)) + return false; + + /* Use worst case assumptions if we don't have a SYMBOL_REF_DECL. */ + align = (SYMBOL_REF_DECL (x) + ? DECL_ALIGN (SYMBOL_REF_DECL (x)) + : 1); + size = (SYMBOL_REF_DECL (x) && DECL_SIZE (SYMBOL_REF_DECL (x)) + ? tree_to_uhwi (DECL_SIZE (SYMBOL_REF_DECL (x))) + : 2*BITS_PER_WORD); + } + else + { + align = GET_MODE_ALIGNMENT (mode); + size = GET_MODE_BITSIZE (mode); + } + + /* We may need to split multiword moves, so make sure that each word + can be accessed without inducing a carry. */ + if (size > BITS_PER_WORD + && (!TARGET_STRICT_ALIGN || size > align)) + return false; + + return true; +} + static bool loongarch_valid_index_p (struct loongarch_address_info *info, rtx x, machine_mode mode, bool strict_p) @@ -1880,6 +1978,26 @@ loongarch_classify_address (struct loongarch_address_info *info, rtx x, info->offset = XEXP (x, 1); return (loongarch_valid_base_register_p (info->reg, mode, strict_p) && loongarch_valid_offset_p (info->offset, mode)); + + case LO_SUM: + info->type = ADDRESS_LO_SUM; + info->reg = XEXP (x, 0); + info->offset = XEXP (x, 1); + /* We have to trust the creator of the LO_SUM to do something vaguely + sane. Target-independent code that creates a LO_SUM should also + create and verify the matching HIGH. Target-independent code that + adds an offset to a LO_SUM must prove that the offset will not + induce a carry. Failure to do either of these things would be + a bug, and we are not required to check for it here. The MIPS + backend itself should only create LO_SUMs for valid symbolic + constants, with the high part being either a HIGH or a copy + of _gp. */ + info->symbol_type + = loongarch_classify_symbolic_expression (info->offset); + return (loongarch_valid_base_register_p (info->reg, mode, strict_p) + && loongarch_valid_lo_sum_p (info->symbol_type, mode, + info->offset)); + default: return false; } @@ -1940,6 +2058,9 @@ loongarch_address_insns (rtx x, machine_mode mode, bool might_split_p) case ADDRESS_CONST_INT: return factor; + case ADDRESS_LO_SUM: + return factor + 1; + case ADDRESS_SYMBOLIC: return factor * loongarch_symbol_insns (addr.symbol_type, mode); } @@ -1967,7 +2088,8 @@ loongarch_signed_immediate_p (unsigned HOST_WIDE_INT x, int bits, return loongarch_unsigned_immediate_p (x, bits, shift); } -/* Return true if X is a legitimate address with a 12-bit offset. +/* Return true if X is a legitimate address with a 12-bit offset + or addr.type is ADDRESS_LO_SUM. MODE is the mode of the value being accessed. */ bool @@ -1976,9 +2098,10 @@ loongarch_12bit_offset_address_p (rtx x, machine_mode mode) struct loongarch_address_info addr; return (loongarch_classify_address (&addr, x, mode, false) - && addr.type == ADDRESS_REG - && CONST_INT_P (addr.offset) - && LARCH_12BIT_OFFSET_P (INTVAL (addr.offset))); + && ((addr.type == ADDRESS_REG + && CONST_INT_P (addr.offset) + && LARCH_12BIT_OFFSET_P (INTVAL (addr.offset))) + || addr.type == ADDRESS_LO_SUM)); } /* Return true if X is a legitimate address with a 14-bit offset shifted 2. @@ -2020,6 +2143,14 @@ loongarch_const_insns (rtx x) switch (GET_CODE (x)) { + case HIGH: + if (!loongarch_symbolic_constant_p (XEXP (x, 0), &symbol_type) + || !loongarch_split_symbol_type (symbol_type)) + return 0; + + /* This is simply a PCALAU12I. */ + return 1; + case CONST_INT: return loongarch_integer_cost (INTVAL (x)); @@ -2080,6 +2211,8 @@ loongarch_split_const_insns (rtx x) return low + high; } +static bool loongarch_split_move_insn_p (rtx dest, rtx src); + /* Return the number of instructions needed to implement INSN, given that it loads from or stores to MEM. */ @@ -2197,6 +2330,15 @@ loongarch_unspec_address (rtx address, enum loongarch_symbol_type symbol_type) return loongarch_unspec_address_offset (base, offset, symbol_type); } +/* Emit an instruction of the form (set TARGET SRC). */ + +static rtx +loongarch_emit_set (rtx target, rtx src) +{ + emit_insn (gen_rtx_SET (target, src)); + return target; +} + /* If OP is an UNSPEC address, return the address to which it refers, otherwise return OP itself. */ @@ -2278,6 +2420,7 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0) { rtx loc, a0; rtx_insn *insn; + rtx tmp = gen_reg_rtx (Pmode); a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST); @@ -2288,12 +2431,22 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0) start_sequence (); - if (type == SYMBOL_TLSLDM) - emit_insn (loongarch_got_load_tls_ld (a0, loc)); - else if (type == SYMBOL_TLSGD) - emit_insn (loongarch_got_load_tls_gd (a0, loc)); + if (TARGET_EXPLICIT_RELOCS) + { + /* Split tls symbol to high and low. */ + rtx high = gen_rtx_HIGH (Pmode, copy_rtx (loc)); + high = loongarch_force_temporary (tmp, high); + emit_insn (gen_tls_low (Pmode, a0, high, loc)); + } else - gcc_unreachable (); + { + if (type == SYMBOL_TLSLDM) + emit_insn (loongarch_got_load_tls_ld (a0, loc)); + else if (type == SYMBOL_TLSGD) + emit_insn (loongarch_got_load_tls_gd (a0, loc)); + else + gcc_unreachable (); + } insn = emit_call_insn (gen_call_value_internal (v0, loongarch_tls_symbol, const0_rtx)); @@ -2308,12 +2461,12 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0) /* Generate the code to access LOC, a thread-local SYMBOL_REF, and return its address. The return value will be both a valid address and a valid - SET_SRC. */ + SET_SRC (either a REG or a LO_SUM). */ static rtx loongarch_legitimize_tls_address (rtx loc) { - rtx dest, tp, tmp; + rtx dest, tp, tmp, tmp1, tmp2, tmp3; enum tls_model model = SYMBOL_REF_TLS_MODEL (loc); rtx_insn *insn; @@ -2334,21 +2487,45 @@ loongarch_legitimize_tls_address (rtx loc) break; case TLS_MODEL_INITIAL_EXEC: - /* la.tls.ie; tp-relative add. */ - tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); - tmp = gen_reg_rtx (Pmode); - emit_insn (loongarch_got_load_tls_ie (tmp, loc)); - dest = gen_reg_rtx (Pmode); - emit_insn (gen_add3_insn (dest, tmp, tp)); + { + /* la.tls.ie; tp-relative add. */ + tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); + tmp1 = gen_reg_rtx (Pmode); + dest = gen_reg_rtx (Pmode); + if (TARGET_EXPLICIT_RELOCS) + { + tmp2 = loongarch_unspec_address (loc, SYMBOL_TLS_IE); + tmp3 = gen_reg_rtx (Pmode); + rtx high = gen_rtx_HIGH (Pmode, copy_rtx (tmp2)); + high = loongarch_force_temporary (tmp3, high); + emit_insn (gen_ld_from_got (Pmode, tmp1, high, tmp2)); + } + else + emit_insn (loongarch_got_load_tls_ie (tmp1, loc)); + emit_insn (gen_add3_insn (dest, tmp1, tp)); + } break; case TLS_MODEL_LOCAL_EXEC: - /* la.tls.le; tp-relative add. */ - tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); - tmp = gen_reg_rtx (Pmode); - emit_insn (loongarch_got_load_tls_le (tmp, loc)); - dest = gen_reg_rtx (Pmode); - emit_insn (gen_add3_insn (dest, tmp, tp)); + { + /* la.tls.le; tp-relative add. */ + tp = gen_rtx_REG (Pmode, THREAD_POINTER_REGNUM); + tmp1 = gen_reg_rtx (Pmode); + dest = gen_reg_rtx (Pmode); + + if (TARGET_EXPLICIT_RELOCS) + { + tmp2 = loongarch_unspec_address (loc, SYMBOL_TLS_LE); + tmp3 = gen_reg_rtx (Pmode); + rtx high = gen_rtx_HIGH (Pmode, copy_rtx (tmp2)); + high = loongarch_force_temporary (tmp3, high); + emit_insn (gen_ori_l_lo12 (Pmode, tmp1, high, tmp2)); + } + else + emit_insn (loongarch_got_load_tls_le (tmp1, loc)); + emit_insn (gen_add3_insn (dest, tmp1, tp)); + + } break; default: @@ -2397,6 +2574,68 @@ loongarch_force_address (rtx x, machine_mode mode) return x; } +/* If MODE is MAX_MACHINE_MODE, ADDR appears as a move operand, otherwise + it appears in a MEM of that mode. Return true if ADDR is a legitimate + constant in that context and can be split into high and low parts. + If so, and if LOW_OUT is nonnull, emit the high part and store the + low part in *LOW_OUT. Leave *LOW_OUT unchanged otherwise. + + Return false if build with '-mno-explicit-relocs'. + + TEMP is as for loongarch_force_temporary and is used to load the high + part into a register. + + When MODE is MAX_MACHINE_MODE, the low part is guaranteed to be + a legitimize SET_SRC for an .md pattern, otherwise the low part + is guaranteed to be a legitimate address for mode MODE. */ + +bool +loongarch_split_symbol (rtx temp, rtx addr, machine_mode mode, rtx *low_out) +{ + enum loongarch_symbol_type symbol_type; + rtx high; + + /* If build with '-mno-explicit-relocs', don't split symbol. */ + if (!TARGET_EXPLICIT_RELOCS) + return false; + + if ((GET_CODE (addr) == HIGH && mode == MAX_MACHINE_MODE) + || !loongarch_symbolic_constant_p (addr, &symbol_type) + || loongarch_symbol_insns (symbol_type, mode) == 0 + || !loongarch_split_symbol_type (symbol_type)) + return false; + + if (temp == NULL) + temp = gen_reg_rtx (Pmode); + + /* Get the 12-31 bits of the address. */ + high = gen_rtx_HIGH (Pmode, copy_rtx (addr)); + high = loongarch_force_temporary (temp, high); + + if (low_out) + switch (symbol_type) + { + case SYMBOL_PCREL: + *low_out = gen_rtx_LO_SUM (Pmode, high, addr); + break; + + case SYMBOL_GOT_DISP: + /* SYMBOL_GOT_DISP symbols are loaded from the GOT. */ + { + rtx low = gen_rtx_LO_SUM (Pmode, high, addr); + rtx mem = gen_rtx_MEM (Pmode, low); + *low_out = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, mem), + UNSPEC_LOAD_FROM_GOT); + break; + } + + default: + gcc_unreachable (); + } + + return true; +} + /* This function is used to implement LEGITIMIZE_ADDRESS. If X can be legitimized in a way that the generic machinery might not expect, return a new address, otherwise return NULL. MODE is the mode of @@ -2412,6 +2651,10 @@ loongarch_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, if (loongarch_tls_symbol_p (x)) return loongarch_legitimize_tls_address (x); + /* See if the address can split into a high part and a LO_SUM. */ + if (loongarch_split_symbol (NULL, x, mode, &addr)) + return loongarch_force_address (addr, mode); + /* Handle BASE + OFFSET using loongarch_add_offset. */ loongarch_split_plus (x, &base, &offset); if (offset != 0) @@ -2499,6 +2742,13 @@ loongarch_legitimize_const_move (machine_mode mode, rtx dest, rtx src) return; } + /* Split moves of symbolic constants into high and low. */ + if (loongarch_split_symbol (dest, src, MAX_MACHINE_MODE, &src)) + { + loongarch_emit_set (dest, src); + return; + } + /* Generate the appropriate access sequences for TLS symbols. */ if (loongarch_tls_symbol_p (src)) { @@ -3241,21 +3491,12 @@ loongarch_split_move (rtx dest, rtx src, rtx insn_) /* Return true if a move from SRC to DEST in INSN should be split. */ -bool +static bool loongarch_split_move_insn_p (rtx dest, rtx src) { return loongarch_split_move_p (dest, src); } -/* Split a move from SRC to DEST in INSN, given that - loongarch_split_move_insn_p holds. */ - -void -loongarch_split_move_insn (rtx dest, rtx src, rtx insn) -{ - loongarch_split_move (dest, src, insn); -} - /* Implement TARGET_CONSTANT_ALIGNMENT. */ static HOST_WIDE_INT @@ -3369,13 +3610,16 @@ loongarch_output_move (rtx dest, rtx src) case 2: return "st.h\t%z1,%0"; case 4: - /* Matching address type with a 12bit offset. */ - if (const_arith_operand (offset, Pmode)) + /* Matching address type with a 12bit offset and + ADDRESS_LO_SUM. */ + if (const_arith_operand (offset, Pmode) + || GET_CODE (offset) == LO_SUM) return "st.w\t%z1,%0"; else return "stptr.w\t%z1,%0"; case 8: - if (const_arith_operand (offset, Pmode)) + if (const_arith_operand (offset, Pmode) + || GET_CODE (offset) == LO_SUM) return "st.d\t%z1,%0"; else return "stptr.d\t%z1,%0"; @@ -3408,13 +3652,16 @@ loongarch_output_move (rtx dest, rtx src) case 2: return "ld.hu\t%0,%1"; case 4: - /* Matching address type with a 12bit offset. */ - if (const_arith_operand (offset, Pmode)) + /* Matching address type with a 12bit offset and + ADDRESS_LO_SUM. */ + if (const_arith_operand (offset, Pmode) + || GET_CODE (offset) == LO_SUM) return "ld.w\t%0,%1"; else return "ldptr.w\t%0,%1"; case 8: - if (const_arith_operand (offset, Pmode)) + if (const_arith_operand (offset, Pmode) + || GET_CODE (offset) == LO_SUM) return "ld.d\t%0,%1"; else return "ldptr.d\t%0,%1"; @@ -3423,6 +3670,21 @@ loongarch_output_move (rtx dest, rtx src) } } + if (src_code == HIGH) + { + rtx offset, x; + split_const (XEXP (src, 0), &x, &offset); + enum loongarch_symbol_type type = SYMBOL_PCREL; + + if (UNSPEC_ADDRESS_P (x)) + type = UNSPEC_ADDRESS_TYPE (x); + + if (type == SYMBOL_TLS_LE) + return "lu12i.w\t%0,%h1"; + else + return "pcalau12i\t%0,%h1"; + } + if (src_code == CONST_INT) { if (LU12I_INT (src)) @@ -3438,7 +3700,8 @@ loongarch_output_move (rtx dest, rtx src) } } - if (dest_code == REG && symbolic_operand (src, VOIDmode)) + if (!TARGET_EXPLICIT_RELOCS + && dest_code == REG && symbolic_operand (src, VOIDmode)) { if (loongarch_classify_symbol (src) == SYMBOL_PCREL) return "la.local\t%0,%1"; @@ -4307,6 +4570,49 @@ loongarch_memmodel_needs_release_fence (enum memmodel model) } } +/* Print symbolic operand OP, which is part of a HIGH or LO_SUM + in context CONTEXT. HI_RELOC indicates a high-part reloc. */ + +static void +loongarch_print_operand_reloc (FILE *file, rtx op, bool hi_reloc) +{ + const char *reloc; + + switch (loongarch_classify_symbolic_expression (op)) + { + case SYMBOL_PCREL: + reloc = hi_reloc ? "%pc_hi20" : "%pc_lo12"; + break; + + case SYMBOL_GOT_DISP: + reloc = hi_reloc ? "%got_pc_hi20" : "%got_pc_lo12"; + break; + + case SYMBOL_TLS_IE: + reloc = hi_reloc ? "%ie_pc_hi20" : "%ie_pc_lo12"; + break; + + case SYMBOL_TLS_LE: + reloc = hi_reloc ? "%le_hi20" : "%le_lo12"; + break; + + case SYMBOL_TLSGD: + reloc = hi_reloc ? "%gd_pc_hi20" : "%got_pc_lo12"; + break; + + case SYMBOL_TLSLDM: + reloc = hi_reloc ? "%ld_pc_hi20" : "%got_pc_lo12"; + break; + + default: + gcc_unreachable (); + } + + fprintf (file, "%s(", reloc); + output_addr_const (file, loongarch_strip_unspec_address (op)); + fputc (')', file); +} + /* Implement TARGET_PRINT_OPERAND. The LoongArch-specific operand codes are: 'A' Print a _DB suffix if the memory model requires a release. @@ -4315,7 +4621,10 @@ loongarch_memmodel_needs_release_fence (enum memmodel model) 'd' Print CONST_INT OP in decimal. 'F' Print the FPU branch condition for comparison OP. 'G' Print a DBAR insn if the memory model requires a release. + 'H' Print address 52-61bit relocation associated with OP. + 'h' Print the high-part relocation associated with OP. 'i' Print i if the operand is not a register. + 'L' Print the low-part relocation associated with OP. 'm' Print one less than CONST_INT OP in decimal. 'N' Print the inverse of the integer branch condition for comparison OP. 'T' Print 'f' for (eq:CC ...), 't' for (ne:CC ...), @@ -4372,11 +4681,21 @@ loongarch_print_operand (FILE *file, rtx op, int letter) fputs ("dbar\t0", file); break; + case 'h': + if (code == HIGH) + op = XEXP (op, 0); + loongarch_print_operand_reloc (file, op, true /* hi_reloc */); + break; + case 'i': if (code != REG) fputs ("i", file); break; + case 'L': + loongarch_print_operand_reloc (file, op, false /* lo_reloc */); + break; + case 'm': if (CONST_INT_P (op)) fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op) - 1); @@ -4517,6 +4836,11 @@ loongarch_print_operand_address (FILE *file, machine_mode /* mode */, rtx x) reg_names[REGNO (addr.offset)]); return; + case ADDRESS_LO_SUM: + fprintf (file, "%s,", reg_names[REGNO (addr.reg)]); + loongarch_print_operand_reloc (file, addr.offset, false /* hi_reloc */); + return; + case ADDRESS_CONST_INT: fprintf (file, "%s,", reg_names[GP_REG_FIRST]); output_addr_const (file, x); @@ -5891,6 +6215,12 @@ loongarch_starting_frame_offset (void) #undef TARGET_TRAMPOLINE_INIT #define TARGET_TRAMPOLINE_INIT loongarch_trampoline_init +#undef TARGET_MIN_ANCHOR_OFFSET +#define TARGET_MIN_ANCHOR_OFFSET (-IMM_REACH/2) + +#undef TARGET_MAX_ANCHOR_OFFSET +#define TARGET_MAX_ANCHOR_OFFSET (IMM_REACH/2-1) + #undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV #define TARGET_ATOMIC_ASSIGN_EXPAND_FENV loongarch_atomic_assign_expand_fenv diff --git a/gcc/config/loongarch/loongarch.md b/gcc/config/loongarch/loongarch.md index 376879f..6b6df22 100644 --- a/gcc/config/loongarch/loongarch.md +++ b/gcc/config/loongarch/loongarch.md @@ -57,6 +57,10 @@ ;; CRC UNSPEC_CRC UNSPEC_CRCC + + UNSPEC_LOAD_FROM_GOT + UNSPEC_ORI_L_LO12 + UNSPEC_TLS_LOW ]) (define_c_enum "unspecv" [ @@ -1743,73 +1747,6 @@ [(set_attr "move_type" "move,load,store") (set_attr "mode" "DF")]) - -;; 128-bit integer moves - -(define_expand "movti" - [(set (match_operand:TI 0) - (match_operand:TI 1))] - "TARGET_64BIT" -{ - if (loongarch_legitimize_move (TImode, operands[0], operands[1])) - DONE; -}) - -(define_insn "*movti" - [(set (match_operand:TI 0 "nonimmediate_operand" "=r,r,r,m") - (match_operand:TI 1 "move_operand" "r,i,m,rJ"))] - "TARGET_64BIT - && (register_operand (operands[0], TImode) - || reg_or_0_operand (operands[1], TImode))" - { return loongarch_output_move (operands[0], operands[1]); } - [(set_attr "move_type" "move,const,load,store") - (set (attr "mode") - (if_then_else (eq_attr "move_type" "imul") - (const_string "SI") - (const_string "TI")))]) - -;; 128-bit floating point moves - -(define_expand "movtf" - [(set (match_operand:TF 0) - (match_operand:TF 1))] - "TARGET_64BIT" -{ - if (loongarch_legitimize_move (TFmode, operands[0], operands[1])) - DONE; -}) - -;; This pattern handles both hard- and soft-float cases. -(define_insn "*movtf" - [(set (match_operand:TF 0 "nonimmediate_operand" "=r,r,m,f,r,f,m") - (match_operand:TF 1 "move_operand" "rG,m,rG,rG,f,m,f"))] - "TARGET_64BIT - && (register_operand (operands[0], TFmode) - || reg_or_0_operand (operands[1], TFmode))" - "#" - [(set_attr "move_type" "move,load,store,mgtf,mftg,fpload,fpstore") - (set_attr "mode" "TF")]) - -(define_split - [(set (match_operand:MOVE64 0 "nonimmediate_operand") - (match_operand:MOVE64 1 "move_operand"))] - "reload_completed && loongarch_split_move_insn_p (operands[0], operands[1])" - [(const_int 0)] -{ - loongarch_split_move_insn (operands[0], operands[1], curr_insn); - DONE; -}) - -(define_split - [(set (match_operand:MOVE128 0 "nonimmediate_operand") - (match_operand:MOVE128 1 "move_operand"))] - "reload_completed && loongarch_split_move_insn_p (operands[0], operands[1])" - [(const_int 0)] -{ - loongarch_split_move_insn (operands[0], operands[1], curr_insn); - DONE; -}) - ;; Emit a doubleword move in which exactly one of the operands is ;; a floating-point register. We can't just emit two normal moves ;; because of the constraints imposed by the FPU register model; @@ -1938,6 +1875,57 @@ [(set_attr "type" "arith") (set_attr "mode" "DI")]) +;; Instructions for adding the low 12 bits of an address to a register. +;; Operand 2 is the address: loongarch_print_operand works out which relocation +;; should be applied. + +(define_insn "*low" + [(set (match_operand:P 0 "register_operand" "=r") + (lo_sum:P (match_operand:P 1 "register_operand" " r") + (match_operand:P 2 "symbolic_operand" "")))] + "TARGET_EXPLICIT_RELOCS" + "addi.\t%0,%1,%L2" + [(set_attr "type" "arith") + (set_attr "mode" "")]) + +(define_insn "@tls_low" + [(set (match_operand:P 0 "register_operand" "=r") + (unspec:P [(mem:P (lo_sum:P (match_operand:P 1 "register_operand" "r") + (match_operand:P 2 "symbolic_operand" "")))] + UNSPEC_TLS_LOW))] + "TARGET_EXPLICIT_RELOCS" + "addi.\t%0,%1,%L2" + [(set_attr "type" "arith") + (set_attr "mode" "")]) + +;; Instructions for loading address from GOT entry. +;; operands[1] is pc plus the high half of the address difference with the got +;; entry; +;; operands[2] is low 12 bits for low 12 bit of the address difference with the +;; got entry. +;; loongarch_print_operand works out which relocation should be applied. + +(define_insn "@ld_from_got" + [(set (match_operand:P 0 "register_operand" "=r") + (unspec:P [(mem:P (lo_sum:P + (match_operand:P 1 "register_operand" "r") + (match_operand:P 2 "symbolic_operand")))] + UNSPEC_LOAD_FROM_GOT))] + "TARGET_EXPLICIT_RELOCS" + "ld.\t%0,%1,%L2" + [(set_attr "type" "move")] +) + +(define_insn "@ori_l_lo12" + [(set (match_operand:P 0 "register_operand" "=r") + (unspec:P [(match_operand:P 1 "register_operand" "r") + (match_operand:P 2 "symbolic_operand")] + UNSPEC_ORI_L_LO12))] + "" + "ori\t%0,%1,%L2" + [(set_attr "type" "move")] +) + ;; Convert floating-point numbers to integers (define_insn "frint_" [(set (match_operand:ANYF 0 "register_operand" "=f") diff --git a/gcc/config/loongarch/loongarch.opt b/gcc/config/loongarch/loongarch.opt index 3ff0d86..7a8c5b4 100644 --- a/gcc/config/loongarch/loongarch.opt +++ b/gcc/config/loongarch/loongarch.opt @@ -161,6 +161,10 @@ mmax-inline-memcpy-size= Target Joined RejectNegative UInteger Var(loongarch_max_inline_memcpy_size) Init(1024) -mmax-inline-memcpy-size=SIZE Set the max size of memcpy to inline, default is 1024. +mexplicit-relocs +Target Var(TARGET_EXPLICIT_RELOCS) Init(1) +Use %reloc() assembly operators. + ; The code model option names for -mcmodel. Enum Name(cmodel) Type(int) diff --git a/gcc/config/loongarch/predicates.md b/gcc/config/loongarch/predicates.md index 2243ef7..cd3528c 100644 --- a/gcc/config/loongarch/predicates.md +++ b/gcc/config/loongarch/predicates.md @@ -110,6 +110,10 @@ (define_predicate "const_call_insn_operand" (match_code "const,symbol_ref,label_ref") { + /* Split symbol to high and low if return false. + If defined TARGET_CMODEL_LARGE, all symbol would be splited, + else if offset is not zero, the symbol would be splited. */ + enum loongarch_symbol_type symbol_type; loongarch_symbolic_constant_p (op, &symbol_type); @@ -125,7 +129,7 @@ return 1; case SYMBOL_GOT_DISP: - if (!flag_plt) + if (TARGET_CMODEL_LARGE || !flag_plt) return false; else return 1; @@ -213,7 +217,19 @@ case CONST: case SYMBOL_REF: case LABEL_REF: - return loongarch_symbolic_constant_p (op, &symbol_type); + return (loongarch_symbolic_constant_p (op, &symbol_type) + && (!TARGET_EXPLICIT_RELOCS + || !loongarch_split_symbol_type (symbol_type))); + + case HIGH: + /* '-mno-explicit-relocs' don't generate high/low pairs. */ + if (!TARGET_EXPLICIT_RELOCS) + return false; + + op = XEXP (op, 0); + return (loongarch_symbolic_constant_p (op, &symbol_type) + && loongarch_split_symbol_type (symbol_type)); + default: return true; } diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-1.c b/gcc/testsuite/gcc.target/loongarch/func-call-1.c index b048276..01b8ea2 100644 --- a/gcc/testsuite/gcc.target/loongarch/func-call-1.c +++ b/gcc/testsuite/gcc.target/loongarch/func-call-1.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-mabi=lp64d -O0 -fpic -fplt" } */ +/* { dg-options "-mabi=lp64d -O0 -fpic -fplt -mno-explicit-relocs" } */ /* { dg-final { scan-assembler "test:.*bl\t%plt\\(g\\)\n" } } */ /* { dg-final { scan-assembler "test1:.*bl\t%plt\\(f\\)\n" } } */ /* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-2.c b/gcc/testsuite/gcc.target/loongarch/func-call-2.c index f5e061c..4565baa 100644 --- a/gcc/testsuite/gcc.target/loongarch/func-call-2.c +++ b/gcc/testsuite/gcc.target/loongarch/func-call-2.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt" } */ +/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt -mno-explicit-relocs" } */ /* { dg-final { scan-assembler "test:.*bl\t%plt\\(g\\)\n" } } */ /* { dg-final { scan-assembler "test1:.*bl\tf\n" } } */ /* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-3.c b/gcc/testsuite/gcc.target/loongarch/func-call-3.c index 75082c5..4f669a0 100644 --- a/gcc/testsuite/gcc.target/loongarch/func-call-3.c +++ b/gcc/testsuite/gcc.target/loongarch/func-call-3.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt" } */ +/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mno-explicit-relocs" } */ /* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */ /* { dg-final { scan-assembler "test1:.*la\.global\t.*f\n\tjirl" } } */ /* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-4.c b/gcc/testsuite/gcc.target/loongarch/func-call-4.c index e8a8395..943adb6 100644 --- a/gcc/testsuite/gcc.target/loongarch/func-call-4.c +++ b/gcc/testsuite/gcc.target/loongarch/func-call-4.c @@ -1,5 +1,5 @@ /* { dg-do compile } */ -/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt" } */ +/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mno-explicit-relocs" } */ /* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */ /* { dg-final { scan-assembler "test1:.*bl\tf\n" } } */ /* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-5.c b/gcc/testsuite/gcc.target/loongarch/func-call-5.c new file mode 100644 index 0000000..2c2a1c8 --- /dev/null +++ b/gcc/testsuite/gcc.target/loongarch/func-call-5.c @@ -0,0 +1,33 @@ +/* { dg-do compile } */ +/* { dg-options "-mabi=lp64d -O0 -fpic -fplt -mexplicit-relocs" } */ +/* { dg-final { scan-assembler "test:.*bl\t%plt\\(g\\)\n" } } */ +/* { dg-final { scan-assembler "test1:.*bl\t%plt\\(f\\)\n" } } */ +/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ + +extern void g (void); + +void +f (void) +{} + +static void +l (void) +{} + +void +test (void) +{ + g (); +} + +void +test1 (void) +{ + f (); +} + +void +test2 (void) +{ + l (); +} diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-6.c b/gcc/testsuite/gcc.target/loongarch/func-call-6.c new file mode 100644 index 0000000..4b0e426 --- /dev/null +++ b/gcc/testsuite/gcc.target/loongarch/func-call-6.c @@ -0,0 +1,33 @@ +/* { dg-do compile } */ +/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt -mexplicit-relocs" } */ +/* { dg-final { scan-assembler "test:.*bl\t%plt\\(g\\)\n" } } */ +/* { dg-final { scan-assembler "test1:.*bl\tf\n" } } */ +/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ + +extern void g (void); + +void +f (void) +{} + +static void +l (void) +{} + +void +test (void) +{ + g (); +} + +void +test1 (void) +{ + f (); +} + +void +test2 (void) +{ + l (); +} diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-7.c b/gcc/testsuite/gcc.target/loongarch/func-call-7.c new file mode 100644 index 0000000..5179271 --- /dev/null +++ b/gcc/testsuite/gcc.target/loongarch/func-call-7.c @@ -0,0 +1,34 @@ +/* { dg-do compile } */ +/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mexplicit-relocs" } */ +/* { dg-final { scan-assembler "test:.*pcalau12i\t.*%got_pc_hi20\\(g\\)\n\tld\.d\t.*%got_pc_lo12\\(g\\)\n\tjirl" } } */ +/* { dg-final { scan-assembler "test1:.*pcalau12i\t.*%got_pc_hi20\\(f\\)\n\tld\.d\t.*%got_pc_lo12\\(f\\)\n\tjirl" } } */ +/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ + + +extern void g (void); + +void +f (void) +{} + +static void +l (void) +{} + +void +test (void) +{ + g (); +} + +void +test1 (void) +{ + f (); +} + +void +test2 (void) +{ + l (); +} diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-8.c b/gcc/testsuite/gcc.target/loongarch/func-call-8.c new file mode 100644 index 0000000..330140d --- /dev/null +++ b/gcc/testsuite/gcc.target/loongarch/func-call-8.c @@ -0,0 +1,33 @@ +/* { dg-do compile } */ +/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mexplicit-relocs" } */ +/* { dg-final { scan-assembler "test:.*pcalau12i\t.*%got_pc_hi20\\(g\\)\n\tld\.d\t.*%got_pc_lo12\\(g\\)\n\tjirl" } } */ +/* { dg-final { scan-assembler "test1:.*bl\tf\n" } } */ +/* { dg-final { scan-assembler "test2:.*bl\tl\n" } } */ + +extern void g (void); + +void +f (void) +{} + +static void +l (void) +{} + +void +test (void) +{ + g (); +} + +void +test1 (void) +{ + f (); +} + +void +test2 (void) +{ + l (); +} diff --git a/gcc/testsuite/gcc.target/loongarch/relocs-symbol-noaddend.c b/gcc/testsuite/gcc.target/loongarch/relocs-symbol-noaddend.c new file mode 100644 index 0000000..bfcc9bc --- /dev/null +++ b/gcc/testsuite/gcc.target/loongarch/relocs-symbol-noaddend.c @@ -0,0 +1,23 @@ +/* { dg-do compile } */ +/* { dg-options "-mabi=lp64d -mexplicit-relocs -fno-pic -O2" } */ +/* { dg-final { scan-assembler "pcalau12i.*%pc_hi20\\(\.LANCHOR0\\)\n" } } */ +/* { dg-final { scan-assembler "addi\.d.*%pc_lo12\\(\.LANCHOR0\\)\n" } } */ +/* { dg-final { scan-assembler "ldptr.d\t\\\$r4,.*,0\n" } } */ +/* { dg-final { scan-assembler "ld.d\t\\\$r5,.*,8\n" } } */ +/* { dg-final { scan-assembler-not "\.LANCHOR0+8" } } */ + + +struct S +{ + char *a; + unsigned short int b; +}; + +struct S s1; + +void test(struct S); +void test1(void) +{ + test(s1); +} + -- cgit v1.1 From bb04f9f23ac0dee2c003118c85372ece50a52220 Mon Sep 17 00:00:00 2001 From: Richard Biener Date: Mon, 25 Jul 2022 17:24:57 +0200 Subject: tree-optimization/106189 - avoid division by zero exception The diagnostic code can end up with zero sized array elements with T[][0] and the wide-int code nicely avoids exceptions when dividing by zero in one codepath but not in another. The following fixes the exception by using wide-int in both paths. PR tree-optimization/106189 * gimple-array-bounds.cc (array_bounds_checker::check_mem_ref): Divide using offset_ints. * gcc.dg/pr106189.c: New testcase. --- gcc/gimple-array-bounds.cc | 2 +- gcc/testsuite/gcc.dg/pr106189.c | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/gcc.dg/pr106189.c (limited to 'gcc') diff --git a/gcc/gimple-array-bounds.cc b/gcc/gimple-array-bounds.cc index 0b48bdb..e190b93 100644 --- a/gcc/gimple-array-bounds.cc +++ b/gcc/gimple-array-bounds.cc @@ -534,7 +534,7 @@ array_bounds_checker::check_mem_ref (location_t location, tree ref, int i = 0; if (aref.offmax[i] < -aref.sizrng[1] || aref.offmax[i = 1] > ubound) { - HOST_WIDE_INT tmpidx = aref.offmax[i].to_shwi () / eltsize.to_shwi (); + HOST_WIDE_INT tmpidx = (aref.offmax[i] / eltsize).to_shwi (); if (warning_at (location, OPT_Warray_bounds, "intermediate array offset %wi is outside array bounds " diff --git a/gcc/testsuite/gcc.dg/pr106189.c b/gcc/testsuite/gcc.dg/pr106189.c new file mode 100644 index 0000000..0eca834 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr106189.c @@ -0,0 +1,5 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -Warray-bounds=2 -w" } */ + +int a_n_0_0_a[][0]; +void a_n_0_0() { T(((char *)a_n_0_0_a)[1]); } -- cgit v1.1