From a4a9d52d15289b9ce9589694c632a52e014dde3a Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 9 Sep 2016 14:53:01 -0400
Subject: [PATCH 11/36] FIXME: RTL interpreter

---
 gcc/Makefile.in          |   1 +
 gcc/rtl-interpreter.c    | 352 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/rtl-interpreter.h    |  86 ++++++++++++
 gcc/selftest-run-tests.c |   1 +
 gcc/selftest.h           |   1 +
 5 files changed, 441 insertions(+)
 create mode 100644 gcc/rtl-interpreter.c
 create mode 100644 gcc/rtl-interpreter.h

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 341c188..db4816c 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1419,6 +1419,7 @@ OBJS = \
 	resource.o \
 	rtl-chkp.o \
 	rtl-error.o \
+	rtl-interpreter.o \
 	rtl-tests.o \
 	rtl.o \
 	rtlhash.o \
diff --git a/gcc/rtl-interpreter.c b/gcc/rtl-interpreter.c
new file mode 100644
index 0000000..52ebf7f
--- /dev/null
+++ b/gcc/rtl-interpreter.c
@@ -0,0 +1,352 @@
+/* RTL interpreter.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "backend.h"
+#include "target.h"
+#include "rtl.h"
+#include "emit-rtl.h"
+#include "rtl-interpreter.h"
+#include "selftest.h"
+#include "selftest-rtl.h"
+
+
+/* FIXME.  */
+
+rtl_value
+rtl_state::eval (rtx expr) const
+{
+  switch (GET_CODE (expr))
+    {
+    case CONST_INT:
+      return rtl_value (INTVAL (expr));
+
+    case REG:
+      return get_reg_value (REGNO (expr));
+
+    case PLUS:
+      {
+	rtl_value lhs = eval (XEXP (expr, 0));
+	rtl_value rhs = eval (XEXP (expr, 1));
+	return lhs + rhs;
+      }
+      break;
+
+    default:
+      abort (); // unhandled
+      break;
+    }
+}
+
+/* FIXME.  */
+
+rtl_value
+rtl_state::get_reg_value (int regno) const
+{
+  rtl_value *value = const_cast <rtl_state *> (this)->m_reg_values.get (regno);
+  if (value)
+    return *value;
+  else
+    abort (); // FIXME
+}
+
+/* FIXME.  */
+
+void
+rtl_state::set_reg_value (int regno, const rtl_value &value)
+{
+  m_reg_values.put (regno, value);
+}
+
+/* FIXME.  */
+
+void
+rtl_state::debug () const
+{
+  for (reg_value_iterator_t iter = m_reg_values.begin ();
+       iter != m_reg_values.end ();
+       ++iter)
+    {
+      int regno = (*iter).first;
+      rtl_value value = (*iter).second;
+
+      fprintf (stdout, "r%d: %d\n", regno, (int)value);
+    }
+}
+
+/* FIXME.  */
+
+rtl_interpreter::rtl_interpreter (rtx_insn *insn, rtl_state *state)
+: m_cur_insn (insn), m_state (state)
+{
+}
+
+/* FIXME.  */
+
+void
+rtl_interpreter::run_insn ()
+{
+  gcc_assert (m_cur_insn);
+
+  rtx_insn *next_insn = NEXT_INSN (m_cur_insn);
+
+  switch (GET_CODE (m_cur_insn))
+    {
+    case DEBUG_INSN:
+      on_debug_insn (as_a <rtx_debug_insn *> ((rtx)m_cur_insn));
+      break;
+
+    case INSN:
+      on_nonjump_insn (as_a <rtx_nonjump_insn *> ((rtx)m_cur_insn));
+      break;
+
+    case JUMP_INSN:
+      next_insn = on_jump_insn (as_a <rtx_jump_insn *> ((rtx)m_cur_insn));
+      break;
+
+    case CALL_INSN:
+      on_call_insn (as_a <rtx_call_insn *> ((rtx)m_cur_insn));
+      break;
+
+    case JUMP_TABLE_DATA:
+      on_jump_table_data (as_a <rtx_jump_table_data *> ((rtx)m_cur_insn));
+      break;
+
+    case BARRIER:
+      on_barrier (as_a <rtx_barrier *> ((rtx)m_cur_insn));
+      break;
+
+    case CODE_LABEL:
+      on_code_label (as_a <rtx_code_label *> ((rtx)m_cur_insn));
+      break;
+
+    case NOTE:
+      on_note (as_a <rtx_note *> ((rtx)m_cur_insn));
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
+
+  m_cur_insn = next_insn;
+}
+
+void
+rtl_interpreter::on_debug_insn (rtx_debug_insn *)
+{
+  /* no-op */
+}
+
+void
+rtl_interpreter::on_nonjump_insn (rtx_nonjump_insn *insn)
+{
+  rtx pat = PATTERN (insn);
+  switch (GET_CODE (pat))
+    {
+    case SET:
+      {
+	rtx src = SET_SRC (pat);
+	rtx dest = SET_DEST (pat);
+	switch (GET_CODE (dest))
+	  {
+	  case REG:
+	    {
+	      rtl_value new_value = m_state->eval (src);
+	      m_state->set_reg_value (REGNO (dest), new_value);
+	    }
+	    break;
+
+	  default:
+	    abort (); // unhandled
+	    break;
+	  }
+	// TODO
+      }
+      break;
+
+    default:
+      break;
+    }
+}
+
+rtx_insn *
+rtl_interpreter::on_jump_insn (rtx_jump_insn *insn)
+{
+  rtx pat = PATTERN (insn);
+  if (pat == simple_return_rtx)
+    return NULL;
+  abort (); // unimplemented
+  return NEXT_INSN (m_cur_insn); // FIXME
+}
+
+void
+rtl_interpreter::on_call_insn (rtx_call_insn *)
+{
+  /* no-op */
+}
+
+void
+rtl_interpreter::on_jump_table_data (rtx_jump_table_data *)
+{
+  /* no-op */
+}
+
+void
+rtl_interpreter::on_barrier (rtx_barrier *)
+{
+  /* no-op */
+}
+
+void
+rtl_interpreter::on_code_label (rtx_code_label *)
+{
+  /* no-op */
+}
+
+void
+rtl_interpreter::on_note (rtx_note *)
+{
+  /* no-op */
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests for RTL interpreter.  */
+
+/* Verify interpreting an empty function.  */
+
+static void
+test_interpret_empty_function ()
+{
+  /* Dump of the dump from cc1 in "test.c.289r.dwarf2" given this input:
+       void test_1 (void) {}
+     and compiling with -Os (for x86_64).  */
+  const char *dump
+    = (";; Function test_1 (test_1, funcdef_no=0, decl_uid=1758, cgraph_uid=0, symbol_order=0)\n"
+       "(note 1 0 3 (nil) NOTE_INSN_DELETED)\n"
+       "(note 3 1 8 2 [bb 2] NOTE_INSN_BASIC_BLOCK)\n"
+       "(note 8 3 2 2 NOTE_INSN_PROLOGUE_END)\n"
+       "(note 2 8 9 2 NOTE_INSN_FUNCTION_BEG)\n"
+       "(note 9 2 10 2 NOTE_INSN_EPILOGUE_BEG)\n"
+       "(jump_insn:TI 10 9 11 2 (simple_return) test.c:3 697 {simple_return_internal}\n"
+       "     (nil)\n"
+       " -> simple_return)\n"
+       "(barrier 11 10 7)\n"
+       "(note 7 11 0 (nil) NOTE_INSN_DELETED)\n");
+
+  rtl_dump_test t (dump);
+
+  rtl_state state;
+  rtl_interpreter interp (get_insns (), &state);
+  ASSERT_EQ (1, INSN_UID (interp.get_cur_insn ()));
+  interp.run_insn ();
+  ASSERT_EQ (3, INSN_UID (interp.get_cur_insn ()));
+  interp.run_insn ();
+  ASSERT_EQ (8, INSN_UID (interp.get_cur_insn ()));
+  interp.run_insn ();
+  ASSERT_EQ (2, INSN_UID (interp.get_cur_insn ()));
+  interp.run_insn ();
+  ASSERT_EQ (9, INSN_UID (interp.get_cur_insn ()));
+  interp.run_insn ();
+  ASSERT_EQ (10, INSN_UID (interp.get_cur_insn ()));
+  interp.run_insn ();
+  ASSERT_EQ (NULL, interp.get_cur_insn ());
+}
+
+/* FIXME.  */
+
+static void
+test_interpret_simple_set ()
+{
+  const char *dump
+    =  ("(insn 100 0 0 2\n"
+	"  (set (reg:SI 100) (const_int 42 [0x2a]))\n"
+	"  test.c:2 -1 (nil))\n");
+
+  rtl_dump_test t (dump);
+
+  rtl_state state;
+  rtl_interpreter interp (get_insns (), &state);
+  ASSERT_EQ (100, INSN_UID (interp.get_cur_insn ()));
+  interp.run_insn ();
+  ASSERT_EQ (NULL, interp.get_cur_insn ());
+  ASSERT_EQ (42, state.get_reg_value (100));
+}
+
+/* FIXME.  */
+
+static void
+test_interpret_simple_arith ()
+{
+  const char *dump
+    =  (/* r100 = 5;  */
+	"(insn 100 0 101 2\n"
+	"  (set (reg:SI 100) (const_int 5 [0x5]))\n"
+	"  test.c:2 -1 (nil))\n"
+	/* r101 = 3; */
+	"(insn 101 100 102 2\n"
+	"  (set (reg:SI 101) (const_int 3 [0x3]))\n"
+	"  test.c:2 -1 (nil))\n"
+	/* r102 = r100 + r101;  */
+	"(insn 102 101 0 2\n"
+	"  (set (reg:SI 102) (plus:SI (reg:SI 100) (reg:SI 101)))\n"
+	"  test.c:2 -1 (nil))\n");
+  rtl_dump_test t (dump);
+
+  rtl_state state;
+  rtl_interpreter interp (get_insns (), &state);
+  ASSERT_EQ (100, INSN_UID (interp.get_cur_insn ()));
+  interp.run_insn ();
+  ASSERT_EQ (5, state.get_reg_value (100));
+  ASSERT_EQ (101, INSN_UID (interp.get_cur_insn ()));
+  interp.run_insn ();
+  ASSERT_EQ (3, state.get_reg_value (101));
+  ASSERT_EQ (102, INSN_UID (interp.get_cur_insn ()));
+  interp.run_insn ();
+  ASSERT_EQ (8, state.get_reg_value (102));
+  ASSERT_EQ (NULL, interp.get_cur_insn ());
+  state.debug ();
+}
+
+/* FIXME.  */
+
+static void
+test_interpret_undefined_read ()
+{
+  // TODO: read from a paradoxical subreg, use of undefined bits
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+rtl_interpreter_c_tests ()
+{
+  test_interpret_empty_function ();
+  test_interpret_simple_set ();
+  test_interpret_simple_arith ();
+  test_interpret_undefined_read ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/rtl-interpreter.h b/gcc/rtl-interpreter.h
new file mode 100644
index 0000000..e7c251e
--- /dev/null
+++ b/gcc/rtl-interpreter.h
@@ -0,0 +1,86 @@
+/* RTL interpreter.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_RTL_INTERPRETER_H
+#define GCC_RTL_INTERPRETER_H
+
+class rtl_state;
+class rtl_interpreter;
+
+/* FIXME.  */
+
+class rtl_value
+{
+ public:
+  rtl_value (long value) : m_value (value) {}
+  ~rtl_value () {}
+
+  operator long () const { return m_value; }
+
+ private:
+  long m_value;
+#if 0
+  sbitmap m_defined_bits;
+  sbitmap m_value;
+#endif
+};
+
+class rtl_state
+{
+ public:
+  rtl_value eval (rtx expr) const;
+  rtl_value get_reg_value (int regno) const;
+  void set_reg_value (int regno, const rtl_value &value);
+
+  void debug () const;
+
+ private:
+  struct regno_hash : int_hash <int, -1, -2> {};
+  hash_map<regno_hash, rtl_value> m_reg_values;
+  typedef hash_map<regno_hash, rtl_value>::iterator reg_value_iterator_t;
+};
+
+/* FIXME.  */
+
+class rtl_interpreter
+{
+ public:
+  rtl_interpreter (rtx_insn *insn, rtl_state *state);
+  virtual ~rtl_interpreter () {}
+
+  void run_insn ();
+
+  rtx_insn *get_cur_insn () const { return m_cur_insn; }
+
+ private:
+  virtual void on_debug_insn (rtx_debug_insn *insn);
+  virtual void on_nonjump_insn (rtx_nonjump_insn *insn);
+  virtual rtx_insn *on_jump_insn (rtx_jump_insn *insn);
+  virtual void on_call_insn (rtx_call_insn *insn);
+  virtual void on_jump_table_data (rtx_jump_table_data *insn);
+  virtual void on_barrier (rtx_barrier *insn);
+  virtual void on_code_label (rtx_code_label *insn);
+  virtual void on_note (rtx_note *insn);
+
+ private:
+  rtx_insn *m_cur_insn;
+  rtl_state *m_state;
+};
+
+#endif  /* GCC_RTL_INTERPRETER_H  */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 5fdfb42..49576e9 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -78,6 +78,7 @@ selftest::run_tests ()
   spellcheck_tree_c_tests ();
   tree_cfg_c_tests ();
   final_c_tests ();
+  rtl_interpreter_c_tests ();
 
   /* This one relies on most of the above.  */
   function_tests_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index c0dc862..9fa0fd6 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -208,6 +208,7 @@ extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
+extern void rtl_interpreter_c_tests ();
 extern void rtl_tests_c_tests ();
 extern void selftest_c_tests ();
 extern void spellcheck_c_tests ();
-- 
1.8.5.3

