490 lines
13 KiB
Diff
490 lines
13 KiB
Diff
|
|
From 2b4db34d3b21ff8597373e9e67858b3b60cc7dae Mon Sep 17 00:00:00 2001
|
||
|
|
From: eastb233 <xiezhiheng@huawei.com>
|
||
|
|
Date: Fri, 21 Jul 2023 11:20:51 +0800
|
||
|
|
Subject: [PATCH 17/22] [StructReorg] Some bugfix for structure reorganization
|
||
|
|
|
||
|
|
Some bugfix for structure reorganization,
|
||
|
|
1. disable type simplify in LTO within optimizations
|
||
|
|
2. only enable optimizations in C language
|
||
|
|
3. use new to initialize allocated memory in symbol-summary.h
|
||
|
|
4. cover escape scenarios not considered
|
||
|
|
---
|
||
|
|
gcc/ipa-free-lang-data.cc | 11 ++
|
||
|
|
gcc/ipa-struct-reorg/ipa-struct-reorg.cc | 101 +++++++++++--------
|
||
|
|
gcc/symbol-summary.h | 13 ++-
|
||
|
|
gcc/testsuite/gcc.dg/struct/struct_reorg-5.c | 31 ++++++
|
||
|
|
gcc/testsuite/gcc.dg/struct/struct_reorg-6.c | 54 ++++++++++
|
||
|
|
gcc/testsuite/gcc.dg/struct/struct_reorg-7.c | 38 +++++++
|
||
|
|
gcc/testsuite/gcc.dg/struct/struct_reorg-8.c | 25 +++++
|
||
|
|
gcc/testsuite/gcc.dg/struct/struct_reorg-9.c | 54 ++++++++++
|
||
|
|
8 files changed, 283 insertions(+), 44 deletions(-)
|
||
|
|
create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-5.c
|
||
|
|
create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-6.c
|
||
|
|
create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-7.c
|
||
|
|
create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-8.c
|
||
|
|
create mode 100644 gcc/testsuite/gcc.dg/struct/struct_reorg-9.c
|
||
|
|
|
||
|
|
diff --git a/gcc/ipa-free-lang-data.cc b/gcc/ipa-free-lang-data.cc
|
||
|
|
index a74215685..5450be9fe 100644
|
||
|
|
--- a/gcc/ipa-free-lang-data.cc
|
||
|
|
+++ b/gcc/ipa-free-lang-data.cc
|
||
|
|
@@ -102,6 +102,12 @@ fld_worklist_push (tree t, class free_lang_data_d *fld)
|
||
|
|
static tree
|
||
|
|
fld_simplified_type_name (tree type)
|
||
|
|
{
|
||
|
|
+ /* Simplify type will cause that struct A and struct A within
|
||
|
|
+ struct B are different type pointers, so skip it in structure
|
||
|
|
+ optimizations. */
|
||
|
|
+ if (flag_ipa_struct_reorg)
|
||
|
|
+ return TYPE_NAME (type);
|
||
|
|
+
|
||
|
|
if (!TYPE_NAME (type) || TREE_CODE (TYPE_NAME (type)) != TYPE_DECL)
|
||
|
|
return TYPE_NAME (type);
|
||
|
|
/* Drop TYPE_DECLs in TYPE_NAME in favor of the identifier in the
|
||
|
|
@@ -340,6 +346,11 @@ fld_simplified_type (tree t, class free_lang_data_d *fld)
|
||
|
|
{
|
||
|
|
if (!t)
|
||
|
|
return t;
|
||
|
|
+ /* Simplify type will cause that struct A and struct A within
|
||
|
|
+ struct B are different type pointers, so skip it in structure
|
||
|
|
+ optimizations. */
|
||
|
|
+ if (flag_ipa_struct_reorg)
|
||
|
|
+ return t;
|
||
|
|
if (POINTER_TYPE_P (t))
|
||
|
|
return fld_incomplete_type_of (t, fld);
|
||
|
|
/* FIXME: This triggers verification error, see PR88140. */
|
||
|
|
diff --git a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc
|
||
|
|
index c8b975a92..9f790b28b 100644
|
||
|
|
--- a/gcc/ipa-struct-reorg/ipa-struct-reorg.cc
|
||
|
|
+++ b/gcc/ipa-struct-reorg/ipa-struct-reorg.cc
|
||
|
|
@@ -105,6 +105,7 @@ along with GCC; see the file COPYING3. If not see
|
||
|
|
#include "ipa-param-manipulation.h"
|
||
|
|
#include "gimplify-me.h"
|
||
|
|
#include "cfgloop.h"
|
||
|
|
+#include "langhooks.h"
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
@@ -196,6 +197,39 @@ gimplify_build1 (gimple_stmt_iterator *gsi, enum tree_code code, tree type,
|
||
|
|
GSI_SAME_STMT);
|
||
|
|
}
|
||
|
|
|
||
|
|
+/* Check whether in C language or LTO with only C language. */
|
||
|
|
+
|
||
|
|
+static bool
|
||
|
|
+lang_c_p (void)
|
||
|
|
+{
|
||
|
|
+ const char *language_string = lang_hooks.name;
|
||
|
|
+
|
||
|
|
+ if (!language_string)
|
||
|
|
+ return false;
|
||
|
|
+
|
||
|
|
+ if (strcmp (language_string, "GNU GIMPLE") == 0)
|
||
|
|
+ {
|
||
|
|
+ unsigned i = 0;
|
||
|
|
+ tree t = NULL;
|
||
|
|
+ const char *unit_string = NULL;
|
||
|
|
+
|
||
|
|
+ FOR_EACH_VEC_SAFE_ELT (all_translation_units, i, t)
|
||
|
|
+ {
|
||
|
|
+ unit_string = TRANSLATION_UNIT_LANGUAGE (t);
|
||
|
|
+ if (!unit_string
|
||
|
|
+ || (strncmp (unit_string, "GNU C", 5) != 0)
|
||
|
|
+ || (!ISDIGIT (unit_string[5])))
|
||
|
|
+ return false;
|
||
|
|
+ }
|
||
|
|
+ return true;
|
||
|
|
+ }
|
||
|
|
+ else if (strncmp (language_string, "GNU C", 5) == 0
|
||
|
|
+ && ISDIGIT (language_string[5]))
|
||
|
|
+ return true;
|
||
|
|
+
|
||
|
|
+ return false;
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
enum srmode
|
||
|
|
{
|
||
|
|
NORMAL = 0,
|
||
|
|
@@ -1018,7 +1052,6 @@ public:
|
||
|
|
void analyze_types (void);
|
||
|
|
void clear_visited (void);
|
||
|
|
bool create_new_types (void);
|
||
|
|
- void restore_field_type (void);
|
||
|
|
void create_new_decls (void);
|
||
|
|
srdecl *find_decl (tree);
|
||
|
|
void create_new_functions (void);
|
||
|
|
@@ -2107,7 +2140,12 @@ ipa_struct_reorg::find_vars (gimple *stmt)
|
||
|
|
srtype *t = find_type (inner_type (TREE_TYPE (rhs)));
|
||
|
|
srdecl *d = find_decl (lhs);
|
||
|
|
if (!d && t)
|
||
|
|
- current_function->record_decl (t, lhs, -1);
|
||
|
|
+ {
|
||
|
|
+ current_function->record_decl (t, lhs, -1);
|
||
|
|
+ tree var = SSA_NAME_VAR (lhs);
|
||
|
|
+ if (var && VOID_POINTER_P (TREE_TYPE (var)))
|
||
|
|
+ current_function->record_decl (t, var, -1);
|
||
|
|
+ }
|
||
|
|
}
|
||
|
|
if (TREE_CODE (rhs) == SSA_NAME
|
||
|
|
&& VOID_POINTER_P (TREE_TYPE (rhs))
|
||
|
|
@@ -2116,7 +2154,12 @@ ipa_struct_reorg::find_vars (gimple *stmt)
|
||
|
|
srtype *t = find_type (inner_type (TREE_TYPE (lhs)));
|
||
|
|
srdecl *d = find_decl (rhs);
|
||
|
|
if (!d && t)
|
||
|
|
- current_function->record_decl (t, rhs, -1);
|
||
|
|
+ {
|
||
|
|
+ current_function->record_decl (t, rhs, -1);
|
||
|
|
+ tree var = SSA_NAME_VAR (rhs);
|
||
|
|
+ if (var && VOID_POINTER_P (TREE_TYPE (var)))
|
||
|
|
+ current_function->record_decl (t, var, -1);
|
||
|
|
+ }
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else
|
||
|
|
@@ -2796,8 +2839,14 @@ ipa_struct_reorg::maybe_record_call (cgraph_node *node, gcall *stmt)
|
||
|
|
if (escapes != does_not_escape)
|
||
|
|
{
|
||
|
|
for (unsigned i = 0; i < gimple_call_num_args (stmt); i++)
|
||
|
|
- mark_type_as_escape (TREE_TYPE (gimple_call_arg (stmt, i)),
|
||
|
|
- escapes);
|
||
|
|
+ {
|
||
|
|
+ mark_type_as_escape (TREE_TYPE (gimple_call_arg (stmt, i)),
|
||
|
|
+ escapes);
|
||
|
|
+ srdecl *d = current_function->find_decl (
|
||
|
|
+ gimple_call_arg (stmt, i));
|
||
|
|
+ if (d)
|
||
|
|
+ d->type->mark_escape (escapes, stmt);
|
||
|
|
+ }
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
@@ -3731,42 +3780,6 @@ ipa_struct_reorg::analyze_types (void)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
-/* When struct A has a struct B member, B's type info
|
||
|
|
- is not stored in
|
||
|
|
- TYPE_FIELDS (TREE_TYPE (TYPE_FIELDS (typeA)))
|
||
|
|
- Try to restore B's type information. */
|
||
|
|
-
|
||
|
|
-void
|
||
|
|
-ipa_struct_reorg::restore_field_type (void)
|
||
|
|
-{
|
||
|
|
- for (unsigned i = 0; i < types.length (); i++)
|
||
|
|
- {
|
||
|
|
- for (unsigned j = 0; j < types[i]->fields.length (); j++)
|
||
|
|
- {
|
||
|
|
- srfield *field = types[i]->fields[j];
|
||
|
|
- if (TREE_CODE (inner_type (field->fieldtype)) == RECORD_TYPE)
|
||
|
|
- {
|
||
|
|
- /* If field type has TYPE_FIELDS information,
|
||
|
|
- we do not need to do this. */
|
||
|
|
- if (TYPE_FIELDS (field->type->type) != NULL)
|
||
|
|
- continue;
|
||
|
|
- for (unsigned k = 0; k < types.length (); k++)
|
||
|
|
- {
|
||
|
|
- if (i == k)
|
||
|
|
- continue;
|
||
|
|
- const char *type1 = get_type_name (field->type->type);
|
||
|
|
- const char *type2 = get_type_name (types[k]->type);
|
||
|
|
- if (type1 == NULL || type2 == NULL)
|
||
|
|
- continue;
|
||
|
|
- if (type1 == type2
|
||
|
|
- && TYPE_FIELDS (types[k]->type))
|
||
|
|
- field->type = types[k];
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
- }
|
||
|
|
-}
|
||
|
|
-
|
||
|
|
/* Create all new types we want to create. */
|
||
|
|
|
||
|
|
bool
|
||
|
|
@@ -4647,7 +4660,6 @@ ipa_struct_reorg::rewrite_functions (void)
|
||
|
|
{
|
||
|
|
unsigned retval = 0;
|
||
|
|
|
||
|
|
- restore_field_type ();
|
||
|
|
/* Create new types, if we did not create any new types,
|
||
|
|
then don't rewrite any accesses. */
|
||
|
|
if (!create_new_types ())
|
||
|
|
@@ -4866,7 +4878,10 @@ pass_ipa_struct_reorg::gate (function *)
|
||
|
|
&& flag_ipa_struct_reorg
|
||
|
|
/* Don't bother doing anything if the program has errors. */
|
||
|
|
&& !seen_error ()
|
||
|
|
- && flag_lto_partition == LTO_PARTITION_ONE);
|
||
|
|
+ && flag_lto_partition == LTO_PARTITION_ONE
|
||
|
|
+ /* Only enable struct optimizations in C since other
|
||
|
|
+ languages' grammar forbid. */
|
||
|
|
+ && lang_c_p ());
|
||
|
|
}
|
||
|
|
|
||
|
|
} // anon namespace
|
||
|
|
diff --git a/gcc/symbol-summary.h b/gcc/symbol-summary.h
|
||
|
|
index c54d3084c..3fe64047c 100644
|
||
|
|
--- a/gcc/symbol-summary.h
|
||
|
|
+++ b/gcc/symbol-summary.h
|
||
|
|
@@ -103,6 +103,12 @@ protected:
|
||
|
|
/* Allocates new data that are stored within map. */
|
||
|
|
T* allocate_new ()
|
||
|
|
{
|
||
|
|
+ /* In structure optimizatons, we call new to ensure that
|
||
|
|
+ the allocated memory is initialized to 0. */
|
||
|
|
+ if (flag_ipa_struct_reorg)
|
||
|
|
+ return is_ggc () ? new (ggc_internal_alloc (sizeof (T))) T ()
|
||
|
|
+ : new T ();
|
||
|
|
+
|
||
|
|
/* Call gcc_internal_because we do not want to call finalizer for
|
||
|
|
a type T. We call dtor explicitly. */
|
||
|
|
return is_ggc () ? new (ggc_internal_alloc (sizeof (T))) T ()
|
||
|
|
@@ -115,7 +121,12 @@ protected:
|
||
|
|
if (is_ggc ())
|
||
|
|
ggc_delete (item);
|
||
|
|
else
|
||
|
|
- m_allocator.remove (item);
|
||
|
|
+ {
|
||
|
|
+ if (flag_ipa_struct_reorg)
|
||
|
|
+ delete item;
|
||
|
|
+ else
|
||
|
|
+ m_allocator.remove (item);
|
||
|
|
+ }
|
||
|
|
}
|
||
|
|
|
||
|
|
/* Unregister all call-graph hooks. */
|
||
|
|
diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-5.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-5.c
|
||
|
|
new file mode 100644
|
||
|
|
index 000000000..273baa9a3
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-5.c
|
||
|
|
@@ -0,0 +1,31 @@
|
||
|
|
+/* { dg-do compile } */
|
||
|
|
+/* { dg-additional-options "-flto -fno-use-linker-plugin" } */
|
||
|
|
+
|
||
|
|
+struct D
|
||
|
|
+{
|
||
|
|
+ int n;
|
||
|
|
+ int c [8];
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+struct A
|
||
|
|
+{
|
||
|
|
+ int i;
|
||
|
|
+ char *p;
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+struct B
|
||
|
|
+{
|
||
|
|
+ struct A *a;
|
||
|
|
+ struct D *d;
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+int dtInsert1 (struct B *b)
|
||
|
|
+{
|
||
|
|
+ struct A a = { 0, 0 };
|
||
|
|
+ struct D *d;
|
||
|
|
+ b->a = &a;
|
||
|
|
+ d = b->d;
|
||
|
|
+ &d->c [d->n];
|
||
|
|
+ return 0;
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-6.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-6.c
|
||
|
|
new file mode 100644
|
||
|
|
index 000000000..455f9b501
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-6.c
|
||
|
|
@@ -0,0 +1,54 @@
|
||
|
|
+/* { dg-do compile } */
|
||
|
|
+/* { dg-additional-options "-flto -fno-use-linker-plugin" } */
|
||
|
|
+
|
||
|
|
+typedef struct basic_block_def *basic_block;
|
||
|
|
+typedef struct gimple_seq_node_d *gimple_seq_node;
|
||
|
|
+typedef struct gimple_seq_d *gimple_seq;
|
||
|
|
+typedef struct
|
||
|
|
+{
|
||
|
|
+ gimple_seq_node ptr;
|
||
|
|
+ gimple_seq seq;
|
||
|
|
+ basic_block bb;
|
||
|
|
+} gimple_stmt_iterator;
|
||
|
|
+typedef void *gimple;
|
||
|
|
+extern void exit(int);
|
||
|
|
+struct gimple_seq_node_d
|
||
|
|
+{
|
||
|
|
+ gimple stmt;
|
||
|
|
+ struct gimple_seq_node_d *next;
|
||
|
|
+};
|
||
|
|
+struct gimple_seq_d
|
||
|
|
+{
|
||
|
|
+};
|
||
|
|
+static __inline__ gimple_stmt_iterator
|
||
|
|
+gsi_start (gimple_seq seq)
|
||
|
|
+{
|
||
|
|
+ gimple_stmt_iterator i;
|
||
|
|
+ i.seq = seq;
|
||
|
|
+ return i;
|
||
|
|
+}
|
||
|
|
+static __inline__ unsigned char
|
||
|
|
+gsi_end_p (gimple_stmt_iterator i)
|
||
|
|
+{
|
||
|
|
+ return i.ptr == ((void *)0);
|
||
|
|
+}
|
||
|
|
+static __inline__ void
|
||
|
|
+gsi_next (gimple_stmt_iterator *i)
|
||
|
|
+{
|
||
|
|
+ i->ptr = i->ptr->next;
|
||
|
|
+}
|
||
|
|
+static __inline__ gimple
|
||
|
|
+gsi_stmt (gimple_stmt_iterator i)
|
||
|
|
+{
|
||
|
|
+ return i.ptr->stmt;
|
||
|
|
+}
|
||
|
|
+void
|
||
|
|
+c_warn_unused_result (gimple_seq seq)
|
||
|
|
+{
|
||
|
|
+ gimple_stmt_iterator i;
|
||
|
|
+ for (i = gsi_start (seq); !gsi_end_p (i); gsi_next (&i))
|
||
|
|
+ {
|
||
|
|
+ gimple g = gsi_stmt (i);
|
||
|
|
+ if (!g) exit(0);
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-7.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-7.c
|
||
|
|
new file mode 100644
|
||
|
|
index 000000000..afc0bd86c
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-7.c
|
||
|
|
@@ -0,0 +1,38 @@
|
||
|
|
+/* { dg-do run } */
|
||
|
|
+
|
||
|
|
+#include <stdio.h>
|
||
|
|
+#include <stdlib.h>
|
||
|
|
+
|
||
|
|
+struct gki_elem {
|
||
|
|
+ char *key;
|
||
|
|
+ int idx;
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+typedef struct {
|
||
|
|
+ struct gki_elem *table;
|
||
|
|
+
|
||
|
|
+ int primelevel;
|
||
|
|
+ int nhash;
|
||
|
|
+ int nkeys;
|
||
|
|
+} GKI;
|
||
|
|
+
|
||
|
|
+void *
|
||
|
|
+sre_malloc(size_t size)
|
||
|
|
+{
|
||
|
|
+ void *ptr = malloc (size);
|
||
|
|
+ return ptr;
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+__attribute__((noinline)) int
|
||
|
|
+GKIStoreKey(GKI *hash)
|
||
|
|
+{
|
||
|
|
+ hash->table = sre_malloc(sizeof(struct gki_elem));
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+int
|
||
|
|
+main ()
|
||
|
|
+{
|
||
|
|
+ GKI *hash = malloc (sizeof(GKI));
|
||
|
|
+ GKIStoreKey(hash);
|
||
|
|
+ return 0;
|
||
|
|
+}
|
||
|
|
diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-8.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-8.c
|
||
|
|
new file mode 100644
|
||
|
|
index 000000000..9bcfaf368
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-8.c
|
||
|
|
@@ -0,0 +1,25 @@
|
||
|
|
+/* { dg-do run } */
|
||
|
|
+
|
||
|
|
+#include <stdio.h>
|
||
|
|
+#include <stdlib.h>
|
||
|
|
+#include <string.h>
|
||
|
|
+
|
||
|
|
+typedef struct {
|
||
|
|
+ unsigned char blue;
|
||
|
|
+ unsigned char green;
|
||
|
|
+} Pixel;
|
||
|
|
+
|
||
|
|
+typedef struct {
|
||
|
|
+ unsigned short colormaplength;
|
||
|
|
+ Pixel *colormapdata;
|
||
|
|
+} TargaImage;
|
||
|
|
+
|
||
|
|
+TargaImage *img;
|
||
|
|
+
|
||
|
|
+int main() {
|
||
|
|
+ img = (TargaImage *) malloc( sizeof(TargaImage) );
|
||
|
|
+ if (img->colormaplength > 0) {
|
||
|
|
+ img->colormapdata = (Pixel *) malloc(sizeof(Pixel) * img->colormaplength);
|
||
|
|
+ memset(img->colormapdata, 0, (sizeof(Pixel) * img->colormaplength) );
|
||
|
|
+ }
|
||
|
|
+}
|
||
|
|
diff --git a/gcc/testsuite/gcc.dg/struct/struct_reorg-9.c b/gcc/testsuite/gcc.dg/struct/struct_reorg-9.c
|
||
|
|
new file mode 100644
|
||
|
|
index 000000000..052f4e3bd
|
||
|
|
--- /dev/null
|
||
|
|
+++ b/gcc/testsuite/gcc.dg/struct/struct_reorg-9.c
|
||
|
|
@@ -0,0 +1,54 @@
|
||
|
|
+/* { dg-do run } */
|
||
|
|
+
|
||
|
|
+extern void abort(void);
|
||
|
|
+
|
||
|
|
+struct packed_ushort {
|
||
|
|
+ unsigned short ucs;
|
||
|
|
+} __attribute__((packed));
|
||
|
|
+
|
||
|
|
+struct source {
|
||
|
|
+ int pos, length;
|
||
|
|
+};
|
||
|
|
+
|
||
|
|
+static int flag;
|
||
|
|
+
|
||
|
|
+static void __attribute__((noinline)) fetch(struct source *p)
|
||
|
|
+{
|
||
|
|
+ p->length = 128;
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+static struct packed_ushort __attribute__((noinline)) next(struct source *p)
|
||
|
|
+{
|
||
|
|
+ struct packed_ushort rv;
|
||
|
|
+
|
||
|
|
+ if (p->pos >= p->length) {
|
||
|
|
+ if (flag) {
|
||
|
|
+ flag = 0;
|
||
|
|
+ fetch(p);
|
||
|
|
+ return next(p);
|
||
|
|
+ }
|
||
|
|
+ flag = 1;
|
||
|
|
+ rv.ucs = 0xffff;
|
||
|
|
+ return rv;
|
||
|
|
+ }
|
||
|
|
+ rv.ucs = 0;
|
||
|
|
+ return rv;
|
||
|
|
+}
|
||
|
|
+
|
||
|
|
+int main(void)
|
||
|
|
+{
|
||
|
|
+ struct source s;
|
||
|
|
+ int i;
|
||
|
|
+
|
||
|
|
+ s.pos = 0;
|
||
|
|
+ s.length = 0;
|
||
|
|
+ flag = 0;
|
||
|
|
+
|
||
|
|
+ for (i = 0; i < 16; i++) {
|
||
|
|
+ struct packed_ushort rv = next(&s);
|
||
|
|
+ if ((i == 0 && rv.ucs != 0xffff)
|
||
|
|
+ || (i > 0 && rv.ucs != 0))
|
||
|
|
+ abort();
|
||
|
|
+ }
|
||
|
|
+ return 0;
|
||
|
|
+}
|
||
|
|
--
|
||
|
|
2.33.0
|
||
|
|
|