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