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