diff mbox

[12/12,i386,testsuite] Test program for ms to sysv abi function calls.

Message ID 20170427080932.11703-12-daniel.santos@pobox.com
State New
Headers show

Commit Message

Daniel Santos April 27, 2017, 8:09 a.m. UTC
A comprehensive program for testing x86_64 ms_abi functions that call
sysv_abi functions to help validate -mcall-ms2sysv-xlogues and use of
aligned SSE MOVs after a (non-DRAP) realigned stack.

Signed-off-by: Daniel Santos <daniel.santos@pobox.com>
---
 gcc/Makefile.in                                    |   2 +
 .../gcc.target/x86_64/abi/ms-sysv/do-test.S        | 163 +++++
 gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/gen.cc | 807 +++++++++++++++++++++
 .../gcc.target/x86_64/abi/ms-sysv/ms-sysv.c        | 373 ++++++++++
 .../gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp      | 178 +++++
 5 files changed, 1523 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/do-test.S
 create mode 100644 gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/gen.cc
 create mode 100644 gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.c
 create mode 100644 gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp

Comments

Thomas Preudhomme May 17, 2017, 9:33 a.m. UTC | #1
Hi Daniel,

On 27/04/17 09:09, Daniel Santos wrote:
> A comprehensive program for testing x86_64 ms_abi functions that call
> sysv_abi functions to help validate -mcall-ms2sysv-xlogues and use of
> aligned SSE MOVs after a (non-DRAP) realigned stack.
>
> Signed-off-by: Daniel Santos <daniel.santos@pobox.com>
> ---
>  gcc/Makefile.in                                    |   2 +
>  .../gcc.target/x86_64/abi/ms-sysv/do-test.S        | 163 +++++
>  gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/gen.cc | 807 +++++++++++++++++++++
>  .../gcc.target/x86_64/abi/ms-sysv/ms-sysv.c        | 373 ++++++++++
>  .../gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp      | 178 +++++
>  5 files changed, 1523 insertions(+)
>  create mode 100644 gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/do-test.S
>  create mode 100644 gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/gen.cc
>  create mode 100644 gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.c
>  create mode 100644 gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp
>

[SNIP]

> diff --git a/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp b/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp
> new file mode 100644
> index 00000000000..e317af9bd85
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp
> @@ -0,0 +1,178 @@

[SNIP]

> +
> +# Exit immediately if this isn't a native x86_64 target.
> +if { (![istarget x86_64-*-*] && ![istarget i?86-*-*])
> +     || ![is-effective-target lp64] || ![isnative] } then {
> +    unsupported "$subdir"
> +    return
> +}

This reports these tests as UNSUPPORTED for non x86_64 target rather than just 
not showing these tests. The usual pattern from what I could see is to just 
return (see gcc.target/arm/acle.exp)

Best regards,

Thomas
diff mbox

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index f675e073ecc..7f7c238127b 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -3807,7 +3807,9 @@  site.exp: ./config.status Makefile
 	@echo "set CFLAGS \"\"" >> ./site.tmp
 	@echo "set CXXFLAGS \"\"" >> ./site.tmp
 	@echo "set HOSTCC \"$(CC)\"" >> ./site.tmp
+	@echo "set HOSTCXX \"$(CXX)\"" >> ./site.tmp
 	@echo "set HOSTCFLAGS \"$(CFLAGS)\"" >> ./site.tmp
+	@echo "set HOSTCXXFLAGS \"$(CXXFLAGS)\"" >> ./site.tmp
 # TEST_ALWAYS_FLAGS are flags that should be passed to every compilation.
 # They are passed first to allow individual tests to override them.
 	@echo "set TEST_ALWAYS_FLAGS \"$(SYSROOT_CFLAGS_FOR_TARGET)\"" >> ./site.tmp
