New Solution for memory leak in tblgen

wuhui1973 wuhui1973 at 163.com
Tue Nov 25 17:17:49 PST 2014


Correct some statements. 


Hello David:


Have I answered your question?


The whole things are not just trees but DAG!


TreePattern corresponds to Pattern Def and PatFrag Def in the td file, at first those original TreePattern are saved by CodeGenDAGPatterns::PatternFragments.
While every TreePatternNode corresponds to the dag structure in Pattern Def and PatFrag. Many of these dag structures refer another PatFrag, as you can see in most of td files.
So there is an important operation during parsing Pattern Def and PatFrag Def -- PatFrag Def would be inlined into the PatFrag Def using it, and all PatFrag Defs would finally be inlined into the Pattern using them. 
After this step CodeGenDAGPatterns::PatternFragments only record the inlined form, so some of the original TreePatterns are invisible from CodeGenDAGPatterns::PatternFragments (but they can still be visited following the trees).


But we can't delete those original TreePatterns, see the comment for TreePredicateFn::PatFragRec (TreePredicateFn is a field of TreePatternNode)
         class TreePredicateFn {
             /// PatFragRec - This is the TreePattern for the PatFrag that we
             /// originally came from.
             TreePattern *PatFragRec;


So obvious, there exist cross edges among trees. And some trees are unreachable directly from CodeGenDAGPatterns::PatternFragments.








At 2014-11-18 06:20:45, "David Blaikie" <dblaikie at gmail.com> wrote:

Is it possible for you to give a small complete example of a handful of tree nodes showing the full ownership graph?

Some of the things I'm unclear on:

This thing is described as a tree - is it? Trees should be able to be represented with single ownership (unique_ptr), not even shared or cyclic. That's not to say a node can't have parent pointers/backedges, but so long as they just point up into things owned by someone else, that should be fine.

Who owns the roots of the tree - it looks like it's CodeGenDAGPatterns::PatternFragments, yes? (and indeed, /those/ TreePatterns are currently deleted correctly, by the sounds of things - which goes against the previous idea that these were all leaked except for some on the stack)

Is everything accessible from the root? Can we detect when they become disconnected easily and destroy them?

The backedges (which form cycles) are the "PatFragRec" pointers - do these need to be owning?

You mention/show that two TreePatternNodes can have PredicateFns with PatFragRecs that refer to some common TreePattern. Is it possible that these PredicateFns/PatFragRecs should be separately duplicated so they refer to their own TreePattern? Alternatively, perhaps it's guaranteed that these two TreePatternNodes will be within the same TreePattern - in which case the PatFragRec is just a parent (or grandparent) pointer and doesn't need to be owning.

I guess what I'm eventually saying is if all the TreePatterns/TreePatternNodes are kept alive via chains of CodeGenDAGPatterns::PatternFragments -> TreePattern -> TreePattern::Trees -> (TreePatternNode -> TreePatternNode::Children)*

(ie: we don't ever clone a TreePatternNode, then lose the original TreePattern that was its PatFragRec and still go looking for it via the cloned TreePatternNode)

If that is true, then we don't need PatFragRec to own - it's just a raw pointer. We would use unique_ptrs in CodeGenDAGPatterns::PatternFragments, TreePattern::Trees, and TreePatternNode::Children - and we'd be done... 




So, with that in mind, I've unique_ptr'd the first two (CodeGenDAGPatterns::PatternFragments and TreePattern::Trees), and the 3rd I haven't touched yet. There are other places that the elements from Trees get passed to, so you'll find that my patches (r222183, r221926) did leave a few 'release()' calls around that could be cleaner/cleaned up, too.

Perhaps, if you want to pursue this, you could try doing some more incremental patches, like the ones I've committed - I think you could tidy up the release() calls I introduced in r222183 by pushing unique_ptr through a few more interfaces.

The last step, of using unique_ptr in TreePatternNode::Children might be a bit more monolithic - but hopefully it can be broken into fairly obvious/beneficial steps.





On Tue, Nov 11, 2014 at 6:54 PM, wuhui1973 <wuhui1973 at 163.com> wrote:


At 2014-11-12 10:22:22, "David Blaikie" <dblaikie at gmail.com> wrote:

OK, so you're saying all the heap allocated TreePatterns and TreePatternNodes are currently leaked? And we create some on the stack (that obviously don't leak)?


 >> [huiwu] Yes, almost all. And quite a few TreePatterns are created on stack, which will not leak.

And from previous discussions we've seen that TreePatterns and TreePatternNodes form unbreakable(?) cycles. (are they unbreakable - could we use weak_ptr or raw pointer in there somewhere to break them safely? I'll have to go back & look at the prior threads or you could copy/paste the summary of the cycles you saw? - though the only reason this would be worth understanding is if we actually destroy things at some point other than the end - I don't know anything about tablegen or when nodes are created and destroyed, I'm not sure if they're destroyed at any time other than "we've finished doing all the work, cleanup now")


>> [huiwu] 
>> TreePatternNode  has field PredicateFns , which contains PatFragRec, the pointer of TreePattern


>> TreePattern has Trees which is std::vector<TreePatternNodePtr>
>> The cycles exist among PatFragRec & Trees. May like this:



>> TreePatternNode  <---------------------------------------------------------------+
>>     PredicateFns (std::vector<TreePredicateFn>)                                   |
>>         PatFragRec ------> TreePattern                                                      |
>>                                          ^   Trees (std::vector<TreePatternNodePtr>) +------------> Other TreePatternNode
>>                                          |
>> Other TreePatternNode   |
>>        PredicateFns             |
>>               PatFragRec -----+


>> In previous solution, every TreePatternNode & TreePattern hold the reference count of objects pointed by PatFragRec & Trees. If they find the reference count reaches 0, 
>> the associated object will be freed. 
>> At the end of the processing, I found around 700 TreePatternNodes & 100 TreePatterns (I forget the exact number) remains. Obviously, they form cycles which can't be
>> broken by reference count.
>> That is why we need FreeCycleRef, in which we can't explicit delete those objects, but just assign NULL to PatFragRec & Trees, their owner will do the rest for us.


>> But in current design, all heap allocated TreePatternNode & TreePattern will be recorded, and there is the mechanism (the operator delete) to remove the record if the object is 
>> freed manually somewhere in the source. Then at the end of the process (the dtor of CodeGenDAGPatterns), these records are all leaked objects. We just go through both 
>> containers to free them. 
>> So as a requirement, TreePatternNode's dtor is not allowed to delete pointers within PatFragRec & Children (std::vector<TreePatternNode*>), and 
>> TreePattern's dtor is not allowed to delete pointers within Trees. All these objects are assumed to be deleted by MemReclaimer::reclaim.



If that's all the case, then it seems obvious enough that we should just have a big list of all the heap allocated ones, that goes away when we're done.



On Tue, Nov 11, 2014 at 6:07 PM, wuhui1973 <wuhui1973 at 163.com> wrote:

Sorry David:


I make a mistake in yesterday explanation.


Here is some data I collect during building Tablegen, the data is from x86 target


llvm[3]: Building X86.td instruction information with tblgen
NumTPNAllocCur: 120754, NumTPNAllocAccu: 121469 <-- it indicates only 121469-120754=715 heap alloc TreePatternNodes get freed during processing (~0.5%)
NumTPAllocCur: 9488, NumTPAllocAccu: 9488              <-- in fact NO heap alloc TreePatterns get freed during processing
NumTPNAllocCur: 0, NumTPNDel: 121469
NumTPAllocCur: 0, NumTPDel: 9488
TPN dtorNum: 121469, TP dtorNum: 16821                  <-- the diff "NumTPAllocAccu"-"TPN dtorNum"=0 indicates that all TreePatternNodes are heap allocated
TPN ctorNum: 121469, TP ctorNum: 16821                  <-- the diff "TP ctorNum"-"NumTPDel"=7333 indicates that 7333 TreePatterns are placed into stack!


llvm[3]: Building X86.td DAG instruction selector implementation with tblgen
NumTPNAllocCur: 180382, NumTPNAllocAccu: 197494    <-- 17112 TreePatternNodes (~8%) get freed
NumTPAllocCur: 9488, NumTPAllocAccu: 9488            <--  NO heap alloc TreePatterns get freed during processing
NumTPNAllocCur: 0, NumTPNDel: 197494                   <-- "NumTPNDel" == "TPN ctorNum": all TreePatternNodes all heap allocated
NumTPAllocCur: 0, NumTPDel: 9488
TPN dtorNum: 197494, TP dtorNum: 16821                <-- the diff 16821-9488=7333 indicates that 7333 TreePatterns are placed into stack!
TPN ctorNum: 197494, TP ctorNum: 16821


So more than 43% TreePatterns are stack residue for both processing.


And other target also gives similar data, for instance AMDGPU


llvm[3]: Building AMDGPU.td instruction information with tblgen
NumTPNAllocCur: 28248, NumTPNAllocAccu: 28348    <-- 100 TreePatternNodes (~0.3%) get freed
NumTPAllocCur: 2422, NumTPAllocAccu: 2422              <-- NO heap alloc TreePatterns get freed during processing
NumTPNAllocCur: 0, NumTPNDel: 28348
NumTPAllocCur: 0, NumTPDel: 2422
TPN dtorNum: 28348, TP dtorNum: 3792
TPN ctorNum: 28348, TP ctorNum: 3792                       <-- 1370 TreePatterns are inside stack (>36%)
llvm[3]: Building AMDGPU.td DAG instruction selector implementation with tblgen
NumTPNAllocCur: 44076, NumTPNAllocAccu: 47030    <-- 2954 TreePatternNodes (~6%) get freed
NumTPAllocCur: 2422, NumTPAllocAccu: 2422              <-- NO heap alloc TreePatterns get freed during processing
NumTPNAllocCur: 0, NumTPNDel: 47030
NumTPAllocCur: 0, NumTPDel: 2422
TPN dtorNum: 47030, TP dtorNum: 3792
TPN ctorNum: 47030, TP ctorNum: 3792                      <-- 1370 TreePatterns are inside stack (>36%)


There are several functions in CodeGenDAGPatterns.cpp will allocate TreePatternNodes & TreePatterns from heap.
ParsePatternFragments (5 spots allocate TreePatternNodes)
ParsePatterns (2 spots allocate TreePatternNodes, 2 spots allocate TreePatterns)
CombineChildVariants (1 spot allocate TreePatternNodes)
clone (2 spots allocate TreePatternNodes)


So I don't think there is any special construction path.


Regards
Hui Wu

在 2014-11-12 06:33:37,"David Blaikie" <dblaikie at gmail.com> 写道:






On Mon, Nov 10, 2014 at 11:36 PM, wuhui1973 <wuhui1973 at 163.com> wrote:

Hope I can explain it clear enough :-)


