diff test/plain_c++/test_main.cc @ 533:71c884e610f0

- integration of patches from Raphael Luba, Thekla, Inc.: * integration of aggregate-by-value (struct, union) support patch for x64 (win and sysv) * windows/x64 asm additions to specify how stack unwinds (help for debuggers, exception handling, etc.) * see Changelog for details - new calling convention modes for thiscalls (platform agnostic, was specific before) * new signature character for platform agnostic thiscalls ('*' / DC_SIGCHAR_CC_THISCALL) - dcCallF(), dcVCallF(), dcArgF() and dcVArgF(): * added support for aggregates-by-value (wasn't part of patch) * change that those functions don't implicitly call dcReset() anymore, which was unflexible (breaking change) - added macros to feature test implementation for aggregate-by-value and syscall support - changed libdyncall_s.lib and libdyncallback_s.lib order in callback test makefiles, as some toolchains are picky about order - doc: * man page updates to describe aggregate interface * manual overview changes to highlight platforms with aggregate-by-value support - test/plain: replaced tests w/ old/stale sctruct interface with new aggregate one
author Tassilo Philipp
date Thu, 21 Apr 2022 13:35:47 +0200
parents ddfb9577a00e
children 0c3f5355769d
line wrap: on
line diff
--- a/test/plain_c++/test_main.cc	Sat Apr 16 15:00:58 2022 +0200
+++ b/test/plain_c++/test_main.cc	Thu Apr 21 13:35:47 2022 +0200
@@ -6,7 +6,7 @@
  Description:
  License:
 
-   Copyright (c) 2007-2019 Daniel Adler <dadler@uni-goettingen.de>,
+   Copyright (c) 2007-2022 Daniel Adler <dadler@uni-goettingen.de>,
                            Tassilo Philipp <tphilipp@potion-studios.com>
 
    Permission to use, copy, modify, and distribute this software for any
@@ -30,9 +30,11 @@
 #include "../common/platformInit.h"
 #include "../common/platformInit.c" /* Impl. for functions only used in this translation unit */
 
+#include "../../dyncall/dyncall_aggregate.h"
 
 #include <signal.h>
 #include <setjmp.h>
+#include <stdarg.h>
 
 jmp_buf jbuf;
 
@@ -110,6 +112,7 @@
 #define VTBI_GET_DOUBLE VTBI_BASE+11
 #define VTBI_SET_POINTER VTBI_BASE+12
 #define VTBI_GET_POINTER VTBI_BASE+13
+#define VTBI_SUM_3_INTS VTBI_BASE+14
 
 class Value
 {
@@ -130,31 +133,10 @@
   virtual DCdouble   __cdecl getDouble()               { return mValue.d; }
   virtual void       __cdecl setPtr(DCpointer x)       { mValue.p = x; }
   virtual DCpointer  __cdecl getPtr()                  { return mValue.p; }
-private:
-  ValueUnion mValue;
-};
-
-/* C++ class using (on win32: microsoft) this call */
-
-class ValueMS
-{
-public:
-  virtual ~ValueMS()    {}
 
-  virtual void       setBool(DCbool x)         { mValue.B = x; }
-  virtual DCbool     getBool()                 { return mValue.B; }
-  virtual void       setInt(DCint x)           { mValue.i = x; }
-  virtual DCint      getInt()                  { return mValue.i; }
-  virtual void       setLong(DClong x)         { mValue.j = x; }
-  virtual DClong     getLong()                 { return mValue.j; }
-  virtual void       setLongLong(DClonglong x) { mValue.l = x; }
-  virtual DClonglong getLongLong()             { return mValue.l; }
-  virtual void       setFloat(DCfloat x)       { mValue.f = x; }
-  virtual DCfloat    getFloat()                { return mValue.f; }
-  virtual void       setDouble(DCdouble x)     { mValue.d = x; }
-  virtual DCdouble   getDouble()               { return mValue.d; }
-  virtual void       setPtr(DCpointer x)       { mValue.p = x; }
-  virtual DCpointer  getPtr()                  { return mValue.p; }
+  /* ellipsis test w/ this ptr */
+  virtual int        __cdecl sum3Ints(DCint x, ...)    { va_list va; va_start(va,x); x += va_arg(va,int) + va_arg(va,int); va_end(va); return x; }
+
 private:
   ValueUnion mValue;
 };
@@ -263,20 +245,59 @@
   printf("p  (%s): %d\n", name, b);
   r = r && b;
 
+  /* ellipsis test w/ this pointer */
+
+  dcReset(pc);
+  dcMode(pc, DC_CALL_C_ELLIPSIS);
+  dcArgPointer(pc, pThis);
+  dcArgInt(pc, 23);
+  dcMode(pc, DC_CALL_C_ELLIPSIS_VARARGS);
+  dcArgInt(pc, -223);
+  dcArgInt(pc, 888);
+  int r_ = dcCallInt(pc, vtbl[VTBI_SUM_3_INTS]);
+  b = (r_ == 688);
+  printf("...  (%s): %d\n", name, b);
+  r = r && b;
+
   return r;
 }
 
 