diff --git a/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/do-test.S b/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/do-test.S
new file mode 100644
index 00000000000..1395235fd1e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/do-test.S
@@ -0,0 +1,163 @@ 
+/* Assembly proxy functions for ms_abi tests.
+   Copyright (C) 2016-2017 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_BEGIN(fn)       .type fn,@function
+#  define ELFFN_END(fn)         .size fn,.-fn
+# else
+#  define ELFFN_BEGIN(fn)
+#  define ELFFN_END(fn)
+# endif
+
+# define FUNC(fn)		\
+	.global fn;		\
+	ELFFN_BEGIN(fn);	\
+fn:
+
+#define FUNC_END(fn) ELFFN_END(fn)
+
+# ifdef __AVX__
+#  define MOVAPS vmovaps
+# else
+#  define MOVAPS movaps
+# endif
+
+/* TODO: Is there a cleaner way to provide these offsets?  */
+	.struct 0
+test_data_save:
+
+	.struct test_data_save + 224
+test_data_input:
+
+	.struct test_data_save + 448
+test_data_output:
+
+	.struct test_data_save + 672
+test_data_fn:
+
+	.struct test_data_save + 680
+test_data_retaddr:
+
+	.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
+	# The below alignment checks are to verify correctness of the test
+	# its self.
+
+	# 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 + test_data_input(%rip), %rax
+	call	mem_to_regs
+
+	# Save original return address
+	pop	%rax
+	movq    %rax, test_data + test_data_retaddr(%rip)
+
+	# Call the test function
+	call	*test_data + test_data_fn(%rip)
+
+	# Restore the original return address
+	movq    test_data + test_data_retaddr(%rip), %rcx
+	push	%rcx
+
+	# Save test function return value and store resulting register values
+	push	%rax
+	lea	test_data + test_data_output(%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/x86_64/abi/ms-sysv/gen.cc b/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/gen.cc
new file mode 100644
index 00000000000..947a12bf2ae
--- /dev/null
+++ b/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/gen.cc
@@ -0,0 +1,807 @@ 
+/* Test program generator for 64-bit Microsoft ABI.
+   Copyright (C) 2016-2017 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 <ios>
+#include <iomanip>
+#include <sstream>
+#include <fstream>
+#include <memory>
+#include <regex>
+#include <stdexcept>
+
+#include <unistd.h>
+#include <getopt.h>
+
+using namespace std;
+
+/* A basic Effective C++ Item 6. */
+class uncopyable
+{
+private:
+  uncopyable (const uncopyable &) = delete;
+  const uncopyable& operator= (const uncopyable &) = delete;
+
+protected:
+  uncopyable() {}
+  ~uncopyable() {}
+};
+
+/* A simple class for adding text delimiters.  */
+class list_delimiter : protected uncopyable
+{
+  int m_pos;
+  string m_delim;
+  static string s_empty;
+
+  list_delimiter ();
+
+public:
+  list_delimiter (const char *delim, int init_pos = 0)
+      : m_pos (init_pos), 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 = "";
+
+/* Bitmasks for representing non-volatile retisters of an ms_abi call that
+   are not already clobbered by a sysv_abi call.  */
+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",
+};
+
+/* A simple type & name representation of a function parameter.  */
+class arg
+{
+  string name;
+  string type;
+  bool type_is_integral:1;
+
+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;}
+};
+
+arg::arg(const char *name, const char *type, bool type_is_integral)
+    : name (name), type (type), type_is_integral (type_is_integral)
+{
+}
+
+/* A stupid operator<< implementation for arg objects.  */
+template<class T> T &operator<< (T &out, const arg &a)
+{
+  return out << a.get_type () << " " << a.get_name ();
+}
+
+/* Bitmask representation of all possible varients of a test function. The
+   value FN_VAR_MSABI is only used internally to distinguish between an
+   ms_abi and sysv_abi function.  */
+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
+};
+
+/* Representation of a Microsoft or System V ABI function with varying
+   parameters, quirks and optimization goals.
+
+   Function name nomenclature:
+     (msabi|sysv)_[xx_][r|f][a][v][s][w]<n>
+      |            |    |    |  |  |  |  |
+      |            |    |    |  |  |  |  Number of extra (long) parameters
+      |            |    |    |  |  |  shrink wrap
+      |            |    |    |  |  sibling call
+      |            |    |    |  varargs
+      |            |    |    alloca
+      |            |    Forced realignment or hard frame pointer
+      |            Explicit clobbers (hexidecimal mask, ms_abi only)
+      Calling Convention  */
+class fn : protected uncopyable
+{
+private:
+  const vector<arg> &m_args;
+  string m_name;
+  string m_attr_decl_str;
+  string m_attr_def_str;
+  int m_clobbers:FN_VAR_COUNT;
+  int m_var;
+
+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_noinfo_def (ostream &out) 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_decl_str ()
+    , m_attr_def_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");
+
+  assert (!(get_hfp () || get_realign ()) || !(clobbers & OPTIONAL_REG_RBP));
+
+  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" : "")
+       << setw(0) << dec << (unsigned)args.size();
+  m_name = name.str();
+
+  list_delimiter decl_comma (", ", !m_attr_decl_str.empty ());
+  list_delimiter def_comma (", ", !m_attr_def_str.empty ());
+  if (get_msabi ())
+    {
+	m_attr_decl_str += decl_comma.get ();
+	m_attr_decl_str += "ms_abi";
+	m_attr_def_str += def_comma.get ();
+	m_attr_def_str += "ms_abi";
+    }
+
+  if (get_realign ())
+    {
+      m_attr_def_str += def_comma.get();
+      m_attr_def_str += "__force_align_arg_pointer__";
+    }
+  else if (get_hfp ())
+    {
+      m_attr_def_str += def_comma.get();
+      m_attr_def_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;
+
+  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
+{
+  const string &attr_str = (for_def ? m_attr_def_str : m_attr_decl_str);
+  if (!for_def)
+    out << "extern ";
+
+  if (!attr_str.empty ())
+    out << "__attribute__ ((" << attr_str << ")) ";
+
+  out << "long " << m_name << " (";
+  print_params (out);
+  out << ")";
+  if (!for_def)
+    out << ";" << endl;
+}
+
+/* Output a volatile "_noinfo" function pointer definition.  */
+void fn::print_noinfo_def (ostream &out) const
+{
+  out << "static ";
+  if (!m_attr_decl_str.empty ())
+    out << "__attribute__ ((" << m_attr_decl_str << ")) ";
+  out << "long (*const volatile " << m_name << "_noinfo) (";
+  print_params (out);
+  out << ") = " << m_name << ";" << 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 << "  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_shrink_wrap ())
+    out << "  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_noinfo (";
+
+      comma.reset ();
+      out << "sysv_"
+	  << (get_alloca () ? "a" : "")
+	  << (get_varargs () ? "v" : "")
+	  << m_args.size ()
+	  << "_noinfo (";
+
+      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;
+}
+
+/* Global variables.  */
+string argv0;
+string out_file_name;
+unsigned int extra_params_min = 0;
+unsigned int extra_params_max = 5;
+unsigned fn_variant_mask = FN_VAR_MASK;
+bool omit_rbp_clobbers = false;
+vector<class fn*> sysv_funcs;
+vector<class fn*> msabi_funcs;
+
+
+/* Emit extern for do_test_aligned and do_test_unaligned (defined in do_test.S)
+   followed by all of the various do_test* function function pointers that
+   are just aliases of them.  */
+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 = extra_params_min; 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 (*const 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;
+	}
+}
+
+/* Generate do_tests function.  We actually break it up into multiple
+   do_test_xxxx functions to keep compile times down (with just one large
+   function, it is a very slow build).  */
+void make_do_test (const vector<class arg> &args,
+		   const vector<class fn*> &msabi_funcs,
+		   ostream &out)
+{
+  const unsigned TESTS_PER_FN_MAX = 64;
+  unsigned i;
+  vector<string> do_tests_fn_names;
+  unsigned fn_count = 0;
+  unsigned test_count = TESTS_PER_FN_MAX;
+  string params_str;
+  string param_names_str;
+  string param_types_str;
+
+  /* Init some commonly used strings.  */
+  {
+    stringstream s1, s2, s3;
+    list_delimiter comma(", ");
+    for (auto arg : args)
+      {
+	const string &c = comma.get ();
+	s1 << c << arg;
+	s2 << c << arg.get_name ();
+	s3 << c << arg.get_type ();
+      }
+    params_str = s1.str ();
+    param_names_str = s2.str ();
+    param_types_str = s3.str ();
+  }
+
+  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 ();
+
+	    /* To prevent unwieldy build times, we split up tests to 64-ish per
+	       function.  */
+	    if (++test_count > TESTS_PER_FN_MAX)
+	      {
+		test_count = 1;
+		if (fn_count > 0) {
+		  out << "}" << endl << endl;
+		}
+
+		stringstream fn_name;
+		fn_name << "do_tests_" << setfill('0') << setw(4) << hex
+		     << fn_count++;
+		do_tests_fn_names.push_back (fn_name.str ());
+
+		out << "static __attribute__((noinline)) void "
+		    << fn_name.str () << " (" << params_str << ")" << endl
+		    << "{" << endl
+		    << "  long ret;" << endl;
+	      }
+
+	    /* Call init_test.  */
+	    out << endl
+		<< "  init_test (" << f.get_name () << ", \""
+		<< f.get_name () << "\", ";
+
+	    if (f.get_realign ())
+	      out << (unaligned ? "ALIGNMENT_MISALIGNED"
+				: "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 << ", ";
+
+	    /* Calculated the expected return value.  */
+	    if (f.get_shrink_wrap () && shrink_wrap == 0)
+	      out << "FLAG_SHRINK_WRAP_FAST_PATH";
+	    else
+	      {
+		list_delimiter plus (" + ");
+		for (auto const &arg : fargs)
+		  out << plus.get () << arg.get_name ();
+		if (f.get_sibcall ())
+		  out << plus.get () << "FLAG_SIBCALL";
+		if (f.get_alloca ())
+		  out << plus.get () << "FLAG_ALLOCA";
+		if (f.get_shrink_wrap () && shrink_wrap == 1)
+		  out << plus.get () << "FLAG_SHRINK_WRAP_SLOW_PATH";
+		if (!plus.get_pos ())
+		  out << "0";
+	      }
+	    out << ");" << endl;
+	    /* End if init_test call.  */
+
+	    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 (auto const &arg : fargs)
+	      out << comma.get () << arg.get_name ();
+	    out << ");" << endl;
+
+	    if (f.get_realign () && unaligned == 1)
+	      out << "  __asm__ __volatile__ (\"addq $8,%%rsp\":::\"cc\");"
+		  << endl;
+
+	    out << "  check_results (ret);" << endl;
+	  }
+    }
+
+  /* Close the last function and define the main do_tests function.  */
+  out << "}" << endl << endl;
+
+  /* Define _noinfo pointers to each do_tests_* function.  */
+  for (auto const &fn_name : do_tests_fn_names)
+    out << "  static void (*volatile " << fn_name << "_noinfo) ("
+	<< param_types_str << ") = " << fn_name << ";" << endl;
+
+  /* Define main do_tests () function.  */
+  out << endl
+      << "void do_tests ()" << endl
+      << "{" << endl;
+  i = 1;
+  for (auto const &arg : args)
+    {
+      out << "  " << arg.get_type () << " " << arg.get_name () << " = " << i
+	  << ";" << endl;
+      i <<= 1;
+    }
+  out << endl;
+
+  /* Call do_tests_*_noinfo functions.  */
+  for (auto const &fn_name : do_tests_fn_names)
+    out << "  " << fn_name << "_noinfo (" << param_names_str << ");" << endl;
+  out << "}" << endl << endl;
+}
+
+/* Generate output file.  */
+void generate_header (const string &args)
+{
+  vector<class arg> all_args;
+  vector<vector<class arg> > arg_sets;
+
+  ofstream out;
+  out.exceptions (ios::failbit | ios::badbit);
+  out.open (out_file_name);
+  out << "/* Generated with " << args << " */" << endl << endl;
+
+  assert (extra_params_max < 26);
+
+  /* Build the extra argument array.  */
+  for (unsigned int i = 0; i < extra_params_max; ++i)
+    {
+      char name[2] = "a";
+      name[0] += i;
+      class arg myarg (name, "long", true);
+
+      all_args.push_back (myarg);
+    }
+
+  arg_sets.resize (extra_params_max - extra_params_min + 1);
+  for (unsigned int i = 0; i < arg_sets.size (); ++i)
+      arg_sets[i].insert (arg_sets[i].end(), all_args.begin(),
+			  all_args.begin () + i + extra_params_min);
+
+  /* Print sysv functions */
+  for (const vector<class arg> &as : arg_sets)
+    {
+      const int alloca_max = !!(fn_variant_mask & FN_VAR_MSABI);
+      const int varargs_max = !!(fn_variant_mask & FN_VAR_VARARGS);
+      fn *fn;
+      for (int _alloca = 0; _alloca <= alloca_max; ++_alloca)
+	for (int varargs = 0; varargs <= varargs_max; ++varargs)
+	{
+	  try {
+	    int var = (_alloca ? FN_VAR_ALLOCA : 0)
+		    | (varargs ? FN_VAR_VARARGS : 0);
+	    fn = new ::fn (as, 0, var);
+	  } catch (invalid_argument) {
+	    continue;
+	  }
+	  sysv_funcs.push_back (fn);
+	  fn->print_def (out);
+	}
+    }
+
+  /* Print _noinfo function pointers for sysv functions.  */
+  for (const fn *f : sysv_funcs)
+    f->print_noinfo_def (out);
+
+  /* Print ms_abi functions.  */
+  unsigned int var;
+  for (var = 0; var <= FN_VAR_MASK; ++var)
+    {
+      /* We only want ms_abi fns for this.  */
+      if (! (var & FN_VAR_MSABI))
+	continue;
+
+      /*  */
+      if ((var & fn_variant_mask) != var)
+	continue;
+
+      unsigned clobbers;
+      for (clobbers = 0; clobbers <= OPTIONAL_REG_ALL; ++clobbers)
+	{
+	  /* Skip clobbers that would be invalid.  */
+	  if (clobbers & OPTIONAL_REG_RBP)
+	    {
+	      /* Whole program built with hard frame pointer.  */
+	      if (omit_rbp_clobbers)
+		continue;
+
+	      /* 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 (auto const &as : arg_sets)
+	    {
+	      fn *fn;
+	      try {
+		fn = new ::fn (as, clobbers, var);
+	      } catch (invalid_argument) {
+		continue;
+	      }
+
+	      msabi_funcs.push_back (fn);
+	      fn->print_def (out);
+	    }
+	}
+    }
+
+  out << endl;
+  make_do_tests_decl (all_args, out);
+  out << endl;
+
+  make_do_test (all_args, msabi_funcs, out);
+  out.close ();
+}
+
+/* Parse a string into a long and return true upon success.  */
+static bool long_optarg (const char *optarg, long &dest)
+{
+  char *end;
+
+  errno = 0;
+  dest = strtol(optarg, &end, 0);
+  if (errno)
+    cerr << strerror(errno) << endl;
+
+  while (isspace(*end))
+    ++end;
+
+  /* Error if errno non-zero or junk at end of string.  */
+  return errno || *end;
+}
+
+void usage ()
+{
+  cerr
+<< "Usage: " << argv0 << " [options] <output_file>" << endl
+<< endl
+<< "    -p <n|n-n>, --max-extra-params <expr>" << endl
+<< "        A single or range of extra parameters" << endl
+<< "        Examples:" << endl
+<< "            -p0-5" << endl
+<< "            -p12" << endl
+<< endl
+<< "    -v <n>, --variant-mask <n>" << endl
+<< "        Set mask of test variants (see enum fn_variants for values," << endl
+<< "        defaults to 0x" << hex << FN_VAR_MASK << " [FN_VAR_MASK])" << endl
+<< endl
+<< "    -0, --omit-rbp-clobbers" << endl
+<< "        Omit tests that clobber RBP." << endl;
+  exit (-1);
+}
+
+/* Parse string representing a number range or a list of numbers.  */
+void set_extra_param_counts (const char *str)
+{
+  char copy[0x40];
+  char *max_str;
+  bool bad = false;
+  long int min, max;
+
+  strncpy (copy, str, sizeof (copy) - 1);
+  max_str = strchr(copy, '-');
+  if (max_str)
+      *max_str++ = 0;
+
+  bad = long_optarg (copy, min);
+  if (max_str)
+    bad = bad || long_optarg (max_str, max);
+  else
+    max = min;
+
+  if (min > max)
+    usage ();
+
+  extra_params_min = min;
+  extra_params_max = max;
+}
+
+int main (int argc, char *argv[])
+{
+  argv0 = argv[0];
+  const char *short_options = "p:v:0";
+  const struct option long_options[] = {
+    {"extra-params",		required_argument, 0, 'p'},
+    {"variant-mask",		required_argument, 0, 'v'},
+    {"omit-rbp-clobbers",	no_argument,	   0, '0'},
+    {"help",			no_argument,	   0, 'h'},
+    {0, 0, 0, 0},
+  };
+
+  int option_index = 0;
+  int c;
+  while ((c = getopt_long (argc, argv, short_options, long_options,
+			   &option_index)) != -1)
+    {
+      switch (c)
+	{
+	long l;
+
+	case 'p':
+	  set_extra_param_counts (optarg);
+	  break;
+
+	case 'v':
+	  if (long_optarg (optarg, l) || (l & ~FN_VAR_MASK))
+	  {
+	    cerr << "ERROR: Bad value for -v: `" << optarg <<  "`" << endl;
+	    usage ();
+	  }
+	  fn_variant_mask = (unsigned)l;
+	  break;
+
+	case '0':
+	  omit_rbp_clobbers = true;
+	  break;
+
+	case 'h':
+	default:
+	  usage ();
+	}
+    }
+
+  if (argc - optind != 1)
+    usage ();
+  out_file_name = argv[optind];
+
+  /* Can't skip msabi funcions.  */
+  fn_variant_mask |= FN_VAR_MSABI;
+
+  /* If whole program has HFP, explicit tests that enable it are redundant.  */
+  if (omit_rbp_clobbers)
+    fn_variant_mask &= ~FN_VAR_HFP;
+
+  stringstream argv_str;
+
+  for (int i = 0; i < argc; ++i)
+    argv_str << (i ? " " : "") << argv[i];
+
+  int ret = 0;
+  try
+    {
+      generate_header (argv_str.str());
+    }
+  catch (exception &e)
+    {
+      cerr << "ERROR: While writing `" << out_file_name << "': "
+	   << strerror(errno) << endl;
+      ret = 1;
+    }
+  for_each (sysv_funcs.begin (), sysv_funcs.end (), default_delete<fn> ());
+  for_each (msabi_funcs.begin (), msabi_funcs.end (), default_delete<fn> ());
+
+  return ret;
+}
diff --git a/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.c b/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.c
new file mode 100644
index 00000000000..2a011f5103d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.c
@@ -0,0 +1,373 @@ 
+/* Test program for 64-Bit Microsoft to System V function calls.
+   Copyright (C) 2016-2017 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_abi 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, re-aligns the stack or neither,
+   * Uses alloca (and thus DRAP) or not,
+   * Uses sibling call optimization or not,
+   * Uses variable argument list or not, and
+   * Has shrink-wrapped code or not.
+
+  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 an 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.  */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <alloca.h>
+#include <stdarg.h>
+#include <assert.h>
+#include <errno.h>
+#include <ctype.h>
+
+#ifndef __x86_64__
+# error Test only valid on x86_64
+#endif
+
+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_MISALIGNED,
+
+  ALIGNMENT_COUNT,
+};
+
+enum shrink_wrap_option
+{
+  SHRINK_WRAP_NONE,
+  SHRINK_WRAP_FAST_PATH,
+  SHRINK_WRAP_SLOW_PATH,
+
+  SHRINK_WRAP_COUNT
+};
+
+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)));
+
+struct test_data
+{
+  union regdata regdata[REG_SET_COUNT];
+  void *fn;
+  void *retaddr;
+  const char *name;
+  enum alignment_option alignment;
+  enum shrink_wrap_option shrink_wrap;
+  long ret_expected;
+} test_data;
+
+static int shrink_wrap_global;
+static void __attribute((sysv_abi)) do_tests ();
+static void init_test (void *fn, const char *name,
+		       enum alignment_option alignment,
+		       enum shrink_wrap_option shrink_wrap, long ret_expected);
+static void check_results (long ret);
+static __attribute__((ms_abi)) long do_sibcall (long arg);
+static __attribute__((ms_abi)) long
+(*const volatile do_sibcall_noinfo) (long) = do_sibcall;
+
+/* Defines do_tests ().  */
+#include "ms-sysv-generated.h"
+
+static int arbitrarily_fail;
+static const char *argv0;
+
+static void __attribute__((noinline))
+init_test (void *fn, const char *name, enum alignment_option alignment,
+	   enum shrink_wrap_option shrink_wrap, long ret_expected)
+{
+  int i;
+  union regdata *data = &test_data.regdata[REG_SET_INPUT];
+
+  assert (alignment < ALIGNMENT_COUNT);
+  assert (shrink_wrap < SHRINK_WRAP_COUNT);
+
+  memset (&test_data, 0, sizeof (test_data));
+  for (i = 55; i >= 0; --i)
+    data->u32_arr[i] = (uint32_t)lrand48 ();
+  test_data.fn = fn;
+  test_data.name = name;
+  test_data.alignment = alignment;
+  test_data.shrink_wrap = shrink_wrap;
+  test_data.ret_expected = ret_expected;
+
+  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", "misaligned"
+};
+
+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.alignment || test_data.shrink_wrap)
+    snprintf (buffer, sizeof (buffer) - 1, "`%s' (%s%s%s)",
+	      test_data.name,
+	      alignment_str[test_data.alignment],
+	      (test_data.alignment && test_data.shrink_wrap ? ", " : ""),
+	      shrink_wrap_str[test_data.shrink_wrap]);
+  else
+    snprintf (buffer, sizeof (buffer) - 1, "`%s'", test_data.name);
+
+  return buffer;
+}
+
+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;
+    }
+}
+
+
+static void __attribute__((noinline)) check_results (long ret)
+{
+  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 ();
+    if (u64 % 100 == 0)
+      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 (ret != test_data.ret_expected)
+    {
+      fprintf (stderr, "Wrong return value: got 0x%016lx, expected 0x%016lx\n",
+	       ret, test_data.ret_expected);
+      bad = 1;
+    }
+
+  if (bad)
+    {
+      fprintf (stderr, "Failed on test function %s\n", test_descr ());
+      raise (SIGTRAP);
+      exit (-1);
+    }
+}
+
+static __attribute__((ms_abi, noinline)) long do_sibcall (long arg) {
+  return arg + FLAG_SIBCALL;
+}
+
+void usage ()
+{
+  fprintf (stderr, "Usage: %s [-s <seed>] [-f]\n", argv0);
+  exit (-1);
+}
+
+static long long_optarg (const char *optarg, const char *optstr)
+{
+  char *end;
+  long ret;
+
+  errno = 0;
+  ret = strtol(optarg, &end, 0);
+
+  while (isspace (*end))
+    ++end;
+
+  if (errno || *end)
+    {
+      fprintf (stderr, "ERROR: Bad value for %s: `%s`\n", optstr, optarg);
+      if (errno)
+	fprintf (stderr, "%s\n", strerror (errno));
+      exit (-1);
+    }
+
+  return ret;
+}
+
+int main (int argc, char *argv[])
+{
+  long seed = 0;
+  int c;
+  argv0 = argv[0];
+
+  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));
+
+  while ((c = getopt (argc, argv, "s:f")) != -1)
+    {
+      switch (c)
+	{
+	case 's':
+	  seed = long_optarg (optarg, "-s");
+	  break;
+
+	case 'f':
+	  arbitrarily_fail = 1;
+	  fprintf (stderr, "NOTE: Aribrary failure enabled (-f).\n");
+	  break;
+	}
+    }
+
+  srand48 (seed);
+  do_tests ();
+
+  /* Just in case we don't have enough tests to randomly trigger the
+     failure.  */
+  if (arbitrarily_fail)
+    return -1;
+
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp b/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp
new file mode 100644
index 00000000000..e317af9bd85
--- /dev/null
+++ b/gcc/testsuite/gcc.target/x86_64/abi/ms-sysv/ms-sysv.exp
@@ -0,0 +1,178 @@ 
+# Tests for ms_abi to sysv_abi calls.
+# Copyright (C) 2016-2017 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/>.
+
+# Exit immediately if this isn't a native x86_64 target.
+if { (![istarget x86_64-*-*] && ![istarget i?86-*-*])
+     || ![is-effective-target lp64] || ![isnative] } then {
+    unsupported "$subdir"
+    return
+}
+
+global GCC_RUNTEST_PARALLELIZE_DIR
+
+load_lib gcc-dg.exp
+
+proc runtest_ms_sysv { cflags generator_args } {
+    global GCC_UNDER_TEST HOSTCXX HOSTCXXFLAGS tmpdir srcdir subdir \
+	   parallel_dir next_test
+
+    set objdir "$tmpdir/ms-sysv"
+    set generator "$tmpdir/ms-sysv-generate.exe"
+    set generated_header "$objdir/ms-sysv-generated.h"
+    set do_test_o "$objdir/do-test.o"
+    set ms_sysv_o "$objdir/ms-sysv.o"
+    set ms_sysv_exe "$objdir/ms-sysv.exe"
+    set status 0
+    set warn_flags "-Wall"
+    set this_test $next_test
+    incr next_test
+
+    # Do parallelization here
+    if [catch {set fd [open "$parallel_dir/$this_test" \
+			    [list RDWR CREAT EXCL]]} ] {
+	if { [lindex $::errorCode 1] eq "EEXIST" } then {
+	    # Another job is running this test
+	    return
+	} else {
+	    error "Failed to open $parallel_dir/$this_test: $::errorCode"
+	    set status 1
+	}
+    } else {
+      close $fd
+    }
+
+    # Detect when hard frame pointers are enabled (or required) so we know not
+    # to generate bp clobbers.
+    if [regexp "^(.+ +| *)-(O0|fno-omit-frame-pointer|p|pg)( +.*)?$" \
+	       $cflags match] then {
+	set generator_args "$generator_args --omit-rbp-clobbers"
+    }
+
+    set descr "$subdir CFLAGS=\"$cflags\" generator_args=\"$generator_args\""
+    verbose "$tmpdir: Running test $descr" 1
+
+    # Cleanup any previous test in objdir
+    file delete -force $objdir
+    file mkdir $objdir
+
+    # Build the generator (only needs to be done once).
+    set src "$srcdir/$subdir/gen.cc"
+    if { $status == 0 } then {
+	if { (![file exists "$generator"]) || ([file mtime "$generator"]
+					    < [file mtime "$src"]) } {
+	    # Temporarily switch to the environment for the host compiler.
+	    restore_ld_library_path_env_vars
+	    set cxx "$HOSTCXX $HOSTCXXFLAGS $warn_flags -std=c++11"
+	    set status [remote_exec host "$cxx -o $generator $src"]
+	    set status [lindex $status 0]
+	    set_ld_library_path_env_vars
+	    if { $status != 0 } then {
+		warning "Could not build $subdir generator"
+	    }
+	}
+    }
+
+    # Generate header
+    if { $status == 0 } then {
+	set status [remote_exec host "$generator $generator_args $generated_header"]
+	set status [lindex $status 0]
+	if { $status != 0 } then {
+	    warning "Could not generate $generated_header"
+	}
+    }
+
+    set cc "$GCC_UNDER_TEST -I$objdir -I$srcdir/$subdir $cflags $warn_flags"
+
+    # Assemble do-test.S
+    set src "$srcdir/$subdir/do-test.S"
+    if { $status == 0 } then {
+	set status [remote_exec build "$cc -c -o $do_test_o $src"]
+	set status [lindex $status 0]
+	if { $status != 0 } then {
+	    warning "Could not assemble $src"
+	}
+    }
+
+    # Build ms-sysv.c
+    set src "$srcdir/$subdir/ms-sysv.c"
+    if { $status == 0 } then {
+	set status [remote_exec build "$cc -c -o $ms_sysv_o $src" "" "" "" 1200]
+	set status [lindex $status 0]
+	if { $status != 0 } then {
+	    warning "Could not build $src."
+	}
+    }
+
+    # Link
+    if { $status == 0 } then {
+	set status [remote_exec build "$cc -o $ms_sysv_exe $ms_sysv_o $do_test_o"]
+	set status [lindex $status 0]
+	if { $status != 0 } then {
+	    warning "Link failed."
+	}
+    }
+
+    # Execute
+    if { $status == 0 } then {
+	set status [remote_exec build "$ms_sysv_exe"]
+	set status [lindex $status 0]
+    }
+
+    if { $status != 0 } then {
+	fail $descr
+    } else {
+	pass $descr
+    }
+}
+
+dg-init
+
+# Setup parallelization
+set next_test 0
+set parallel_dir "$env(GCC_RUNTEST_PARALLELIZE_DIR)/abi-ms-sysv"
+file mkdir "$env(GCC_RUNTEST_PARALLELIZE_DIR)"
+file mkdir "$parallel_dir"
+
+if { ![file isdirectory "$parallel_dir"] } then {
+    error "Failed to create directory $parallel_dir: $::errorCode"
+    return
+}
+
+set gen_opts "-p0-5"
+set all_options [list "-O2" "-O0 -g3"]
+
+# Run without -mcall-ms2sysv-xlogues always
+foreach opt $all_options {
+    runtest_ms_sysv "$opt" "$gen_opts"
+}
+
+# Skip -mcall-ms2sysv-xlogues on Windows (not supported)
+if { ![istarget *-*-cygwin*] && ![istarget *-*-mingw*] } {
+    foreach opt $all_options {
+	runtest_ms_sysv "-mcall-ms2sysv-xlogues $opt" "$gen_opts"
+    }
+}
+
+dg-finish