public inbox for gcc@gcc.gnu.org
 help / color / mirror / Atom feed
* Help with integrating my test program into dejagnu
@ 2016-12-30 19:55 Daniel Santos
  2016-12-31  0:01 ` Mike Stump
  0 siblings, 1 reply; 12+ messages in thread
From: Daniel Santos @ 2016-12-30 19:55 UTC (permalink / raw)
  To: gcc; +Cc: Uros Bizjak, Jan Hubicka, Rainer Orth, Mike Stump

[-- Attachment #1: Type: text/plain, Size: 1065 bytes --]

Still being pretty new to GCC and having never used dejagnu, expect or 
Tcl, I'm trying to determine how to best integrate my test program into 
GCC's test harness.  I wrote this to help find breakages while working 
on optimizations for Microsoft 64-bit ABI pro/epilogues.  Rather than 
testing specific cases, it generates a few thousand unique tests by 
iterating through variations.  It consists of a C++ program that 
generates a pair of .c files (that I don't want in the same translation 
unit).  These are built along with a static .c and .S file and linked 
into the test program.  It is intended to be built and executed on the 
target machine and I currently run it manually with a frequently-edited 
Makefile.

The first thing I need help with is figuring out if this should be run 
by dejagnu or if I should just write a proper Makefile.in and add it to 
gcc's Makefile.in.  Integrating with dejagnu seems to be the most 
intuitive and simple, but I don't properly understand how this would 
affect a cross-compiler build.  Any advice?

Thanks!
Daniel

[-- Attachment #2: 0001-i386-Test-program-for-ms_abi-functions.patch --]
[-- Type: text/x-patch, Size: 36812 bytes --]

From d38bc80fc793224fb0fbd586824786f5ec178f65 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/testsuite/gcc.target/i386/msabi/Makefile  |  56 +++
 gcc/testsuite/gcc.target/i386/msabi/do_test.S | 142 ++++++
 gcc/testsuite/gcc.target/i386/msabi/gen.cc    | 653 ++++++++++++++++++++++++++
 gcc/testsuite/gcc.target/i386/msabi/msabi.c   | 304 ++++++++++++
 gcc/testsuite/gcc.target/i386/msabi/msabi.h   | 110 +++++
 5 files changed, 1265 insertions(+)
 create mode 100644 gcc/testsuite/gcc.target/i386/msabi/Makefile
 create mode 100644 gcc/testsuite/gcc.target/i386/msabi/do_test.S
 create mode 100644 gcc/testsuite/gcc.target/i386/msabi/gen.cc
 create mode 100644 gcc/testsuite/gcc.target/i386/msabi/msabi.c
 create mode 100644 gcc/testsuite/gcc.target/i386/msabi/msabi.h

diff --git a/gcc/testsuite/gcc.target/i386/msabi/Makefile b/gcc/testsuite/gcc.target/i386/msabi/Makefile
new file mode 100644
index 00000000000..8d762d704ef
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/Makefile
@@ -0,0 +1,56 @@
+GEN_OPTIONS ?= -p 6 -m 0
+GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc-github/build/head
+#GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc-github/build/head-test-sp-realigned
+#GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc-github/build/head-test-patched
+#GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc.work0/build/head
+GCC_SRC_DIR  = /home/daniel/proj/sys/gcc-github
+#GCC_SRC_DIR  = /home/daniel/proj/sys/gcc.work0
+COMPFLAGS    = -B$(GCC_BUILD_DIR)/gcc
+#CC           = $(GCC_BUILD_DIR)/gcc/xgcc $(COMPFLAGS)
+#CXX          = $(GCC_BUILD_DIR)/gcc/xg++ $(COMPFLAGS) -B$(GCC_BUILD_DIR)/x86_64-pc-linux-gnu/libstdc++-v3/src/.libs \
+#		-isystem $(GCC_BUILD_DIR)/x86_64-pc-linux-gnu/libstdc++-v3/include \
+#		-isystem $(GCC_BUILD_DIR)/x86_64-pc-linux-gnu/libstdc++-v3/include/x86_64-pc-linux-gnu  \
+#		-isystem $(GCC_SRC_DIR)/libstdc++-v3/libsupc++
+WARN_FLAGS  ?= -Wall -Wextra -Wno-unused-parameter
+CFLAGS	    ?= -g3 -O2 -m64
+#CFLAGS	    ?= -g3 -O2 -m64 -moutline-msabi-xlogues
+
+CXXFLAGS    ?= $(CFLAGS)
+CFLAGS	    += $(WARN_FLAGS)
+CXXFLAGS    += -std=gnu++11 $(WARN_FLAGS)
+ASFLAGS	    ?= -g3
+
+gen_object_files	= gen.o
+gen_program		= gen
+generated_sources	= fns.c do_tests.c
+headers			= msabi.h
+test_object_files	= msabi.o do_test.o fns.o do_tests.o
+test_program		= msabi
+
+all: $(test_program)
+
+check: $(test_program)
+	$(CURDIR)/$(test_program) && echo PASS || echo FAIL
+
+.PHONY : clean
+clean:
+	rm -f $(gen_object_files) $(gen_program) $(generated_sources) \
+	      $(test_object_files) $(test_program)
+
+%.o: %.cc
+	$(CXX) $(CXXFLAGS) -I. -c -o $@ $<
+
+$(gen_program): $(gen_object_files)
+	$(CXX) $(CXXFLAGS) -o $@ $<
+
+fns.c : $(gen_program)
+	$(CURDIR)/$(gen_program) $(GEN_OPTIONS) fns.c > $@ || rm $@
+
+do_tests.c : $(gen_program)
+	$(CURDIR)/$(gen_program) $(GEN_OPTIONS) do_tests.c > $@ || rm $@
+
+%.o: %.c $(headers)
+	$(CC) $(CFLAGS) -I. -c -o $@ $<
+
+$(test_program): $(headers) $(test_object_files)
+	$(CC) $(CFLAGS) -I. -o $@ $(test_object_files)
diff --git a/gcc/testsuite/gcc.target/i386/msabi/do_test.S b/gcc/testsuite/gcc.target/i386/msabi/do_test.S
new file mode 100644
index 00000000000..f01ea0943ef
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/do_test.S
@@ -0,0 +1,142 @@
+/* Assembly proxy functions for ms_abi tests.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   Contributed by Daniel Santos <daniel.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
+	.text
+
+regs_to_mem:
+	MOVAPS	%xmm6, (%rax)
+	MOVAPS	%xmm7, 0x10(%rax)
+	MOVAPS	%xmm8, 0x20(%rax)
+	MOVAPS	%xmm9, 0x30(%rax)
+	MOVAPS	%xmm10, 0x40(%rax)
+	MOVAPS	%xmm11, 0x50(%rax)
+	MOVAPS	%xmm12, 0x60(%rax)
+	MOVAPS	%xmm13, 0x70(%rax)
+	MOVAPS	%xmm14, 0x80(%rax)
+	MOVAPS	%xmm15, 0x90(%rax)
+	mov	%rsi, 0xa0(%rax)
+	mov	%rdi, 0xa8(%rax)
+	mov	%rbx, 0xb0(%rax)
+	mov	%rbp, 0xb8(%rax)
+	mov	%r12, 0xc0(%rax)
+	mov	%r13, 0xc8(%rax)
+	mov	%r14, 0xd0(%rax)
+	mov	%r15, 0xd8(%rax)
+	retq
+
+mem_to_regs:
+	MOVAPS	(%rax), %xmm6
+	MOVAPS	0x10(%rax),%xmm7
+	MOVAPS	0x20(%rax),%xmm8
+	MOVAPS	0x30(%rax),%xmm9
+	MOVAPS	0x40(%rax),%xmm10
+	MOVAPS	0x50(%rax),%xmm11
+	MOVAPS	0x60(%rax),%xmm12
+	MOVAPS	0x70(%rax),%xmm13
+	MOVAPS	0x80(%rax),%xmm14
+	MOVAPS	0x90(%rax),%xmm15
+	mov	0xa0(%rax),%rsi
+	mov	0xa8(%rax),%rdi
+	mov	0xb0(%rax),%rbx
+	mov	0xb8(%rax),%rbp
+	mov	0xc0(%rax),%r12
+	mov	0xc8(%rax),%r13
+	mov	0xd0(%rax),%r14
+	mov	0xd8(%rax),%r15
+	retq
+
+# NOTE: Not MT safe
+FUNC(do_test_unaligned)
+	.cfi_startproc
+	# Verify that incoming stack is aligned + 8
+	pushf
+	test	$0x8, %rsp
+	jne	L0
+	int	$3		# Stack not unaligned
+
+FUNC(do_test_aligned)
+	# Verify that incoming stack is aligned
+	pushf
+	test	$0xf, %rsp
+	je	L0
+	int	$3		# Stack not aligned
+L0:
+	popf
+
+	# Save registers
+	lea	test_data(%rip), %rax
+	call	regs_to_mem
+
+	# Load register with random data
+	lea	test_data+224(%rip), %rax
+	call	mem_to_regs
+
+	# Save original return address
+	pop	%rax
+	movq    %rax, test_data+680(%rip)
+
+	# Call the test function
+	call	*test_data+672(%rip)
+
+	# Restore the original return address
+	movq    test_data+680(%rip), %rcx
+	push	%rcx
+
+	# Save test function return value and store resulting register values
+	push	%rax
+	lea	test_data+448(%rip), %rax
+	call	regs_to_mem
+
+	# Restore registers
+	lea	test_data(%rip), %rax
+	call	mem_to_regs
+	pop	%rax
+	retq
+        .cfi_endproc
+FUNC_END(do_test_aligned)
+FUNC_END(do_test_unaligned)
+
+#endif /* __x86_64__ */
diff --git a/gcc/testsuite/gcc.target/i386/msabi/gen.cc b/gcc/testsuite/gcc.target/i386/msabi/gen.cc
new file mode 100644
index 00000000000..fea0eadb0c7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/gen.cc
@@ -0,0 +1,653 @@
+/* Test program generator for 64-bit Microsoft ABI.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   Contributed by Daniel Santos <daniel.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 <iomanip>
+#include <sstream>
+#include <memory>
+#include <stdexcept>
+#include <unistd.h>
+
+using namespace std;
+
+/*****************************************************************************
+ * class list_delimiter
+ *
+ * A bit bucket ostream.
+ ****************************************************************************/
+class null_stream : public std::ostream
+{
+  ostream &write( const char_type* s, std::streamsize count ) {return *this;}
+  ostream &put( char_type ch ) {return *this;}
+};
+
+/*****************************************************************************
+ * class uncopyable
+ *
+ * Standard Effective C++ Item 6.
+ ****************************************************************************/
+class uncopyable
+{
+private:
+  uncopyable (const uncopyable &) = delete;
+  const uncopyable& operator= (const uncopyable &) = delete;
+
+protected:
+  uncopyable() {}
+  ~uncopyable() {}
+};
+
+/*****************************************************************************
+ * class list_delimiter
+ *
+ * A simple class for adding delimiters (no output on the first call to get).
+ ****************************************************************************/
+class list_delimiter : protected uncopyable
+{
+  int m_pos;
+  string m_delim;
+  static string s_empty;
+
+  list_delimiter ();
+
+public:
+  list_delimiter (const char *delim) : m_pos (0), m_delim(delim) {}
+  const string &get () {return m_pos++ ? m_delim : s_empty;}
+  void reset () {m_pos = 0;}
+  int get_pos () {return m_pos;}
+};
+
+string list_delimiter::s_empty = "";
+
+enum optional_regs
+{
+  OPTIONAL_REG_RBX = 0x01,
+  OPTIONAL_REG_RBP = 0x02,
+  OPTIONAL_REG_R12 = 0x04,
+  OPTIONAL_REG_R13 = 0x08,
+  OPTIONAL_REG_R14 = 0x10,
+  OPTIONAL_REG_R15 = 0x20,
+
+  OPTIONAL_REG_ALL = 0x3f,
+  OPTIONAL_REG_HFP_ALL = OPTIONAL_REG_ALL & (~OPTIONAL_REG_RBP)
+};
+
+static const char * const optional_regs_str[] = {
+  "rbx",
+  "rbp",
+  "r12",
+  "r13",
+  "r14",
+  "r15",
+};
+
+/*****************************************************************************
+ * class arg
+ *
+ * A simple type & name representation of a function parameter.
+ ****************************************************************************/
+class arg
+{
+  string name;
+  string type;
+  bool type_is_integral:1;
+
+  arg(); /* no default ctor */
+
+public:
+  arg(const char *name, const char *type, bool type_is_integral);
+
+  bool is_type_integral () const	{return type_is_integral;}
+  const string &get_name () const	{return name;}
+  const string &get_type () const	{return type;}
+
+  void print (ostream &out) const
+  {
+    out << type << " " << name.c_str();
+  }
+};
+
+arg::arg(const char *name, const char *type, bool type_is_integral)
+    : name (name), type (type), type_is_integral (type_is_integral)
+{
+}
+
+enum fn_variants {
+  FN_VAR_MSABI		= 0x01,
+  FN_VAR_HFP		= 0x02,
+  FN_VAR_REALIGN	= 0x04,
+  FN_VAR_ALLOCA		= 0x08,
+  FN_VAR_VARARGS	= 0x10,
+  FN_VAR_SIBCALL	= 0x20,
+  FN_VAR_SHRINK_WRAP	= 0x40,
+
+  FN_VAR_HFP_OR_REALIGN	= FN_VAR_HFP | FN_VAR_REALIGN,
+  FN_VAR_MASK		= 0x7f,
+  FN_VAR_COUNT		= 7
+};
+
+/*****************************************************************************
+ * class fn
+ *
+ * Class that represents a Microsoft or System V ABI function with varying
+ * parameters, quirks and optimization goals.
+ ****************************************************************************/
+class fn : protected uncopyable
+{
+private:
+  const vector<arg> &m_args;
+  string m_name;
+  string m_attr_str;
+  int m_clobbers:FN_VAR_COUNT;
+  int m_var;
+
+  fn () = delete; /* no default ctor */
+
+public:
+  fn (const vector<arg> &args, int clobbers, int var);
+
+  void print_params (ostream &out) const;
+  void print_decl (ostream &out, bool for_def = false) const;
+  void print_def (ostream &out) const;
+  const string &get_name () const	{return m_name;}
+  const vector<arg> &get_args () const	{return m_args;}
+
+  bool get_hfp_or_realign () const	{return m_var & FN_VAR_HFP_OR_REALIGN;}
+  bool get_msabi () const		{return m_var & FN_VAR_MSABI;}
+  bool get_hfp () const			{return m_var & FN_VAR_HFP;}
+  bool get_realign () const		{return m_var & FN_VAR_REALIGN;}
+  bool get_alloca () const		{return m_var & FN_VAR_ALLOCA;}
+  bool get_varargs () const		{return m_var & FN_VAR_VARARGS;}
+  bool get_sibcall () const		{return m_var & FN_VAR_SIBCALL;}
+  bool get_shrink_wrap () const		{return m_var & FN_VAR_SHRINK_WRAP;}
+};
+
+fn::fn (const vector<arg> &args, int clobbers, int var)
+    : m_args (args)
+    , m_name ()
+    , m_attr_str ("noinline")
+    , m_clobbers (clobbers)
+    , m_var (var)
+{
+  assert (!(var & ~FN_VAR_MASK));
+
+  if (get_hfp () && get_realign ())
+    throw invalid_argument ("`hfp' with `realign' does nothing.");
+
+  if (get_varargs () && args.empty ())
+    throw invalid_argument ("Need at least one normal argument to use varargs");
+
+  std::stringstream name;
+  name << (get_msabi () ? "msabi_" : "sysv_");
+  if (get_msabi ())
+    name << setfill('0') << setw(2) << hex << m_clobbers << "_";
+  name << (get_realign () ? "r" : (get_hfp () ? "f" : ""))
+       << (get_alloca () ? "a" : "")
+       << (get_varargs () ? "v" : "")
+       << (get_sibcall () ? "s" : "")
+       << (get_shrink_wrap () ? "w" : "");
+  name << setw(0) << dec << (unsigned)args.size();
+  m_name = name.str();
+
+  if (get_msabi ())
+      m_attr_str += ", ms_abi";
+
+  if (get_realign ())
+    m_attr_str += ", __force_align_arg_pointer__";
+  else if (get_hfp ())
+    m_attr_str += ", optimize (\"no-omit-frame-pointer\")";
+
+}
+
+/* Print the parameters for a function declaration.  */
+void fn::print_params (ostream &out) const
+{
+  list_delimiter comma (", ");
+
+  vector<arg>::const_iterator i;
+  if (get_alloca () && !get_msabi ())
+    out << comma.get () << "void *alloca_mem";
+  for (i = m_args.begin(); i != m_args.end(); ++i)
+    {
+      out << comma.get ();
+      i->print (out);
+    }
+
+  if (get_varargs ())
+    out << comma.get () << (get_msabi () ? "..." : "va_list argptr");
+}
+
+/* Print the declaration for a function.  */
+void fn::print_decl (ostream &out, bool for_def) const
+{
+  assert (!(get_hfp () || get_realign ()) || !(m_clobbers & OPTIONAL_REG_RBP));
+
+  if (!for_def)
+    out << "extern ";
+  out << "__attribute__ ((" << m_attr_str << ")) long " << m_name << " (";
+  print_params (out);
+  out << ")";
+  if (!for_def)
+    out << ";" << endl;
+}
+
+/* Print the definition of a function.  */
+void fn::print_def (ostream &out) const
+{
+  vector<arg>::const_iterator i;
+
+  print_decl (out, true);
+  out << endl << "{" << endl;
+
+  if (get_msabi () && get_alloca ())
+    {
+      const char *size_str = m_args.empty () ? "42" : "a";
+      out << "  /* Uses alloca.  */" << endl
+	  << "  void *alloca_mem = alloca (8 + " << size_str << ");" << endl
+	  << "  *(long*)alloca_mem = FLAG_ALLOCA;" << endl;
+    }
+  if (get_msabi () && get_varargs ())
+      out << "  va_list argptr;" << endl;
+  if (get_sibcall ())
+    out << "  /* Sibling call.  */" << endl;
+  if (get_shrink_wrap ())
+    out << "  /* Shrink wrap.  */" << endl
+	<< "  if (shrink_wrap_global == FLAG_SHRINK_WRAP_FAST_PATH)" << endl
+	<< "    return FLAG_SHRINK_WRAP_FAST_PATH;" << endl;
+
+  list_delimiter comma (", ");
+  if (m_clobbers)
+  {
+    out << "  __asm__ __volatile__ (\"\" :::";
+    unsigned c;
+    unsigned mask = m_clobbers;
+    comma.reset ();
+    for (c = 0, mask = m_clobbers; mask; ++c, mask >>= 1)
+      if (mask & 1)
+	out << comma.get () << "\"" << optional_regs_str[c] << "\"";
+    out << ");" << endl;
+  }
+
+  if (get_msabi () && get_varargs ())
+  {
+    assert (!m_args.empty ());
+    out << "  va_start(argptr, " << m_args.back ().get_name () << ");" << endl;
+  }
+
+  out << "  return ";
+  if (get_msabi ())
+    {
+      if (get_sibcall ())
+	out << "do_sibcall (";
+
+      comma.reset ();
+      out << "sysv_"
+	  << (get_alloca () ? "a" : "")
+	  << (get_varargs () ? "v" : "")
+	  << m_args.size ()
+	  << " (";
+
+      if (get_alloca ())
+	out << comma.get () << "alloca_mem";
+      for (i = m_args.begin(); i != m_args.end(); ++i)
+	out << comma.get () << i->get_name ();
+      if (get_varargs ())
+	out << comma.get () << "argptr";
+      out << ")";
+      if (get_shrink_wrap ())
+	out << " + FLAG_SHRINK_WRAP_SLOW_PATH";
+      if (get_sibcall ())
+	out << ")";
+    }
+  else
+    {
+      list_delimiter plus (" + ");
+      for (i = m_args.begin(); i != m_args.end(); ++i)
+	  if (i->is_type_integral ())
+	    out << plus.get () << i->get_name ();
+      if (get_alloca ())
+	out << plus.get () << "*(long*)alloca_mem";
+      if (!plus.get_pos ())
+	out << "0";
+    }
+  out << ";" << endl;
+  if (get_msabi () && get_varargs ())
+    out << "  va_end(argptr);" << endl;
+  out << "}" << endl << endl;
+}
+
+/*****************************************************************************
+ *
+ ****************************************************************************/
+
+static void make_do_tests_decl (const vector<class arg> &args, ostream &out)
+{
+  vector<class arg>::const_iterator ai;
+  unsigned i, varargs, unaligned;
+
+  out << "extern __attribute__ ((ms_abi)) long do_test_aligned ();" << endl
+      << "extern __attribute__ ((ms_abi)) long do_test_unaligned ();" << endl;
+
+  list_delimiter comma (", ");
+  for (i = 0; i <= args.size (); ++i)
+    for (unaligned = 0; unaligned <= 1; ++unaligned)
+      for (varargs = 0; varargs <= 1; ++varargs)
+	{
+	  if (!i && varargs)  /* skip varargs version when no other args */
+	    continue;
+
+	  comma.reset ();
+	  out << "static __attribute__ ((ms_abi)) long (*do_test_"
+	      << (unaligned ? "u" : "")
+	      << (varargs ? "v" : "") << i << ") (";
+
+	  unsigned j;
+	  for (j = 0, ai = args.begin (); j < i; ++j, ++ai)
+	    out << comma.get () << ai->get_type () << " "
+		<< ai->get_name ();
+	  if (varargs)
+	    out << comma.get () << "...";
+	  out << ") = (void*)do_test_" << (unaligned ? "un" : "")
+	      << "aligned;" << endl;
+	}
+}
+
+void make_do_test (const vector<class arg> &args,
+		   const vector<class fn*> &msabi_funcs,
+		   ostream &out)
+{
+  unsigned i;
+  vector<class arg>::const_iterator ai;
+
+  out << "void do_tests ()" << endl
+      << "{" << endl
+      << "  long ret, expect;" << endl << endl;
+
+  for (i = 1, ai = args.begin (); ai != args.end (); ++i, ++ai)
+    out << "  " << ai->get_type () << " " << ai->get_name () << " = " << i
+	<< ";" << endl;
+  out << endl;
+
+  vector<class fn*>::const_iterator fi;
+  for (fi = msabi_funcs.begin(); fi != msabi_funcs.end(); ++fi)
+    {
+      const fn &f = **fi;
+      unsigned unaligned, shrink_wrap;
+
+      for (unaligned = 0; unaligned <= !!f.get_realign (); ++unaligned)
+	for (shrink_wrap = 0; shrink_wrap <= !!f.get_shrink_wrap ();
+	     ++shrink_wrap)
+	  {
+	    const vector<class arg> &fargs = f.get_args ();
+	    out << "  init_test (" << f.get_name () << ", \""
+		<< f.get_name () << "\", ";
+
+	    if (f.get_realign ())
+	      out << (unaligned ? "ALIGNMENT_ALIGNED_PLUS_8"
+				: "ALIGNMENT_ALIGNED");
+	    else
+	      out << "ALIGNMENT_NOT_TESTED";
+
+	    out << ", ";
+	    if (f.get_shrink_wrap ())
+	      out << (shrink_wrap ? "SHRINK_WRAP_SLOW_PATH"
+				  : "SHRINK_WRAP_FAST_PATH");
+	    else
+	      out << "SHRINK_WRAP_NONE";
+	    out << ");" << endl;
+
+	    if (f.get_realign () && unaligned == 1)
+	      out << "  __asm__ __volatile__ (\"subq $8,%%rsp\":::\"cc\");"
+		  << endl;
+
+	    out << "  ret = do_test_"
+		<< (f.get_realign () && unaligned == 1 ? "u" : "")
+		<< (f.get_varargs () ? "v" : "")
+		<< fargs.size () << " (";
+
+	    list_delimiter comma (", ");
+	    for (ai = fargs.begin (); ai != fargs.end (); ++ai)
+	      out << comma.get () << ai->get_name ();
+
+	    out << ");" << endl;
+	    if (f.get_realign () && unaligned == 1)
+	      out << "  __asm__ __volatile__ (\"addq $8,%%rsp\":::\"cc\");"
+		  << endl;
+	    if (f.get_shrink_wrap () && shrink_wrap == 0)
+	      out << "  expect = FLAG_SHRINK_WRAP_FAST_PATH";
+	    else
+	      {
+		out << "  expect = 0";
+		for (ai = fargs.begin (); ai != fargs.end (); ++ai)
+		  out << " + " << ai->get_name ();
+		if (f.get_sibcall ())
+		  out << " + FLAG_SIBCALL";
+		if (f.get_alloca ())
+		  out  << " + FLAG_ALLOCA";
+		if (f.get_shrink_wrap () && shrink_wrap == 1)
+		  out << " + FLAG_SHRINK_WRAP_SLOW_PATH";
+	      }
+	    out << ";" << endl
+		<< "  fail_if_not_equal (ret, expect);" << endl
+		<< "  compare_regdata ();" << endl
+		<< endl;
+	  }
+    }
+  out << "}" << endl;
+}
+
+void usage (const char *arg0)
+{
+  cerr << "Usage: " << arg0 << "[-p=<n>] [-m=<n>] [-f] <msabi.c|do_tests.c>"
+       << endl
+       << endl
+       << "    -p <n>    Set maxiumum number of extra parameters "
+       << "(defaults to 8)" << endl
+       << "    -m <n>    Set mask of test variants (see enum fn_variants for "
+       << "values, defaults to 0x" << hex << FN_VAR_MASK << " [FN_VAR_MASK])"
+       << endl;
+  exit (-1);
+}
+
+static bool long_optarg (const char *optarg, long &l)
+{
+  const char *start = optarg;
+  int base = 10;
+  char *end;
+
+  if (!strncasecmp (optarg, "0x", 2))
+  {
+    base = 16;
+    start += 2;
+  }
+
+  l = strtol(start, &end, base);
+
+  return end == start || *end != 0;
+}
+
+int main (int argc, char *argv[])
+{
+  vector<class arg> all_args;
+  vector<vector<class arg> > arg_sets;
+  vector<class fn*> sysv_funcs;
+  vector<class fn*> msabi_funcs;
+  unsigned extra_param_count_max = 8;
+  unsigned fn_variant_mask = 0;
+  unsigned i;
+  int c;
+
+  while ((c = getopt (argc, argv, "p:m:")) != -1)
+    {
+      switch (c)
+	{
+	long l;
+	case 'p':
+	  if (long_optarg (optarg, l) || l < 0 || l > 16)
+	  {
+	    cerr << "ERROR: Bad value for -p: `" << optarg <<  "`" << endl;
+	    usage (argv[0]);
+	  }
+	  extra_param_count_max = (unsigned)l;
+	  break;
+
+	case 'm':
+	  if (long_optarg (optarg, l) || (l & ~FN_VAR_MASK))
+	  {
+	    cerr << "ERROR: Bad value for -m: `" << optarg <<  "`" << endl;
+	    usage (argv[0]);
+	  }
+	  else if (l & FN_VAR_MSABI)
+	  {
+	    cerr << "ERROR: Can't mask out MSABI functions." << endl;
+	    usage (argv[0]);
+	  }
+	  fn_variant_mask = (unsigned)l;
+	  break;
+
+	default:
+	  usage (argv[0]);
+	}
+    }
+  if (argc - optind != 1)
+    usage (argv[0]);
+
+  /* Setup output streams.  We only output one at a time, so for laziness,
+     we just set one stream to a null_stream (bit bucket) and the other to
+     cout. */
+  null_stream ns;
+  ostream *fns_c = &ns;
+  ostream *do_tests_c = &ns;
+  string arg (argv[optind]);
+  if (arg == "fns.c")
+    fns_c = &cout;
+  else if (arg == "do_tests.c")
+    do_tests_c = &cout;
+  else
+    usage (argv[0]);
+
+  *fns_c << "#include \"msabi.h\"" << endl << endl;
+  *do_tests_c << "#include \"msabi.h\"" << endl << endl;
+
+  for (i = 0; i < extra_param_count_max; ++i)
+  {
+    char name[2] = "a";
+    name[0] += i;
+    class arg myarg (name, "long", true);
+
+    all_args.push_back (myarg);
+  }
+
+  arg_sets.resize (extra_param_count_max + 1);
+  for (i = 0; i < arg_sets.size(); ++i)
+    {
+      unsigned j;
+      for (j = i; j < arg_sets.size() - 1; ++j)
+	arg_sets[j + 1].push_back (all_args[i]);
+    }
+
+  /* Print sysv functions */
+  vector<vector<class arg> >::const_iterator asi;
+  for (asi = arg_sets.begin(); asi != arg_sets.end(); ++asi)
+    {
+      fn *fn;
+      for (int _alloca = 0; _alloca <= 1; ++_alloca)
+	for (int varargs = 0; varargs <= 1; ++varargs)
+	{
+	  try {
+	    int var = (_alloca ? FN_VAR_ALLOCA : 0)
+		    | (varargs ? FN_VAR_VARARGS : 0);
+	    fn = new ::fn (*asi, 0, var);
+	  } catch (invalid_argument) {
+	    continue;
+	  }
+	  sysv_funcs.push_back (fn);
+	  fn->print_decl (*fns_c);
+	}
+    }
+
+  /* Print ms_abi functions.  */
+  int var;
+  for (var = 0; var <= FN_VAR_MASK; ++var)
+    {
+      /* We only want msabi fns now.  */
+      if (! (var & FN_VAR_MSABI))
+	continue;
+
+      /* Skip explicitly masked variants */
+      if (var & fn_variant_mask)
+	continue;
+
+      unsigned clobbers;
+      for (clobbers = 0; clobbers <= OPTIONAL_REG_ALL; ++clobbers)
+	{
+	  /* Skip clobbers that would be invalid.  */
+	  if (clobbers & OPTIONAL_REG_RBP)
+	    {
+	      /* Uses BP explicitly.  */
+	      if (var & FN_VAR_HFP_OR_REALIGN)
+		continue;
+
+	      /* Alloca seems to require DRAP, which uses BP.  */
+	      if (var & FN_VAR_ALLOCA)
+		continue;
+	    }
+
+	  for (asi = arg_sets.begin(); asi != arg_sets.end(); ++asi)
+	    {
+	      fn *fn;
+	      try {
+		fn = new ::fn (*asi, clobbers, var);
+	      } catch (invalid_argument) {
+		continue;
+	      }
+
+	      msabi_funcs.push_back (fn);
+	      fn->print_decl (*do_tests_c);
+	      fn->print_def (*fns_c);
+	    }
+	}
+    }
+
+  *do_tests_c << endl;
+  make_do_tests_decl (all_args, *do_tests_c);
+  *do_tests_c << endl;
+
+  vector<class fn*>::const_iterator fi;
+  for (fi = sysv_funcs.begin (); fi != sysv_funcs.end (); ++fi)
+    (*fi)->print_def (*do_tests_c);
+
+  make_do_test (all_args, msabi_funcs, *do_tests_c);
+
+  for_each (sysv_funcs.begin (), sysv_funcs.end (), default_delete<fn> ());
+  for_each (msabi_funcs.begin (), msabi_funcs.end (), default_delete<fn> ());
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/i386/msabi/msabi.c b/gcc/testsuite/gcc.target/i386/msabi/msabi.c
new file mode 100644
index 00000000000..cc10981c331
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/msabi.c
@@ -0,0 +1,304 @@
+/* Microsoft 64 ABI test program.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   Contributed by Daniel Santos <daniel.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 functions to assure clobbered registers are properly saved
+   and restored and attempt to detect any flaws in the behavior of these
+   functions.  The following variants are tested:
+
+   * Either uses hard frame pointer (-fno-omit-frame-pointer), re-aligns
+     the stack (e.g., attribute  force_align_arg_pointer) or neither,
+   * Uses alloca (and thus DRAP) or not,
+   * Uses sibling call optimization or not (NOTE: we don't actually verify
+     that sibcall optimization is used),
+   * Uses variable argument list or not, and
+   * Has shrink-wrapped code or not (NOTE: again, we don't actually verify
+     anything is shrink-wrapped).
+
+  In addition, an ms_abi function is generated for each of these combinations
+  clobbering each unique combination additional registers (excluding BP when
+  a frame pointer is used). Shrink-wrap variants are called in a way that
+  both the fast and slow path are used. Re-aligned variants are called with
+  an aligned and mis-aligned stack.
+
+  Each ms_abi function is called via a hacky assembly stub that first saves
+  all volatile registers and fills them with random values. The ms_abi
+  function is then called.  After the function returns, the value of all
+  volatile registers is verified against the random data and then restored.
+
+  Currently, we only test passing a variable number of long arguments as
+  parameters.  It could be helpful to also pass larger parameters that would
+  always get passed on the stack.
+*/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+
+#include "msabi.h"
+
+#ifndef __x86_64__
+# error Test only valid on x86_64
+#endif
+
+struct test_data test_data;
+int shrink_wrap_global;
+static struct drand48_data rand_data;
+static int arbitrarily_fail;
+static const char *argv0;
+
+void init_test (void *fn, const char *name, enum alignment_option unaligned,
+		enum shrink_wrap_option shrink_wrap)
+{
+  int i;
+  union regdata *data = &test_data.regdata[REG_SET_INPUT];
+
+  assert (unaligned < ALIGNMENT_COUNT);
+  assert (shrink_wrap < SHRINK_WRAP_COUNT);
+
+  memset (&test_data, 0, sizeof (test_data));
+  for (i = 55; i >= 0; --i)
+    {
+      uint64_t u64;
+      lrand48_r (&rand_data, (long*)&u64);
+      data->u32_arr[i] = (uint32_t)u64;
+    }
+  test_data.fn = fn;
+  test_data.name = name;
+  test_data.unaligned = unaligned;
+  test_data.shrink_wrap = shrink_wrap;
+
+  switch (shrink_wrap)
+  {
+    case SHRINK_WRAP_NONE:
+    case SHRINK_WRAP_COUNT:
+      break;
+    case SHRINK_WRAP_FAST_PATH:
+      shrink_wrap_global = FLAG_SHRINK_WRAP_FAST_PATH;
+      break;
+    case SHRINK_WRAP_SLOW_PATH:
+      shrink_wrap_global = FLAG_SHRINK_WRAP_SLOW_PATH;
+      break;
+  }
+}
+
+static const char *alignment_str[ALIGNMENT_COUNT] =
+{
+  "", "aligned", "unaligned"
+};
+
+static const char *shrink_wrap_str[SHRINK_WRAP_COUNT] =
+{
+  "", "shrink-wrap fast path", "shrink-wrap slow path"
+};
+
+static const char *test_descr ()
+{
+  static char buffer[0x400];
+
+  if (test_data.unaligned || test_data.shrink_wrap)
+    snprintf (buffer, sizeof (buffer) - 1, "`%s' (%s%s%s)",
+	      test_data.name,
+	      alignment_str[test_data.unaligned],
+	      (test_data.unaligned && test_data.shrink_wrap ? ", " : ""),
+	      shrink_wrap_str[test_data.shrink_wrap]);
+  else
+    snprintf (buffer, sizeof (buffer) - 1, "`%s'", test_data.name);
+
+  return buffer;
+}
+
+void fail_if_not_equal (long got, long expected)
+{
+  if (got != expected)
+    {
+      fprintf (stderr, "Failed on test function %s: got 0x%016lx, "
+	       "expected 0x%016lx\n", test_descr (), got, expected);
+      raise (SIGTRAP);
+      exit (-1);
+    }
+}
+
+static const char *regnames[] = {
+  "XMM6",
+  "XMM7",
+  "XMM8",
+  "XMM9",
+  "XMM10",
+  "XMM11",
+  "XMM12",
+  "XMM13",
+  "XMM14",
+  "XMM15",
+  "RSI",
+  "RDI",
+  "RBX",
+  "RBP",
+  "R12",
+  "R13",
+  "R14",
+  "R15",
+};
+
+static void print_header (int *header_printed)
+{
+  if (!*header_printed)
+    fprintf (stderr, "       %-35s    %-35s\n", "Expected", "Got");
+  *header_printed = 1;
+}
+
+static int compare_reg128 (const __uint128_t *a, const __uint128_t *b,
+			   const char *name, int *header_printed)
+{
+  if (!memcmp (a, b, sizeof (*a)))
+    return 0;
+  else
+    {
+      long ha = *((long*)a);
+      long la = *((long*)a + 16);
+      long hb = *((long*)b);
+      long lb = *((long*)a + 16);
+      print_header (header_printed);
+      fprintf (stderr, "%-5s: 0x%016lx %016lx != 0x%016lx %016lx\n",
+	       name, ha, la, hb, lb);
+      return 1;
+    }
+}
+
+static int compare_reg64 (long a, long b, const char *name,
+			  int *header_printed)
+{
+  if (a == b)
+    return 0;
+  else
+    {
+      print_header (header_printed);
+      fprintf (stderr, "%s: 0x%016lx != 0x%016lx\n", name, a, b);
+      return 1;
+    }
+}
+
+__attribute__((noinline))
+void compare_regdata ()
+{
+  unsigned i;
+  unsigned bad = 0;
+  int header_printed = 0;
+
+  union regdata *a = &test_data.regdata[REG_SET_INPUT];
+  union regdata *b = &test_data.regdata[REG_SET_OUTPUT];
+
+  a = __builtin_assume_aligned(a, 16);
+  b = __builtin_assume_aligned(b, 16);
+
+  if (arbitrarily_fail) {
+    uint64_t u64;
+    lrand48_r (&rand_data, (long*)&u64);
+    if (!(u64 & 0xff))
+      b->u32_arr[u64 % 56] = 0xfdfdfdfd;
+  }
+
+  for (i = 0; i < 10; ++i)
+    bad |= compare_reg128 (&a->sseregs[i], &b->sseregs[i], regnames[i],
+			   &header_printed);
+
+  for (i = 0; i < 8; ++i)
+    bad |= compare_reg64 (a->intregs[i], b->intregs[i], regnames[i + 10],
+			  &header_printed);
+
+  if (bad)
+    {
+      fprintf (stderr, "Failed on test function %s\n", test_descr ());
+      raise (SIGTRAP);
+      exit (-1);
+    }
+}
+
+__attribute__((ms_abi)) long do_sibcall (long arg) {
+  return arg + FLAG_SIBCALL;
+}
+
+
+void usage ()
+{
+  fprintf (stderr, "Usage: %s [-s <seed>] [-f]\n", argv0);
+  exit (-1);
+}
+
+static int long_optarg (const char *optarg, long *l)
+{
+  const char *start = optarg;
+  int base = 10;
+  char *end;
+
+  if (!strncasecmp (optarg, "0x", 2))
+  {
+    base = 16;
+    start += 2;
+  }
+
+  *l = strtol(start, &end, base);
+
+  return end == start || *end != 0;
+}
+
+int main (int argc, char *argv[])
+{
+  long seed = 0;
+  int c;
+  argv0 = argv[0];
+
+  while ((c = getopt (argc, argv, "s:f")) != -1)
+    {
+      switch (c)
+	{
+	case 's':
+	  if (long_optarg (optarg, &seed))
+	  {
+	    fprintf (stderr, "ERROR: Bad value for -s: `%s`\n", optarg);
+	    exit (-1);
+	  }
+	  break;
+
+	case 'f':
+	  arbitrarily_fail = 1;
+	  break;
+	}
+    }
+
+  assert (!((long)&test_data.regdata[REG_SET_SAVE] & 15));
+  assert (!((long)&test_data.regdata[REG_SET_INPUT] & 15));
+  assert (!((long)&test_data.regdata[REG_SET_OUTPUT] & 15));
+
+  srand48_r (seed, &rand_data);
+  do_tests ();
+
+  return 0;
+}
+
+
diff --git a/gcc/testsuite/gcc.target/i386/msabi/msabi.h b/gcc/testsuite/gcc.target/i386/msabi/msabi.h
new file mode 100644
index 00000000000..ac046dc40dd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/msabi.h
@@ -0,0 +1,110 @@
+/* Microsoft 64 ABI test program header.
+   Copyright (C) 2016 Free Software Foundation, Inc.
+   Contributed by Daniel Santos <daniel.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/>.  */
+
+#ifndef _MSABI_H
+#define _MSABI_H
+
+#include <stdint.h>
+#include <alloca.h>
+#include <stdarg.h>
+#include <assert.h>
+
+union regdata {
+  struct {
+    __uint128_t	sseregs[10];
+    union {
+      uint64_t	intregs[8];
+      struct {
+	uint64_t	rsi;
+	uint64_t	rdi;
+	uint64_t	rbx;
+	uint64_t	rbp;
+	uint64_t	r12;
+	uint64_t	r13;
+	uint64_t	r14;
+	uint64_t	r15;
+      };
+    };
+  };
+  uint32_t		u32_arr[56];
+} __attribute__((aligned (16)));
+
+enum reg_data_sets
+{
+  REG_SET_SAVE,
+  REG_SET_INPUT,
+  REG_SET_OUTPUT,
+
+  REG_SET_COUNT
+};
+
+enum flags
+{
+  FLAG_ALLOCA			= 0x01000000,
+  FLAG_SIBCALL			= 0x02000000,
+  FLAG_SHRINK_WRAP_FAST_PATH	= 0x08000000,
+  FLAG_SHRINK_WRAP_SLOW_PATH	= 0x0c000000,
+};
+
+enum alignment_option
+{
+  ALIGNMENT_NOT_TESTED,
+  ALIGNMENT_ALIGNED,
+  ALIGNMENT_ALIGNED_PLUS_8,
+
+  ALIGNMENT_COUNT,
+};
+
+enum shrink_wrap_option
+{
+  SHRINK_WRAP_NONE,
+  SHRINK_WRAP_FAST_PATH,
+  SHRINK_WRAP_SLOW_PATH,
+
+  SHRINK_WRAP_COUNT
+};
+
+struct test_data
+{
+  union regdata regdata[REG_SET_COUNT];
+  void *fn;
+  void *retaddr;
+  const char *name;
+  enum alignment_option unaligned;
+  enum shrink_wrap_option shrink_wrap;
+};
+
+extern struct test_data test_data;
+extern int shrink_wrap_global;
+
+extern void do_tests ();
+extern void init_test (void *fn, const char *name,
+		       enum alignment_option unaligned,
+		       enum shrink_wrap_option shrink_wrap);
+extern void fail_if_not_equal (long a, long b);
+extern void compare_regdata ();
+extern __attribute__((ms_abi)) long do_sibcall (long arg);
+
+#endif /* _MSABI_H */
-- 
2.11.0


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Help with integrating my test program into dejagnu
  2016-12-30 19:55 Help with integrating my test program into dejagnu Daniel Santos
@ 2016-12-31  0:01 ` Mike Stump
  2016-12-31 19:15   ` Daniel Santos
  0 siblings, 1 reply; 12+ messages in thread
From: Mike Stump @ 2016-12-31  0:01 UTC (permalink / raw)
  To: Daniel Santos; +Cc: gcc, Uros Bizjak, Jan Hubicka, Rainer Orth

On Dec 30, 2016, at 11:58 AM, Daniel Santos <daniel.santos@pobox.com> wrote:
> 
> Still being pretty new to GCC and having never used dejagnu, expect or Tcl, I'm trying to determine how to best integrate my test program into GCC's test harness.  I wrote this to help find breakages while working on optimizations for Microsoft 64-bit ABI pro/epilogues.  Rather than testing specific cases, it generates a few thousand unique tests by iterating through variations.  It consists of a C++ program that generates a pair of .c files (that I don't want in the same translation unit).  These are built along with a static .c and .S file and linked into the test program.  It is intended to be built and executed on the target machine and I currently run it manually with a frequently-edited Makefile.
> 
> The first thing I need help with is figuring out if this should be run by dejagnu or if I should just write a proper Makefile.in and add it to gcc's Makefile.in.  Integrating with dejagnu seems to be the most intuitive and simple, but I don't properly understand how this would affect a cross-compiler build.  Any advice?

Yeah, first step:

+GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc-github/build/head
+#GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc-github/build/head-test-sp-realigned
+#GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc-github/build/head-test-patched
+#GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc.work0/build/head
+GCC_SRC_DIR  = /home/daniel/proj/sys/gcc-github
+#GCC_SRC_DIR  = /home/daniel/proj/sys/gcc.work0

Any absolute filename is wrong, remove all.

Do:

  srcdir = .
  objdir := $(shell pwd)

in the Makefile at the top, and use use $(srcdir) to locate files in the source tree and use $(objdir) to locate files in the build tree.  You can think of . as gcc/testsuite/gcc.target/i386/msabi.  You can place any temporary files into .

Next step, add some glue code in i386.exp to run it:

Index: testsuite/gcc.target/i386/i386.exp
===================================================================
--- testsuite/gcc.target/i386/i386.exp  (revision 243984)
+++ testsuite/gcc.target/i386/i386.exp  (working copy)
@@ -418,6 +418,28 @@ if ![info exists DEFAULT_CFLAGS] then {
 dg-init
 clearcap-init
 
+proc run_msabi { } {
+    global srcdir subdir objdir rootme GCC_UNDER_TEST
+
+    verbose "orig srcdir is $srcdir" 0
+    verbose "orig objdir is $objdir" 0
+    verbose "orig subdir is $subdir" 0
+    verbose "GCC_UNDER_TEST is $GCC_UNDER_TEST" 0
+    if { ![isnative] } {
+       unsupported "$subdir/msabi"
+        return
+    }
+    set status [remote_exec build "make -f $srcdir/$subdir/msabi/Makefile srcdir=$srcdir/$subdir/msabi objdir=$rootme GCC_UNDER_TEST=$GCC_UNDER_TEST check"]
+    set status [lindex $status 0]
+    return $status
+}
+
+if { [run_msabi] == 1 } {
+  fail $subdir/msabi
+} else {
+  pass $subdir/msabi
+}
+
 global runtests
 # Special case compilation of vect-args.c so we don't have to
 # replicate it 16 times.

See site.exp in gcc's build directory for all the variables you can use and what values they have.  That's be a good start.  I think that would bring it up to at least a minimal level for inclusion.

From there, I'd get rid of the rest of the Makefile, and just put the code into run_msabi.  You'll want to examine struct-layout-1(aka gcc/testsuite/gcc.dg/compat/struct-layout-1.exp), it has a recipe for running a generator program.  compat can also check binary compatibility between two compilers.  For that to work, one would have to run in an environment where one can run the other compiler.

Another way to simplify it would be to just check in the generated program as a .h file, and then #include all the bits in msabi.c, and then test msabi.c normally.  This is reasonable if the generated program isn't that big.  If big, then that should be avoided.

I'll punt to the x86 people on wether or not they want the tester in the tree.  Also, the .exp file can be created in the msabi directory, and dejagnu will run it from there.  You then would have no changes to i386.exp.  Seems slightly cleaner.  Once you get comfortable with it, you can remove the verbose lines.  I just put them in so that you can see those variables.

You might need to conditionalize the test to only run on those systems that can compile and run the code, to do that you might need [istarget "triplet"], where triplet is a system where you know it will work.  I didn't check the code to see how portable it was.

Hope that helps.  Also, see testsuite/lib/*.exp, and testsuite/.../*.exp in the source tree for hints on variables, code, coding style, and examples of all sorts of things.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Help with integrating my test program into dejagnu
  2016-12-31  0:01 ` Mike Stump
@ 2016-12-31 19:15   ` Daniel Santos
  2016-12-31 20:53     ` Mike Stump
  0 siblings, 1 reply; 12+ messages in thread
From: Daniel Santos @ 2016-12-31 19:15 UTC (permalink / raw)
  To: Mike Stump; +Cc: gcc, Uros Bizjak, Jan Hubicka, Rainer Orth

First of all, thank you for your thoughtful response!

On 12/30/2016 06:01 PM, Mike Stump wrote:
> On Dec 30, 2016, at 11:58 AM, Daniel Santos <daniel.santos@pobox.com> wrote:
>> Still being pretty new to GCC and having never used dejagnu, expect or Tcl, I'm trying to determine how to best integrate my test program into GCC's test harness.  I wrote this to help find breakages while working on optimizations for Microsoft 64-bit ABI pro/epilogues.  Rather than testing specific cases, it generates a few thousand unique tests by iterating through variations.  It consists of a C++ program that generates a pair of .c files (that I don't want in the same translation unit).  These are built along with a static .c and .S file and linked into the test program.  It is intended to be built and executed on the target machine and I currently run it manually with a frequently-edited Makefile.
>>
>> The first thing I need help with is figuring out if this should be run by dejagnu or if I should just write a proper Makefile.in and add it to gcc's Makefile.in.  Integrating with dejagnu seems to be the most intuitive and simple, but I don't properly understand how this would affect a cross-compiler build.  Any advice?
> Yeah, first step:
>
> +GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc-github/build/head
> +#GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc-github/build/head-test-sp-realigned
> +#GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc-github/build/head-test-patched
> +#GCC_BUILD_DIR       = /home/daniel/proj/sys/gcc.work0/build/head
> +GCC_SRC_DIR  = /home/daniel/proj/sys/gcc-github
> +#GCC_SRC_DIR  = /home/daniel/proj/sys/gcc.work0
>
> Any absolute filename is wrong, remove all.

Yes, I wouldn't have intended to submit the Makefile like this, I just 
sent it as it was. I should have communicated that better. I was using 
this to test two different patch sets.

> Do:
>
>    srcdir = .
>    objdir := $(shell pwd)
>
> in the Makefile at the top, and use use $(srcdir) to locate files in the source tree and use $(objdir) to locate files in the build tree.  You can think of . as gcc/testsuite/gcc.target/i386/msabi.  You can place any temporary files into .
>
> Next step, add some glue code in i386.exp to run it:
>
> Index: testsuite/gcc.target/i386/i386.exp
> ===================================================================
> --- testsuite/gcc.target/i386/i386.exp  (revision 243984)
> +++ testsuite/gcc.target/i386/i386.exp  (working copy)
> @@ -418,6 +418,28 @@ if ![info exists DEFAULT_CFLAGS] then {
>   dg-init
>   clearcap-init
>   
> +proc run_msabi { } {
> +    global srcdir subdir objdir rootme GCC_UNDER_TEST
> +
> +    verbose "orig srcdir is $srcdir" 0
> +    verbose "orig objdir is $objdir" 0
> +    verbose "orig subdir is $subdir" 0
> +    verbose "GCC_UNDER_TEST is $GCC_UNDER_TEST" 0
> +    if { ![isnative] } {
> +       unsupported "$subdir/msabi"
> +        return
> +    }
> +    set status [remote_exec build "make -f $srcdir/$subdir/msabi/Makefile srcdir=$srcdir/$subdir/msabi objdir=$rootme GCC_UNDER_TEST=$GCC_UNDER_TEST check"]
> +    set status [lindex $status 0]
> +    return $status
> +}
> +
> +if { [run_msabi] == 1 } {
> +  fail $subdir/msabi
> +} else {
> +  pass $subdir/msabi
> +}
> +
>   global runtests
>   # Special case compilation of vect-args.c so we don't have to
>   # replicate it 16 times.

Oh, that's the magic I was looking for!

> See site.exp in gcc's build directory for all the variables you can use and what values they have.  That's be a good start.  I think that would bring it up to at least a minimal level for inclusion.
>
>  From there, I'd get rid of the rest of the Makefile, and just put the code into run_msabi.  You'll want to examine struct-layout-1(aka gcc/testsuite/gcc.dg/compat/struct-layout-1.exp), it has a recipe for running a generator program.

Being able to completely eliminate the Makefile would definitely seem 
ideal, especially if there's a recipe for a generator program already.

> compat can also check binary compatibility between two compilers.  For that to work, one would have to run in an environment where one can run the other compiler.
>
> Another way to simplify it would be to just check in the generated program as a .h file, and then #include all the bits in msabi.c, and then test msabi.c normally.  This is reasonable if the generated program isn't that big.  If big, then that should be avoided.

The generated sources are 2MiB with the default generation parameters.  
Also, I can't have the two generated .c files in the same translation 
unit (at least in their current form) because gcc's too smart with 
optimizations. :)  If I call a very simple global function marked 
"noinline" within the same translation unit, it will get rid of the 
function call anyway.  I suppose I could defeat that by calling it via a 
global pointer.  So perhaps it can be further simplified now that I 
think more about it.

> I'll punt to the x86 people on wether or not they want the tester in the tree.  Also, the .exp file can be created in the msabi directory, and dejagnu will run it from there.  You then would have no changes to i386.exp.  Seems slightly cleaner.  Once you get comfortable with it, you can remove the verbose lines.  I just put them in so that you can see those variables.

That was the original approach I was trying, I just couldn't figure out 
enough of the code to make an msabi.exp work, so this is a huge help!

> You might need to conditionalize the test to only run on those systems that can compile and run the code, to do that you might need [istarget "triplet"], where triplet is a system where you know it will work.  I didn't check the code to see how portable it was.

Other than being x86_64-specific and making heavy use of gcc extensions, 
the C & C++ code is platform portable.  I did manage to find my target 
test code examining the stackalign .exp.

if { ![istarget x86_64-*-*] } then {
   return
}

> Hope that helps.  Also, see testsuite/lib/*.exp, and testsuite/.../*.exp in the source tree for hints on variables, code, coding style, and examples of all sorts of things.

This is perfect, thank you so much!

Daniel

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Help with integrating my test program into dejagnu
  2016-12-31 19:15   ` Daniel Santos
@ 2016-12-31 20:53     ` Mike Stump
  2016-12-31 22:10       ` Daniel Santos
  2017-01-11  5:09       ` Daniel Santos
  0 siblings, 2 replies; 12+ messages in thread
From: Mike Stump @ 2016-12-31 20:53 UTC (permalink / raw)
  To: Daniel Santos; +Cc: gcc, Uros Bizjak, Jan Hubicka, Rainer Orth

On Dec 31, 2016, at 11:18 AM, Daniel Santos <daniel.santos@pobox.com> wrote:
> 
> The generated sources are 2MiB

Yeah, too big, better to have a generator.

> Also, I can't have the two generated .c files in the same translation unit (at least in their current form) because gcc's too smart with optimizations. :)

You can inform the optimizer to stop doing that.  volatile is but one way.  This informs it that it doesn't get to know what is really going on.

void foo(int, float) { }

void (* volatile foo_noinfo) (int, float) = foo;

  foo_noinfo (1, 3.4);

mean call foo, and inform the optimizer it doesn't get to know what is going on.

  foo (1, 3.4);

means call foo, and inform the optimizer that it knows everything.  This is more complete than noinline.

>> You might need to conditionalize the test to only run on those systems that can compile and run the code, to do that you might need [istarget "triplet"], where triplet is a system where you know it will work.  I didn't check the code to see how portable it was.
> 
> Other than being x86_64-specific and making heavy use of gcc extensions, the C & C++ code is platform portable.

There need be no gcc or g++ on the build system, nor the host system.  The only thing you get is a C++ compiler on the build system.  It is important that the generator program not use anything that isn't in the language standard (no gcc extensions here).  In the under test code, you can use gcc extensions, but, if you do that, the cost is that you can't then test binary compatibility with Microsoft's compiler (if it doesn't support the extensions you use) a la the struct-layout-1 methodology.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Help with integrating my test program into dejagnu
  2016-12-31 20:53     ` Mike Stump
@ 2016-12-31 22:10       ` Daniel Santos
  2017-01-01  4:51         ` Mike Stump
  2017-01-11  5:09       ` Daniel Santos
  1 sibling, 1 reply; 12+ messages in thread
From: Daniel Santos @ 2016-12-31 22:10 UTC (permalink / raw)
  To: Mike Stump; +Cc: gcc, Uros Bizjak, Jan Hubicka, Rainer Orth

On 12/31/2016 02:53 PM, Mike Stump wrote:
>> Also, I can't have the two generated .c files in the same translation unit (at least in their current form) because gcc's too smart with optimizations. :)
> You can inform the optimizer to stop doing that.  volatile is but one way.  This informs it that it doesn't get to know what is really going on.
>
> void foo(int, float) { }
>
> void (* volatile foo_noinfo) (int, float) = foo;
>
>    foo_noinfo (1, 3.4);
>
> mean call foo, and inform the optimizer it doesn't get to know what is going on.
>
>    foo (1, 3.4);
>
> means call foo, and inform the optimizer that it knows everything.  This is more complete than noinline.

That is interesting.  That means that I can cram all of this into a 
single translation unit, so I can just generate a single header instead 
(I had originally started this way).  Very nice! :)

>>> You might need to conditionalize the test to only run on those systems that can compile and run the code, to do that you might need [istarget "triplet"], where triplet is a system where you know it will work.  I didn't check the code to see how portable it was.
>> Other than being x86_64-specific and making heavy use of gcc extensions, the C & C++ code is platform portable.
> There need be no gcc or g++ on the build system, nor the host system.  The only thing you get is a C++ compiler on the build system.  It is important that the generator program not use anything that isn't in the language standard (no gcc extensions here).  In the under test code, you can use gcc extensions, but, if you do that, the cost is that you can't then test binary compatibility with Microsoft's compiler (if it doesn't support the extensions you use) a la the struct-layout-1 methodology.

Well I'm learning all sorts of new things; I wasn't aware that the 
testsuite was designed to run with other compilers!  Does the Microsoft 
compiler support building functions using the System V ABI?  I had 
presumed that they would just never do such a thing because it would 
make it would easier to use FOSS on Windows, which is just 
anti-monopolistic.  Anyway, the test name "msabi" may be misleading; 
it's goal is to test 64-bit Microsoft ABI functions that call System V 
functions.  So for the test to be meaningful, I would still need to be 
able to at least declare a function as System V (even if I use an 
assembler implementation) and for the compiler to properly call it.

I'm also testing gcc's force_align_arg_pointer attribute in this 
program, and calling such ms_abi functions both normally and with an 
intentionally mis-aligned stack using inline asm -- that test would 
probably be useless on msvc.  So I guess I will have to investigate to 
find out if it's even possible to build this test on msvc.  I suppose 
one advantage of having part of the code generated is that some of the 
tests can be skipped by just passing a parameter to the generator.  I 
made a bitmask for each test variation, so just passing -m 0x7b would 
prevent generation of forced re-alignment tests.

Daniel

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Help with integrating my test program into dejagnu
  2016-12-31 22:10       ` Daniel Santos
@ 2017-01-01  4:51         ` Mike Stump
  0 siblings, 0 replies; 12+ messages in thread
From: Mike Stump @ 2017-01-01  4:51 UTC (permalink / raw)
  To: Daniel Santos; +Cc: gcc, Uros Bizjak, Jan Hubicka, Rainer Orth

On Dec 31, 2016, at 2:13 PM, Daniel Santos <daniel.santos@pobox.com> wrote:
> 
> Well I'm learning all sorts of new things; I wasn't aware that the testsuite was designed to run with other compilers!  Does the Microsoft compiler support building functions using the System V ABI?

IDK.  I kinda doubt it.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Help with integrating my test program into dejagnu
  2016-12-31 20:53     ` Mike Stump
  2016-12-31 22:10       ` Daniel Santos
@ 2017-01-11  5:09       ` Daniel Santos
  2017-01-11 16:46         ` Mike Stump
  2017-01-11 18:25         ` Joseph Myers
  1 sibling, 2 replies; 12+ messages in thread
From: Daniel Santos @ 2017-01-11  5:09 UTC (permalink / raw)
  To: Mike Stump; +Cc: gcc, Rainer Orth

[-- Attachment #1: Type: text/plain, Size: 1497 bytes --]

Thanks again for help getting started with this Mike!

I've gotten rid of the Makefile and everything is run now from 
msabi.exp.  I've also gotten rid of the header file, now that I know how 
to define a "_noinfo" fn pointer, so it's down to just 4 files: 
msabi.exp, gen.cc, msabi.c and do_test.S.

After running using DG_TORTURE_OPTIONS, it became clear that the 
resulting program was just too big, so I've modified the generator so 
that the test can be done in little bits.  Otherwise, the build eats 
6GiB+ and takes forever on the final set of cflags.  I also needed to 
add an option to the generator to skip RBP clobbers in cases where the 
whole program is being built with hard frame pointers (-O0 or 
-fno-omit-frame-pointers).

And now for 50 questions. :)  Am I using DG_TORTURE_OPTIONS correctly or 
should such a test only exist under gcc.torture?  I'm not sure if I'm 
managing this correctly, as I'm calling pass/fail $subdir after each 
iteration of the test (should this only be called once?).  Also, being 
that the generator is C++, I've added HOSTCXX and HOSTCXXFLAGS to 
site.exp, I hope that's OK.  Finally, would you please look at my 
runtest_msabi procedure to make sure that I'm doing the build 
correctly?  I'm using "remote_exec build" for most of it and I'm not 
100% certain if that is the correct way to do it.

Once I get this cleaned up a bit more I'm going to send it as an RFC and 
hopefully get some feedback from the i386 maintainers.

Thanks again
Daniel

[-- Attachment #2: msabi-xlogue-tests.patch --]
[-- Type: text/x-patch, Size: 4665 bytes --]

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 2aae684cad0..15e51b4cdc5 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -3789,7 +3789,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/msabi.exp b/gcc/testsuite/gcc.target/i386/msabi/msabi.exp
new file mode 100644
index 00000000000..d89f78f7ae1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/msabi/msabi.exp
@@ -0,0 +1,129 @@
+# Copyright (C) 2017 Free Software Foundation, Inc.
+
+# This program 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 of the License, or
+# (at your option) any later version.
+# 
+# This program 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.
+# 
+# You should have received a copy of the GNU General Public License
+# along with GCC; see the file COPYING3.  If not see
+# <http://www.gnu.org/licenses/>.
+
+if $tracelevel then {
+    strace $tracelevel
+}
+
+# Exit immediately if this isn't a native x86_64 GNU target.
+if { ![istarget x86_64-*-gnu] || ![isnative] } then {
+    unsupported "$subdir"
+    return
+}
+
+global DG_TORTURE_OPTIONS
+
+# Load procedures from common libraries.
+load_lib gcc-dg.exp
+load_lib c-torture.exp
+
+dg-init
+
+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"
+	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" "" "" "" 7200]
+	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]
+    }
+
+    if { $status != 0 } then {
+	fail $subdir
+    } else {
+	pass $subdir
+    }
+}
+
+foreach options $DG_TORTURE_OPTIONS {
+    set gen_extra_options ""
+    if { [string match *-O0* "$options"]
+		    || [string match *-fno-omit-frame-pointer* "$options"]} {
+	set gen_extra_options "--omit-rbp-clobbers"
+    }
+
+    for {set i 0} {$i < 5} {incr i} {
+      runtest_msabi "$options" "-p$i $gen_extra_options"
+    }
+}
+
+dg-finish



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Help with integrating my test program into dejagnu
  2017-01-11  5:09       ` Daniel Santos
@ 2017-01-11 16:46         ` Mike Stump
  2017-01-14  2:25           ` Daniel Santos
  2017-01-11 18:25         ` Joseph Myers
  1 sibling, 1 reply; 12+ messages in thread
From: Mike Stump @ 2017-01-11 16:46 UTC (permalink / raw)
  To: Daniel Santos; +Cc: gcc, Rainer Orth

On Jan 10, 2017, at 9:13 PM, Daniel Santos <daniel.santos@pobox.com> wrote:
> I've gotten rid of the Makefile and everything is run now from msabi.exp.  I've also gotten rid of the header file, now that I know how to define a "_noinfo" fn pointer, so it's down to just 4 files: msabi.exp, gen.cc, msabi.c and do_test.S.

Sounds better.

> After running using DG_TORTURE_OPTIONS,

But why?  I think you missed what you're testing.  You aren't creating or looking for bugs in the optimizer.  Your test case isn't for an optimizer, therefore, you should not torture the poor test case.  I think what you are testing is argument passing.  That typically is the decision about what bits are where, and that is an optimization irrelevant thing to test.

> it became clear that the resulting program was just too big, so I've modified the generator so that the test can be done in little bits.

A sum of little bits always is likely more costly the just one large thing.  I don't think there is an economy to be had there, other than the ability to say test case 15 fails, and you want a person to be able to drill into test case 15 by itself without the others around.  With a well structured large test case, it should be clear how each subpart can be separated out and run as a single small test case.

For example:

test1() { ... }

main() {
  test1();
  test2();
  [ ... ]
}

here, we see that we remove 99% of the test case, and run just a single case.  Normal, manual edit leaving just one line, and then the transitive closure of the one test routine.  I think if you time it, you discover that you can fit in more cases this way, then if you break them up; also, if you torture, you can't fit in as many cases in the time given.  This is at the heart of why I don't think you want to torture.

> Otherwise, the build eats 6GiB+ and takes forever on the final set of flags.

So, one review point will be, is the added time testing at all useful in general.  That's an open review point.  The compiler can be easily damaged with random edits, but we have fairly good coverage that will catch most of it.  We typically don't spend time in the test suite methodically to catch every single thing that can go wrong, just the things that usually do go wrong based upon reported bugs.  What is the added time in seconds to test on what type of machine?

> And now for 50 questions. :)  Am I using DG_TORTURE_OPTIONS correctly

I want to say no.  See above.  No one should ever use it, unless they have a very specific well though out reason.  I've not heard the reason in this case.

> or should such a test only exist under gcc.torture?

gcc.torture is for a very narrow and specific type of bug.  There are bugs that people that work on the optimizer add for test cases that go though the optimizer that they want to ensure that the bug they just fixed, doesn't re-appear.  So, the first question, are you working on the optimizer?  If not, then it would likely be inappropriate.

> I'm not sure if I'm managing this correctly, as I'm calling pass/fail $subdir after each iteration of the test (should this only be called once?).

No, if you did it, you would call it once per iteration, and you would mix in the torture flags to the pass/fail line.  pass "$file.c $torture_option" would be the typical, in your code, it would be $generator_args.

> Also, being that the generator is C++, I've added HOSTCXX and HOSTCXXFLAGS to site.exp, I hope that's OK.

Hum.  I worry about the knock on effect of some sort.  Generally I don't like adding anything to site.exp unless needed.  In this case, I think it'd be fine.  It is the most simple and direct way to do it.

> Finally, would you please look at my runtest_msabi procedure to make sure that I'm doing the build correctly?  I'm using "remote_exec build" for most of it and I'm not 100% certain if that is the correct way to do it.

Yeah, close enough to likely not worry about it too much.  If you wanted to improve it, then next step would be to remove the isnative part and finish the code for cross builds, and just after that finish the code for canadian cross builds.  A canadian cross is one in which the build machine and the host machine are different.  With the isnative, you can get the details of host/build and target machine completely wrong and pay no price for it.  Once you remove it, you then have understand which code works for which system and ensure it works.  A cross build loosely, is one in which the target machine and the host machine are different.  The reason why I suggested isnative, is then you don't have to worry about it, and you can punt the finishing to a cross or canadian cross person.  For them, it is rather trivial to clean up the test case to get it to work i a cross environment.  Without testing, it is easy enough to get wrong.  Also, for them, testing it is then trivial.  If you can find someone that can test in a cross environment and report back if it works or not, that might be a way to step it forward, if you want.

> Once I get this cleaned up a bit more I'm going to send it as an RFC and hopefully get some feedback from the i386 maintainers.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Help with integrating my test program into dejagnu
  2017-01-11  5:09       ` Daniel Santos
  2017-01-11 16:46         ` Mike Stump
@ 2017-01-11 18:25         ` Joseph Myers
  2017-01-11 23:25           ` Daniel Santos
  1 sibling, 1 reply; 12+ messages in thread
From: Joseph Myers @ 2017-01-11 18:25 UTC (permalink / raw)
  To: Daniel Santos; +Cc: Mike Stump, gcc, Rainer Orth

A test [istarget x86_64-*-gnu] is wrong; i?86-* -m64 should always be 
handled exactly the same as x86_64-* -m64.

You need to work out which ABIs (-m32, -mx32, -m64) this testing is 
meaningful for.  Then, allow both x86_64- and i?86- targets, together with 
an appropriate effective-target test ("lp64" to allow just -m64, for 
example).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Help with integrating my test program into dejagnu
  2017-01-11 18:25         ` Joseph Myers
@ 2017-01-11 23:25           ` Daniel Santos
  2017-01-13 17:36             ` Mike Stump
  0 siblings, 1 reply; 12+ messages in thread
From: Daniel Santos @ 2017-01-11 23:25 UTC (permalink / raw)
  To: Joseph Myers; +Cc: Mike Stump, gcc, Rainer Orth

On 01/11/2017 12:25 PM, Joseph Myers wrote:
> A test [istarget x86_64-*-gnu] is wrong; i?86-* -m64 should always be
> handled exactly the same as x86_64-* -m64.
>
> You need to work out which ABIs (-m32, -mx32, -m64) this testing is
> meaningful for.  Then, allow both x86_64- and i?86- targets, together with
> an appropriate effective-target test ("lp64" to allow just -m64, for
> example).
Thank your help!  (testsuite/target-supports.exp is quite a library!)  
So this test is 64-bit only and makes heavy use of gcc extensions 
(mostly attributes ms_abi).  It's aim is to test pro & epilogue creation 
for 64-bit ms_abi functions that call sysv_abi functions (these are the 
ones that result in the massive SSE clobbers).  I do not believe msvc 
supports sysv_abi functions, so the test the test can't be built there.  
As such, I'm only intending to run this when gcc is being tested on 
64-bit x86 platforms.  The resulting program must be executed on the 
target as well, so I was using [isnative].  Would this then be the 
correct test?

if { (![istarget x86_64-*] && ![istarget i?86-*])
      || ![is-effective-target lp64] || ![isnative] } then {
     unsupported "$subdir"
     return
}

Thanks,
Daniel

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Help with integrating my test program into dejagnu
  2017-01-11 23:25           ` Daniel Santos
@ 2017-01-13 17:36             ` Mike Stump
  0 siblings, 0 replies; 12+ messages in thread
From: Mike Stump @ 2017-01-13 17:36 UTC (permalink / raw)
  To: Daniel Santos; +Cc: Joseph Myers, Mike Stump, gcc, Rainer Orth

On Jan 11, 2017, at 3:29 PM, Daniel Santos <daniel.santos@pobox.com> wrote:
> On 01/11/2017 12:25 PM, Joseph Myers wrote:
> Would this then be the correct test?

Yeah, looks good.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: Help with integrating my test program into dejagnu
  2017-01-11 16:46         ` Mike Stump
@ 2017-01-14  2:25           ` Daniel Santos
  0 siblings, 0 replies; 12+ messages in thread
From: Daniel Santos @ 2017-01-14  2:25 UTC (permalink / raw)
  To: Mike Stump; +Cc: gcc, Rainer Orth

Sorry for my delayed response.  I've been a bit under the weather lately.

On 01/11/2017 10:46 AM, Mike Stump wrote:
>> After running using DG_TORTURE_OPTIONS,
> But why?  I think you missed what you're testing.  You aren't creating or looking for bugs in the optimizer.  Your test case isn't for an optimizer, therefore, you should not torture the poor test case.  I think what you are testing is argument passing.  That typically is the decision about what bits are where, and that is an optimization irrelevant thing to test.

Hmm.  There are a few optimizations (that I'm aware of) being involved 
in what I'm intending to test, but I should probably back up a bit.  The 
aim is to test prologue and epilogue creation of 64-bit ms_abi functions 
that call sysv_abi functions.  Differences in these ABIs requires RSI, 
RDI and XMM6-15 to be considered clobbered when calling the sysv_abi 
function.  This test is intended to support two patch sets, the first to 
emit aligned SSE movs when force_align_arg_pointer is used 
(https://gcc.gnu.org/ml/gcc-patches/2016-12/msg01859.html) and the 
second implements out-of-lined stubs for these pro/epilogues to reduce 
text size.  The argument passing part of the test is mainly to ensure I 
don't accidentally clobber something I shouldn't and to shuffle around 
the stack save area in different ways.

This first patch set probably will be unaffected by optimizations, but 
the second one is -- mainly shrink-wrapping and sibling calls. Uros 
indicated an interest in the first patch set for the next stage 1 and I 
haven't re-submitted the second patch set yet as I wanted to have good 
tests to back it up.  Still, I probably don't need to test -O3 
-funroll-all-loops, etc., so I can probably just test once each with -O0 
and -O2, as I do need to verify correctness both with and without 
sibling calls enabled.

>> it became clear that the resulting program was just too big, so I've modified the generator so that the test can be done in little bits.
> A sum of little bits always is likely more costly the just one large thing.

Well first off I'm still using an old Phenom/DDR2 machine and for some 
reason splitting it up (into 6 tests vs 1) is 3.8 times faster than 
running it as one large test (220 seconds vs 841).  I should note that 
I've configured with --enable-stage1-checking=yes,rtl and that running 
the actual test program takes about 1/50th of a second. I'll build 
another bootstrap w/o checking and see how that performs.

> I don't think there is an economy to be had there, other than the ability to say test case 15 fails, and you want a person to be able to drill into test case 15 by itself without the others around.  With a well structured large test case, it should be clear how each subpart can be separated out and run as a single small test case.
>
> For example:
>
> test1() { ... }
>
> main() {
>    test1();
>    test2();
>    [ ... ]
> }
>
> here, we see that we remove 99% of the test case, and run just a single case.  Normal, manual edit leaving just one line, and then the transitive closure of the one test routine.  I think if you time it, you discover that you can fit in more cases this way, then if you break them up; also, if you torture, you can't fit in as many cases in the time given.  This is at the heart of why I don't think you want to torture.

Yes, this makes good sense.

>> Otherwise, the build eats 6GiB+ and takes forever on the final set of flags.
> So, one review point will be, is the added time testing at all useful in general.  That's an open review point.  The compiler can be easily damaged with random edits, but we have fairly good coverage that will catch most of it.  We typically don't spend time in the test suite methodically to catch every single thing that can go wrong, just the things that usually do go wrong based upon reported bugs.  What is the added time in seconds to test on what type of machine?

Yes, I hope to be successful in making this case.  I actually wrote it 
to help me find flaws in my out-of-lined pro/epilogues for ms to sysv 
function calls.  Prior to writing the test program I was debugging Wine 
builds to try to determine what I got wrong, and that's just a pain.  
But it did help me figure out what I needed to include in my test.

>> I'm not sure if I'm managing this correctly, as I'm calling pass/fail $subdir after each iteration of the test (should this only be called once?).
> No, if you did it, you would call it once per iteration, and you would mix in the torture flags to the pass/fail line.  pass "$file.c $torture_option" would be the typical, in your code, it would be $generator_args.

Thanks, that's what I thought.

>> Finally, would you please look at my runtest_msabi procedure to make sure that I'm doing the build correctly?  I'm using "remote_exec build" for most of it and I'm not 100% certain if that is the correct way to do it.
> Yeah, close enough to likely not worry about it too much.  If you wanted to improve it, then next step would be to remove the isnative part and finish the code for cross builds,

The issue that I see here is that the resulting program needs to be 
executed on the target.  Is there a way to run a cross build test on a 
host machine but have target executables run in an emulator or some such?

> and just after that finish the code for canadian cross builds.  A canadian cross is one in which the build machine and the host machine are different.  With the isnative, you can get the details of host/build and target machine completely wrong and pay no price for it.  Once you remove it, you then have understand which code works for which system and ensure it works.  A cross build loosely, is one in which the target machine and the host machine are different.  The reason why I suggested isnative, is then you don't have to worry about it, and you can punt the finishing to a cross or canadian cross person.  For them, it is rather trivial to clean up the test case to get it to work i a cross environment.  Without testing, it is easy enough to get wrong.  Also, for them, testing it is then trivial.  If you can find someone that can test in a cross environment and report back if it works or not, that might be a way to step it forward, if you want.

Good, then I'm happy to punt this part until later. :)  I just googled 
Canadian cross build and that's entirely new to me!

Thank you for your thoughtful reply.
Daniel


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2017-01-14  2:25 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-12-30 19:55 Help with integrating my test program into dejagnu Daniel Santos
2016-12-31  0:01 ` Mike Stump
2016-12-31 19:15   ` Daniel Santos
2016-12-31 20:53     ` Mike Stump
2016-12-31 22:10       ` Daniel Santos
2017-01-01  4:51         ` Mike Stump
2017-01-11  5:09       ` Daniel Santos
2017-01-11 16:46         ` Mike Stump
2017-01-14  2:25           ` Daniel Santos
2017-01-11 18:25         ` Joseph Myers
2017-01-11 23:25           ` Daniel Santos
2017-01-13 17:36             ` Mike Stump

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).