-#if defined(DC__OS_Win32)
+#if defined(DC__OS_Win32) && defined(DC__C_MSVC)
+
+/* C++ class using (on win32: microsoft) this call */
+
+class ValueMS
+{
+public:
+  virtual ~ValueMS()    {}
 
-int testCallThisMS()
+  virtual void       setBool(DCbool x)         { mValue.B = x; }
+  virtual DCbool     getBool()                 { return mValue.B; }
+  virtual void       setInt(DCint x)           { mValue.i = x; }
+  virtual DCint      getInt()                  { return mValue.i; }
+  virtual void       setLong(DClong x)         { mValue.j = x; }
+  virtual DClong     getLong()                 { return mValue.j; }
+  virtual void       setLongLong(DClonglong x) { mValue.l = x; }
+  virtual DClonglong getLongLong()             { return mValue.l; }
+  virtual void       setFloat(DCfloat x)       { mValue.f = x; }
+  virtual DCfloat    getFloat()                { return mValue.f; }
+  virtual void       setDouble(DCdouble x)     { mValue.d = x; }
+  virtual DCdouble   getDouble()               { return mValue.d; }
+  virtual void       setPtr(DCpointer x)       { mValue.p = x; }
+  virtual DCpointer  getPtr()                  { return mValue.p; }
+private:
+  ValueUnion mValue;
+};
+
+static bool testCallThisMS()
 {
   bool r = false;
   DCCallVM* pc = dcNewCallVM(4096);
   dcMode(pc, DC_CALL_C_X86_WIN32_THIS_MS);
   dcReset(pc);
   if(setjmp(jbuf) != 0)
-    printf("sigsegv\n");
+    printf("sigsegv\n"), r=false;
   else
     r = testCallValue<ValueMS>(pc, "MS");
   dcFree(pc);
@@ -286,14 +307,14 @@
 #endif
 
 
-int testCallThisC()
+static bool testCallThisC()
 {
   bool r = false;
   DCCallVM* pc = dcNewCallVM(4096);
   dcMode(pc, DC_CALL_C_DEFAULT_THIS);
   dcReset(pc);
   if(setjmp(jbuf) != 0)
-    printf("sigsegv\n");
+    printf("sigsegv\n"), r=false;
   else
     r = testCallValue<Value>(pc, "c");
   dcFree(pc);
@@ -301,6 +322,159 @@
 }
 
 
+#if defined(DC__Feature_AggrByVal)
+
+class ValueAggr
+{
+public:
+  struct S { int i, j, k, l, m; };
+
+  virtual ~ValueAggr()    {}
+
+  virtual void  __cdecl setAggr(S x)  { mS.i = x.i; mS.j = x.j; mS.k = x.k; mS.l = x.l; mS.m = x.m; }
+  virtual S     __cdecl getAggr()     { return mS; }
+
+  /* ellipsis test w/ this ptr and big (!) aggregate return */
+  struct Big { int sum; long long dummy[50]; /*dummy to make it not fit in any regs*/ };
+  virtual struct Big  __cdecl sum3RetAggr(DCint x, ...) { va_list va; va_start(va,x); struct Big r = { x + va_arg(va,int) + va_arg(va,int) }; va_end(va); return r; }
+
+  /* non-trivial aggregate */
+  struct NonTriv {
+    int i, j;
+    NonTriv(int a, int b) : i(a),j(b) { }
+    NonTriv(const NonTriv& rhs) { static int a=13, b=37; i = a++; j = b++; }
+  };
+  /* by value, so on first invocation a = 13,37, b = 14,38 and retval = 13*14,37*38, no matter the contents of the instances as copy ctor is called */
+  /* NOTE: copy of return value is subject to C++ "copy elision", so it is *not* calling the copy ctor for the return value */
+  virtual struct NonTriv  __cdecl squareFields(NonTriv a, NonTriv b) { return NonTriv(a.i*b.i, a.j*b.j); }
+
+private:
+  struct S mS;
+};
+
+#if (__cplusplus >= 201103L)
+#  include <type_traits>
+#endif
+
+/* special case w/ e.g. MS x64 C++ calling cconf: struct return ptr is passed as *2nd* arg */
+static bool testCallThisAggr()
+{
+  bool r = false;
+  DCCallVM* pc = dcNewCallVM(4096);
+  dcMode(pc, DC_CALL_C_DEFAULT_THIS);
+
+  if(setjmp(jbuf) != 0)
+    printf("sigsegv\n"), r=false;
+  else
+  {
+    ValueAggr o;
+
+    DCpointer* vtbl =  *( (DCpointer**) &o ); /* vtbl is located at beginning of class */
+    ValueAggr::S st = { 124, -12, 434, 20202, -99999 }, returned;
+
+#if (__cplusplus >= 201103L)
+    bool istriv = std::is_trivial<ValueAggr::S>::value;
+#else
+    bool istriv = true; /* own deduction as no type trait */
+#endif
+    DCaggr *s = dcNewAggr(5, sizeof(ValueAggr::S));
+    dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, i), 1);
+    dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, j), 1);
+    dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, k), 1);
+    dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, l), 1);
+    dcAggrField(s, DC_SIGCHAR_INT, offsetof(ValueAggr::S, m), 1);
+    dcCloseAggr(s);
+
+    // set S::mS
+    dcReset(pc);
+    dcArgPointer(pc, &o); // this ptr
+    dcArgAggr(pc, s, &st);
+    dcCallVoid(pc, vtbl[VTBI_BASE+0]);
+
+    // get it back
+    dcReset(pc);
+    dcBeginCallAggr(pc, s);
+    dcArgPointer(pc, &o); // this ptr
+    dcCallAggr(pc, vtbl[VTBI_BASE+1], s, &returned);
+
+    dcFreeAggr(s);
+
+    r = returned.i == st.i && returned.j == st.j && returned.k == st.k && returned.l == st.l && returned.m == st.m && istriv;
+    printf("r:{iiiii}  (this/trivial): %d\n", r);
+
+
+
+    /* ellipsis test w/ this pointer returning big aggregate (quite an edge
+     * case) by value (won't fit in regs, so hidden pointer is is used to write
+     * return values to), showing the need to use the DC_CALL_C_DEFAULT_THIS
+     * mode first, for the this ptr alone, then DC_CALL_C_ELLIPSIS, then
+     * DC_CALL_C_ELLIPSIS_VARARGS (test is useful on win64 where thisptr is
+     * passed *after* return aggregate's hidden ptr) */
+#if (__cplusplus >= 201103L)
+    istriv = std::is_trivial<ValueAggr::Big>::value;
+#else
+    istriv = true; /* own deduction as no type trait */
+#endif
+    s = dcNewAggr(2, sizeof(struct ValueAggr::Big));
+    dcAggrField(s, DC_SIGCHAR_INT, offsetof(struct ValueAggr::Big, sum), 1);
+    dcAggrField(s, DC_SIGCHAR_LONGLONG, offsetof(struct ValueAggr::Big, dummy), 50);
+    dcCloseAggr(s);
+    dcReset(pc);
+    dcMode(pc, DC_CALL_C_DEFAULT_THIS); /* <-- needed on x64/win64 */
+
+    dcBeginCallAggr(pc, s);
+    dcMode(pc, DC_CALL_C_ELLIPSIS);
+    dcArgPointer(pc, &o);
+    dcArgInt(pc, 89);
+    dcMode(pc, DC_CALL_C_ELLIPSIS_VARARGS);
+    dcArgInt(pc, -157);
+    dcArgInt(pc, 888);
+    struct ValueAggr::Big big;
+    dcCallAggr(pc, vtbl[VTBI_BASE+2], s, &big);
+
+    dcFreeAggr(s);
+
+    bool b = (big.sum == 820) && istriv;
+    r = r && b;
+    printf("r:{il[50]}  (this/trivial/ellipsis): %d\n", b);
+
+
+
+    /* non-trivial test ----------------------------------------------------------- */
+
+#if (__cplusplus >= 201103L)
+    istriv = std::is_trivial<ValueAggr::NonTriv>::value;
+#else
+    istriv = false; /* own deduction as no type trait */
+#endif
+    dcReset(pc);
+    dcMode(pc, DC_CALL_C_DEFAULT_THIS);
+
+    /* non trivial aggregates: pass NULL for DCaggr* and do copy on our own (see doc) */
+    dcBeginCallAggr(pc, NULL);
+
+    ValueAggr::NonTriv nt0(5, 6), nt1(7, 8), ntr(0, 0);
+    dcArgAggr(pc, NULL, &o); // this ptr
+    /* make *own* copies, as dyncall cannot know how to call copy ctor */ //@@@ put into doc
+    ValueAggr::NonTriv nt0_ = nt0, nt1_ = nt1;
+    dcArgAggr(pc, NULL, &nt0_); /* use *own* copy */
+    dcArgAggr(pc, NULL, &nt1_); /* use *own* copy */
+
+    dcCallAggr(pc, vtbl[VTBI_BASE+3], NULL, &ntr); /* note: "copy elision", so retval might *not* call copy ctor */
+
+
+    b = ntr.i == 13*14 && ntr.j == 37*38 && !istriv;
+    r = r && b;
+    printf("r:{ii}  (this/nontrivial/retval_copy_elision): %d\n", b);
+  }
+
+  dcFree(pc);
+  return r;
+}
+
+#endif
+
+
 extern "C" {
 
 int main(int argc, char* argv[])
@@ -312,9 +486,12 @@
   bool r = true;
 
   r = testCallThisC() && r;
-#if defined(DC__OS_Win32)
+#if defined(DC__OS_Win32) && defined(DC__C_MSVC)
   r = testCallThisMS() && r;
 #endif
+#if defined(DC__Feature_AggrByVal)
+  r = testCallThisAggr() && r;
+#endif
 
   printf("result: plain_cpp: %d\n", r);