]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
pru: Reject bit-fields for TI ABI
authorDimitar Dimitrov <dimitar@dinux.eu>
Sat, 19 Oct 2024 20:40:35 +0000 (23:40 +0300)
committerDimitar Dimitrov <dimitar@dinux.eu>
Mon, 22 Sep 2025 07:02:32 +0000 (10:02 +0300)
TI ABI has non-conventional requirements for bit-fields, which cannot
be implemented with the current target hooks in GCC.

Target hooks are focused on packing and alignment.  But PRU uses packed
structs by default, and has 1 byte alignment for all types.  As an
example, this makes it difficult to implement the TI ABI requirement
for the following struct to be sized to 4 bytes, per the bit-field type:

  struct S { int i : 1; }

Instead of introducing new target hooks and making risky changes to
common GCC code, simply declare bit-fields as not supported in TI ABI
mode.

PRU is a baremetal target.  It has neither support for interrupts nor an
RTOS.  Hence ABI compatibility is not that critical.  I have not seen
any projects which rely on ABI compatibility in order to mix object
files from GCC and the TI proprietary compiler.

The target-specific pass to scan for TI ABI compatibility was rewritten
as an IPA pass.  This allowed scanning not only of function bodies, but
also global variable declarations.  Diagnostic locations should now be
more accurate.  Thus some test cases had to be adjusted.

PR target/116205

gcc/ChangeLog:

* config/pru/pru-passes.cc (class pass_pru_tiabi_check): Make
this an IPA pass.
(chkp_type_has_function_pointer): Remove.
(check_type_tiabi_compatibility): New function.
(chk_function_decl): Rename.
(check_function_decl): Simplify.
(check_op_callback): Rework to use
check_type_tiabi_compatibility.
(pass_pru_tiabi_check::execute): Rework to scan all symbols and
gimple contents of all defined functions.
* config/pru/pru-passes.def (INSERT_PASS_AFTER): Move after
pass_ipa_auto_profile_offline.
* config/pru/pru-protos.h (make_pru_tiabi_check): New
declaration to mark as IPA pass.
(make_pru_minrt_check): Specify it is making a gimple pass.
* doc/invoke.texi: Document that bit-fields are now rejected for
TI ABI.

gcc/testsuite/ChangeLog:

* gcc.target/pru/mabi-ti-1.c: Adjust diagnostic location.
* gcc.target/pru/mabi-ti-2.c: Ditto.
* gcc.target/pru/mabi-ti-3.c: Ditto.
* gcc.target/pru/mabi-ti-5.c: Ditto.
* gcc.target/pru/mabi-ti-6.c: Ditto.
* gcc.target/pru/mabi-ti-7.c: Adjust diagnostic locations and
add global variables for checking.
* gcc.target/pru/mabi-ti-11.c: New test.
* gcc.target/pru/mabi-ti-12.c: New test.
* gcc.target/pru/mabi-ti-8.c: New test.
* gcc.target/pru/mabi-ti-9.c: New test.

Signed-off-by: Dimitar Dimitrov <dimitar@dinux.eu>
14 files changed:
gcc/config/pru/pru-passes.cc
gcc/config/pru/pru-passes.def
gcc/config/pru/pru-protos.h
gcc/doc/invoke.texi
gcc/testsuite/gcc.target/pru/mabi-ti-1.c
gcc/testsuite/gcc.target/pru/mabi-ti-11.c [new file with mode: 0644]
gcc/testsuite/gcc.target/pru/mabi-ti-12.c [new file with mode: 0644]
gcc/testsuite/gcc.target/pru/mabi-ti-2.c
gcc/testsuite/gcc.target/pru/mabi-ti-3.c
gcc/testsuite/gcc.target/pru/mabi-ti-5.c
gcc/testsuite/gcc.target/pru/mabi-ti-6.c
gcc/testsuite/gcc.target/pru/mabi-ti-7.c
gcc/testsuite/gcc.target/pru/mabi-ti-8.c [new file with mode: 0644]
gcc/testsuite/gcc.target/pru/mabi-ti-9.c [new file with mode: 0644]

