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.san...@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.san...@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.san...@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.san...@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.san...@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

Reply via email to