From dc41460270b6be881de95190853aed1d94c2a184 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 19 Oct 2020 16:18:42 -0400
Subject: [PATCH 31/49] FIXME: WIP on invalid shifts

---
 gcc/analyzer/analyzer.opt                     |  4 ++
 gcc/analyzer/region-model.cc                  | 50 +++++++++++++++++++
 .../gcc.dg/analyzer/invalid-shift-1.c         | 28 +++++++++++
 3 files changed, 82 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/analyzer/invalid-shift-1.c

diff --git a/gcc/analyzer/analyzer.opt b/gcc/analyzer/analyzer.opt
index c9df6dc7673..e2b16a29ee4 100644
--- a/gcc/analyzer/analyzer.opt
+++ b/gcc/analyzer/analyzer.opt
@@ -90,6 +90,10 @@ Wanalyzer-unsafe-call-within-signal-handler
 Common Var(warn_analyzer_unsafe_call_within_signal_handler) Init(1) Warning
 Warn about code paths in which an async-signal-unsafe function is called from a signal handler.
 
+Wanalyzer-negative-shift
+Common Var(warn_analyzer_negative_shift) Init(1) Warning
+Warn about code paths in which a shift by a negative amount is attempted.
+
 Wanalyzer-null-argument
 Common Var(warn_analyzer_null_argument) Init(1) Warning
 Warn about code paths in which NULL is passed to a must-not-be-NULL function argument.
diff --git a/gcc/analyzer/region-model.cc b/gcc/analyzer/region-model.cc
index 06c0c8668ac..26820b48cd9 100644
--- a/gcc/analyzer/region-model.cc
+++ b/gcc/analyzer/region-model.cc
@@ -363,6 +363,44 @@ private:
   enum poison_kind m_pkind;
 };
 
+/* A subclass of pending_diagnostic for complaining about shifts
+   by negative amounts.  */
+
+class negative_shift_diagnostic
+: public pending_diagnostic_subclass<negative_shift_diagnostic>
+{
+public:
+  negative_shift_diagnostic (const gassign *assign, tree cst)
+  : m_assign (assign), m_cst (cst)
+  {}
+
+  const char *get_kind () const FINAL OVERRIDE
+  {
+    return "negative_shift_diagnostic";
+  }
+
+  bool operator== (const negative_shift_diagnostic &other) const
+  {
+    return (m_assign == other.m_assign
+	    && same_tree_p (m_cst, other.m_cst));
+  }
+
+  bool emit (rich_location *rich_loc) FINAL OVERRIDE
+  {
+    return warning_at (rich_loc, OPT_Wanalyzer_negative_shift,
+		       "shift by negative amount (%qE)", m_cst);
+  }
+
+  label_text describe_final_event (const evdesc::final_event &ev) FINAL OVERRIDE
+  {
+    return ev.formatted_print ("shift by negative amount here (%qE)", m_cst);
+  }
+
+private:
+  const gassign *m_assign;
+  tree m_cst;
+};
+
 /* If ASSIGN is a stmt that can be modelled via
      set_value (lhs_reg, SVALUE, CTXT)
    for some SVALUE, get the SVALUE.
@@ -514,6 +552,18 @@ region_model::get_gassign_result (const gassign *assign,
 	const svalue *rhs1_sval = get_rvalue (rhs1, ctxt);
 	const svalue *rhs2_sval = get_rvalue (rhs2, ctxt);
 
+	if (op == LSHIFT_EXPR || op == RSHIFT_EXPR)
+	  {
+	    /* "INT34-C. Do not shift an expression by a negative number of bits
+	       or by greater than or equal to the number of bits that exist in
+	       the operand."  */
+	    if (ctxt)
+	      if (const tree rhs2_cst = rhs2_sval->maybe_get_constant ())
+		if (TREE_CODE (rhs2_cst) == INTEGER_CST)
+		  if (tree_int_cst_sgn (rhs2_cst) < 0)
+		    ctxt->warn (new negative_shift_diagnostic (assign, rhs2_cst));
+	  }
+
 	const svalue *sval_binop
 	  = m_mgr->get_or_create_binop (TREE_TYPE (lhs), op,
 					rhs1_sval, rhs2_sval);
diff --git a/gcc/testsuite/gcc.dg/analyzer/invalid-shift-1.c b/gcc/testsuite/gcc.dg/analyzer/invalid-shift-1.c
new file mode 100644
index 00000000000..02cbe49e082
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/analyzer/invalid-shift-1.c
@@ -0,0 +1,28 @@
+/* PR tree-optimization/97424.  */
+
+#include <stdint.h>
+
+static inline uint32_t
+_dl_hwcaps_subdirs_build_bitmask (int subdirs, int active)
+{
+  /* Leading subdirectories that are not active.  */
+  int inactive = subdirs - active;
+  if (inactive == 32)
+    return 0;
+
+  uint32_t mask;
+  if (subdirs != 32)
+    mask = (1 << subdirs) - 1;
+  else
+    mask = -1;
+  return mask ^ ((1U << inactive) - 1); /* { dg-message "shift by negative amount \\('-1'\\)" } */
+}
+
+void f1 (int);
+
+void
+f2 (void)
+{
+  f1 (_dl_hwcaps_subdirs_build_bitmask (1, 2));
+  f1 (_dl_hwcaps_subdirs_build_bitmask (33, 31));
+}
-- 
2.26.2

