]> git.llucax.com Git - personal/website.git/blob - source/blog/posts/2010/10/10-trying-cdgc-howto/0001-Create-pointer-map-bitmask-to-allow-precise-heap-sca.patch
Move eventxx submodule to the repo subdir
[personal/website.git] / source / blog / posts / 2010 / 10 / 10-trying-cdgc-howto / 0001-Create-pointer-map-bitmask-to-allow-precise-heap-sca.patch
1 From 60a2bf694f1166ca012fcb0a3fb8c092a1afd26b Mon Sep 17 00:00:00 2001
2 From: Leandro Lucarella <llucax@gmail.com>
3 Date: Wed, 28 Jul 2010 18:05:37 -0300
4 Subject: [PATCH] Create pointer map bitmask to allow precise heap scanning
5
6 This is the patch[1] published in D's bug #3463[2], written by Vincent
7 Lang (AKA wm4) based on David Simcha's idea.
8
9 Here are some interesting comments in the bug report about the patch
10 (completely rephrased by me to group the information *I* found interesting
11 =):
12
13     All user programs will make use of the precise scanning; no
14     modifications required.
15
16     The dmd patch makes dmd generate an additional field in
17     TypeInfo_Struct and ClassInfo. These fields point to pointer
18     bitmasks for the referenced type.  The patch is written in such
19     a way, that it won't break unpatched Tango or Phobos1 runtimes. In
20     particular, this means the patched compiler should be usable with
21     an unpatched Phobos1.
22
23     The patch should be able to handle all D types. The internal nodes
24     for associative arrays are still scanned conservatively.
25
26     I also wanted to make scanning of AA nodes precise, but it turned
27     out the compiler never passes the TypeInfo of AA values to the
28     runtime functions.  I guess I could hack the compiler to fix this,
29     but I'd rather not.
30
31     The idea I wanted to implement was to dynamically allocate & build
32     a PointerMap for the aaA type each time an associative array is
33     instantiated. As an alternative solution, one could expose the aaA
34     type to the compiler (would have to move aaA to object.d), and
35     then let the compiler put a PointerMap into
36     TypeInfo_AssociativeArray.
37
38     Note that the bitmask format is a bitmap, where each bit
39     represents an aligned, pointer sized chunk of memory.
40
41 [1] http://d.puremagic.com/issues/attachment.cgi?id=700
42 [2] http://d.puremagic.com/issues/show_bug.cgi?id=3463
43 ---
44  src/aggregate.h   |    7 ++-
45  src/attrib.c      |   14 ++++
46  src/attrib.h      |    2 +
47  src/declaration.c |    7 ++
48  src/declaration.h |    1 +
49  src/dsymbol.c     |    4 +
50  src/dsymbol.h     |    2 +
51  src/idgen.c       |    4 +
52  src/mars.c        |    2 +
53  src/mtype.c       |  191 ++++++++++++++++++++++++++++++++++++++++++++++++++--
54  src/mtype.h       |   50 ++++++++++++++
55  src/template.c    |   10 +++
56  src/template.h    |    2 +
57  src/toobj.c       |   45 ++++++++++---
58  src/typinf.c      |   66 ++++++++++++++++++
59  15 files changed, 387 insertions(+), 20 deletions(-)
60
61 diff --git a/src/aggregate.h b/src/aggregate.h
62 index bd4aa0f..871876d 100644
63 --- a/src/aggregate.h
64 +++ b/src/aggregate.h
65 @@ -188,7 +188,12 @@ struct BaseClass
66  #if DMDV2
67  #define CLASSINFO_SIZE  (0x3C+16+4)     // value of ClassInfo.size
68  #else
69 -#define CLASSINFO_SIZE  (0x3C+12+4)     // value of ClassInfo.size
70 +    // warning: if the user's object.d contains a ClassInfo.pointermap field,
71 +    //  CLASSINFO_SIZE grows by an additonal 2 words - this is handled
72 +    //  dynamically in ClassDeclaration::toObjFile
73 +    //now, the ONLY purpose of this is for the object.d incompatibility warning
74 +#define CLASSINFO_SIZE_1  (0x3C+12+4)     // value of ClassInfo.size
75 +#define CLASSINFO_SIZE_2  (0x3C+12+4+8)     // value of ClassInfo.size
76  #endif
77  
78  struct ClassDeclaration : AggregateDeclaration
79 diff --git a/src/attrib.c b/src/attrib.c
80 index 18d4385..684932f 100644
81 --- a/src/attrib.c
82 +++ b/src/attrib.c
83 @@ -277,6 +277,20 @@ int AttribDeclaration::hasPointers()
84      return 0;
85  }
86  
87 +void AttribDeclaration::fillPointerMap(PointerMap *pm, size_t offset)
88 +{
89 +    Array *d = include(NULL, NULL);
90 +
91 +    if (d)
92 +    {
93 +        for (size_t i = 0; i < d->dim; i++)
94 +        {
95 +            Dsymbol *s = (Dsymbol *)d->data[i];
96 +            s->fillPointerMap(pm, offset);
97 +        }
98 +    }
99 +}
100 +
101  const char *AttribDeclaration::kind()
102  {
103      return "attribute";
104 diff --git a/src/attrib.h b/src/attrib.h
105 index 1747f5b..b4f2718 100644
106 --- a/src/attrib.h
107 +++ b/src/attrib.h
108 @@ -23,6 +23,7 @@ struct LabelDsymbol;
109  struct Initializer;
110  struct Module;
111  struct Condition;
112 +struct PointerMap;
113  #ifdef _DH
114  struct HdrGenState;
115  #endif
116 @@ -51,6 +52,7 @@ struct AttribDeclaration : Dsymbol
117      const char *kind();
118      int oneMember(Dsymbol **ps);
119      int hasPointers();
120 +    void fillPointerMap(PointerMap *pm, size_t offset);
121      void checkCtorConstInit();
122      void addLocalClass(ClassDeclarations *);
123      void toCBuffer(OutBuffer *buf, HdrGenState *hgs);
124 diff --git a/src/declaration.c b/src/declaration.c
125 index 64d6c3a..e58ad92 100644
126 --- a/src/declaration.c
127 +++ b/src/declaration.c
128 @@ -1377,6 +1377,13 @@ int VarDeclaration::hasPointers()
129      return (!isDataseg() && type->hasPointers());
130  }
131  
132 +void VarDeclaration::fillPointerMap(PointerMap *pm, size_t a_offset)
133 +{
134 +    //printf("VarDeclaration::fillPointerMap() %s, ty = %d, offs=%d\n", toChars(), type->ty, (int)offset);
135 +    if (!isDataseg())
136 +        type->fillPointerMap(pm, offset + a_offset);
137 +}
138 +
139  /******************************************
140   * If a variable has an auto destructor call, return call for it.
141   * Otherwise, return NULL.
142 diff --git a/src/declaration.h b/src/declaration.h
143 index c558630..d8922ca 100644
144 --- a/src/declaration.h
145 +++ b/src/declaration.h
146 @@ -279,6 +279,7 @@ struct VarDeclaration : Declaration
147      int isThreadlocal();
148      int isCTFE();
149      int hasPointers();
150 +    void fillPointerMap(PointerMap *pm, size_t offset);
151  #if DMDV2
152      int canTakeAddressOf();
153      int needsAutoDtor();
154 diff --git a/src/dsymbol.c b/src/dsymbol.c
155 index dd47434..f619fc1 100644
156 --- a/src/dsymbol.c
157 +++ b/src/dsymbol.c
158 @@ -148,6 +148,10 @@ int Dsymbol::hasPointers()
159      return 0;
160  }
161  
162 +void Dsymbol::fillPointerMap(PointerMap *pm, size_t offset)
163 +{
164 +}
165 +
166  char *Dsymbol::toChars()
167  {
168      return ident ? ident->toChars() : (char *)"__anonymous";
169 diff --git a/src/dsymbol.h b/src/dsymbol.h
170 index 68d261b..18813b0 100644
171 --- a/src/dsymbol.h
172 +++ b/src/dsymbol.h
173 @@ -70,6 +70,7 @@ struct DeleteDeclaration;
174  struct HdrGenState;
175  struct OverloadSet;
176  struct AA;
177 +struct PointerMap;
178  #if TARGET_NET
179  struct PragmaScope;
180  #endif
181 @@ -180,6 +181,7 @@ struct Dsymbol : Object
182      virtual int oneMember(Dsymbol **ps);
183      static int oneMembers(Array *members, Dsymbol **ps);
184      virtual int hasPointers();
185 +    virtual void fillPointerMap(PointerMap *pm, size_t offset);
186      virtual void addLocalClass(ClassDeclarations *) { }
187      virtual void checkCtorConstInit() { }
188  
189 diff --git a/src/idgen.c b/src/idgen.c
190 index 46bc269..6714824 100644
191 --- a/src/idgen.c
192 +++ b/src/idgen.c
193 @@ -91,6 +91,10 @@ Msgtable msgtable[] =
194      { "_argptr" },
195      { "_match" },
196  
197 +    // check for PointerMap-aware runtime
198 +    { "pointermap" }, //ClassInfo.pointermap
199 +    { "m_pointermap" }, //TypeInfo_Struct.m_pointermap
200 +
201      { "LINE", "__LINE__" },
202      { "FILE", "__FILE__" },
203      { "DATE", "__DATE__" },
204 diff --git a/src/mars.c b/src/mars.c
205 index fbb4f74..15b9248 100644
206 --- a/src/mars.c
207 +++ b/src/mars.c
208 @@ -394,6 +394,8 @@ int main(int argc, char *argv[])
209  #error "fix this"
210  #endif
211  
212 +    VersionCondition::addPredefinedGlobalIdent("D_HavePointerMap");
213 +
214      VersionCondition::addPredefinedGlobalIdent("LittleEndian");
215      //VersionCondition::addPredefinedGlobalIdent("D_Bits");
216  #if DMDV2
217 diff --git a/src/mtype.c b/src/mtype.c
218 index ce959ff..6386ff0 100644
219 --- a/src/mtype.c
220 +++ b/src/mtype.c
221 @@ -833,7 +833,14 @@ Expression *Type::toExpression()
222  
223  int Type::hasPointers()
224  {
225 -    return FALSE;
226 +    //fprintf(stderr, "Type::hasPointers(): '%s'\n", toChars());
227 +    assert(FALSE);
228 +}
229 +
230 +void Type::fillPointerMap(PointerMap *pm, size_t offset)
231 +{
232 +    //fprintf(stderr, "Type::fillPointerMap(): '%s'\n", toChars());
233 +    assert(FALSE);
234  }
235  
236  /* ============================= TypeError =========================== */
237 @@ -1584,6 +1591,29 @@ TypeBasic *TypeBasic::isTypeBasic()
238      return (TypeBasic *)this;
239  }
240  
241 +int TypeBasic::hasPointers()
242 +{
243 +    return FALSE;
244 +}
245 +
246 +void TypeBasic::fillPointerMap(PointerMap *pm, size_t offset)
247 +{
248 +    // special case: static void arrays inside structs/classes
249 +    // justification: union { void[8] a; void[] b; } (often used for variants)
250 +    //  should just be fully scanned, instead of only b.ptr
251 +    // [dynamic void[] arrays are handled by TypeInfo_v.flags() (that's why
252 +    //  hasPointers() is not special cased for Tvoid?)]
253 +    if (ty == Tvoid)
254 +    {
255 +        if (offset + PTRSIZE <= pm->size())
256 +            pm->maybepointer(offset);
257 +    }
258 +    else
259 +    {
260 +        pm->nopointer(offset, Type::size());
261 +    }
262 +}
263 +
264  /***************************** TypeArray *****************************/
265  
266  TypeArray::TypeArray(TY ty, Type *next)
267 @@ -2060,6 +2090,18 @@ int TypeSArray::hasPointers()
268      return next->hasPointers();
269  }
270  
271 +void TypeSArray::fillPointerMap(PointerMap *pm, size_t offset)
272 +{
273 +    // for all array items
274 +    // static arrays of size 0 produce nothing
275 +    size_t d = dim->toInteger();
276 +    size_t s = next->size();
277 +    for (size_t n = 0; n < d; n++)
278 +    {
279 +        next->fillPointerMap(pm, offset + n * s);
280 +    }
281 +}
282 +
283  /***************************** TypeDArray *****************************/
284  
285  TypeDArray::TypeDArray(Type *t)
286 @@ -2209,6 +2251,13 @@ int TypeDArray::hasPointers()
287      return TRUE;
288  }
289  
290 +void TypeDArray::fillPointerMap(PointerMap *pm, size_t offset)
291 +{
292 +    // like struct Array { size_t length; byte* data; }
293 +    pm->nopointer(offset, PTRSIZE);
294 +    pm->pointer(offset + PTRSIZE);
295 +}
296 +
297  /***************************** TypeAArray *****************************/
298  
299  TypeAArray::TypeAArray(Type *t, Type *index)
300 @@ -2451,6 +2500,12 @@ int TypeAArray::hasPointers()
301      return TRUE;
302  }
303  
304 +void TypeAArray::fillPointerMap(PointerMap *pm, size_t offset)
305 +{
306 +    // a pointer to the internal AA struct
307 +    pm->pointer(offset);
308 +}
309 +
310  /***************************** TypePointer *****************************/
311  
312  TypePointer::TypePointer(Type *t)
313 @@ -2559,6 +2614,11 @@ int TypePointer::hasPointers()
314      return TRUE;
315  }
316  
317 +void TypePointer::fillPointerMap(PointerMap *pm, size_t offset)
318 +{
319 +    pm->pointer(offset);
320 +}
321 +
322  
323  /***************************** TypeReference *****************************/
324  
325 @@ -3187,6 +3247,15 @@ int TypeDelegate::hasPointers()
326      return TRUE;
327  }
328  
329 +void TypeDelegate::fillPointerMap(PointerMap *pm, size_t offset)
330 +{
331 +    // ABI: .ptr followed by .funcptr
332 +    // assume .funcptr always points to static data (code)
333 +    // thus, only .ptr needs to be scanned
334 +    pm->pointer(offset);
335 +    pm->nopointer(offset + PTRSIZE, PTRSIZE);
336 +}
337 +
338  
339  
340  /***************************** TypeQualified *****************************/
341 @@ -4098,6 +4167,11 @@ int TypeEnum::hasPointers()
342      return toBasetype()->hasPointers();
343  }
344  
345 +void TypeEnum::fillPointerMap(PointerMap *pm, size_t offset)
346 +{
347 +    toBasetype()->fillPointerMap(pm, offset);
348 +}
349 +
350  /***************************** TypeTypedef *****************************/
351  
352  TypeTypedef::TypeTypedef(TypedefDeclaration *sym)
353 @@ -4306,6 +4380,11 @@ int TypeTypedef::hasPointers()
354      return toBasetype()->hasPointers();
355  }
356  
357 +void TypeTypedef::fillPointerMap(PointerMap *pm, size_t offset)
358 +{
359 +    toBasetype()->fillPointerMap(pm, offset);
360 +}
361 +
362  /***************************** TypeStruct *****************************/
363  
364  TypeStruct::TypeStruct(StructDeclaration *sym)
365 @@ -4637,18 +4716,27 @@ int TypeStruct::hasPointers()
366      StructDeclaration *s = sym;
367  
368      sym->size(0);               // give error for forward references
369 -    if (s->members)
370 +    for (size_t i = 0; i < s->fields.dim; i++)
371      {
372 -        for (size_t i = 0; i < s->members->dim; i++)
373 -        {
374 -            Dsymbol *sm = (Dsymbol *)s->members->data[i];
375 -            if (sm->hasPointers())
376 -                return TRUE;
377 -        }
378 +        Dsymbol *sm = (Dsymbol *)s->fields.data[i];
379 +        if (sm->hasPointers())
380 +            return TRUE;
381      }
382      return FALSE;
383  }
384  
385 +void TypeStruct::fillPointerMap(PointerMap *pm, size_t offset)
386 +{
387 +    StructDeclaration *s = sym;
388 +
389 +    sym->size(0);               // give error for forward references
390 +    //NOTE: using s->members instead of s->fields would call fillPointerMap on TypeTuple
391 +    for (size_t i = 0; i < s->fields.dim; i++)
392 +    {
393 +        Dsymbol *sm = (Dsymbol *)s->fields.data[i];
394 +        sm->fillPointerMap(pm, offset);
395 +    }
396 +}
397  
398  /***************************** TypeClass *****************************/
399  
400 @@ -5064,6 +5152,11 @@ int TypeClass::hasPointers()
401      return TRUE;
402  }
403  
404 +void TypeClass::fillPointerMap(PointerMap *pm, size_t offset)
405 +{
406 +    pm->pointer(offset);
407 +}
408 +
409  /***************************** TypeTuple *****************************/
410  
411  TypeTuple::TypeTuple(Parameters *arguments)
412 @@ -5201,6 +5294,16 @@ Expression *TypeTuple::getProperty(Loc loc, Identifier *ident)
413      return e;
414  }
415  
416 +int TypeTuple::hasPointers()
417 +{
418 +    assert(FALSE);
419 +}
420 +
421 +void TypeTuple::fillPointerMap(PointerMap *pm, size_t offset)
422 +{
423 +    assert(FALSE);
424 +}
425 +
426  /***************************** TypeSlice *****************************/
427  
428  /* This is so we can slice a TypeTuple */
429 @@ -5568,3 +5671,75 @@ Parameter *Parameter::getNth(Parameters *args, size_t nth, size_t *pn)
430          *pn += n;
431      return NULL;
432  }
433 +
434 +
435 +PointerMap::PointerMap()
436 +{
437 +    m_data = 0;
438 +    m_dlen = 0;
439 +    m_size = 0;
440 +}
441 +
442 +PointerMap::~PointerMap()
443 +{
444 +    mem.free(m_data);
445 +}
446 +
447 +/***
448 + * Set size in bytes and reinit all bits to 0.
449 + */
450 +void PointerMap::setSize(size_t bytes)
451 +{
452 +    mem.free(m_data);
453 +    m_size = bytes;
454 +    m_dlen = m_size / PTRSIZE;
455 +    m_data = (ubyte*)mem.calloc(m_dlen, 1);
456 +    if (m_dlen > 0)
457 +        assert(m_data);
458 +}
459 +
460 +size_t PointerMap::size()
461 +{
462 +    return m_size;
463 +}
464 +
465 +/***
466 + * Mark position at offset as pointer.
467 + * Actually does nothing if the offset isn't aligned.
468 + */
469 +void PointerMap::pointer(size_t offset)
470 +{
471 +    assert(m_data);
472 +    assert(offset < m_size);
473 +    //reject unaligned pointers
474 +    if ((offset & (PTRSIZE - 1)) != 0)
475 +        return;
476 +    size_t elem = offset / PTRSIZE;
477 +    assert(elem < m_dlen);
478 +    m_data[elem] |= PTR;
479 +}
480 +
481 +void PointerMap::nopointer(size_t offset, size_t size)
482 +{
483 +    assert(size > 0);
484 +    if (m_dlen > 0)
485 +        assert(m_data);
486 +    assert(offset < m_size);
487 +    assert(offset + size <= m_size);
488 +    size_t pos = offset & ~(PTRSIZE - 1); //align down to cover all words
489 +    for (; pos < offset + size; pos += PTRSIZE)
490 +    {
491 +        //when m_size is unaligned, the last bytes will be in elem==m_dlen
492 +        if (pos >= (m_size & ~(PTRSIZE - 1)))
493 +            break;
494 +        size_t elem = pos / PTRSIZE;
495 +        assert(elem < m_dlen);
496 +        m_data[elem] |= NOPTR;
497 +    }
498 +}
499 +
500 +void PointerMap::maybepointer(size_t offset)
501 +{
502 +    pointer(offset);
503 +    nopointer(offset, PTRSIZE);
504 +}
505 diff --git a/src/mtype.h b/src/mtype.h
506 index ab87f81..e28f81c 100644
507 --- a/src/mtype.h
508 +++ b/src/mtype.h
509 @@ -38,6 +38,7 @@ enum LINK;
510  struct TypeBasic;
511  struct HdrGenState;
512  struct Parameter;
513 +struct PointerMap;
514  
515  // Back end
516  #if IN_GCC
517 @@ -246,6 +247,7 @@ struct Type : Object
518      virtual Type *reliesOnTident();
519      virtual Expression *toExpression();
520      virtual int hasPointers();
521 +    virtual void fillPointerMap(PointerMap *pm, size_t offset);
522      Type *next;
523      Type *nextOf() { return next; }
524  
525 @@ -325,6 +327,8 @@ struct TypeBasic : Type
526      Expression *defaultInit(Loc loc);
527      int isZeroInit(Loc loc);
528      int builtinTypeInfo();
529 +    int hasPointers();
530 +    void fillPointerMap(PointerMap *pm, size_t offset);
531  
532      // For eliminating dynamic_cast
533      TypeBasic *isTypeBasic();
534 @@ -362,6 +366,7 @@ struct TypeSArray : TypeArray
535      TypeInfoDeclaration *getTypeInfoDeclaration();
536      Expression *toExpression();
537      int hasPointers();
538 +    void fillPointerMap(PointerMap *pm, size_t offset);
539  
540      type *toCtype();
541      type *toCParamtype();
542 @@ -386,6 +391,7 @@ struct TypeDArray : TypeArray
543      int builtinTypeInfo();
544      TypeInfoDeclaration *getTypeInfoDeclaration();
545      int hasPointers();
546 +    void fillPointerMap(PointerMap *pm, size_t offset);
547  
548      type *toCtype();
549  };
550 @@ -409,6 +415,7 @@ struct TypeAArray : TypeArray
551      int checkBoolean();
552      TypeInfoDeclaration *getTypeInfoDeclaration();
553      int hasPointers();
554 +    void fillPointerMap(PointerMap *pm, size_t offset);
555  
556      // Back end
557      Symbol *aaGetSymbol(const char *func, int flags);
558 @@ -429,6 +436,7 @@ struct TypePointer : Type
559      int isZeroInit(Loc loc);
560      TypeInfoDeclaration *getTypeInfoDeclaration();
561      int hasPointers();
562 +    void fillPointerMap(PointerMap *pm, size_t offset);
563  
564      type *toCtype();
565  };
566 @@ -489,6 +497,7 @@ struct TypeDelegate : Type
567      TypeInfoDeclaration *getTypeInfoDeclaration();
568      Expression *dotExp(Scope *sc, Expression *e, Identifier *ident);
569      int hasPointers();
570 +    void fillPointerMap(PointerMap *pm, size_t offset);
571  
572      type *toCtype();
573  };
574 @@ -577,6 +586,7 @@ struct TypeStruct : Type
575      MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes);
576      TypeInfoDeclaration *getTypeInfoDeclaration();
577      int hasPointers();
578 +    void fillPointerMap(PointerMap *pm, size_t offset);
579  
580      type *toCtype();
581  };
582 @@ -607,6 +617,7 @@ struct TypeEnum : Type
583      MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes);
584      TypeInfoDeclaration *getTypeInfoDeclaration();
585      int hasPointers();
586 +    void fillPointerMap(PointerMap *pm, size_t offset);
587  #if CPP_MANGLE
588      void toCppMangle(OutBuffer *buf, CppMangleState *cms);
589  #endif
590 @@ -646,6 +657,7 @@ struct TypeTypedef : Type
591      MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes);
592      TypeInfoDeclaration *getTypeInfoDeclaration();
593      int hasPointers();
594 +    void fillPointerMap(PointerMap *pm, size_t offset);
595  
596      type *toCtype();
597      type *toCParamtype();
598 @@ -674,6 +686,7 @@ struct TypeClass : Type
599      int checkBoolean();
600      TypeInfoDeclaration *getTypeInfoDeclaration();
601      int hasPointers();
602 +    void fillPointerMap(PointerMap *pm, size_t offset);
603      int builtinTypeInfo();
604  #if DMDV2
605      Type *toHeadMutable();
606 @@ -702,6 +715,8 @@ struct TypeTuple : Type
607      void toDecoBuffer(OutBuffer *buf);
608      Expression *getProperty(Loc loc, Identifier *ident);
609      TypeInfoDeclaration *getTypeInfoDeclaration();
610 +    int hasPointers();
611 +    void fillPointerMap(PointerMap *pm, size_t offset);
612  };
613  
614  struct TypeSlice : Type
615 @@ -750,4 +765,39 @@ extern int Tptrdiff_t;
616  
617  int arrayTypeCompatible(Loc loc, Type *t1, Type *t2);
618  
619 +//"abstracted" because we might want to support other representations
620 +//e.g. a list of offsets
621 +struct PointerMap
622 +{
623 +    typedef unsigned char ubyte;
624 +
625 +    static const ubyte PTR = 1;
626 +    static const ubyte NOPTR = 2;
627 +
628 +    //each ubyte represents a word; bitmask of PTR and NOPTR
629 +    //PTR|NOPTR means non-moveable
630 +    ubyte* m_data;
631 +    size_t m_dlen; //length of m_data
632 +    size_t m_size; //size of the type
633 +
634 +    PointerMap();
635 +    ~PointerMap();
636 +
637 +    void setSize(size_t bytes);
638 +    size_t size();
639 +
640 +    //if both pointer and nopointer are called for the same word, the word is
641 +    //registered as non-moveable pointer
642 +    void pointer(size_t offset);
643 +    void nopointer(size_t offset, size_t size);
644 +    //pointer-aligned field that may or may not be a pointer
645 +    void maybepointer(size_t offset);
646 +
647 +    //write a PointerMap instance as described in object_.d
648 +    void toDt(dt_t **pdt);
649 +
650 +    //write an "invalid" PointerMap instance
651 +    static void toDtInvalid(dt_t **pdt);
652 +};
653 +
654  #endif /* DMD_MTYPE_H */
655 diff --git a/src/template.c b/src/template.c
656 index a3b5b6a..7f87730 100644
657 --- a/src/template.c
658 +++ b/src/template.c
659 @@ -4812,6 +4812,16 @@ int TemplateMixin::hasPointers()
660      return 0;
661  }
662  
663 +void TemplateMixin::fillPointerMap(PointerMap *pm, size_t offset)
664 +{
665 +    for (size_t i = 0; i < members->dim; i++)
666 +    {
667 +        Dsymbol *s = (Dsymbol *)members->data[i];
668 +        //printf(" s = %s %s\n", s->kind(), s->toChars());
669 +        s->fillPointerMap(pm, offset);
670 +    }
671 +}
672 +
673  char *TemplateMixin::toChars()
674  {
675      OutBuffer buf;
676 diff --git a/src/template.h b/src/template.h
677 index 2f9b834..992b731 100644
678 --- a/src/template.h
679 +++ b/src/template.h
680 @@ -36,6 +36,7 @@ struct Expression;
681  struct AliasDeclaration;
682  struct FuncDeclaration;
683  struct HdrGenState;
684 +struct PointerMap;
685  enum MATCH;
686  
687  struct Tuple : Object
688 @@ -337,6 +338,7 @@ struct TemplateMixin : TemplateInstance
689      const char *kind();
690      int oneMember(Dsymbol **ps);
691      int hasPointers();
692 +    void fillPointerMap(PointerMap *pm, size_t offset);
693      char *toChars();
694      void toCBuffer(OutBuffer *buf, HdrGenState *hgs);
695  
696 diff --git a/src/toobj.c b/src/toobj.c
697 index 0b16044..7db76a2 100644
698 --- a/src/toobj.c
699 +++ b/src/toobj.c
700 @@ -351,10 +351,14 @@ void ClassDeclaration::toObjFile(int multiobj)
701         }
702       */
703      dt_t *dt = NULL;
704 +    assert(classinfo != NULL);
705 +    size_t CLASSINFO_SIZE = classinfo->structsize;
706 +    bool have_pointer_map = classinfo->search(NULL, Id::pointermap, 0);
707      offset = CLASSINFO_SIZE;                    // must be ClassInfo.size
708      if (classinfo)
709      {
710 -        if (classinfo->structsize != CLASSINFO_SIZE)
711 +        if ((!have_pointer_map && (classinfo->structsize != CLASSINFO_SIZE_1))
712 +            || (have_pointer_map && (classinfo->structsize != CLASSINFO_SIZE_2)))
713          {
714              error("mismatch between dmd and object.d or object.di found. Check installation and import paths with -v compiler switch.");
715              fatal();
716 @@ -464,6 +468,26 @@ void ClassDeclaration::toObjFile(int multiobj)
717      dtxoff(&dt, type->vtinfo->toSymbol(), 0, TYnptr);   // typeinfo
718      //dtdword(&dt, 0);
719  
720 +    // pointermap - only with pointermap-aware runtime
721 +    if (have_pointer_map)
722 +    {
723 +        PointerMap pm;
724 +        pm.setSize(structsize);
725 +        for (ClassDeclaration *cd = this; cd; cd = cd->baseClass)
726 +        {
727 +            if (cd->members)
728 +            {
729 +                for (size_t i = 0; i < cd->members->dim; i++)
730 +                {
731 +                    Dsymbol *sm = (Dsymbol *)cd->members->data[i];
732 +                    //printf("sm = %s %s\n", sm->kind(), sm->toChars());
733 +                    sm->fillPointerMap(&pm, 0);
734 +                }
735 +            }
736 +        }
737 +        pm.toDt(&dt);
738 +    }
739 +
740      //////////////////////////////////////////////
741  
742      // Put out vtblInterfaces->data[]. Must immediately follow csym, because
743 @@ -717,7 +741,8 @@ unsigned ClassDeclaration::baseVtblOffset(BaseClass *bc)
744      int i;
745  
746      //printf("ClassDeclaration::baseVtblOffset('%s', bc = %p)\n", toChars(), bc);
747 -    csymoffset = CLASSINFO_SIZE;
748 +    assert(classinfo != NULL);
749 +    csymoffset = classinfo->structsize;
750      csymoffset += vtblInterfaces->dim * (4 * PTRSIZE);
751  
752      for (i = 0; i < vtblInterfaces->dim; i++)
753 @@ -841,6 +866,7 @@ void InterfaceDeclaration::toObjFile(int multiobj)
754              const(MemberInfo[]) function(string) xgetMembers;   // module getMembers() function
755  #endif
756              TypeInfo typeinfo;
757 +            PointerMap pointermap; //optional!
758         }
759       */
760      dt_t *dt = NULL;
761 @@ -869,15 +895,8 @@ void InterfaceDeclaration::toObjFile(int multiobj)
762      dtdword(&dt, vtblInterfaces->dim);
763      if (vtblInterfaces->dim)
764      {
765 -        if (classinfo)
766 -        {
767 -            if (classinfo->structsize != CLASSINFO_SIZE)
768 -            {
769 -                error("mismatch between dmd and object.d or object.di found. Check installation and import paths with -v compiler switch.");
770 -                fatal();
771 -            }
772 -        }
773 -        offset = CLASSINFO_SIZE;
774 +        assert(classinfo != NULL);
775 +        offset = classinfo->structsize;
776          dtxoff(&dt, csym, offset, TYnptr);      // (*)
777      }
778      else
779 @@ -913,6 +932,10 @@ void InterfaceDeclaration::toObjFile(int multiobj)
780  
781      dtxoff(&dt, type->vtinfo->toSymbol(), 0, TYnptr);   // typeinfo
782  
783 +    bool have_pointer_map = classinfo->search(NULL, Id::pointermap, 0);
784 +    if (have_pointer_map)
785 +        PointerMap::toDtInvalid(&dt);
786 +
787      //////////////////////////////////////////////
788  
789      // Put out vtblInterfaces->data[]. Must immediately follow csym, because
790 diff --git a/src/typinf.c b/src/typinf.c
791 index 53a8648..8852c31 100644
792 --- a/src/typinf.c
793 +++ b/src/typinf.c
794 @@ -573,6 +573,15 @@ void TypeInfoStructDeclaration::toDt(dt_t **pdt)
795      // uint m_flags;
796      dtdword(pdt, tc->hasPointers());
797  
798 +    // m_pointermap - only with pointermap-aware runtime
799 +    if (Type::typeinfostruct->search(NULL, Id::m_pointermap, 0))
800 +    {
801 +        PointerMap pm;
802 +        pm.setSize(sd->structsize);
803 +        tc->fillPointerMap(&pm, 0);
804 +        pm.toDt(pdt);
805 +    }
806 +
807  #if DMDV2
808      // xgetMembers
809      FuncDeclaration *sgetmembers = sd->findGetMembers();
810 @@ -599,6 +608,63 @@ void TypeInfoStructDeclaration::toDt(dt_t **pdt)
811      dtnbytes(pdt, namelen + 1, name);
812  }
813  
814 +//set the n-th bit in bits[0..length*PTRSIZE]
815 +//bits is an array of target size_t
816 +static void setbit(unsigned char *bits, size_t bits_length, size_t bit)
817 +{
818 +    //TODO: this probably only works on little endian machines
819 +    //      the generated program reads this bit array as size_t[]
820 +    size_t bitpos = bit % 8;
821 +    size_t npos = bit / 8;
822 +    assert(npos < bits_length * PTRSIZE);
823 +    bits[npos] |= 1 << bitpos;
824 +}
825 +
826 +void PointerMap::toDt(dt_t **pdt)
827 +{
828 +    size_t BITS = PTRSIZE * 8;
829 +
830 +    size_t nelem = (m_dlen + BITS - 1) / BITS;  //number of bitmap elements needed for mwords
831 +    size_t data_len = 1 + nelem * 2;            //length of the final bitmask array (header + 2 bitmaps)
832 +    ubyte *data = (ubyte *)mem.calloc(data_len, PTRSIZE);
833 +    assert(data);
834 +
835 +    //number of pointer-sized words in type
836 +    uinteger_t target_len = m_dlen;
837 +    assert(PTRSIZE <= sizeof(target_len));
838 +    //TODO: this only works on little endian machines
839 +    memcpy(data, &target_len, PTRSIZE);
840 +
841 +    ubyte *p_bits = data + PTRSIZE;                  //bitmap for all pointers
842 +    ubyte *m_bits = data + PTRSIZE * (1 + nelem);    //bitmap for moveable pointers
843 +
844 +    for (size_t cur = 0; cur < m_dlen; cur++)
845 +    {
846 +        ubyte p = m_data[cur];
847 +        if (p & PTR)
848 +        {
849 +            setbit(p_bits, nelem, cur);
850 +            if (!(p & NOPTR))
851 +            {
852 +                setbit(m_bits, nelem, cur);
853 +            }
854 +        }
855 +    }
856 +
857 +    //length (inline size field + bits)
858 +    dtdword(pdt, data_len);
859 +    //data
860 +    dtabytes(pdt, TYnptr, 0, data_len * PTRSIZE, (char*)data);
861 +
862 +    mem.free(data);
863 +}
864 +
865 +void PointerMap::toDtInvalid(dt_t **pdt) {
866 +    //null array
867 +    dtdword(pdt, 0);
868 +    dtdword(pdt, 0);
869 +}
870 +
871  void TypeInfoClassDeclaration::toDt(dt_t **pdt)
872  {
873      //printf("TypeInfoClassDeclaration::toDt() %s\n", tinfo->toChars());
874 -- 
875 1.7.1
876