index a763e1ee65ed169c45128899d39c242ae7f509c1..ba95bc4144127af179a5d0da0e34b534a32c5243 100644 (file)
@@ -35,6 +35,7 @@
 #include "gimple-iterator.h"
 #include "gimple-walk.h"
 #include "gimple-expr.h"
+#include "cgraph.h"
 #include "tree-pass.h"
 
 #include "pru-protos.h"
@@ -46,11 +47,11 @@ namespace {
    output a conforming code, raise an error.  */
 const pass_data pass_data_pru_tiabi_check =
 {
-  GIMPLE_PASS, /* type */
+  SIMPLE_IPA_PASS, /* type */
   "*pru_tiabi_check", /* name */
   OPTGROUP_NONE, /* optinfo_flags */
   TV_NONE, /* tv_id */
-  PROP_gimple_any, /* properties_required */
+  0, /* properties_required */
   0, /* properties_provided */
   0, /* properties_destroyed */
   0, /* todo_flags_start */
@@ -58,11 +59,11 @@ const pass_data pass_data_pru_tiabi_check =
 };
 
 /* Implementation class for the TI ABI compliance-check pass.  */
-class pass_pru_tiabi_check : public gimple_opt_pass
+class pass_pru_tiabi_check : public simple_ipa_opt_pass
 {
 public:
   pass_pru_tiabi_check (gcc::context *ctxt)
-    : gimple_opt_pass (pass_data_pru_tiabi_check, ctxt)
+    : simple_ipa_opt_pass (pass_data_pru_tiabi_check, ctxt)
   {}
 
   /* opt_pass methods: */
@@ -75,54 +76,79 @@ public:
 
 }; // class pass_pru_tiabi_check
 \f
-/* Return 1 if type TYPE is a pointer to function type or a
-   structure having a pointer to function type as one of its fields.
-   Otherwise return 0.  */
-static bool
-chkp_type_has_function_pointer (const_tree type)
+/* Issue an error diagnostic if the given TYPE is not compatible with
+   TI's ABI.  */
+static void
+check_type_tiabi_compatibility (tree type, location_t loc,
+                               hash_set<tree> *visited_nodes)
 {
-  bool res = false;
+  /* Do not visit the same type twice.  */
+  if (visited_nodes->add (type))
+    return;
 
-  if (POINTER_TYPE_P (type) && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (type)))
-    res = true;
+  if (POINTER_TYPE_P (type))
+    {
+      /* TODO: All declarations and statements share the same object for a
+        function pointer tree type.  So if there are several variables
+        with the same type (function pointer with same function signature),
+        only the first declaration would get a diagnostic.  */
+      if (FUNC_OR_METHOD_TYPE_P (TREE_TYPE (type)))
+       {
+         location_t ptrloc = DECL_P (type) ? DECL_SOURCE_LOCATION (type) : loc;
+         error_at (ptrloc,
+                   "function pointers not supported with %<-mabi=ti%> option");
+       }
+      else
+       check_type_tiabi_compatibility (TREE_TYPE (type), loc, visited_nodes);
+    }
   else if (RECORD_OR_UNION_TYPE_P (type))
     {
-      tree field;
-
-      for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
+      for (tree field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
        if (TREE_CODE (field) == FIELD_DECL)
-         res = res || chkp_type_has_function_pointer (TREE_TYPE (field));
+         {
+           if (DECL_BIT_FIELD (field))
+             error_at (DECL_SOURCE_LOCATION (field),
+                       "bit-fields not supported with %<-mabi=ti%> option");
+
+           check_type_tiabi_compatibility (TREE_TYPE (field),
+                                           DECL_SOURCE_LOCATION (field),
+                                           visited_nodes);
+         }
     }
   else if (TREE_CODE (type) == ARRAY_TYPE)
-    res = chkp_type_has_function_pointer (TREE_TYPE (type));
-
-  return res;
+    check_type_tiabi_compatibility (TREE_TYPE (type), loc, visited_nodes);
 }
 
-/* Check the function declaration FNTYPE for TI ABI compatibility.  */
+/* Check the GCC function declaration FNTYPE for TI ABI compatibility.  */
 static void
-chk_function_decl (const_tree fntype, location_t call_location)
+check_function_decl (tree fntype, location_t loc,
+                    hash_set<tree> *visited_nodes)
 {
+  /* Do not visit the same type twice.  */
+  if (visited_nodes->add (fntype))
+    return;
+
   /* GCC does not check if the RETURN VALUE pointer is NULL,
      so do not allow GCC functions with large return values.  */
   if (!VOID_TYPE_P (TREE_TYPE (fntype))
       && pru_return_in_memory (TREE_TYPE (fntype), fntype))
-    error_at (call_location,
+    error_at (loc,
              "large return values not supported with %<-mabi=ti%> option");
 
+  /* Rest of checks for the returned type.  */
+  check_type_tiabi_compatibility (TREE_TYPE (fntype), loc, visited_nodes);
+
   /* Check this function's arguments.  */
   for (tree p = TYPE_ARG_TYPES (fntype); p; p = TREE_CHAIN (p))
-    {
-      tree arg_type = TREE_VALUE (p);
-      if (chkp_type_has_function_pointer (arg_type))
-       error_at (call_location,
-                 "function pointers not supported with %<-mabi=ti%> option");
-    }
+    check_type_tiabi_compatibility (TREE_VALUE (p), loc, visited_nodes);
 }
 
-/* Callback for walk_gimple_seq that checks TP tree for TI ABI compliance.  */
+/* Callback for walk_gimple_seq that checks TP tree for TI ABI compliance.
+   Note that TP would have been marked as visited before calling
+   this function.  But the underlying type of TP may or may not have
+   been visited before.  */
 static tree
-check_op_callback (tree *tp, int *walk_subtrees, void *data)
+check_op_callback (tree *tp, int *, void *data)
 {
   struct walk_stmt_info *wi = (struct walk_stmt_info *) data;
 
@@ -150,66 +176,91 @@ check_op_callback (tree *tp, int *walk_subtrees, void *data)
     {
     case FUNCTION_TYPE:
     case METHOD_TYPE:
-       {
-         /* Note: Do not enforce a small return value.  It is safe to
-            call any TI ABI function from GCC, since GCC will
-            never pass NULL.  */
-
-         /* Check arguments for function pointers.  */
-         for (tree p = TYPE_ARG_TYPES (type); p; p = TREE_CHAIN (p))
-           {
-             tree arg_type = TREE_VALUE (p);
-             if (chkp_type_has_function_pointer (arg_type))
-               error_at (gimple_location (wi->stmt), "function pointers "
-                         "not supported with %<-mabi=ti%> option");
-           }
-         break;
-       }
+      {
+       /* Has this function already been inspected?  */
+       if (wi->pset->add (type))
+         return NULL;
+
+       /* Note: Do not enforce a small return value.  It is safe to
+          call any TI ABI function from GCC, since GCC will
+          never pass NULL.  */
+
+       /* Check arguments for function pointers.  */
+       for (tree p = TYPE_ARG_TYPES (type); p; p = TREE_CHAIN (p))
+         check_type_tiabi_compatibility (TREE_VALUE (p),
+                                         gimple_location (wi->stmt),
+                                         wi->pset);
+       break;
+      }
     case RECORD_TYPE:
     case UNION_TYPE:
     case QUAL_UNION_TYPE:
-    case POINTER_TYPE:
-       {
-         if (chkp_type_has_function_pointer (type))
-           {
-             error_at (gimple_location (wi->stmt),
-                       "function pointers not supported with "
-                       "%<-mabi=ti%> option");
-             *walk_subtrees = false;
-           }
-         break;
-       }
+      {
+       /* walk_tree would not descend and visit field declarations.
+          So do it here.  */
+       check_type_tiabi_compatibility (type,
+                                       gimple_location (wi->stmt),
+                                       wi->pset);
+       break;
+      }
     default:
-         break;
+      break;
     }
   return NULL;
 }
 
 /* Pass implementation.  */
 unsigned
-pass_pru_tiabi_check::execute (function *fun)
+pass_pru_tiabi_check::execute (function *)
 {
-  struct walk_stmt_info wi;
-  const_tree fntype = TREE_TYPE (fun->decl);
+  struct cgraph_node *node;
+  hash_set<tree> visited_nodes;
+  symtab_node *snode;
 
-  gimple_seq body = gimple_body (current_function_decl);
+  FOR_EACH_SYMBOL (snode)
+    {
+      tree decl = snode->decl;
+      if (decl)
+       {
+         if (FUNC_OR_METHOD_TYPE_P (TREE_TYPE (decl)))
+           check_function_decl (TREE_TYPE (decl),
+                                DECL_SOURCE_LOCATION (decl),
+                                &visited_nodes);
+         else
+           check_type_tiabi_compatibility (TREE_TYPE (decl),
+                                           DECL_SOURCE_LOCATION (decl),
+                                           &visited_nodes);
+       }
+    }
 
-  memset (&wi, 0, sizeof (wi));
-  wi.info = NULL;
-  wi.want_locations = true;
+  FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
+    {
+      function *fn = node->get_fun ();
+      gimple_stmt_iterator gsi;
+      basic_block bb;
 
-  /* Check the function body.  */
-  walk_gimple_seq (body, NULL, check_op_callback, &wi);
+      FOR_EACH_BB_FN (bb, fn)
+       {
+         for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+           {
+             struct walk_stmt_info wi;
+             memset (&wi, 0, sizeof (wi));
+             wi.info = NULL;
+             wi.want_locations = true;
 
-  /* Check the function declaration.  */
-  chk_function_decl (fntype, fun->function_start_locus);
+             wi.pset = &visited_nodes;
 
+             /* Check the function body.  */
+             walk_gimple_stmt (&gsi, NULL, check_op_callback, &wi);
+           }
+       }
+    }
   return 0;
 }
 
 } // anon namespace
 
-gimple_opt_pass *
+simple_ipa_opt_pass *
 make_pru_tiabi_check (gcc::context *ctxt)
 {
   return new pass_pru_tiabi_check (ctxt);
index da48a2c20989a612e557722b91afdc8235144a4a..c0d3528c734f5b7962d91cbcd764b1a88274bd30 100644 (file)
@@ -21,7 +21,7 @@
    that the compiled code by GCC conforms to the TI ABI specification.
    If GCC cannot output a conforming code, then an error is raised.  */
 
-INSERT_PASS_AFTER (pass_warn_unused_result, 1, pru_tiabi_check);
+INSERT_PASS_AFTER (pass_ipa_auto_profile_offline, 1, pru_tiabi_check);
 
 /* If -minrt option is used, then this pass would validate
    that the compiled code by GCC is compatible with the minimal
index 4750f0e100773c54dd8d550c727878ed1667ecf2..d2e51ea44d2f757284f031694d99891adc7a8e7f 100644 (file)
@@ -72,8 +72,12 @@ extern int pru_get_ctable_base_offset (HOST_WIDE_INT caddr);
 
 extern int pru_symref2ioregno (rtx op);
 
-extern rtl_opt_pass *make_pru_tiabi_check (gcc::context *);
-extern rtl_opt_pass *make_pru_minrt_check (gcc::context *);
+/* Forward declarations to avoid unnecessarily including headers.  */
+class simple_ipa_opt_pass;
+class gimple_opt_pass;
+
+extern simple_ipa_opt_pass *make_pru_tiabi_check (gcc::context *);
+extern gimple_opt_pass *make_pru_minrt_check (gcc::context *);
 
 #endif /* RTX_CODE */
 
index 26e6e5bea60976b649cb35f8ef4851ad18c2fee7..2eab5140bc2a075cce29af6bc6db3fcc792accc9 100644 (file)
@@ -31367,6 +31367,29 @@ pointer as the first argument of the function.  TI ABI, though, mandates that
 the pointer can be NULL in case the caller is not using the returned value.
 GNU always passes and expects a valid return value pointer.
 
+@item Size Of Struct Containing Bit-fields
+TI ABI mandates that struct size is determined by the bit-field type, if it
+contains any.  Whereas GNU allocates the smallest amount of bytes which would
+fit the bit-field.
+
+For example, TI ABI reserves 4 bytes for this struct, whereas GNU reserves
+a single byte:
+
+@smallexample
+struct S @{ int i:1; @};
+@end smallexample
+
+@item Access Size For Volatile Bit-fields
+TI ABI mandates that volatile bit-fields are accessed using their type.
+In contrast, GNU ABI uses the smallest integer type fitting the bit-field.
+
+For example, TI ABI requires a single load of 4 bytes for the
+following bit-field.  Whereas GNU generates a load of 1 byte:
+
+@smallexample
+struct S @{ volatile int i:1; @};
+@end smallexample
+
 @end table
 
 The current @option{-mabi=ti} implementation simply raises a compile error
index 117ae8fd176bb47a97be7ec3d470a52fd3e53e14..44f5202f33053334a4b8c632cf3217c2da6a3493 100644 (file)
@@ -4,7 +4,7 @@
 /* { dg-options "-O1 -mabi=ti" } */
 
 
-int test(int a, int b, void (*fp)(void))
-{ /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
+int test(int a, int b, void (*fp)(void)) /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
+{
   return a+b;
 }
diff --git a/gcc/testsuite/gcc.target/pru/mabi-ti-11.c b/gcc/testsuite/gcc.target/pru/mabi-ti-11.c
new file mode 100644 (file)
index 0000000..c5df2ad
--- /dev/null
@@ -0,0 +1,14 @@
+/* Test TI ABI unsupported constructs */
+
+/* { dg-do assemble } */
+/* { dg-options "-O1 -mabi=ti" } */
+
+struct s1 {
+    int (*f)(void);  /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
+    int a;
+};
+
+int test1(void)
+{
+  return ((struct s1 *)0x11223344)->a;
+}
diff --git a/gcc/testsuite/gcc.target/pru/mabi-ti-12.c b/gcc/testsuite/gcc.target/pru/mabi-ti-12.c
new file mode 100644 (file)
index 0000000..4658f60
--- /dev/null
@@ -0,0 +1,15 @@
+/* Test TI ABI unsupported constructs */
+
+/* { dg-do assemble } */
+/* { dg-options "-O1 -mabi=ti" } */
+
+struct s1 {
+    int (*f)(void);  /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
+    int a;
+};
+
+struct s1 g1;
+struct s1 g2;
+struct s1 g3;
+
+int (*g_f1)(char);  /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
index d4a3aff546c040c5bf2a060a7e956aca832d96c6..66bdfd0aa903e14001d6bc2cc4429a9701b561fe 100644 (file)
@@ -8,8 +8,8 @@ struct big {
        char c[9];
 };
 
-struct big test(void)
-{ /* { dg-error "large return values not supported with '-mabi=ti' option" } */
+struct big test(void) /* { dg-error "large return values not supported with '-mabi=ti' option" } */
+{
   static struct big b;
   return b;
 }
index c49f6653589c78e770c78bb3ac29a0f7dd6c27eb..472452b499a3a589598d20c8707ee235c9baa183 100644 (file)
@@ -4,9 +4,9 @@
 /* { dg-options "-O1 -mabi=ti" } */
 
 
-extern void extfunc(void (*fp)(void));
+extern void extfunc(void (*fp)(void)); /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
 
 void test(void)
 {
-  extfunc(test); /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
+  extfunc(test);
 }
index 38eeaa4435dc8a13faf312d976379e89adba8efe..9ef73b1754c661a9c5209ec26c78db8322a4cac1 100644 (file)
@@ -4,13 +4,13 @@
 /* { dg-options "-O1 -mabi=ti" } */
 
 struct s1 {
-    void (*f)(void);
+    void (*f)(void); /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
     int a;
 };
 
 struct s2 {
     union {
-       void (*f)(void);
+       void (*f)(int); /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
        int a;
        long b;
     } u;
@@ -18,16 +18,16 @@ struct s2 {
 
 int test1(struct s1 *p)
 {
-  return p->a; /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
+  return p->a;
   return 1;
 }
 
 int test1_unused_arg(struct s1 p, int a)
-{ /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
+{
   return a;
 }
 
 int test2(struct s2 v)
-{ /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
+{
   return 2;
 }
index c8aa018a21e2adac91ea078a234d7adbe4496c7a..b94971878db947831841ed4764995eac4b06b87e 100644 (file)
@@ -4,9 +4,9 @@
 /* { dg-options "-O1 -mabi=ti" } */
 
 
-extern void (*extfuncp)(int);
+extern void (*extfuncp)(int); /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
 
 void test(void)
 {
-  extfuncp(1); /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
+  extfuncp(1);
 }
index cc095facf646f81f17a122d8d520fd2a635df020..7738255fafac2936b4170ded63f6e70a7fcf43ea 100644 (file)
@@ -4,18 +4,32 @@
 /* { dg-options "-O1 -mabi=ti" } */
 
 struct s1 {
-    int (*f)(void);
+    int (*f)(void);  /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
     int a;
 };
 
-extern struct s1 s;
+struct s2 {
+    int (*f)(short);  /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
+    int a;
+};
+
+struct s3 {
+    int (*f)(char);  /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
+    int a;
+    int b;
+    int c;
+};
+
+extern struct s1 g_s1;
+extern struct s2 g_s2;
+struct s3 g_s3;
 
 int test1(void)
 {
-  return s.f(); /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
+  return g_s1.f();
 }
 
 int test2(void)
 {
-  return s.a; /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
+  return g_s2.a;
 }
diff --git a/gcc/testsuite/gcc.target/pru/mabi-ti-8.c b/gcc/testsuite/gcc.target/pru/mabi-ti-8.c
new file mode 100644 (file)
index 0000000..6f4a91a
--- /dev/null
@@ -0,0 +1,48 @@
+/* Test TI ABI unsupported constructs */
+
+/* { dg-do assemble } */
+/* { dg-options "-O1 -mabi=ti" } */
+
+struct s0 {
+    int f0 : 2; /* { dg-error "bit-fields not supported with '-mabi=ti' option" } */
+    int f1;
+};
+struct s0 g_s;
+
+struct s1 {
+    int f : 2; /* { dg-error "bit-fields not supported with '-mabi=ti' option" } */
+    int a;
+};
+struct s1 g_s1;
+int test1(void)
+{
+  struct s1 s;
+
+  return sizeof(s);
+}
+
+struct s2 {
+    int a2;
+    int f2 : 3; /* { dg-error "bit-fields not supported with '-mabi=ti' option" } */
+};
+struct s22 {
+       struct s2 *p;
+};
+int test2(struct s22 *s)
+{
+  return s->p->f2 + s->p->a2;
+}
+
+int test2a(struct s2 *s)
+{
+  return s->f2 - s->a2;
+}
+
+struct s3 {
+    int a3;
+    int f3 : 3; /* { dg-error "bit-fields not supported with '-mabi=ti' option" } */
+};
+int test3(struct s3 s)
+{
+  return 0;
+}
diff --git a/gcc/testsuite/gcc.target/pru/mabi-ti-9.c b/gcc/testsuite/gcc.target/pru/mabi-ti-9.c
new file mode 100644 (file)
index 0000000..37c74e0
--- /dev/null
@@ -0,0 +1,16 @@
+/* Test TI ABI unsupported constructs */
+
+/* { dg-do assemble } */
+/* { dg-options "-O1 -mabi=ti" } */
+
+struct s1 {
+    int (*f)(void);  /* { dg-error "function pointers not supported with '-mabi=ti' option" } */
+    int a;
+};
+
+struct s2 {
+    struct s1 *s;
+    int a;
+};
+
+struct s2 g_s2;