1/* -*- mode: C; c-basic-offset: 3; indent-tabs-mode: nil; -*- */
2/*
3  This file is part of drd, a thread error detector.
4
5  Copyright (C) 2006-2011 Bart Van Assche <bvanassche@acm.org>.
6
7  This program is free software; you can redistribute it and/or
8  modify it under the terms of the GNU General Public License as
9  published by the Free Software Foundation; either version 2 of the
10  License, or (at your option) any later version.
11
12  This program is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of
14  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  General Public License for more details.
16
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20  02111-1307, USA.
21
22  The GNU General Public License is contained in the file COPYING.
23*/
24
25
26#ifndef __DRD_BITMAP_H
27#define __DRD_BITMAP_H
28
29
30#include "pub_drd_bitmap.h"
31#include "pub_tool_basics.h"
32#include "pub_tool_oset.h"
33#include "pub_tool_libcbase.h"
34#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
35#include "pub_tool_libcassert.h"
36#endif
37
38
39/* Bitmap representation. A bitmap is a data structure in which two bits are
40 * reserved per 32 bit address: one bit that indicates that the data at the
41 * specified address has been read, and one bit that indicates that the data
42 * has been written to.
43 */
44
45/* Client addresses are split into bitfields as follows:
46 * ------------------------------------------------------
47 * | Address MSB |      Address LSB      | Ignored bits |
48 * ------------------------------------------------------
49 * | Address MSB | UWord MSB | UWord LSB | Ignored bits |
50 * ------------------------------------------------------
51 */
52
53
54
55/* Address MSB / LSB split. */
56
57
58/** Number of least significant address bits that are ignored. */
59#define ADDR_IGNORED_BITS 0
60#define ADDR_IGNORED_MASK ((1U << ADDR_IGNORED_BITS) - 1U)
61#define ADDR_GRANULARITY  (1U << ADDR_IGNORED_BITS)
62
63/**
64 * Round argument a up to a multiple of (1 << ADDR_GRANULARITY), and next
65 * shift it right ADDR_GRANULARITY bits. The expression below is optimized
66 * for the case where a is a constant.
67 */
68#define SCALED_SIZE(a)                                                  \
69   (((((a) - 1U) | ADDR_IGNORED_MASK) + 1U) >> ADDR_IGNORED_BITS)
70
71/**
72 * Number of bits assigned to the least significant component of an address.
73 */
74#define ADDR_LSB_BITS 12
75
76/**
77 * Mask that has to be applied to an address of type Addr in order to
78 * compute the least significant part of an address split, after having
79 * shifted the address bits ADDR_GRANULARITY to the right.
80 */
81#define ADDR_LSB_MASK (((UWord)1 << ADDR_LSB_BITS) - 1U)
82
83/** Compute least significant bits of an address of type Addr. */
84static __inline__
85UWord address_lsb(const Addr a)
86{ return (a >> ADDR_IGNORED_BITS) & ADDR_LSB_MASK; }
87
88/**
89 * Compute the first address for which address_lsb() is equal to
90 * address_lsb(a).
91 */
92static __inline__
93Addr first_address_with_same_lsb(const Addr a)
94{
95   return ((a | ADDR_IGNORED_MASK) ^ ADDR_IGNORED_MASK);
96}
97
98/**
99 * Compute the first address for which address_lsb() is greater than
100 * address_lsb(a).
101 */
102static __inline__
103Addr first_address_with_higher_lsb(const Addr a)
104{
105   return ((a | ADDR_IGNORED_MASK) + 1U);
106}
107
108/** Compute most significant bits of an address of type Addr. */
109static __inline__
110UWord address_msb(const Addr a)
111{ return a >> (ADDR_LSB_BITS + ADDR_IGNORED_BITS); }
112
113static __inline__
114Addr first_address_with_higher_msb(const Addr a)
115{
116   return ((a | ((ADDR_LSB_MASK << ADDR_IGNORED_BITS) | ADDR_IGNORED_MASK))
117           + 1U);
118}
119
120/**
121 * Convert LSB and MSB back into an address.
122 *
123 * @note It is assumed that sizeof(Addr) == sizeof(UWord).
124 */
125static __inline__
126Addr make_address(const UWord a1, const UWord a0)
127{
128   return ((a1 << (ADDR_LSB_BITS + ADDR_IGNORED_BITS))
129           | (a0 << ADDR_IGNORED_BITS));
130}
131
132
133
134
135
136/** Number of bits that fit in a variable of type UWord. */
137#define BITS_PER_UWORD (8U * sizeof(UWord))
138
139/** Log2 of BITS_PER_UWORD. */
140#if defined(VGA_x86) || defined(VGA_ppc32) || defined(VGA_arm)
141#define BITS_PER_BITS_PER_UWORD 5
142#elif defined(VGA_amd64) || defined(VGA_ppc64) || defined(VGA_s390x)
143#define BITS_PER_BITS_PER_UWORD 6
144#else
145#error Unknown platform.
146#endif
147
148/** Number of UWord's needed to store one bit per address LSB. */
149#define BITMAP1_UWORD_COUNT (1U << (ADDR_LSB_BITS - BITS_PER_BITS_PER_UWORD))
150
151/**
152 * Mask that has to be applied to an (Addr >> ADDR_IGNORED_BITS) expression
153 * in order to compute the least significant part of an UWord.
154 */
155#define UWORD_LSB_MASK (((UWord)1 << BITS_PER_BITS_PER_UWORD) - 1)
156
157/**
158 * Compute index into bm0[] array.
159 *
160 * @param a Address shifted right ADDR_IGNORED_BITS bits.
161 */
162static __inline__
163UWord uword_msb(const UWord a)
164{
165#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
166   tl_assert(a < (1U << ADDR_LSB_BITS));
167#endif
168   return a >> BITS_PER_BITS_PER_UWORD;
169}
170
171/**
172 * Return the least significant bits.
173 *
174 * @param a Address shifted right ADDR_IGNORED_BITS bits.
175 */
176static __inline__
177UWord uword_lsb(const UWord a)
178{
179#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
180   tl_assert(a < (1U << ADDR_LSB_BITS));
181#endif
182   return a & UWORD_LSB_MASK;
183}
184
185/**
186 * Compute the highest address lower than a for which
187 * uword_lsb(address_lsb(a)) == 0.
188 *
189 * @param a Address.
190 */
191static __inline__
192Addr first_address_with_same_uword_lsb(const Addr a)
193{
194   return (a & (~UWORD_LSB_MASK << ADDR_IGNORED_BITS));
195}
196
197/**
198 * First address that will go in the UWord past the one 'a' goes in.
199 *
200 *  @param a Address.
201 */
202static __inline__
203Addr first_address_with_higher_uword_msb(const Addr a)
204{
205   return ((a | ((UWORD_LSB_MASK << ADDR_IGNORED_BITS) | ADDR_IGNORED_MASK))
206           + 1);
207}
208
209
210
211/* Local variables. */
212
213static ULong s_bitmap2_creation_count;
214
215
216
217/*********************************************************************/
218/*           Functions for manipulating a struct bitmap1.            */
219/*********************************************************************/
220
221
222/* Lowest level, corresponding to the lowest ADDR_LSB_BITS of an address. */
223struct bitmap1
224{
225   UWord bm0_r[BITMAP1_UWORD_COUNT];
226   UWord bm0_w[BITMAP1_UWORD_COUNT];
227};
228
229static __inline__ UWord bm0_mask(const UWord a)
230{
231#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
232   tl_assert(address_msb(make_address(0, a)) == 0);
233#endif
234   return ((UWord)1 << uword_lsb(a));
235}
236
237/** Set the bit corresponding to address a in bitmap bm0. */
238static __inline__ void bm0_set(UWord* bm0, const UWord a)
239{
240#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
241   tl_assert(address_msb(make_address(0, a)) == 0);
242#endif
243   bm0[uword_msb(a)] |= (UWord)1 << uword_lsb(a);
244}
245
246/**
247 * Set the bits corresponding to all of the addresses in range
248 * [ a << ADDR_IGNORED_BITS .. (a + size) << ADDR_IGNORED_BITS [
249 * in bitmap bm0.
250 */
251static __inline__ void bm0_set_range(UWord* bm0,
252                                     const UWord a, const SizeT size)
253{
254#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
255   tl_assert(size > 0);
256   tl_assert(address_msb(make_address(0, a)) == 0);
257   tl_assert(address_msb(make_address(0, a + size - 1)) == 0);
258   tl_assert(uword_msb(a) == uword_msb(a + size - 1));
259#endif
260   bm0[uword_msb(a)]
261      |= (((UWord)1 << size) - 1) << uword_lsb(a);
262}
263
264/** Clear the bit corresponding to address a in bitmap bm0. */
265static __inline__ void bm0_clear(UWord* bm0, const UWord a)
266{
267#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
268   tl_assert(address_msb(make_address(0, a)) == 0);
269#endif
270   bm0[uword_msb(a)] &= ~((UWord)1 << uword_lsb(a));
271}
272
273/**
274 * Clear all of the addresses in range
275 * [ a << ADDR_IGNORED_BITS .. (a + size) << ADDR_IGNORED_BITS [
276 * in bitmap bm0.
277 */
278static __inline__ void bm0_clear_range(UWord* bm0,
279                                       const UWord a, const SizeT size)
280{
281#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
282   tl_assert(address_msb(make_address(0, a)) == 0);
283   tl_assert(size == 0 || address_msb(make_address(0, a + size - 1)) == 0);
284   tl_assert(size == 0 || uword_msb(a) == uword_msb(a + size - 1));
285#endif
286   /*
287    * Note: although the expression below yields a correct result even if
288    * size == 0, do not touch bm0[] if size == 0 because this might otherwise
289    * cause an access of memory just past the end of the bm0[] array.
290    */
291   if (size > 0)
292   {
293      bm0[uword_msb(a)]
294         &= ~((((UWord)1 << size) - 1) << uword_lsb(a));
295   }
296}
297
298/** Test whether the bit corresponding to address a is set in bitmap bm0. */
299static __inline__ UWord bm0_is_set(const UWord* bm0, const UWord a)
300{
301#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
302   tl_assert(address_msb(make_address(0, a)) == 0);
303#endif
304   return (bm0[uword_msb(a)] & ((UWord)1 << uword_lsb(a)));
305}
306
307/**
308 * Return true if a bit corresponding to any of the addresses in range
309 * [ a << ADDR_IGNORED_BITS .. (a + size) << ADDR_IGNORED_BITS [
310 * is set in bm0.
311 */
312static __inline__ UWord bm0_is_any_set(const UWord* bm0,
313                                       const Addr a, const SizeT size)
314{
315#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
316   tl_assert(size > 0);
317   tl_assert(address_msb(make_address(0, a)) == 0);
318   tl_assert(address_msb(make_address(0, a + size - 1)) == 0);
319   tl_assert(uword_msb(a) == uword_msb(a + size - 1));
320#endif
321   return (bm0[uword_msb(a)] & ((((UWord)1 << size) - 1) << uword_lsb(a)));
322}
323
324
325
326/*********************************************************************/
327/*           Functions for manipulating a struct bitmap.             */
328/*********************************************************************/
329
330
331/* Second level bitmap. */
332struct bitmap2
333{
334   Addr           addr;   ///< address_msb(...)
335   Bool           recalc;
336   struct bitmap1 bm1;
337};
338
339
340static void bm2_clear(struct bitmap2* const bm2);
341static __inline__
342struct bitmap2* bm2_insert(struct bitmap* const bm, const UWord a1);
343
344
345
346/**
347 * Rotate elements cache[0..n-1] such that the element at position n-1 is
348 * moved to position 0. This allows to speed up future cache lookups.
349 */
350static __inline__
351void bm_cache_rotate(struct bm_cache_elem cache[], const int n)
352{
353#if 0
354   struct bm_cache_elem t;
355
356   tl_assert(2 <= n && n <= 8);
357
358   t = cache[0];
359   if (n > 1)
360      cache[0] = cache[1];
361   if (n > 2)
362      cache[1] = cache[2];
363   if (n > 3)
364      cache[2] = cache[3];
365   if (n > 4)
366      cache[3] = cache[4];
367   if (n > 5)
368      cache[4] = cache[5];
369   if (n > 6)
370      cache[5] = cache[6];
371   if (n > 7)
372      cache[6] = cache[7];
373   cache[n - 1] = t;
374#endif
375}
376
377static __inline__
378Bool bm_cache_lookup(struct bitmap* const bm, const UWord a1,
379                     struct bitmap2** bm2)
380{
381#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
382   tl_assert(bm);
383   tl_assert(bm2);
384#endif
385
386#if DRD_BITMAP_N_CACHE_ELEM > 8
387#error Please update the code below.
388#endif
389#if DRD_BITMAP_N_CACHE_ELEM >= 1
390   if (a1 == bm->cache[0].a1)
391   {
392      *bm2 = bm->cache[0].bm2;
393      return True;
394   }
395#endif
396#if DRD_BITMAP_N_CACHE_ELEM >= 2
397   if (a1 == bm->cache[1].a1)
398   {
399      *bm2 = bm->cache[1].bm2;
400      return True;
401   }
402#endif
403#if DRD_BITMAP_N_CACHE_ELEM >= 3
404   if (a1 == bm->cache[2].a1)
405   {
406      *bm2 = bm->cache[2].bm2;
407      bm_cache_rotate(bm->cache, 3);
408      return True;
409   }
410#endif
411#if DRD_BITMAP_N_CACHE_ELEM >= 4
412   if (a1 == bm->cache[3].a1)
413   {
414      *bm2 = bm->cache[3].bm2;
415      bm_cache_rotate(bm->cache, 4);
416      return True;
417   }
418#endif
419#if DRD_BITMAP_N_CACHE_ELEM >= 5
420   if (a1 == bm->cache[4].a1)
421   {
422      *bm2 = bm->cache[4].bm2;
423      bm_cache_rotate(bm->cache, 5);
424      return True;
425   }
426#endif
427#if DRD_BITMAP_N_CACHE_ELEM >= 6
428   if (a1 == bm->cache[5].a1)
429   {
430      *bm2 = bm->cache[5].bm2;
431      bm_cache_rotate(bm->cache, 6);
432      return True;
433   }
434#endif
435#if DRD_BITMAP_N_CACHE_ELEM >= 7
436   if (a1 == bm->cache[6].a1)
437   {
438      *bm2 = bm->cache[6].bm2;
439      bm_cache_rotate(bm->cache, 7);
440      return True;
441   }
442#endif
443#if DRD_BITMAP_N_CACHE_ELEM >= 8
444   if (a1 == bm->cache[7].a1)
445   {
446      *bm2 = bm->cache[7].bm2;
447      bm_cache_rotate(bm->cache, 8);
448      return True;
449   }
450#endif
451   *bm2 = 0;
452   return False;
453}
454
455static __inline__
456void bm_update_cache(struct bitmap* const bm,
457                     const UWord a1,
458                     struct bitmap2* const bm2)
459{
460#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
461   tl_assert(bm);
462#endif
463
464#if DRD_BITMAP_N_CACHE_ELEM > 8
465#error Please update the code below.
466#endif
467#if DRD_BITMAP_N_CACHE_ELEM >= 8
468   bm->cache[7] = bm->cache[6];
469#endif
470#if DRD_BITMAP_N_CACHE_ELEM >= 7
471   bm->cache[6] = bm->cache[5];
472#endif
473#if DRD_BITMAP_N_CACHE_ELEM >= 6
474   bm->cache[5] = bm->cache[4];
475#endif
476#if DRD_BITMAP_N_CACHE_ELEM >= 5
477   bm->cache[4] = bm->cache[3];
478#endif
479#if DRD_BITMAP_N_CACHE_ELEM >= 4
480   bm->cache[3] = bm->cache[2];
481#endif
482#if DRD_BITMAP_N_CACHE_ELEM >= 3
483   bm->cache[2] = bm->cache[1];
484#endif
485#if DRD_BITMAP_N_CACHE_ELEM >= 2
486   bm->cache[1] = bm->cache[0];
487#endif
488   bm->cache[0].a1  = a1;
489   bm->cache[0].bm2 = bm2;
490}
491
492/**
493 * Look up the address a1 in bitmap bm and return a pointer to a potentially
494 * shared second level bitmap. The bitmap where the returned pointer points
495 * at may not be modified by the caller.
496 *
497 * @param a1 client address shifted right by ADDR_LSB_BITS.
498 * @param bm bitmap pointer.
499 */
500static __inline__
501const struct bitmap2* bm2_lookup(struct bitmap* const bm, const UWord a1)
502{
503   struct bitmap2* bm2;
504
505#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
506   tl_assert(bm);
507#endif
508
509   if (! bm_cache_lookup(bm, a1, &bm2))
510   {
511      bm2 = VG_(OSetGen_Lookup)(bm->oset, &a1);
512      bm_update_cache(bm, a1, bm2);
513   }
514   return bm2;
515}
516
517/**
518 * Look up the address a1 in bitmap bm and return a pointer to a second
519 * level bitmap that is not shared and hence may be modified.
520 *
521 * @param a1 client address shifted right by ADDR_LSB_BITS.
522 * @param bm bitmap pointer.
523 */
524static __inline__
525struct bitmap2*
526bm2_lookup_exclusive(struct bitmap* const bm, const UWord a1)
527{
528   struct bitmap2* bm2;
529
530#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
531   tl_assert(bm);
532#endif
533
534   if (! bm_cache_lookup(bm, a1, &bm2))
535   {
536      bm2 = VG_(OSetGen_Lookup)(bm->oset, &a1);
537   }
538
539   return bm2;
540}
541
542/** Clear the content of the second-level bitmap. */
543static __inline__
544void bm2_clear(struct bitmap2* const bm2)
545{
546#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
547   tl_assert(bm2);
548#endif
549   VG_(memset)(&bm2->bm1, 0, sizeof(bm2->bm1));
550}
551
552/**
553 * Insert an uninitialized second level bitmap for the address a1.
554 *
555 * @param bm bitmap pointer.
556 * @param a1 client address shifted right by ADDR_LSB_BITS.
557 *
558 * @note bitmap2::recalc isn't initialized here on purpose.
559 */
560static __inline__
561struct bitmap2* bm2_insert(struct bitmap* const bm, const UWord a1)
562{
563   struct bitmap2* bm2;
564
565#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
566   tl_assert(bm);
567#endif
568
569   s_bitmap2_creation_count++;
570
571   bm2 = VG_(OSetGen_AllocNode)(bm->oset, sizeof(*bm2));
572   bm2->addr = a1;
573   VG_(OSetGen_Insert)(bm->oset, bm2);
574
575   bm_update_cache(bm, a1, bm2);
576
577   return bm2;
578}
579
580static __inline__
581struct bitmap2* bm2_insert_copy(struct bitmap* const bm,
582                                struct bitmap2* const bm2)
583{
584   struct bitmap2* bm2_copy;
585
586   bm2_copy = bm2_insert(bm, bm2->addr);
587   VG_(memcpy)(&bm2_copy->bm1, &bm2->bm1, sizeof(bm2->bm1));
588   return bm2_copy;
589}
590
591/**
592 * Look up the address a1 in bitmap bm, and insert it if not found.
593 * The returned second level bitmap may not be modified.
594 *
595 * @param bm bitmap pointer.
596 * @param a1 client address shifted right by ADDR_LSB_BITS.
597 */
598static __inline__
599struct bitmap2* bm2_lookup_or_insert(struct bitmap* const bm, const UWord a1)
600{
601   struct bitmap2* bm2;
602
603#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
604   tl_assert(bm);
605#endif
606
607   if (bm_cache_lookup(bm, a1, &bm2))
608   {
609      if (bm2 == 0)
610      {
611         bm2 = bm2_insert(bm, a1);
612         bm2_clear(bm2);
613      }
614   }
615   else
616   {
617      bm2 = VG_(OSetGen_Lookup)(bm->oset, &a1);
618      if (! bm2)
619      {
620         bm2 = bm2_insert(bm, a1);
621         bm2_clear(bm2);
622      }
623      bm_update_cache(bm, a1, bm2);
624   }
625   return bm2;
626}
627
628/**
629 * Look up the address a1 in bitmap bm, and insert it if not found.
630 * The returned second level bitmap may be modified.
631 *
632 * @param a1 client address shifted right by ADDR_LSB_BITS.
633 * @param bm bitmap pointer.
634 */
635static __inline__
636struct bitmap2* bm2_lookup_or_insert_exclusive(struct bitmap* const bm,
637                                               const UWord a1)
638{
639   return bm2_lookup_or_insert(bm, a1);
640}
641
642static __inline__
643void bm2_remove(struct bitmap* const bm, const UWord a1)
644{
645   struct bitmap2* bm2;
646
647#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
648   tl_assert(bm);
649#endif
650
651   bm2 = VG_(OSetGen_Remove)(bm->oset, &a1);
652   VG_(OSetGen_FreeNode)(bm->oset, bm2);
653
654   bm_update_cache(bm, a1, NULL);
655}
656
657static __inline__
658void bm_access_aligned_load(struct bitmap* const bm,
659                            const Addr a1, const SizeT size)
660{
661   struct bitmap2* bm2;
662
663#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
664   tl_assert(bm);
665#endif
666
667   bm2 = bm2_lookup_or_insert_exclusive(bm, address_msb(a1));
668   bm0_set_range(bm2->bm1.bm0_r,
669                 (a1 >> ADDR_IGNORED_BITS) & ADDR_LSB_MASK,
670                 SCALED_SIZE(size));
671}
672
673static __inline__
674void bm_access_aligned_store(struct bitmap* const bm,
675                             const Addr a1, const SizeT size)
676{
677   struct bitmap2* bm2;
678
679#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
680   tl_assert(bm);
681#endif
682
683   bm2 = bm2_lookup_or_insert_exclusive(bm, address_msb(a1));
684   bm0_set_range(bm2->bm1.bm0_w,
685                 (a1 >> ADDR_IGNORED_BITS) & ADDR_LSB_MASK,
686                 SCALED_SIZE(size));
687}
688
689static __inline__
690Bool bm_aligned_load_has_conflict_with(struct bitmap* const bm,
691                                       const Addr a, const SizeT size)
692{
693   const struct bitmap2* bm2;
694
695#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
696   tl_assert(bm);
697#endif
698
699   bm2 = bm2_lookup(bm, address_msb(a));
700   return (bm2
701           && bm0_is_any_set(bm2->bm1.bm0_w,
702                             address_lsb(a),
703                             SCALED_SIZE(size)));
704}
705
706static __inline__
707Bool bm_aligned_store_has_conflict_with(struct bitmap* const bm,
708                                        const Addr a, const SizeT size)
709{
710   const struct bitmap2* bm2;
711
712#ifdef ENABLE_DRD_CONSISTENCY_CHECKS
713   tl_assert(bm);
714#endif
715
716   bm2 = bm2_lookup(bm, address_msb(a));
717   if (bm2)
718   {
719      if (bm0_is_any_set(bm2->bm1.bm0_r, address_lsb(a), SCALED_SIZE(size))
720          | bm0_is_any_set(bm2->bm1.bm0_w, address_lsb(a), SCALED_SIZE(size)))
721      {
722         return True;
723      }
724   }
725   return False;
726}
727
728#endif /* __DRD_BITMAP_H */
729