> MemReclaimer has two containers to hold pointers of TreePatternNode & TreePattern which are allocated from heap.
> Only one global instance of MemReclaimer  is declared -- memReclaimer.
> operator new & operator delete are defined for TreePatternNode & TreePattern 
          new:    push the new allocated object (pointer) into containers in memReclaimer.
          delete: remove the pointer from the containers in memReclaimer.


          So we can guarantee that objects allocated from heap can always be recorded by memReclaimer. 
          And no double free may incur, as some places in the source delete some pointers manually (maybe we can remove these lines).
> So memReclaimer only keeps the objects that are leaked in current design.
> the author of CodeGenDAGPattern  create nodes, but destroy part of them (for TreePattern, the nodes destroyed manually account for more than 30%, 


Could you provide stack traces/explanation for these case where the TreePatterns are destroyed?
 
          for some target even over 50%; while for TreePatternNode  only around 1%

And for the TreePatternNodes?

If they're mostly just on special case construction paths, perhaps we could special case their removal from the list we could add to CodeGenDAGPattern - or, if they're close enough to construction time, we might be able to avoid adding them to such a list in the first place?
 
) and make the rest leaked deliberately.
> dtor of CodeGenDAGPattern now is the place to destroy all not freed objects
> move the functionality of MemReclaimer into CodeGenDAGPattern  is definitely feasible I think. 


