This is the mail archive of the gcc@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Help with integrating my test program into dejagnu


Still being pretty new to GCC and having never used dejagnu, expect or Tcl, I'm trying to determine how to best integrate my test program into GCC's test harness. I wrote this to help find breakages while working on optimizations for Microsoft 64-bit ABI pro/epilogues. Rather than testing specific cases, it generates a few thousand unique tests by iterating through variations. It consists of a C++ program that generates a pair of .c files (that I don't want in the same translation unit). These are built along with a static .c and .S file and linked into the test program. It is intended to be built and executed on the target machine and I currently run it manually with a frequently-edited Makefile.

The first thing I need help with is figuring out if this should be run by dejagnu or if I should just write a proper Makefile.in and add it to gcc's Makefile.in. Integrating with dejagnu seems to be the most intuitive and simple, but I don't properly understand how this would affect a cross-compiler build. Any advice?

Thanks!
Daniel
>From d38bc80fc793224fb0fbd586824786f5ec178f65 Mon Sep 17 00:00:00 2001
From: Daniel Santos <daniel.santos@pobox.com>
Date: Sat, 17 Dec 2016 15:37:28 -0600
Subject: [PATCH] [i386] Test program for ms_abi functions.

---
 gcc/testsuite/gcc.target/i386/msabi/Makefile  |  56 +++
 gcc/testsuite/gcc.target/i386/msabi/do_test.S | 142 ++++++
 gcc/testsuite/gcc.target/i386/msabi/gen.cc    | 653 ++++++++++++++++++++++++++
 gcc/testsuite/gcc.target/i386/msabi/msabi.c   | 304 ++++++++++++
 gcc/testsuite/gcc.target/i386/msabi/msabi.h   | 110 +++++
 5 files changed, 1265 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/i386/msabi/Makefile
 create mode 100644 gcc/testsuite/gcc.target/i386/msabi/do_test.S
 create mode 100644 gcc/testsuite/gcc.target/i386/msabi/gen.cc
 create mode 100644 gcc/testsuite/gcc.target/i386/msabi/msabi.c
 create mode 100644 gcc/testsuite/gcc.target/i386/msabi/msabi.h

diff --git a/gcc/testsuite/gcc.target/i386/msabi/Makefile b/gcc/testsuite/gcc.target/i386/msabi/Makefile
new file mode 100644
index 00000000000..8d762d704ef
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/Makefile
@@ -0,0 +1,56 @@
+GEN_OPTIONS ?= -p 6 -m 0
+GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc-github/build/head
+#GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc-github/build/head-test-sp-realigned
+#GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc-github/build/head-test-patched
+#GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc.work0/build/head
+GCC_SRC_DIR  = /home/daniel/proj/sys/gcc-github
+#GCC_SRC_DIR  = /home/daniel/proj/sys/gcc.work0
+COMPFLAGS    = -B$(GCC_BUILD_DIR)/gcc
+#CC           = $(GCC_BUILD_DIR)/gcc/xgcc $(COMPFLAGS)
+#CXX          = $(GCC_BUILD_DIR)/gcc/xg++ $(COMPFLAGS) -B$(GCC_BUILD_DIR)/x86_64-pc-linux-gnu/libstdc++-v3/src/.libs \
+#		-isystem $(GCC_BUILD_DIR)/x86_64-pc-linux-gnu/libstdc++-v3/include \
+#		-isystem $(GCC_BUILD_DIR)/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu  \
+#		-isystem $(GCC_SRC_DIR)/libstdc++-v3/libsupc++
+WARN_FLAGS  ?= -Wall -Wextra -Wno-unused-parameter
+CFLAGS	    ?= -g3 -O2 -m64
+#CFLAGS	    ?= -g3 -O2 -m64 -moutline-msabi-xlogues
+
+CXXFLAGS    ?= $(CFLAGS)
+CFLAGS	    += $(WARN_FLAGS)
+CXXFLAGS    += -std=gnu++11 $(WARN_FLAGS)
+ASFLAGS	    ?= -g3
+
+gen_object_files	= gen.o
+gen_program		= gen
+generated_sources	= fns.c do_tests.c
+headers			= msabi.h
+test_object_files	= msabi.o do_test.o fns.o do_tests.o
+test_program		= msabi
+
+all: $(test_program)
+
+check: $(test_program)
+	$(CURDIR)/$(test_program) && echo PASS || echo FAIL
+
+.PHONY : clean
+clean:
+	rm -f $(gen_object_files) $(gen_program) $(generated_sources) \
+	      $(test_object_files) $(test_program)
+
+%.o: %.cc
+	$(CXX) $(CXXFLAGS) -I. -c -o $@ $<
+
+$(gen_program): $(gen_object_files)
+	$(CXX) $(CXXFLAGS) -o $@ $<
+
+fns.c : $(gen_program)
+	$(CURDIR)/$(gen_program) $(GEN_OPTIONS) fns.c > $@ || rm $@
+
+do_tests.c : $(gen_program)
+	$(CURDIR)/$(gen_program) $(GEN_OPTIONS) do_tests.c > $@ || rm $@
+
+%.o: %.c $(headers)
+	$(CC) $(CFLAGS) -I. -c -o $@ $<
+
+$(test_program): $(headers) $(test_object_files)
+	$(CC) $(CFLAGS) -I. -o $@ $(test_object_files)
diff --git a/gcc/testsuite/gcc.target/i386/msabi/do_test.S b/gcc/testsuite/gcc.target/i386/msabi/do_test.S
new file mode 100644
index 00000000000..f01ea0943ef
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/do_test.S
@@ -0,0 +1,142 @@
+/* Assembly proxy functions for ms_abi tests.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   Contributed by Daniel Santos <daniel.santos@pobox.com>
+
+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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#ifdef __x86_64__
+
+# ifdef __ELF__
+#  define ELFFN(fn) .type fn,@function
+# else
+#  define ELFFN(fn)
+# endif
+
+# define FUNC(fn)		\
+	.global fn;		\
+	ELFFN(fn);		\
+fn:
+
+#define FUNC_END(fn) \
+	.size fn,.-fn
+
+# ifdef __AVX__
+#  define MOVAPS vmovaps
+# else
+#  define MOVAPS movaps
+# endif
+	.text
+
+regs_to_mem:
+	MOVAPS	%xmm6, (%rax)
+	MOVAPS	%xmm7, 0x10(%rax)
+	MOVAPS	%xmm8, 0x20(%rax)
+	MOVAPS	%xmm9, 0x30(%rax)
+	MOVAPS	%xmm10, 0x40(%rax)
+	MOVAPS	%xmm11, 0x50(%rax)
+	MOVAPS	%xmm12, 0x60(%rax)
+	MOVAPS	%xmm13, 0x70(%rax)
+	MOVAPS	%xmm14, 0x80(%rax)
+	MOVAPS	%xmm15, 0x90(%rax)
+	mov	%rsi, 0xa0(%rax)
+	mov	%rdi, 0xa8(%rax)
+	mov	%rbx, 0xb0(%rax)
+	mov	%rbp, 0xb8(%rax)
+	mov	%r12, 0xc0(%rax)
+	mov	%r13, 0xc8(%rax)
+	mov	%r14, 0xd0(%rax)
+	mov	%r15, 0xd8(%rax)
+	retq
+
+mem_to_regs:
+	MOVAPS	(%rax), %xmm6
+	MOVAPS	0x10(%rax),%xmm7
+	MOVAPS	0x20(%rax),%xmm8
+	MOVAPS	0x30(%rax),%xmm9
+	MOVAPS	0x40(%rax),%xmm10
+	MOVAPS	0x50(%rax),%xmm11
+	MOVAPS	0x60(%rax),%xmm12
+	MOVAPS	0x70(%rax),%xmm13
+	MOVAPS	0x80(%rax),%xmm14
+	MOVAPS	0x90(%rax),%xmm15
+	mov	0xa0(%rax),%rsi
+	mov	0xa8(%rax),%rdi
+	mov	0xb0(%rax),%rbx
+	mov	0xb8(%rax),%rbp
+	mov	0xc0(%rax),%r12
+	mov	0xc8(%rax),%r13
+	mov	0xd0(%rax),%r14
+	mov	0xd8(%rax),%r15
+	retq
+
+# NOTE: Not MT safe
+FUNC(do_test_unaligned)
+	.cfi_startproc
+	# Verify that incoming stack is aligned + 8
+	pushf
+	test	$0x8, %rsp
+	jne	L0
+	int	$3		# Stack not unaligned
+
+FUNC(do_test_aligned)
+	# Verify that incoming stack is aligned
+	pushf
+	test	$0xf, %rsp
+	je	L0
+	int	$3		# Stack not aligned
+L0:
+	popf
+
+	# Save registers
+	lea	test_data(%rip), %rax
+	call	regs_to_mem
+
+	# Load register with random data
+	lea	test_data+224(%rip), %rax
+	call	mem_to_regs
+
+	# Save original return address
+	pop	%rax
+	movq    %rax, test_data+680(%rip)
+
+	# Call the test function
+	call	*test_data+672(%rip)
+
+	# Restore the original return address
+	movq    test_data+680(%rip), %rcx
+	push	%rcx
+
+	# Save test function return value and store resulting register values
+	push	%rax
+	lea	test_data+448(%rip), %rax
+	call	regs_to_mem
+
+	# Restore registers
+	lea	test_data(%rip), %rax
+	call	mem_to_regs
+	pop	%rax
+	retq
+        .cfi_endproc
+FUNC_END(do_test_aligned)
+FUNC_END(do_test_unaligned)
+
+#endif /* __x86_64__ */
diff --git a/gcc/testsuite/gcc.target/i386/msabi/gen.cc b/gcc/testsuite/gcc.target/i386/msabi/gen.cc
new file mode 100644
index 00000000000..fea0eadb0c7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/gen.cc
@@ -0,0 +1,653 @@
+/* Test program generator for 64-bit Microsoft ABI.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   Contributed by Daniel Santos <daniel.santos@pobox.com>
+
+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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#include <cstdio>
+#include <cassert>
+#include <vector>
+#include <string>
+#include <cstring>
+#include <iostream>
+#include <algorithm>
+#include <iomanip>
+#include <sstream>
+#include <memory>
+#include <stdexcept>
+#include <unistd.h>
+
+using namespace std;
+
+/*****************************************************************************
+ * class list_delimiter
+ *
+ * A bit bucket ostream.
+ ****************************************************************************/
+class null_stream : public std::ostream
+{
+  ostream &write( const char_type* s, std::streamsize count ) {return *this;}
+  ostream &put( char_type ch ) {return *this;}
+};
+
+/*****************************************************************************
+ * class uncopyable
+ *
+ * Standard Effective C++ Item 6.
+ ****************************************************************************/
+class uncopyable
+{
+private:
+  uncopyable (const uncopyable &) = delete;
+  const uncopyable& operator= (const uncopyable &) = delete;
+
+protected:
+  uncopyable() {}
+  ~uncopyable() {}
+};
+
+/*****************************************************************************
+ * class list_delimiter
+ *
+ * A simple class for adding delimiters (no output on the first call to get).
+ ****************************************************************************/
+class list_delimiter : protected uncopyable
+{
+  int m_pos;
+  string m_delim;
+  static string s_empty;
+
+  list_delimiter ();
+
+public:
+  list_delimiter (const char *delim) : m_pos (0), m_delim(delim) {}
+  const string &get () {return m_pos++ ? m_delim : s_empty;}
+  void reset () {m_pos = 0;}
+  int get_pos () {return m_pos;}
+};
+
+string list_delimiter::s_empty = "";
+
+enum optional_regs
+{
+  OPTIONAL_REG_RBX = 0x01,
+  OPTIONAL_REG_RBP = 0x02,
+  OPTIONAL_REG_R12 = 0x04,
+  OPTIONAL_REG_R13 = 0x08,
+  OPTIONAL_REG_R14 = 0x10,
+  OPTIONAL_REG_R15 = 0x20,
+
+  OPTIONAL_REG_ALL = 0x3f,
+  OPTIONAL_REG_HFP_ALL = OPTIONAL_REG_ALL & (~OPTIONAL_REG_RBP)
+};
+
+static const char * const optional_regs_str[] = {
+  "rbx",
+  "rbp",
+  "r12",
+  "r13",
+  "r14",
+  "r15",
+};
+
+/*****************************************************************************
+ * class arg
+ *
+ * A simple type & name representation of a function parameter.
+ ****************************************************************************/
+class arg
+{
+  string name;
+  string type;
+  bool type_is_integral:1;
+
+  arg(); /* no default ctor */
+
+public:
+  arg(const char *name, const char *type, bool type_is_integral);
+
+  bool is_type_integral () const	{return type_is_integral;}
+  const string &get_name () const	{return name;}
+  const string &get_type () const	{return type;}
+
+  void print (ostream &out) const
+  {
+    out << type << " " << name.c_str();
+  }
+};
+
+arg::arg(const char *name, const char *type, bool type_is_integral)
+    : name (name), type (type), type_is_integral (type_is_integral)
+{
+}
+
+enum fn_variants {
+  FN_VAR_MSABI		= 0x01,
+  FN_VAR_HFP		= 0x02,
+  FN_VAR_REALIGN	= 0x04,
+  FN_VAR_ALLOCA		= 0x08,
+  FN_VAR_VARARGS	= 0x10,
+  FN_VAR_SIBCALL	= 0x20,
+  FN_VAR_SHRINK_WRAP	= 0x40,
+
+  FN_VAR_HFP_OR_REALIGN	= FN_VAR_HFP | FN_VAR_REALIGN,
+  FN_VAR_MASK		= 0x7f,
+  FN_VAR_COUNT		= 7
+};
+
+/*****************************************************************************
+ * class fn
+ *
+ * Class that represents a Microsoft or System V ABI function with varying
+ * parameters, quirks and optimization goals.
+ ****************************************************************************/
+class fn : protected uncopyable
+{
+private:
+  const vector<arg> &m_args;
+  string m_name;
+  string m_attr_str;
+  int m_clobbers:FN_VAR_COUNT;
+  int m_var;
+
+  fn () = delete; /* no default ctor */
+
+public:
+  fn (const vector<arg> &args, int clobbers, int var);
+
+  void print_params (ostream &out) const;
+  void print_decl (ostream &out, bool for_def = false) const;
+  void print_def (ostream &out) const;
+  const string &get_name () const	{return m_name;}
+  const vector<arg> &get_args () const	{return m_args;}
+
+  bool get_hfp_or_realign () const	{return m_var & FN_VAR_HFP_OR_REALIGN;}
+  bool get_msabi () const		{return m_var & FN_VAR_MSABI;}
+  bool get_hfp () const			{return m_var & FN_VAR_HFP;}
+  bool get_realign () const		{return m_var & FN_VAR_REALIGN;}
+  bool get_alloca () const		{return m_var & FN_VAR_ALLOCA;}
+  bool get_varargs () const		{return m_var & FN_VAR_VARARGS;}
+  bool get_sibcall () const		{return m_var & FN_VAR_SIBCALL;}
+  bool get_shrink_wrap () const		{return m_var & FN_VAR_SHRINK_WRAP;}
+};
+
+fn::fn (const vector<arg> &args, int clobbers, int var)
+    : m_args (args)
+    , m_name ()
+    , m_attr_str ("noinline")
+    , m_clobbers (clobbers)
+    , m_var (var)
+{
+  assert (!(var & ~FN_VAR_MASK));
+
+  if (get_hfp () && get_realign ())
+    throw invalid_argument ("`hfp' with `realign' does nothing.");
+
+  if (get_varargs () && args.empty ())
+    throw invalid_argument ("Need at least one normal argument to use varargs");
+
+  std::stringstream name;
+  name << (get_msabi () ? "msabi_" : "sysv_");
+  if (get_msabi ())
+    name << setfill('0') << setw(2) << hex << m_clobbers << "_";
+  name << (get_realign () ? "r" : (get_hfp () ? "f" : ""))
+       << (get_alloca () ? "a" : "")
+       << (get_varargs () ? "v" : "")
+       << (get_sibcall () ? "s" : "")
+       << (get_shrink_wrap () ? "w" : "");
+  name << setw(0) << dec << (unsigned)args.size();
+  m_name = name.str();
+
+  if (get_msabi ())
+      m_attr_str += ", ms_abi";
+
+  if (get_realign ())
+    m_attr_str += ", __force_align_arg_pointer__";
+  else if (get_hfp ())
+    m_attr_str += ", optimize (\"no-omit-frame-pointer\")";
+
+}
+
+/* Print the parameters for a function declaration.  */
+void fn::print_params (ostream &out) const
+{
+  list_delimiter comma (", ");
+
+  vector<arg>::const_iterator i;
+  if (get_alloca () && !get_msabi ())
+    out << comma.get () << "void *alloca_mem";
+  for (i = m_args.begin(); i != m_args.end(); ++i)
+    {
+      out << comma.get ();
+      i->print (out);
+    }
+
+  if (get_varargs ())
+    out << comma.get () << (get_msabi () ? "..." : "va_list argptr");
+}
+
+/* Print the declaration for a function.  */
+void fn::print_decl (ostream &out, bool for_def) const
+{
+  assert (!(get_hfp () || get_realign ()) || !(m_clobbers & OPTIONAL_REG_RBP));
+
+  if (!for_def)
+    out << "extern ";
+  out << "__attribute__ ((" << m_attr_str << ")) long " << m_name << " (";
+  print_params (out);
+  out << ")";
+  if (!for_def)
+    out << ";" << endl;
+}
+
+/* Print the definition of a function.  */
+void fn::print_def (ostream &out) const
+{
+  vector<arg>::const_iterator i;
+
+  print_decl (out, true);
+  out << endl << "{" << endl;
+
+  if (get_msabi () && get_alloca ())
+    {
+      const char *size_str = m_args.empty () ? "42" : "a";
+      out << "  /* Uses alloca.  */" << endl
+	  << "  void *alloca_mem = alloca (8 + " << size_str << ");" << endl
+	  << "  *(long*)alloca_mem = FLAG_ALLOCA;" << endl;
+    }
+  if (get_msabi () && get_varargs ())
+      out << "  va_list argptr;" << endl;
+  if (get_sibcall ())
+    out << "  /* Sibling call.  */" << endl;
+  if (get_shrink_wrap ())
+    out << "  /* Shrink wrap.  */" << endl
+	<< "  if (shrink_wrap_global == FLAG_SHRINK_WRAP_FAST_PATH)" << endl
+	<< "    return FLAG_SHRINK_WRAP_FAST_PATH;" << endl;
+
+  list_delimiter comma (", ");
+  if (m_clobbers)
+  {
+    out << "  __asm__ __volatile__ (\"\" :::";
+    unsigned c;
+    unsigned mask = m_clobbers;
+    comma.reset ();
+    for (c = 0, mask = m_clobbers; mask; ++c, mask >>= 1)
+      if (mask & 1)
+	out << comma.get () << "\"" << optional_regs_str[c] << "\"";
+    out << ");" << endl;
+  }
+
+  if (get_msabi () && get_varargs ())
+  {
+    assert (!m_args.empty ());
+    out << "  va_start(argptr, " << m_args.back ().get_name () << ");" << endl;
+  }
+
+  out << "  return ";
+  if (get_msabi ())
+    {
+      if (get_sibcall ())
+	out << "do_sibcall (";
+
+      comma.reset ();
+      out << "sysv_"
+	  << (get_alloca () ? "a" : "")
+	  << (get_varargs () ? "v" : "")
+	  << m_args.size ()
+	  << " (";
+
+      if (get_alloca ())
+	out << comma.get () << "alloca_mem";
+      for (i = m_args.begin(); i != m_args.end(); ++i)
+	out << comma.get () << i->get_name ();
+      if (get_varargs ())
+	out << comma.get () << "argptr";
+      out << ")";
+      if (get_shrink_wrap ())
+	out << " + FLAG_SHRINK_WRAP_SLOW_PATH";
+      if (get_sibcall ())
+	out << ")";
+    }
+  else
+    {
+      list_delimiter plus (" + ");
+      for (i = m_args.begin(); i != m_args.end(); ++i)
+	  if (i->is_type_integral ())
+	    out << plus.get () << i->get_name ();
+      if (get_alloca ())
+	out << plus.get () << "*(long*)alloca_mem";
+      if (!plus.get_pos ())
+	out << "0";
+    }
+  out << ";" << endl;
+  if (get_msabi () && get_varargs ())
+    out << "  va_end(argptr);" << endl;
+  out << "}" << endl << endl;
+}
+
+/*****************************************************************************
+ *
+ ****************************************************************************/
+
+static void make_do_tests_decl (const vector<class arg> &args, ostream &out)
+{
+  vector<class arg>::const_iterator ai;
+  unsigned i, varargs, unaligned;
+
+  out << "extern __attribute__ ((ms_abi)) long do_test_aligned ();" << endl
+      << "extern __attribute__ ((ms_abi)) long do_test_unaligned ();" << endl;
+
+  list_delimiter comma (", ");
+  for (i = 0; i <= args.size (); ++i)
+    for (unaligned = 0; unaligned <= 1; ++unaligned)
+      for (varargs = 0; varargs <= 1; ++varargs)
+	{
+	  if (!i && varargs)  /* skip varargs version when no other args */
+	    continue;
+
+	  comma.reset ();
+	  out << "static __attribute__ ((ms_abi)) long (*do_test_"
+	      << (unaligned ? "u" : "")
+	      << (varargs ? "v" : "") << i << ") (";
+
+	  unsigned j;
+	  for (j = 0, ai = args.begin (); j < i; ++j, ++ai)
+	    out << comma.get () << ai->get_type () << " "
+		<< ai->get_name ();
+	  if (varargs)
+	    out << comma.get () << "...";
+	  out << ") = (void*)do_test_" << (unaligned ? "un" : "")
+	      << "aligned;" << endl;
+	}
+}
+
+void make_do_test (const vector<class arg> &args,
+		   const vector<class fn*> &msabi_funcs,
+		   ostream &out)
+{
+  unsigned i;
+  vector<class arg>::const_iterator ai;
+
+  out << "void do_tests ()" << endl
+      << "{" << endl
+      << "  long ret, expect;" << endl << endl;
+
+  for (i = 1, ai = args.begin (); ai != args.end (); ++i, ++ai)
+    out << "  " << ai->get_type () << " " << ai->get_name () << " = " << i
+	<< ";" << endl;
+  out << endl;
+
+  vector<class fn*>::const_iterator fi;
+  for (fi = msabi_funcs.begin(); fi != msabi_funcs.end(); ++fi)
+    {
+      const fn &f = **fi;
+      unsigned unaligned, shrink_wrap;
+
+      for (unaligned = 0; unaligned <= !!f.get_realign (); ++unaligned)
+	for (shrink_wrap = 0; shrink_wrap <= !!f.get_shrink_wrap ();
+	     ++shrink_wrap)
+	  {
+	    const vector<class arg> &fargs = f.get_args ();
+	    out << "  init_test (" << f.get_name () << ", \""
+		<< f.get_name () << "\", ";
+
+	    if (f.get_realign ())
+	      out << (unaligned ? "ALIGNMENT_ALIGNED_PLUS_8"
+				: "ALIGNMENT_ALIGNED");
+	    else
+	      out << "ALIGNMENT_NOT_TESTED";
+
+	    out << ", ";
+	    if (f.get_shrink_wrap ())
+	      out << (shrink_wrap ? "SHRINK_WRAP_SLOW_PATH"
+				  : "SHRINK_WRAP_FAST_PATH");
+	    else
+	      out << "SHRINK_WRAP_NONE";
+	    out << ");" << endl;
+
+	    if (f.get_realign () && unaligned == 1)
+	      out << "  __asm__ __volatile__ (\"subq $8,%%rsp\":::\"cc\");"
+		  << endl;
+
+	    out << "  ret = do_test_"
+		<< (f.get_realign () && unaligned == 1 ? "u" : "")
+		<< (f.get_varargs () ? "v" : "")
+		<< fargs.size () << " (";
+
+	    list_delimiter comma (", ");
+	    for (ai = fargs.begin (); ai != fargs.end (); ++ai)
+	      out << comma.get () << ai->get_name ();
+
+	    out << ");" << endl;
+	    if (f.get_realign () && unaligned == 1)
+	      out << "  __asm__ __volatile__ (\"addq $8,%%rsp\":::\"cc\");"
+		  << endl;
+	    if (f.get_shrink_wrap () && shrink_wrap == 0)
+	      out << "  expect = FLAG_SHRINK_WRAP_FAST_PATH";
+	    else
+	      {
+		out << "  expect = 0";
+		for (ai = fargs.begin (); ai != fargs.end (); ++ai)
+		  out << " + " << ai->get_name ();
+		if (f.get_sibcall ())
+		  out << " + FLAG_SIBCALL";
+		if (f.get_alloca ())
+		  out  << " + FLAG_ALLOCA";
+		if (f.get_shrink_wrap () && shrink_wrap == 1)
+		  out << " + FLAG_SHRINK_WRAP_SLOW_PATH";
+	      }
+	    out << ";" << endl
+		<< "  fail_if_not_equal (ret, expect);" << endl
+		<< "  compare_regdata ();" << endl
+		<< endl;
+	  }
+    }
+  out << "}" << endl;
+}
+
+void usage (const char *arg0)
+{
+  cerr << "Usage: " << arg0 << "[-p=<n>] [-m=<n>] [-f] <msabi.c|do_tests.c>"
+       << endl
+       << endl
+       << "    -p <n>    Set maxiumum number of extra parameters "
+       << "(defaults to 8)" << endl
+       << "    -m <n>    Set mask of test variants (see enum fn_variants for "
+       << "values, defaults to 0x" << hex << FN_VAR_MASK << " [FN_VAR_MASK])"
+       << endl;
+  exit (-1);
+}
+
+static bool long_optarg (const char *optarg, long &l)
+{
+  const char *start = optarg;
+  int base = 10;
+  char *end;
+
+  if (!strncasecmp (optarg, "0x", 2))
+  {
+    base = 16;
+    start += 2;
+  }
+
+  l = strtol(start, &end, base);
+
+  return end == start || *end != 0;
+}
+
+int main (int argc, char *argv[])
+{
+  vector<class arg> all_args;
+  vector<vector<class arg> > arg_sets;
+  vector<class fn*> sysv_funcs;
+  vector<class fn*> msabi_funcs;
+  unsigned extra_param_count_max = 8;
+  unsigned fn_variant_mask = 0;
+  unsigned i;
+  int c;
+
+  while ((c = getopt (argc, argv, "p:m:")) != -1)
+    {
+      switch (c)
+	{
+	long l;
+	case 'p':
+	  if (long_optarg (optarg, l) || l < 0 || l > 16)
+	  {
+	    cerr << "ERROR: Bad value for -p: `" << optarg <<  "`" << endl;
+	    usage (argv[0]);
+	  }
+	  extra_param_count_max = (unsigned)l;
+	  break;
+
+	case 'm':
+	  if (long_optarg (optarg, l) || (l & ~FN_VAR_MASK))
+	  {
+	    cerr << "ERROR: Bad value for -m: `" << optarg <<  "`" << endl;
+	    usage (argv[0]);
+	  }
+	  else if (l & FN_VAR_MSABI)
+	  {
+	    cerr << "ERROR: Can't mask out MSABI functions." << endl;
+	    usage (argv[0]);
+	  }
+	  fn_variant_mask = (unsigned)l;
+	  break;
+
+	default:
+	  usage (argv[0]);
+	}
+    }
+  if (argc - optind != 1)
+    usage (argv[0]);
+
+  /* Setup output streams.  We only output one at a time, so for laziness,
+     we just set one stream to a null_stream (bit bucket) and the other to
+     cout. */
+  null_stream ns;
+  ostream *fns_c = &ns;
+  ostream *do_tests_c = &ns;
+  string arg (argv[optind]);
+  if (arg == "fns.c")
+    fns_c = &cout;
+  else if (arg == "do_tests.c")
+    do_tests_c = &cout;
+  else
+    usage (argv[0]);
+
+  *fns_c << "#include \"msabi.h\"" << endl << endl;
+  *do_tests_c << "#include \"msabi.h\"" << endl << endl;
+
+  for (i = 0; i < extra_param_count_max; ++i)
+  {
+    char name[2] = "a";
+    name[0] += i;
+    class arg myarg (name, "long", true);
+
+    all_args.push_back (myarg);
+  }
+
+  arg_sets.resize (extra_param_count_max + 1);
+  for (i = 0; i < arg_sets.size(); ++i)
+    {
+      unsigned j;
+      for (j = i; j < arg_sets.size() - 1; ++j)
+	arg_sets[j + 1].push_back (all_args[i]);
+    }
+
+  /* Print sysv functions */
+  vector<vector<class arg> >::const_iterator asi;
+  for (asi = arg_sets.begin(); asi != arg_sets.end(); ++asi)
+    {
+      fn *fn;
+      for (int _alloca = 0; _alloca <= 1; ++_alloca)
+	for (int varargs = 0; varargs <= 1; ++varargs)
+	{
+	  try {
+	    int var = (_alloca ? FN_VAR_ALLOCA : 0)
+		    | (varargs ? FN_VAR_VARARGS : 0);
+	    fn = new ::fn (*asi, 0, var);
+	  } catch (invalid_argument) {
+	    continue;
+	  }
+	  sysv_funcs.push_back (fn);
+	  fn->print_decl (*fns_c);
+	}
+    }
+
+  /* Print ms_abi functions.  */
+  int var;
+  for (var = 0; var <= FN_VAR_MASK; ++var)
+    {
+      /* We only want msabi fns now.  */
+      if (! (var & FN_VAR_MSABI))
+	continue;
+
+      /* Skip explicitly masked variants */
+      if (var & fn_variant_mask)
+	continue;
+
+      unsigned clobbers;
+      for (clobbers = 0; clobbers <= OPTIONAL_REG_ALL; ++clobbers)
+	{
+	  /* Skip clobbers that would be invalid.  */
+	  if (clobbers & OPTIONAL_REG_RBP)
+	    {
+	      /* Uses BP explicitly.  */
+	      if (var & FN_VAR_HFP_OR_REALIGN)
+		continue;
+
+	      /* Alloca seems to require DRAP, which uses BP.  */
+	      if (var & FN_VAR_ALLOCA)
+		continue;
+	    }
+
+	  for (asi = arg_sets.begin(); asi != arg_sets.end(); ++asi)
+	    {
+	      fn *fn;
+	      try {
+		fn = new ::fn (*asi, clobbers, var);
+	      } catch (invalid_argument) {
+		continue;
+	      }
+
+	      msabi_funcs.push_back (fn);
+	      fn->print_decl (*do_tests_c);
+	      fn->print_def (*fns_c);
+	    }
+	}
+    }
+
+  *do_tests_c << endl;
+  make_do_tests_decl (all_args, *do_tests_c);
+  *do_tests_c << endl;
+
+  vector<class fn*>::const_iterator fi;
+  for (fi = sysv_funcs.begin (); fi != sysv_funcs.end (); ++fi)
+    (*fi)->print_def (*do_tests_c);
+
+  make_do_test (all_args, msabi_funcs, *do_tests_c);
+
+  for_each (sysv_funcs.begin (), sysv_funcs.end (), default_delete<fn> ());
+  for_each (msabi_funcs.begin (), msabi_funcs.end (), default_delete<fn> ());
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/msabi/msabi.c b/gcc/testsuite/gcc.target/i386/msabi/msabi.c
new file mode 100644
index 00000000000..cc10981c331
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/msabi.c
@@ -0,0 +1,304 @@
+/* Microsoft 64 ABI test program.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   Contributed by Daniel Santos <daniel.santos@pobox.com>
+
+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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+/* This is a single-threaded test program for Microsoft 64-bit ABI functions.
+   It is aimed at verifying correctness of pro/epilogues of ms_abi functions
+   that call sysv functions to assure clobbered registers are properly saved
+   and restored and attempt to detect any flaws in the behavior of these
+   functions.  The following variants are tested:
+
+   * Either uses hard frame pointer (-fno-omit-frame-pointer), re-aligns
+     the stack (e.g., attribute  force_align_arg_pointer) or neither,
+   * Uses alloca (and thus DRAP) or not,
+   * Uses sibling call optimization or not (NOTE: we don't actually verify
+     that sibcall optimization is used),
+   * Uses variable argument list or not, and
+   * Has shrink-wrapped code or not (NOTE: again, we don't actually verify
+     anything is shrink-wrapped).
+
+  In addition, an ms_abi function is generated for each of these combinations
+  clobbering each unique combination additional registers (excluding BP when
+  a frame pointer is used). Shrink-wrap variants are called in a way that
+  both the fast and slow path are used. Re-aligned variants are called with
+  an aligned and mis-aligned stack.
+
+  Each ms_abi function is called via a hacky assembly stub that first saves
+  all volatile registers and fills them with random values. The ms_abi
+  function is then called.  After the function returns, the value of all
+  volatile registers is verified against the random data and then restored.
+
+  Currently, we only test passing a variable number of long arguments as
+  parameters.  It could be helpful to also pass larger parameters that would
+  always get passed on the stack.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "msabi.h"
+
+#ifndef __x86_64__
+# error Test only valid on x86_64
+#endif
+
+struct test_data test_data;
+int shrink_wrap_global;
+static struct drand48_data rand_data;
+static int arbitrarily_fail;
+static const char *argv0;
+
+void init_test (void *fn, const char *name, enum alignment_option unaligned,
+		enum shrink_wrap_option shrink_wrap)
+{
+  int i;
+  union regdata *data = &test_data.regdata[REG_SET_INPUT];
+
+  assert (unaligned < ALIGNMENT_COUNT);
+  assert (shrink_wrap < SHRINK_WRAP_COUNT);
+
+  memset (&test_data, 0, sizeof (test_data));
+  for (i = 55; i >= 0; --i)
+    {
+      uint64_t u64;
+      lrand48_r (&rand_data, (long*)&u64);
+      data->u32_arr[i] = (uint32_t)u64;
+    }
+  test_data.fn = fn;
+  test_data.name = name;
+  test_data.unaligned = unaligned;
+  test_data.shrink_wrap = shrink_wrap;
+
+  switch (shrink_wrap)
+  {
+    case SHRINK_WRAP_NONE:
+    case SHRINK_WRAP_COUNT:
+      break;
+    case SHRINK_WRAP_FAST_PATH:
+      shrink_wrap_global = FLAG_SHRINK_WRAP_FAST_PATH;
+      break;
+    case SHRINK_WRAP_SLOW_PATH:
+      shrink_wrap_global = FLAG_SHRINK_WRAP_SLOW_PATH;
+      break;
+  }
+}
+
+static const char *alignment_str[ALIGNMENT_COUNT] =
+{
+  "", "aligned", "unaligned"
+};
+
+static const char *shrink_wrap_str[SHRINK_WRAP_COUNT] =
+{
+  "", "shrink-wrap fast path", "shrink-wrap slow path"
+};
+
+static const char *test_descr ()
+{
+  static char buffer[0x400];
+
+  if (test_data.unaligned || test_data.shrink_wrap)
+    snprintf (buffer, sizeof (buffer) - 1, "`%s' (%s%s%s)",
+	      test_data.name,
+	      alignment_str[test_data.unaligned],
+	      (test_data.unaligned && test_data.shrink_wrap ? ", " : ""),
+	      shrink_wrap_str[test_data.shrink_wrap]);
+  else
+    snprintf (buffer, sizeof (buffer) - 1, "`%s'", test_data.name);
+
+  return buffer;
+}
+
+void fail_if_not_equal (long got, long expected)
+{
+  if (got != expected)
+    {
+      fprintf (stderr, "Failed on test function %s: got 0x%016lx, "
+	       "expected 0x%016lx\n", test_descr (), got, expected);
+      raise (SIGTRAP);
+      exit (-1);
+    }
+}
+
+static const char *regnames[] = {
+  "XMM6",
+  "XMM7",
+  "XMM8",
+  "XMM9",
+  "XMM10",
+  "XMM11",
+  "XMM12",
+  "XMM13",
+  "XMM14",
+  "XMM15",
+  "RSI",
+  "RDI",
+  "RBX",
+  "RBP",
+  "R12",
+  "R13",
+  "R14",
+  "R15",
+};
+
+static void print_header (int *header_printed)
+{
+  if (!*header_printed)
+    fprintf (stderr, "       %-35s    %-35s\n", "Expected", "Got");
+  *header_printed = 1;
+}
+
+static int compare_reg128 (const __uint128_t *a, const __uint128_t *b,
+			   const char *name, int *header_printed)
+{
+  if (!memcmp (a, b, sizeof (*a)))
+    return 0;
+  else
+    {
+      long ha = *((long*)a);
+      long la = *((long*)a + 16);
+      long hb = *((long*)b);
+      long lb = *((long*)a + 16);
+      print_header (header_printed);
+      fprintf (stderr, "%-5s: 0x%016lx %016lx != 0x%016lx %016lx\n",
+	       name, ha, la, hb, lb);
+      return 1;
+    }
+}
+
+static int compare_reg64 (long a, long b, const char *name,
+			  int *header_printed)
+{
+  if (a == b)
+    return 0;
+  else
+    {
+      print_header (header_printed);
+      fprintf (stderr, "%s: 0x%016lx != 0x%016lx\n", name, a, b);
+      return 1;
+    }
+}
+
+__attribute__((noinline))
+void compare_regdata ()
+{
+  unsigned i;
+  unsigned bad = 0;
+  int header_printed = 0;
+
+  union regdata *a = &test_data.regdata[REG_SET_INPUT];
+  union regdata *b = &test_data.regdata[REG_SET_OUTPUT];
+
+  a = __builtin_assume_aligned(a, 16);
+  b = __builtin_assume_aligned(b, 16);
+
+  if (arbitrarily_fail) {
+    uint64_t u64;
+    lrand48_r (&rand_data, (long*)&u64);
+    if (!(u64 & 0xff))
+      b->u32_arr[u64 % 56] = 0xfdfdfdfd;
+  }
+
+  for (i = 0; i < 10; ++i)
+    bad |= compare_reg128 (&a->sseregs[i], &b->sseregs[i], regnames[i],
+			   &header_printed);
+
+  for (i = 0; i < 8; ++i)
+    bad |= compare_reg64 (a->intregs[i], b->intregs[i], regnames[i + 10],
+			  &header_printed);
+
+  if (bad)
+    {
+      fprintf (stderr, "Failed on test function %s\n", test_descr ());
+      raise (SIGTRAP);
+      exit (-1);
+    }
+}
+
+__attribute__((ms_abi)) long do_sibcall (long arg) {
+  return arg + FLAG_SIBCALL;
+}
+
+
+void usage ()
+{
+  fprintf (stderr, "Usage: %s [-s <seed>] [-f]\n", argv0);
+  exit (-1);
+}
+
+static int long_optarg (const char *optarg, long *l)
+{
+  const char *start = optarg;
+  int base = 10;
+  char *end;
+
+  if (!strncasecmp (optarg, "0x", 2))
+  {
+    base = 16;
+    start += 2;
+  }
+
+  *l = strtol(start, &end, base);
+
+  return end == start || *end != 0;
+}
+
+int main (int argc, char *argv[])
+{
+  long seed = 0;
+  int c;
+  argv0 = argv[0];
+
+  while ((c = getopt (argc, argv, "s:f")) != -1)
+    {
+      switch (c)
+	{
+	case 's':
+	  if (long_optarg (optarg, &seed))
+	  {
+	    fprintf (stderr, "ERROR: Bad value for -s: `%s`\n", optarg);
+	    exit (-1);
+	  }
+	  break;
+
+	case 'f':
+	  arbitrarily_fail = 1;
+	  break;
+	}
+    }
+
+  assert (!((long)&test_data.regdata[REG_SET_SAVE] & 15));
+  assert (!((long)&test_data.regdata[REG_SET_INPUT] & 15));
+  assert (!((long)&test_data.regdata[REG_SET_OUTPUT] & 15));
+
+  srand48_r (seed, &rand_data);
+  do_tests ();
+
+  return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.target/i386/msabi/msabi.h b/gcc/testsuite/gcc.target/i386/msabi/msabi.h
new file mode 100644
index 00000000000..ac046dc40dd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/msabi.h
@@ -0,0 +1,110 @@
+/* Microsoft 64 ABI test program header.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   Contributed by Daniel Santos <daniel.santos@pobox.com>
+
+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.
+
+Under Section 7 of GPL version 3, you are granted additional
+permissions described in the GCC Runtime Library Exception, version
+3.1, as published by the Free Software Foundation.
+
+You should have received a copy of the GNU General Public License and
+a copy of the GCC Runtime Library Exception along with this program;
+see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef _MSABI_H
+#define _MSABI_H
+
+#include <stdint.h>
+#include <alloca.h>
+#include <stdarg.h>
+#include <assert.h>
+
+union regdata {
+  struct {
+    __uint128_t	sseregs[10];
+    union {
+      uint64_t	intregs[8];
+      struct {
+	uint64_t	rsi;
+	uint64_t	rdi;
+	uint64_t	rbx;
+	uint64_t	rbp;
+	uint64_t	r12;
+	uint64_t	r13;
+	uint64_t	r14;
+	uint64_t	r15;
+      };
+    };
+  };
+  uint32_t		u32_arr[56];
+} __attribute__((aligned (16)));
+
+enum reg_data_sets
+{
+  REG_SET_SAVE,
+  REG_SET_INPUT,
+  REG_SET_OUTPUT,
+
+  REG_SET_COUNT
+};
+
+enum flags
+{
+  FLAG_ALLOCA			= 0x01000000,
+  FLAG_SIBCALL			= 0x02000000,
+  FLAG_SHRINK_WRAP_FAST_PATH	= 0x08000000,
+  FLAG_SHRINK_WRAP_SLOW_PATH	= 0x0c000000,
+};
+
+enum alignment_option
+{
+  ALIGNMENT_NOT_TESTED,
+  ALIGNMENT_ALIGNED,
+  ALIGNMENT_ALIGNED_PLUS_8,
+
+  ALIGNMENT_COUNT,
+};
+
+enum shrink_wrap_option
+{
+  SHRINK_WRAP_NONE,
+  SHRINK_WRAP_FAST_PATH,
+  SHRINK_WRAP_SLOW_PATH,
+
+  SHRINK_WRAP_COUNT
+};
+
+struct test_data
+{
+  union regdata regdata[REG_SET_COUNT];
+  void *fn;
+  void *retaddr;
+  const char *name;
+  enum alignment_option unaligned;
+  enum shrink_wrap_option shrink_wrap;
+};
+
+extern struct test_data test_data;
+extern int shrink_wrap_global;
+
+extern void do_tests ();
+extern void init_test (void *fn, const char *name,
+		       enum alignment_option unaligned,
+		       enum shrink_wrap_option shrink_wrap);
+extern void fail_if_not_equal (long a, long b);
+extern void compare_regdata ();
+extern __attribute__((ms_abi)) long do_sibcall (long arg);
+
+#endif /* _MSABI_H */
-- 
2.11.0


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]