re PR tree-optimization/69801 (gcc ICE on valid code on x86_64-linux-gnu in "operand...
[platform/upstream/gcc.git] / libvtv / vtv_set.h
1 /* Copyright (C) 2012-2015 Free Software Foundation, Inc.
2
3    This file is part of GCC.
4
5    GCC is free software; you can redistribute it and/or modify it
6    under the terms of the GNU General Public License as published by
7    the Free Software Foundation; either version 3, or (at your option)
8    any later version.
9
10    GCC is distributed in the hope that it will be useful, but WITHOUT
11    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12    or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
13    License for more details.
14
15    Under Section 7 of GPL version 3, you are granted additional
16    permissions described in the GCC Runtime Library Exception, version
17    3.1, as published by the Free Software Foundation.
18
19    You should have received a copy of the GNU General Public License
20    and a copy of the GCC Runtime Library Exception along with this
21    program; see the files COPYING3 and COPYING.RUNTIME respectively.
22    If not, see <http://www.gnu.org/licenses/>.  */
23
24 #ifndef _VTV_SET_H
25 #define _VTV_SET_H 1
26
27 /* Code in this file manages a collection of insert-only sets.  We
28    have only tested the case where Key is uintptr_t, though it
29    theoretically should work for some other cases.  All odd keys are
30    reserved, and must not be inserted into any of the sets.  This code
31    is intended primarily for sets of pointers, and the code is
32    optimized for small sets (including size 0 and 1), but regardless
33    of the set size, insert() and contains() have close to O(1) speed
34    in practice.
35
36    TODO(gpike): fix this comment.
37
38    Recommended multithreaded use of a set:
39
40    For speed, we want to use a lock-free test for set membership.  The
41    code handles simultaneous reads and inserts, as long as at most one
42    insertion is in progress at a time.  After an insert, other threads
43    may not immediately "see" the inserted key if they perform a
44    lock-free read, so we recommend retrying, as explained below.
45
46    Also, to make data corruption less likely, we recommend using a
47    "normal" RW page as well as one or pages that are typically RO
48    but that can be switched to RW and back as needed.  The latter
49    pages should contain sets.  The former should contain a lock, L,
50    and an int or similar, num_writers.  Then, to insert, something
51    like this would be safe:
52     o Acquire L.
53     o Increment num_writers; if that made it 1, change pages to RW.
54     o Release L.
55     o while (there are insertions to do in some set, S) {
56         acquire L;
57         do some insertions in S;
58         release L;
59       }
60     o Acquire L.
61     o Decrement num_writers; if that made it 0, change pages to RO.
62     o Release L.
63
64    And to check if the set contains some key, one could use
65      set.contains(key) ||
66        ({ Acquire L; bool b = set.contains(key); Release L; b; })
67
68    In this scheme, the number of threads with reads in progress isn't
69    tracked, so old sets can never be deleted.  In addition, on some
70    architectures the intentionally racy reads might cause contains()
71    to return true when it should have returned false.  This should be
72    no problem on x86, and most other machines, where reading or
73    writing an aligned uintptr_t is atomic.  E.g., on those machines,
74    if *p is 0 and one thread does *p = x while another reads *p, the
75    read will see either 0 or x.
76
77    To make the above easier, the insert_only_hash_sets class provides
78    an interface to manipulate any number of hash sets.  One shouldn't
79    create objects of that class, as it has no member data and its
80    methods are static.
81
82    So the recommended model is to have a single lock, a single
83    num_writers variable, and some number of sets.  If lock contention
84    becomes a problem then the sets can be divided into k groups, each
85    of which has a lock and a num_writers variable; or each set can be
86    represented as a set of values that equal 0 mod m, a set of values
87    that equal 1 mod m, ..., plus a set of values that equal m-1 mod m.
88
89    However, we expect most or all uses of this code to call contains()
90    much more frequently than anything else, so lock contention is
91    likely to be low.  */
92
93 #include <algorithm>
94
95 #ifndef HASHTABLE_STATS
96 #define HASHTABLE_STATS 0
97 #endif
98
99 #ifndef HASHTABLE_STATS_ATOMIC
100 #define HASHTABLE_STATS_ATOMIC 0
101 #endif
102
103 #if HASHTABLE_STATS
104 #if HASHTABLE_STATS_ATOMIC
105 /* Stat counters, with atomics. */
106 #include <bits/atomic_word.h>
107
108 typedef _Atomic_word _AtomicStatCounter;
109
110 void
111 inc_by (_AtomicStatCounter &stat, int amount)
112
113   __atomic_add_fetch (&stat, amount,  __ATOMIC_ACQ_REL);
114 }
115
116 #else
117
118 /* Stat counters, but without atomics. */
119 typedef int _AtomicStatCounter;
120
121 void
122 inc_by (_AtomicStatCounter& stat, int amount)
123
124   stat += amount;
125 }
126
127 #endif
128
129
130 /* Number of calls to contains(), insert(), etc. */
131 extern _AtomicStatCounter stat_insert;
132 extern _AtomicStatCounter stat_contains;
133 extern _AtomicStatCounter stat_resize;
134 extern _AtomicStatCounter stat_create;
135
136 /* Sum of set size over all calls to contains().  */
137 extern _AtomicStatCounter stat_contains_sizes;
138
139 /* contains() calls in a set whose capacity is more than 1. */
140 extern _AtomicStatCounter stat_contains_in_non_trivial_set;
141
142 /* Probes in a set whose capacity is more than 1.  Ideally, this will
143    be pretty close to stat_contains_in_non_trivial_set.  That will
144    happen if our hash function is good and/or important keys were
145    inserted before unimportant keys.  */
146 extern _AtomicStatCounter stat_probes_in_non_trivial_set;
147
148 /* number of calls to contains() with size=0, 1, etc. */
149 extern _AtomicStatCounter stat_contains_size0;
150 extern _AtomicStatCounter stat_contains_size1;
151 extern _AtomicStatCounter stat_contains_size2;
152 extern _AtomicStatCounter stat_contains_size3;
153 extern _AtomicStatCounter stat_contains_size4;
154 extern _AtomicStatCounter stat_contains_size5;
155 extern _AtomicStatCounter stat_contains_size6;
156 extern _AtomicStatCounter stat_contains_size7;
157 extern _AtomicStatCounter stat_contains_size8;
158 extern _AtomicStatCounter stat_contains_size9;
159 extern _AtomicStatCounter stat_contains_size10;
160 extern _AtomicStatCounter stat_contains_size11;
161 extern _AtomicStatCounter stat_contains_size12;
162 extern _AtomicStatCounter stat_contains_size13_or_more;
163 extern _AtomicStatCounter stat_grow_from_size0_to_1;
164 extern _AtomicStatCounter stat_grow_from_size1_to_2;
165 extern _AtomicStatCounter stat_double_the_number_of_buckets;
166 extern _AtomicStatCounter stat_insert_key_that_was_already_present;
167
168 /* Hash collisions detected during insert_no_resize().  Only counts
169    hasher(k) == hasher(k'); hasher(k) % tablesize == hasher(k') %
170    tablesize is not sufficient.  Will count collisions that are
171    detected during table resizes etc., so the same two keys may add to
172    this stat multiple times.  */
173 extern _AtomicStatCounter stat_insert_found_hash_collision;
174
175 #include <string>
176
177 struct insert_only_hash_sets_logger
178 {
179   static char *
180   log (char c, char *buf)
181   {
182     *buf++ = c;
183     return buf;
184   }
185
186   static char *
187   log (const char *s, char *buf)
188   { return strcpy (buf, s) + strlen (s); }
189
190   static char *
191   log (_AtomicStatCounter i, char *buf)
192   {
193     if (i < 10)
194       return log ((char) ('0' + i), buf);
195     else
196       return log ((char) ('0' + i % 10), log (i / 10, buf));
197   }
198
199   static char *
200   log (const char *label, _AtomicStatCounter i, char *buf)
201   {
202     buf = log (label, buf);
203     buf = log (": ", buf);
204     buf = log (i, buf);
205     return log ('\n', buf);
206   }
207 };
208
209 // Write stats to the given buffer, which should be at least 4000 bytes.
210 static inline void
211 insert_only_hash_tables_stats (char *buf)
212 {
213   buf = insert_only_hash_sets_logger::log ("insert", stat_insert, buf);
214   buf = insert_only_hash_sets_logger::log ("contains", stat_contains, buf);
215   buf = insert_only_hash_sets_logger::log ("resize", stat_resize, buf);
216   buf = insert_only_hash_sets_logger::log ("create", stat_create, buf);
217   buf = insert_only_hash_sets_logger::log ("insert_key_that_was_already_"
218                                       "present",
219                                       stat_insert_key_that_was_already_present,
220                                       buf);
221   buf = insert_only_hash_sets_logger::log ("contains_sizes",
222                                            stat_contains_sizes, buf);
223   buf = insert_only_hash_sets_logger::log ("contains_in_non_trivial_set",
224                                            stat_contains_in_non_trivial_set,
225                                            buf);
226   buf = insert_only_hash_sets_logger::log ("probes_in_non_trivial_set",
227                                            stat_probes_in_non_trivial_set,
228                                            buf);
229   buf = insert_only_hash_sets_logger::log ("contains_size0",
230                                            stat_contains_size0, buf);
231   buf = insert_only_hash_sets_logger::log ("contains_size1",
232                                            stat_contains_size1, buf);
233   buf = insert_only_hash_sets_logger::log ("contains_size2",
234                                            stat_contains_size2, buf);
235   buf = insert_only_hash_sets_logger::log ("contains_size3",
236                                            stat_contains_size3, buf);
237   buf = insert_only_hash_sets_logger::log ("contains_size4",
238                                            stat_contains_size4, buf);
239   buf = insert_only_hash_sets_logger::log ("contains_size5",
240                                            stat_contains_size5, buf);
241   buf = insert_only_hash_sets_logger::log ("contains_size6",
242                                            stat_contains_size6, buf);
243   buf = insert_only_hash_sets_logger::log ("contains_size7",
244                                            stat_contains_size7, buf);
245   buf = insert_only_hash_sets_logger::log ("contains_size8",
246                                            stat_contains_size8, buf);
247   buf = insert_only_hash_sets_logger::log ("contains_size9",
248                                            stat_contains_size9, buf);
249   buf = insert_only_hash_sets_logger::log ("contains_size10",
250                                            stat_contains_size10, buf);
251   buf = insert_only_hash_sets_logger::log ("contains_size11",
252                                            stat_contains_size11, buf);
253   buf = insert_only_hash_sets_logger::log ("contains_size12",
254                                            stat_contains_size12, buf);
255   buf = insert_only_hash_sets_logger::log ("contains_size13_or_more",
256                                            stat_contains_size13_or_more, buf);
257   buf = insert_only_hash_sets_logger::log ("grow_from_size0_to_1",
258                                            stat_grow_from_size0_to_1, buf);
259   buf = insert_only_hash_sets_logger::log ("grow_from_size1_to_2",
260                                            stat_grow_from_size1_to_2, buf);
261   buf = insert_only_hash_sets_logger::log ("insert_found_hash_collision",
262                                            stat_insert_found_hash_collision,
263                                            buf);
264   buf = insert_only_hash_sets_logger::log ("double_the_number_of_buckets",
265                                            stat_double_the_number_of_buckets,
266                                            buf);
267   *buf = '\0';
268 }
269
270 #else
271
272 /* No stats. */
273 #define inc_by(statname, amount) do { } while (false && (amount))
274
275 #endif
276
277 #define inc(statname) inc_by (statname, 1)
278
279 template <typename Key, class HashFcn, class Alloc>
280 class insert_only_hash_sets
281 {
282  public:
283   typedef Key key_type;
284   typedef size_t size_type;
285   typedef Alloc alloc_type;
286   enum { illegal_key = 1 };
287   enum { min_capacity = 4 };
288 #if HASHTABLE_STATS
289   enum { stats = true };
290 #else
291   enum { stats = false };
292 #endif
293
294   /* Do not directly use insert_only_hash_set.  Instead, use the
295      static methods below to create and manipulate objects of the
296      following class.
297   
298      Implementation details: each set is represented by a pointer
299      plus, perhaps, out-of-line data, which would be an object of type
300      insert_only_hash_set.  For a pointer, s, the interpretation is: s
301      == NULL means empty set, lsb(s) == 1 means a set with one
302      element, which is (uintptr_t)s - 1, and otherwise s is a pointer
303      of type insert_only_hash_set*.  So, to increase the size of a set
304      we have to change s and/or *s.  To check if a set contains some
305      key we have to examine s and possibly *s.  */
306   class insert_only_hash_set
307   {
308    public:
309     /* Insert a key.  The key must not be a reserved key.  */
310     static inline insert_only_hash_set *insert (key_type key,
311                                                 insert_only_hash_set *s);
312     
313
314     /* Create an empty set.  */
315     static inline insert_only_hash_set *create (size_type capacity);
316
317     /* Return whether the given key is present.  If key is illegal_key
318        then either true or false may be returned, but for all other
319        reserved keys false will be returned.  */
320     static bool
321     contains (key_type key, const insert_only_hash_set *s)
322     {
323       if (stats)
324         {
325           inc (stat_contains);
326           switch (size (s))
327             {
328               case 0: inc (stat_contains_size0); break;
329               case 1: inc (stat_contains_size1); break;
330               case 2: inc (stat_contains_size2); break;
331               case 3: inc (stat_contains_size3); break;
332               case 4: inc (stat_contains_size4); break;
333               case 5: inc (stat_contains_size5); break;
334               case 6: inc (stat_contains_size6); break;
335               case 7: inc (stat_contains_size7); break;
336               case 8: inc (stat_contains_size8); break;
337               case 9: inc (stat_contains_size9); break;
338               case 10: inc (stat_contains_size10); break;
339               case 11: inc (stat_contains_size11); break;
340               case 12: inc (stat_contains_size12); break;
341               default: inc (stat_contains_size13_or_more); break;
342             }
343           inc_by (stat_contains_sizes, size (s));
344         }
345
346       return (singleton (s) ?
347               singleton_key (key) == s :
348               ((s != NULL) && s->contains (key)));
349     }
350
351     /* Return a set's size.  */
352     static size_type
353     size (const insert_only_hash_set *s)
354     { return (s == NULL) ? 0 : (singleton (s) ? 1 : s->num_entries); }
355
356     static inline insert_only_hash_set *resize (size_type target_num_buckets,
357                                                 insert_only_hash_set *s);
358     
359
360    private:
361     /* Return whether a set has size 1. */
362     static bool
363     singleton (const insert_only_hash_set *s)
364     { return (uintptr_t) s & 1; }
365
366     /* Return the representation of a singleton set containing the
367        given key.  */
368     static insert_only_hash_set *
369     singleton_key (key_type key)
370     { return (insert_only_hash_set *) ((uintptr_t) key + 1); }
371
372     /* Given a singleton set, what key does it contain?  */
373     static key_type
374     extract_singleton_key (const insert_only_hash_set *s)
375     {
376       VTV_DEBUG_ASSERT (singleton (s));
377       return (key_type) ((uintptr_t) s - 1);
378     }
379
380     volatile key_type &
381     key_at_index (size_type index)
382     { return buckets[index]; }
383
384     key_type
385     key_at_index (size_type index) const
386     { return buckets[index]; }
387
388     size_type
389     next_index (size_type index, size_type indices_examined) const
390     { return (index + indices_examined) & (num_buckets - 1); }
391
392     inline void insert_no_resize (key_type key);
393     
394     inline bool contains (key_type key) const;
395     
396     inline insert_only_hash_set *resize_if_necessary (void);
397     
398     size_type num_buckets;  /* Must be a power of 2 not less than
399                                min_capacity.  */
400     volatile size_type num_entries;
401     volatile key_type buckets[0];  /* Actual array size is num_buckets.  */
402   };
403
404   /* Create an empty set with the given capacity.  Requires that n be
405      0 or a power of 2.  If 1 < n < min_capacity then treat n as
406      min_capacity.  Sets *handle.  Returns true unless the allocator
407      fails.  Subsequent operations on this set should use the same
408      handle. */
409
410   static inline bool create (size_type n, insert_only_hash_set **handle);
411
412   /* Force the capacity of a set to be n, unless it was more than n
413      already.  Requires that n be 0 or a power of 2.  Sets *handle
414      unless the current capacity is n or more.  Returns true unless
415      the allocator fails.  */
416
417   static inline bool resize (size_type n, insert_only_hash_set **handle);
418
419   /* Insert a key.  *handle is unmodified unless (1) a resize occurs,
420      or (2) the set was initially empty. Returns true unless the
421      allocator fails during a resize.  If the allocator fails during a
422      resize then the set is reset to be the empty set.  The key must
423      not be a reserved key.  */
424
425   static inline bool insert (key_type key, insert_only_hash_set **handle);
426
427   /* Check for the presence of a key.  If key is illegal_key then
428      either true or false may be returned, but for all other reserved
429      keys false will be returned.  */
430
431   static inline bool
432   contains (key_type key, /* const */ insert_only_hash_set **handle)
433   { return insert_only_hash_set::contains (key, *handle); }
434
435   /* Return the size of the given set.  */
436   static size_type
437   size (const insert_only_hash_set **handle)
438   { return insert_only_hash_set::size (*handle); }
439
440   static bool
441   is_reserved_key (key_type key)
442   { return ((uintptr_t) key % 2) == 1; }
443 };
444
445 template <typename Key, class HashFcn, class Alloc>
446 typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set *
447 insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set::resize
448                                          (size_type n, insert_only_hash_set *s)
449 {
450   if (s == NULL)
451     return create (n);
452
453   size_type capacity = singleton (s) ? 1 : s->num_buckets;
454
455   if (n <= capacity)
456     return s;
457
458   insert_only_hash_set *result =
459                                 create (std::max<size_type> (n, min_capacity));
460   if (result != NULL)
461     {
462       if (singleton (s))
463         {
464           result->insert_no_resize (extract_singleton_key (s));
465         }
466       else
467         {
468           for (size_type i = 0; i < s->num_buckets; i++)
469             if (s->buckets[i] != (key_type) illegal_key)
470               result->insert_no_resize (s->buckets[i]);
471         }
472       VTV_DEBUG_ASSERT (size (result) == size (s));
473     }
474   return result;
475 }
476
477 template <typename Key, class HashFcn, class Alloc>
478 typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set *
479 insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set::insert 
480                                         (key_type key, insert_only_hash_set *s)
481 {
482   VTV_DEBUG_ASSERT (!is_reserved_key (key));
483
484   inc_by (stat_grow_from_size0_to_1, s == NULL);
485
486   if (s == NULL)
487     return singleton_key (key);
488
489   if (singleton (s))
490     {
491       const key_type old_key = extract_singleton_key (s);
492       if (old_key == key)
493         return s;
494
495       /* Grow from size 1 to size 2.  */
496       inc (stat_grow_from_size1_to_2);
497       s = create (2);
498       if (s == NULL)
499         return NULL;
500
501       s->insert_no_resize (old_key);
502       s->insert_no_resize (key);
503       VTV_DEBUG_ASSERT (size (s) == 2);
504       return s;
505     }
506   s = s->resize_if_necessary();
507   if (s != NULL)
508     s->insert_no_resize (key);
509   return s;
510 }
511
512 template <typename Key, class HashFcn, class Alloc>
513 typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set *
514 insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set::create
515                                                            (size_type capacity)
516 {
517   if (capacity <= 1)
518     return NULL;
519
520   VTV_DEBUG_ASSERT (capacity > 1 && (capacity & (capacity - 1)) == 0);
521   VTV_DEBUG_ASSERT (sizeof (insert_only_hash_set) == 2 * sizeof (size_type));
522   capacity = std::max <size_type> (capacity, min_capacity);
523   const size_t num_bytes = sizeof (insert_only_hash_set) +
524                                                   sizeof (key_type) * capacity;
525   alloc_type alloc;
526   insert_only_hash_set *result = (insert_only_hash_set *) alloc (num_bytes);
527   result->num_buckets = capacity;
528   result->num_entries = 0;
529   for (size_type i = 0; i < capacity; i++)
530     result->buckets[i] = (key_type) illegal_key;
531   return result;
532 }
533
534 template <typename Key, class HashFcn, class Alloc>
535 void
536 insert_only_hash_sets<Key, HashFcn,
537                                  Alloc>::insert_only_hash_set::insert_no_resize
538                                                                  (key_type key)
539 {
540   HashFcn hasher;
541   const size_type capacity = num_buckets;
542   VTV_DEBUG_ASSERT (capacity >= min_capacity);
543   VTV_DEBUG_ASSERT (!is_reserved_key (key));
544   size_type index = hasher (key) & (capacity - 1);
545   key_type k = key_at_index (index);
546   size_type indices_examined = 0;
547   while (k != key)
548     {
549       ++indices_examined;
550       if (k == (key_type) illegal_key)
551         {
552           key_at_index (index) = key;
553           ++num_entries;
554           return;
555         }
556       else
557         {
558           inc_by (stat_insert_found_hash_collision,
559                   hasher (k) == hasher (key));
560         }
561       VTV_DEBUG_ASSERT (indices_examined < capacity);
562       index = next_index (index, indices_examined);
563       k = key_at_index (index);
564     }
565 }
566
567 template<typename Key, class HashFcn, class Alloc>
568 bool
569 insert_only_hash_sets<Key, HashFcn, Alloc>::insert_only_hash_set::contains
570                                                            (key_type key) const
571 {
572   inc (stat_contains_in_non_trivial_set);
573   HashFcn hasher;
574   const size_type capacity = num_buckets;
575   size_type index = hasher (key) & (capacity - 1);
576   key_type k = key_at_index (index);
577   size_type indices_examined = 0;
578   inc (stat_probes_in_non_trivial_set);
579   while (k != key)
580     {
581       ++indices_examined;
582       if (/*UNLIKELY*/(k == (key_type) illegal_key
583                        || indices_examined == capacity))
584         return false;
585
586       index = next_index (index, indices_examined);
587       k = key_at_index (index);
588       inc (stat_probes_in_non_trivial_set);
589     }
590   return true;
591 }
592
593 template <typename Key, class HashFcn, class Alloc>
594 typename insert_only_hash_sets <Key, HashFcn, Alloc>::insert_only_hash_set *
595    insert_only_hash_sets<Key, HashFcn,
596                        Alloc>::insert_only_hash_set::resize_if_necessary (void)
597 {
598   VTV_DEBUG_ASSERT (num_buckets >= min_capacity);
599   size_type unused = num_buckets - num_entries;
600   if (unused < (num_buckets >> 2))
601     {
602       inc (stat_double_the_number_of_buckets);
603       size_type new_num_buckets = num_buckets * 2;
604       insert_only_hash_set *s = create (new_num_buckets);
605       for (size_type i = 0; i < num_buckets; i++)
606         if (buckets[i] != (key_type) illegal_key)
607           s->insert_no_resize (buckets[i]);
608       VTV_DEBUG_ASSERT (size (this) == size (s));
609       return s;
610     }
611   else
612     return this;
613 }
614
615 template<typename Key, class HashFcn, class Alloc>
616 bool
617 insert_only_hash_sets<Key, HashFcn, Alloc>::create (size_type n,
618                                                  insert_only_hash_set **handle)
619   
620 {
621   inc (stat_create);
622   *handle = insert_only_hash_set::create (n);
623   return (n <= 1) || (*handle != NULL);
624 }
625
626 template<typename Key, class HashFcn, class Alloc>
627 bool
628 insert_only_hash_sets<Key, HashFcn, Alloc>::resize (size_type n,
629                                                  insert_only_hash_set **handle)
630 {
631   inc (stat_resize);
632   *handle = insert_only_hash_set::resize (n, *handle);
633   return (n <= 1) || (*handle != NULL);
634 }
635
636 template<typename Key, class HashFcn, class Alloc>
637 bool
638 insert_only_hash_sets<Key, HashFcn, Alloc>::insert (key_type key,
639                                                  insert_only_hash_set **handle)
640 {
641   inc (stat_insert);
642   const size_type old_size = insert_only_hash_set::size (*handle);
643   *handle = insert_only_hash_set::insert (key, *handle);
644   if (*handle != NULL)
645     {
646       const size_type delta = insert_only_hash_set::size (*handle) - old_size;
647       inc_by (stat_insert_key_that_was_already_present, delta == 0);
648     }
649   return *handle != NULL;
650 }
651
652 #endif /* VTV_SET_H  */