Regards
Hui Wu



At 2014-11-11 14:20:25, "David Blaikie" <dblaikie at gmail.com> wrote:

Could you describe the high level design here?

It looks like there's a static pool (CodeGenDAGPatterns.cpp::memReclaimer) of instances that's used as some kind of last-chance cleanup? Instances of TreePatternNode mostly manage their own lifetime but then if any haven't been destroyed by the time the CodeGenDAGPatterns dtor runs, the remaining elements are destroyed. This is to handle cycles, I take it?

It still seems like a bit of a GC-esque workaround to handle this case when there might be something better... but I don't know much about tablegen, perhaps there isn't.

How often are nodes destroyed by themselves? Should we just give up owning these anywhere else & move all ownership into CodeGenDAGPatterns and clean them up in CodeGenDAGPatterns' dtor? (just have a vector of unique_ptrs (maybe even a list or deque of nodes directly owned, rather than via unique_ptr), only create nodes, never destroy them, then destroy them all at the end)


On Mon, Nov 10, 2014 at 9:58 PM, wuhui1973 <wuhui1973 at 163.com> wrote:

Hi Andrew, David and Hal:


I have made a new solution for this memory leak issue, which is much simpler than previous one. 


I have tested it, it works well!


Please have a look, and appreciate any comment.


Thanks & Regards


Hui Wu
























-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20141126/0f7002c8/attachment.html>


More information about the llvm-commits mailing list