This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
[RFC] [PATCH] [i386] Test program for ms_abi to sysv_abi function calls
- From: Daniel Santos <daniel dot santos at pobox dot com>
- To: gcc-patches <gcc-patches at gcc dot gnu dot org>, Uros Bizjak <ubizjak at gmail dot com>, Jan Hubicka <hubicka at ucw dot cz>, Mike Stump <mikestump at comcast dot net>
- Date: Tue, 7 Feb 2017 12:27:58 -0600
- Subject: [RFC] [PATCH] [i386] Test program for ms_abi to sysv_abi function calls
- Authentication-results: sourceware.org; auth=none
It would appear that I originally posted this to the wrong list! So
sorry to those have gotten this twice.
This is a test program designed to test 64-bit Microsoft ABI functions
that call System V functions in a multitude of permutations to attempt
to discover flaws in the generation of prologues and epilogues and the
optimizations and features that can affect them, specifically
shrink-wrapping, sibling calls and DRAP. This is an accompaniment to
the below patch sets, and was instrumental in finding and fixing flaws,
but is not an ancestor.
* Use aligned SSE movs for re-aligned MS ABI pro/epilogues -
https://gcc.gnu.org/ml/gcc-patches/2016-12/msg01859.html
* Use out-of-line stubs for ms_abi pro/epilogues- (v3 to be posted
shortly)
Summary
=======
A C++ generator program (gen.cc) is built and run to generate an
unguarded C header file, which is included by msabi.c. This header
defines a number of ms_abi functions that call sysv_abi functions with
various conditions. The generator has a CLI for test selection and
arguments I'm currently using in msabi.exp generates about 19k tests.
The results of msabi.c and do_test.S are linked to create the test
program.Build time for msabi.c is takes 200-ish seconds on an old
Phenom, but execution is nearly instantaneous.
Each test function is called via an assembly stub (do_test_aligned or
do_test_unaligned) that:
1. Saves non-volatile registers to a global buffer,
2. Fills non-volatile registers with random data,
3. Calls the test function,
4. Saves resulting non-volatile registers for later comparison, and
5. Restores non-volatile registers to original values.
Upon completion, the resulting register values are compared to the
original random values to verify correctness. The return value is also
verified against what is expected to validate the correctness of both
the generated code as well as the test its self.
What Is Tested
==============
The test permutations consists of:
A. A number of extra long parameters for the function (0-5 being used now).
B. A mask of additional non-volatile registers to explicitly clobber
(aside from RDI, RSI and XMM6-15, which are always clobbered):
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)
};
C. A collection of variants:
enum fn_variants {
FN_VAR_MSABI = 0x01,/* This value is an implementation detail
and NOT a test permutation. */
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
};
The variants deserve a little more explanation.
* FN_VAR_MSABI Only an implementation detail (adds
__attribute__((ms_abi))).
* FN_VAR_HFP Adds__attribute__((optimize(
"no-omit-frame-pointer"))). FN_VAR_HFP and
FN_VAR_REALIGN are mutually exclusive.
* FN_VAR_REALIGN Adds__attribute__((
__force_align_arg_pointer__)). The test will
callthis function twice -- with an aligned and
misaligned stack.
* FN_VAR_ALLOCA The ms_abi function calls alloca and passes the
pointer to the sysv_abi function.
* FN_VAR_VARARGS The ms_abi function takes varargs, but only
passes the argptr to the sysv_abi function.
* FN_VAR_SIBCALL The ms_abi function returns in a way that enables
the sibling call optimization (skipped if
FN_VAR_REALIGN |FN_VAR_HFP are enabled).
* FN_VAR_SHRINK_WRAP Tests a global variable and uses a branch that
enables the use shrink wrapping. Both the fast and
slow path aretested.
The following nomenclature is used for function names:
(msabi|sysv)_<xx>_[r|f][a][v][s][w]<n>
| | | | | | | |
| | | | | | | Number of extra parameters (longs)
| | | | | | shrink wrap
| | | | | sibling call
| | | | varargs
| | | alloca
| | Forced realignment or hard frame pointer
| Explicit clobbers (hexadecimalmask)
Calling Convention
Examples
========
The function msabi_25_ra2looks like this:
__attribute__ ((noinline, ms_abi, __force_align_arg_pointer__))
long msabi_25_ra2 (long a, long b)
{
void *alloca_mem;
alloca_mem = alloca (8 + a);
*(long*)alloca_mem = FLAG_ALLOCA;
__asm__ __volatile__ ("" :::"rbx", "r12", "r15");
return sysv_a2_noinfo (alloca_mem, a, b);
}
And the tests (both aligned and misaligned) looks something like this:
void init_test (void *fn, const char *name, enum alignment_option alignment,
enum shrink_wrap_option shrink_wrap, long ret_expected);
void do_tests ()
{
long ret;
long a = 1;
long b = 2;
...
init_test (msabi_25_ra2, "msabi_25_ra2", ALIGNMENT_ALIGNED,
SHRINK_WRAP_NONE, a + b + FLAG_ALLOCA);
ret = do_test_2 (a, b);
check_results (ret);
init_test (msabi_25_ra2, "msabi_25_ra2", ALIGNMENT_MISALIGNED,
SHRINK_WRAP_NONE, a + b + FLAG_ALLOCA);
__asm__ __volatile__ ("subq $8,%%rsp":::"cc");
ret = do_test_u2 (a, b);
__asm__ __volatile__ ("addq $8,%%rsp":::"cc");
check_results (ret);
...
}
Daniel Santos
>From 2e9fa543e1923c7acc705e06bba006fc5887d805 Mon Sep 17 00:00:00 2001
From: Daniel Santos <daniel.santos@pobox.com>
Date: Sat, 17 Dec 2016 15:37:28 -0600
Subject: [PATCH] [i386] Test program for ms_abi functions.
---
gcc/Makefile.in | 2 +
gcc/testsuite/gcc.target/i386/msabi/do_test.S | 162 ++++++
gcc/testsuite/gcc.target/i386/msabi/gen.cc | 788 ++++++++++++++++++++++++++
gcc/testsuite/gcc.target/i386/msabi/msabi.c | 379 +++++++++++++
gcc/testsuite/gcc.target/i386/msabi/msabi.exp | 125 ++++
5 files changed, 1456 insertions(+)
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.exp
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 821584a1703..97706d44c0c 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -3788,7 +3788,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/i386/msabi/do_test.S b/gcc/testsuite/gcc.target/i386/msabi/do_test.S
new file mode 100644
index 00000000000..463b8aef767
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/do_test.S
@@ -0,0 +1,162 @@
+/* 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(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
+
+/* 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/i386/msabi/gen.cc b/gcc/testsuite/gcc.target/i386/msabi/gen.cc
new file mode 100644
index 00000000000..db87b848ecf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/gen.cc
@@ -0,0 +1,788 @@
+/* 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<class arg>::const_iterator ai;
+ 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 ungodly 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;
+ }
+ out << "static __attribute__((noinline)) void do_tests_"
+ << setfill('0') << setw(4) << hex << fn_count++
+ << " (" << 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
+ << "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;
+ for (unsigned i = 0; i < fn_count; ++i)
+ out << " ((void (*volatile)(" << param_types_str << "))do_tests_"
+ << setfill('0') << setw(4) << hex << i << ") ("
+ << param_names_str << ");" << endl;
+ out << "}" << endl << endl;
+}
+
+/* Generate output file. */
+void generate_header ()
+{
+ vector<class arg> all_args;
+ vector<vector<class arg> > arg_sets;
+
+ ofstream out;
+ out.exceptions (ios::failbit | ios::badbit);
+ out.open (out_file_name);
+
+ 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;
+
+ int ret = 0;
+ try
+ {
+ generate_header ();
+ }
+ 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/i386/msabi/msabi.c b/gcc/testsuite/gcc.target/i386/msabi/msabi.c
new file mode 100644
index 00000000000..eb08a2a2a51
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/msabi.c
@@ -0,0 +1,379 @@
+/* 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 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 "msabi-generated.h"
+
+static struct drand48_data rand_data;
+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)
+ {
+ 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.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_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 (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_r (seed, &rand_data);
+ 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/i386/msabi/msabi.exp b/gcc/testsuite/gcc.target/i386/msabi/msabi.exp
new file mode 100644
index 00000000000..882112592e8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/msabi.exp
@@ -0,0 +1,125 @@
+# 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
+}
+
+load_lib gcc-dg.exp
+
+proc runtest_msabi { cflags generator_args } {
+ global GCC_UNDER_TEST HOSTCXX HOSTCXXFLAGS tmpdir srcdir subdir
+
+ set objdir "$tmpdir/msabi"
+ set generator "$tmpdir/msabi_generate.exe"
+ set generated_header "$objdir/msabi-generated.h"
+ set do_test_o "$objdir/do_test.o"
+ set msabi_o "$objdir/msabi.o"
+ set msabi_exe "$objdir/msabi.exe"
+ set status 0
+
+ file delete -force $objdir
+ file mkdir $objdir
+
+ # Build the generator (only needs to be done once).
+ set src "$srcdir/$subdir/gen.cc"
+ 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 -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"
+
+ # 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 msabi.c
+ set src "$srcdir/$subdir/msabi.c"
+ if { $status == 0 } then {
+ set status [remote_exec build "$cc -c -o $msabi_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 $msabi_exe $msabi_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 "$msabi_exe"]
+ set status [lindex $status 0]
+ }
+
+ set descr "$subdir CFLAGS=\"$cflags\" generator_args=\"$generator_args\""
+ if { $status != 0 } then {
+ fail $descr
+ } else {
+ pass $descr
+ }
+}
+
+dg-init
+
+foreach moutline_options [list "" "-moutline-msabi-xlogues"] {
+ runtest_msabi "-O2 -g $moutline_options" "-p0-5"
+ runtest_msabi "-O0 -g $moutline_options" "-p0-5 --omit-rbp-clobbers"
+}
+
+dg-finish
--
2.11.0