@@ -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
new file mode 100644
@@ -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__ */
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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;
+}
new file mode 100644
@@ -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
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