Static Value-Flow Analysis
Loading...
Searching...
No Matches
MTASVFGBuilder.cpp
Go to the documentation of this file.
1//===- MTASVFGBuilder.cpp -- Thread-aware SVFG builder for FSAM ---------===//
2//
3// SVF: Static Value-Flow Analysis
4//
5// Copyright (C) <2013-> <Yulei Sui>
6//
7
8// This program is free software: you can redistribute it and/or modify
9// it under the terms of the GNU Affero General Public License as published by
10// the Free Software Foundation, either version 3 of the License, or
11// (at your option) any later version.
12
13// This program is distributed in the hope that it will be useful,
14// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16// GNU Affero General Public License for more details.
17
18// You should have received a copy of the GNU Affero General Public License
19// along with this program. If not, see <http://www.gnu.org/licenses/>.
20//
21//===----------------------------------------------------------------------===//
22
23/*
24 * MTASVFGBuilder.cpp
25 *
26 * Author: Jiawei Yang
27 */
28
29#include "MTA/MTASVFGBuilder.h"
30#include "Graphs/SlicedGraphs.h"
31#include "MSSA/MemSSA.h"
32#include "MSSA/MemPartition.h"
34#include "Util/SVFUtil.h"
35#include "Util/Options.h"
37
38using namespace SVF;
39using namespace SVFUtil;
40
41namespace
42{
48template <class BaseMRG>
49class ThreadMRG : public BaseMRG
50{
51public:
52 ThreadMRG(BVDataPTAImpl* pta, bool pointerOnly)
53 : BaseMRG(pta, pointerOnly)
54 {
55 }
56
57protected:
62 void refineCallsiteModRef(NodeBS& mod, NodeBS& ref,
63 const CallICFGNode* cs, const FunObjVar* callee) override
64 {
65 if (const ThreadCallGraph* tcg = SVFUtil::dyn_cast<ThreadCallGraph>(this->getCallGraph()))
66 if (tcg->hasThreadForkEdge(cs))
67 {
68 ref = this->getRefSideEffectOfFunction(callee);
69 // A fork is a call WITHOUT a return: the spawnee's writes must not
70 // be applied as a mod here (which would kill the spawner's own
71 // value flow past the fork). They reach later reads via the
72 // thread-aware interference edges instead.
73 mod.clear();
74 }
75 }
76
82 void propagateAdditionalModRef(CallGraphNode* callGraphNode,
83 MRGenerator::WorkList& worklist) override
84 {
85 ThreadCallGraph* tcg = SVFUtil::dyn_cast<ThreadCallGraph>(this->getCallGraph());
86 if (tcg == nullptr)
87 return;
89 tcg->getJoinSites(callGraphNode, joinSites);
90 if (joinSites.empty())
91 return;
92 const NodeBS& spawneeMod = this->getModSideEffectOfFunction(callGraphNode->getFunction());
93 for (const CallICFGNode* callSite : joinSites)
94 // A join exposes the joined thread's writes at the join point. Those
95 // writes are not necessarily reachable from pthread_join's handle
96 // argument, so use the MOD set as computed for the start routine.
97 if (this->addUnfilteredModSideEffectOfCallSite(callSite, spawneeMod))
98 worklist.push(this->getCallGraph()->getCallGraphNode(
99 callSite->getCaller())->getId());
100 }
101};
102} // anonymous namespace
103
104// Build a thread-aware MRGenerator wrapping the configured partition strategy, so
105// the MemSSA mod-ref generation carries the FSAM fork/join side effects.
106std::unique_ptr<MRGenerator> MTASVFGBuilder::createMRGenerator(BVDataPTAImpl* pta, bool ptrOnlyMSSA)
107{
108 switch (Options::MemPar())
109 {
111 return std::make_unique<ThreadMRG<DistinctMRG>>(pta, ptrOnlyMSSA);
113 return std::make_unique<ThreadMRG<IntraDisjointMRG>>(pta, ptrOnlyMSSA);
115 return std::make_unique<ThreadMRG<InterDisjointMRG>>(pta, ptrOnlyMSSA);
116 default:
117 assert(false && "unrecognised memory partition strategy");
118 return nullptr;
119 }
120}
121
127{
128 svfg->buildSVFG();
130 connectMHPEdges(svfg->getMSSA()->getPTA());
131}
132
134{
136 svfg->removeSVFGEdge(edge);
137 threadAwareEdges.clear();
138}
139
143{
144 assert(svfg != nullptr && "base SVFG must be built before replacing its overlay");
145 assert(mainMHP != nullptr && mainLockAnalysis != nullptr &&
146 "thread-aware overlay requires main ILA results");
147
149 mhp = mainMHP;
151 overlayScope = config.scope;
152 overlayCandidates = config.candidates;
153 recordThreadVFQueries = false;
155
156 storeNodes.clear();
157 loadNodes.clear();
158 threadVFQueryMap.clear();
159 predecessorCache.clear();
160 successorCache.clear();
161 spanHeadCache.clear();
162 spanTailCache.clear();
163
164 connectMHPEdges(svfg->getMSSA()->getPTA());
165 overlayScope = nullptr;
166 overlayCandidates = nullptr;
167}
168
176{
177 NodeBS cpts = formalOut->getPointsTo();
178 const NodeBS& dpts = actualOut->getPointsTo();
179 if (!cpts.intersects(dpts))
180 return;
181 cpts &= dpts;
182
183 SVFGNode* src = svfg->getSVFGNode(formalOut->getId());
184 SVFGNode* dst = svfg->getSVFGNode(actualOut->getId());
185 if (SVFGEdge* edge = svfg->hasInterVFGEdge(
186 src, dst, SVFGEdge::RetIndVF, callSiteId))
187 {
188 SVFUtil::cast<RetIndSVFGEdge>(edge)->addPointsTo(cpts);
189 }
190 else
191 {
193 retEdge->addPointsTo(cpts);
194 svfg->addSVFGEdge(retEdge);
195 }
196}
197
205{
206 ThreadCallGraph* tcg =
207 SVFUtil::dyn_cast<ThreadCallGraph>(svfg->getMSSA()->getPTA()->getCallGraph());
208 if (tcg == nullptr)
209 return;
210
211 MemSSA* mssa = svfg->getMSSA();
212 for (SVFG::const_iterator it = svfg->begin(), eit = svfg->end(); it != eit; ++it)
213 {
214 const FormalOUTSVFGNode* formalOut = SVFUtil::dyn_cast<FormalOUTSVFGNode>(it->second);
215 if (formalOut == nullptr)
216 continue;
217
219 tcg->getJoinSites(tcg->getCallGraphNode(formalOut->getFun()), joinSites);
220 for (const CallICFGNode* callSite : joinSites)
221 {
222 if (!mssa->hasCHI(callSite))
223 continue;
225 svfg->getActualOUTSVFGNodes(callSite);
227 {
229 SVFUtil::cast<ActualOUTSVFGNode>(
230 svfg->getSVFGNode(actualOutId));
233 svfg->getCallSiteID(callSite, formalOut->getFun()));
234 }
235 }
236 }
237}
238
243{
244 for (SVFG::const_iterator it = svfg->begin(), eit = svfg->end(); it != eit; ++it)
245 {
246 const SVFGNode* svfgNode = it->second;
247 const bool isLoad = SVFUtil::isa<LoadSVFGNode>(svfgNode);
248 if (!isLoad && !SVFUtil::isa<StoreSVFGNode>(svfgNode))
249 continue;
250 const StmtSVFGNode* node = SVFUtil::cast<StmtSVFGNode>(svfgNode);
251 if (node->getICFGNode() == nullptr)
252 continue;
253 if (!isInOverlayScope(node))
254 continue;
255 if (isLoad)
256 loadNodes.insert(node);
257 else
258 storeNodes.insert(node);
259 }
260}
261
263{
264 return overlayScope == nullptr || overlayScope->isKeptNode(node);
265}
266
271{
272 SVFGNode* srcNode = svfg->getSVFGNode(srcId);
273 SVFGNode* dstNode = svfg->getSVFGNode(dstId);
275 "thread-aware overlay edge escaped its construction scope");
276
277 // VFG_pre (sliced-only) mode: keep the edge for connectivity but omit its
278 // points-to label -- no slice consumer reads it.
280 {
281 if (SVFGEdge* edge = svfg->hasThreadVFGEdge(srcNode, dstNode, SVFGEdge::TheadMHPIndirectVF))
282 return edge;
284 if (svfg->addSVFGEdge(indirectEdge))
285 {
287 return indirectEdge;
288 }
289 return nullptr;
290 }
291
292 if (SVFGEdge* edge = svfg->hasThreadVFGEdge(srcNode, dstNode, SVFGEdge::TheadMHPIndirectVF))
293 {
294 assert(SVFUtil::isa<IndirectSVFGEdge>(edge) && "should be an indirect value-flow edge!");
295 return (SVFUtil::cast<IndirectSVFGEdge>(edge)->addPointsTo(pts.toNodeBS()) ? edge : nullptr);
296 }
297 else
298 {
300 indirectEdge->addPointsTo(pts.toNodeBS());
301 if (svfg->addSVFGEdge(indirectEdge))
302 {
304 return indirectEdge;
305 }
306 return nullptr;
307 }
308}
309
314 const StmtSVFGNode* node)
315{
316 const auto found = predecessorCache.find(node);
317 if (found != predecessorCache.end())
318 return found->second;
319
321 Set<const SVFGNode*> worklist;
322 Set<const SVFGNode*> visited;
323
324 for (SVFGEdge::SVFGEdgeSetTy::iterator iter = node->InEdgeBegin();
325 iter != node->InEdgeEnd(); ++iter)
326 {
327 SVFGEdge* edge = *iter;
328 if (edge->isIndirectVFGEdge() && !edge->isThreadMHPIndirectVFGEdge() &&
329 isInOverlayScope(edge->getSrcNode()))
330 worklist.insert(edge->getSrcNode());
331 }
332
333 while (!worklist.empty())
334 {
335 const SVFGNode* node = *worklist.begin();
336 worklist.erase(worklist.begin());
337 visited.insert(node);
338 if (SVFUtil::isa<StoreSVFGNode>(node))
339 predecessors.set(node->getId());
340 else
341 {
342 for (SVFGEdge::SVFGEdgeSetTy::iterator iter = node->InEdgeBegin(); iter != node->InEdgeEnd(); ++iter)
343 {
344 SVFGEdge* edge = *iter;
345 if (edge->isIndirectVFGEdge() &&
346 !edge->isThreadMHPIndirectVFGEdge() &&
347 isInOverlayScope(edge->getSrcNode()) &&
348 visited.find(edge->getSrcNode()) == visited.end())
349 worklist.insert(edge->getSrcNode());
350 }
351 }
352 }
354 return predecessors;
355}
356
361 const StmtSVFGNode* node)
362{
363 const auto found = successorCache.find(node);
364 if (found != successorCache.end())
365 return found->second;
366
367 SVFGNodeIDSet successors;
368 Set<const SVFGNode*> worklist;
369 Set<const SVFGNode*> visited;
370
371 for (SVFGEdge::SVFGEdgeSetTy::iterator iter = node->OutEdgeBegin();
372 iter != node->OutEdgeEnd(); ++iter)
373 {
374 SVFGEdge* edge = *iter;
375 if (edge->isIndirectVFGEdge() && !edge->isThreadMHPIndirectVFGEdge() &&
376 isInOverlayScope(edge->getDstNode()))
377 worklist.insert(edge->getDstNode());
378 }
379
380 while (!worklist.empty())
381 {
382 const SVFGNode* node = *worklist.begin();
383 worklist.erase(worklist.begin());
384 visited.insert(node);
385 if (SVFUtil::isa<StoreSVFGNode, LoadSVFGNode>(node))
386 successors.set(node->getId());
387 else
388 {
389 for (SVFGEdge::SVFGEdgeSetTy::iterator iter = node->OutEdgeBegin(); iter != node->OutEdgeEnd(); ++iter)
390 {
391 SVFGEdge* edge = *iter;
392 if (edge->isIndirectVFGEdge() &&
393 !edge->isThreadMHPIndirectVFGEdge() &&
394 isInOverlayScope(edge->getDstNode()) &&
395 visited.find(edge->getDstNode()) == visited.end())
396 worklist.insert(edge->getDstNode());
397 }
398 }
399 }
400 successorCache[node] = successors;
401 return successors;
402}
403
408{
409 const auto found = spanHeadCache.find(node);
410 if (found != spanHeadCache.end())
411 return found->second;
412
414 for (NodeID id : predecessors)
415 {
416 const StmtSVFGNode* prevNode = SVFUtil::dyn_cast<StmtSVFGNode>(svfg->getSVFGNode(id));
417 if (prevNode != nullptr && lockAnalysis->isInSameSpan(
418 prevNode->getICFGNode(), node->getICFGNode()))
419 {
420 spanHeadCache[node] = false;
421 return false;
422 }
423 }
424 spanHeadCache[node] = true;
425 return true;
426}
427
432{
433 assert(SVFUtil::isa<StoreSVFGNode>(node) &&
434 "tail test only for store nodes");
435
436 const auto found = spanTailCache.find(node);
437 if (found != spanTailCache.end())
438 return found->second;
439
440 const SVFGNodeIDSet successors = getSuccessorNodes(node);
441 for (NodeID id : successors)
442 {
443 const SVFGNode* successor = svfg->getSVFGNode(id);
444 if (SVFUtil::isa<LoadSVFGNode>(successor))
445 continue;
447 SVFUtil::dyn_cast<StmtSVFGNode>(successor);
449 successorStatement->getICFGNode(), node->getICFGNode()))
450 {
451 spanTailCache[node] = false;
452 return false;
453 }
454 }
455 spanTailCache[node] = true;
456 return true;
457}
458
467 bool commonLock)
468{
469 // Per-edge Query set. The endpoints are NOT duplicated into the value --
470 // they are recoverable from the map key -- so the value holds only the
471 // additional in-span witnesses below (empty for the common lock-free case).
474
475 if (!commonLock)
476 return;
477
478 // Succ_spl(s) = { x in s's span | x is a store, s --o--> x }.
480 {
481 const SVFGNode* successor = svfg->getSVFGNode(id);
482 if (!SVFUtil::isa<StoreSVFGNode>(successor))
483 continue;
485 SVFUtil::cast<StmtSVFGNode>(successor);
487 successorStatement->getICFGNode(), source->getICFGNode()))
488 query.insert(successorStatement->getICFGNode());
489 }
490
491 // Pred_spl'(s') = { x in s' span | x --o--> s' }.
493 {
494 const StmtSVFGNode* prevNode =
495 SVFUtil::dyn_cast<StmtSVFGNode>(svfg->getSVFGNode(id));
496 if (prevNode != nullptr && lockAnalysis->isInSameSpan(
497 prevNode->getICFGNode(), destination->getICFGNode()))
498 query.insert(prevNode->getICFGNode());
499 }
500}
501
508 const StmtSVFGNode* store, const StmtSVFGNode* load,
509 PointerAnalysis* pta)
510{
511 const ICFGNode* storeNode = store->getICFGNode();
512 const ICFGNode* loadNode = load->getICFGNode();
513
515 if (!mayParallel)
516 return;
517
518 // No alias() re-check: the bucketed candidate generator only pairs accesses
519 // whose raw points-to sets share an object, so the intersection below is
520 // non-empty by construction and alias() could never answer NoAlias here.
521 // The label is only needed when the edge will be solved (main FSMPTA); in
522 // VFG_pre (sliced-only) mode skip the intersection -- it is never read.
525 {
526 pts = pta->getPts(store->getDstNodeID());
527 pts &= pta->getPts(load->getSrcNodeID());
528 }
529
530 // [THREAD-VF] source extraction runs for every candidate pair (both the
531 // pairs that survive and the ones the lock test prunes), so the sliced ILA
532 // can re-derive whether the edge holds.
533 const bool commonLock =
536 recordThreadVFSource(store, load, commonLock);
537
538 if (commonLock)
539 {
540 if (isTailOfSpan(store) && isHeadOfSpan(load))
541 addTDEdge(store->getId(), load->getId(), pts);
542 }
543 else
544 {
545 addTDEdge(store->getId(), load->getId(), pts);
546 }
547}
548
555 PointerAnalysis* pta)
556{
557 const ICFGNode* firstNode = firstStore->getICFGNode();
558 const ICFGNode* secondNode = secondStore->getICFGNode();
559
561 if (!mayParallel)
562 return;
563
564 // No alias() re-check: see handleStoreLoad -- bucketing already guarantees a
565 // shared raw object. Skip the label intersection in VFG_pre (sliced-only) mode.
568 {
569 pts = pta->getPts(firstStore->getDstNodeID());
570 pts &= pta->getPts(secondStore->getDstNodeID());
571 }
572
573 // Both directions are candidate thread-aware edges; extract sources for each.
574 const bool commonLock =
577 {
580 }
581
582 if (commonLock)
583 {
585 addTDEdge(firstStore->getId(), secondStore->getId(), pts);
587 addTDEdge(secondStore->getId(), firstStore->getId(), pts);
588 }
589 else
590 {
591 addTDEdge(firstStore->getId(), secondStore->getId(), pts);
592 addTDEdge(secondStore->getId(), firstStore->getId(), pts);
593 }
594}
595
601{
602 if (overlayCandidates != nullptr)
603 {
604 // The pre-analysis candidate set is a conservative directed universe.
605 // Traverse it directly so main overlay construction is proportional to
606 // retained candidates instead of re-enumerating every alias pair.
609 {
610 const StmtSVFGNode* src = SVFUtil::dyn_cast<StmtSVFGNode>(
611 svfg->getSVFGNode(
612 candidate.sourceNodeId));
613 const StmtSVFGNode* dst = SVFUtil::dyn_cast<StmtSVFGNode>(
614 svfg->getSVFGNode(
615 candidate.destinationNodeId));
616 assert(src != nullptr && dst != nullptr &&
617 "thread-aware candidates must reference statement nodes");
618 assert(SVFUtil::isa<StoreSVFGNode>(src) &&
619 "thread-aware candidate source must be a store");
621 "thread-aware candidate escaped its selected scope");
622
623 if (SVFUtil::isa<LoadSVFGNode>(dst))
624 handleStoreLoad(src, dst, pta);
625 else if (SVFUtil::isa<StoreSVFGNode>(dst))
626 {
628 candidate.sourceNodeId < candidate.destinationNodeId
629 ? candidate
630 : ThreadVFCandidate(candidate.destinationNodeId,
631 candidate.sourceNodeId);
632 if (processedStorePairs.insert(canonicalPair).second)
633 // Main ILA re-decides the unordered pair once; the handler
634 // emits whichever directed edges pass the lock-span rules.
635 handleStoreStore(src, dst, pta);
636 }
637 }
638 return;
639 }
640
642
643 // Inverted access index (object -> access-node bitset): unioning the
644 // bitsets per store visits each may-alias pair exactly once, no dedup tables.
647 for (const StmtSVFGNode* store : storeNodes)
648 for (NodeID objectId : pta->getPts(store->getDstNodeID()))
649 objectToStoreIds[objectId].set(store->getId());
650 for (const StmtSVFGNode* load : loadNodes)
651 for (NodeID objectId : pta->getPts(load->getSrcNodeID()))
652 objectToLoadIds[objectId].set(load->getId());
653
654 for (const StmtSVFGNode* store : storeNodes)
655 {
658 for (NodeID objectId : pta->getPts(store->getDstNodeID()))
659 {
661 const auto loads = objectToLoadIds.find(objectId);
662 if (loads != objectToLoadIds.end())
663 candidateLoads |= loads->second;
664 }
665
667 {
668 const StmtSVFGNode* load =
669 SVFUtil::cast<StmtSVFGNode>(svfg->getSVFGNode(loadId));
670 handleStoreLoad(store, load, pta);
671 }
672
673 // Visit each unordered store pair once: only partners with a larger id.
674 const NodeID storeId = store->getId();
676 {
677 if (otherId <= storeId)
678 continue;
679 const StmtSVFGNode* other =
680 SVFUtil::cast<StmtSVFGNode>(svfg->getSVFGNode(otherId));
681 handleStoreStore(store, other, pta);
682 }
683 }
684}
if(prebuffer< 0)
Definition cJSON.cpp:1269
const FunObjVar * getFunction() const
Get function of this call node.
Definition CallGraph.h:191
const CallGraphNode * getCallGraphNode(const std::string &name) const
Get call graph node.
IDToNodeMapTy::const_iterator const_iterator
iterator OutEdgeEnd()
iterator OutEdgeBegin()
iterators
iterator InEdgeBegin()
iterator InEdgeEnd()
bool isProtectedByCommonLock(const ICFGNode *i1, const ICFGNode *i2)
bool isInSameSpan(const ICFGNode *I1, const ICFGNode *I2)
Definition MHP.h:52
virtual bool mayHappenInParallel(const ICFGNode *i1, const ICFGNode *i2)
Interface to query whether two instructions may happen-in-parallel.
Definition MHP.cpp:854
bool recordThreadVFQueries
false = skip [THREAD-VF] recording
void connectMHPEdges(PointerAnalysis *pta)
Connect inter-thread (interference) value-flow edges for MHP pairs.
Map< const StmtSVFGNode *, SVFGNodeIDSet > predecessorCache
SVFGEdgeSet threadAwareEdges
std::unique_ptr< MRGenerator > createMRGenerator(BVDataPTAImpl *pta, bool ptrOnlyMSSA) override
void handleStoreStore(const StmtSVFGNode *firstStore, const StmtSVFGNode *secondStore, PointerAnalysis *pta)
void clearThreadAwareOverlay()
Remove all currently attached thread-aware interference edges.
SVFGEdge * addTDEdge(NodeID srcId, NodeID dstId, const PointsTo &pts)
Add a thread-MHP indirect value-flow edge srcId -> dstId carrying pts.
const ThreadVFCandidateList * overlayCandidates
Map< const StmtSVFGNode *, bool > spanTailCache
Map< const StmtSVFGNode *, SVFGNodeIDSet > successorCache
void replaceThreadAwareOverlay(MHP *mhp, LockAnalysis *lockAnalysis, const ThreadVFBuildConfig &config)
bool isInOverlayScope(const SVFGNode *node) const
void addJoinRetEdge(const FormalOUTSVFGNode *formalOut, const ActualOUTSVFGNode *actualOut, CallSiteID callSiteId)
void recordThreadVFSource(const StmtSVFGNode *source, const StmtSVFGNode *destination, bool commonLock)
LockAnalysis * lockAnalysis
bool isHeadOfSpan(const StmtSVFGNode *node)
bool labelInterferenceEdges
false = VFG_pre (sliced-only): omit edge points-to labels
ThreadVFQueryMap threadVFQueryMap
[THREAD-VF] per-edge query map (see getThreadVFQueryMap).
const SlicedSVFGView * overlayScope
Active overlay configuration; defaults suit VFG_pre.
bool isTailOfSpan(const StmtSVFGNode *node)
SVFGNodeIDSet getSuccessorNodes(const StmtSVFGNode *node)
void handleStoreLoad(const StmtSVFGNode *store, const StmtSVFGNode *load, PointerAnalysis *pta)
void buildSVFG() override
Rewrite the SVFG build hook: build the stock SVFG, then add MHP edges.
Map< const StmtSVFGNode *, bool > spanHeadCache
SVFGNodeIDSet getPredecessorNodes(const StmtSVFGNode *node)
Lock-span head/tail tests (non-interference lock-pair pruning).
bool hasCHI(const PAGEdge *inst) const
Definition MemSSA.h:338
@ InterDisjoint
Definition MemSSA.h:117
@ IntraDisjoint
Definition MemSSA.h:116
static const OptionMap< u32_t > MemPar
Definition Options.h:141
virtual const PointsTo & getPts(NodeID ptr)=0
Get points-to targets of a pointer. It needs to be implemented in child class.
std::unique_ptr< SVFG > svfg
NodeID getId() const
Get ID.
Definition SVFValue.h:158
bool isKeptNode(const SVFGNode *n) const
Whether the node is retained (see the class comment for the rule).
bool intersects(const SparseBitVector< ElementSize > *RHS) const
void set(unsigned Idx)
NodeID getSrcNodeID() const
Definition VFGNode.h:152
NodeID getDstNodeID() const
Definition VFGNode.h:157
OrderedSet< const CallICFGNode *, CallSiteIdCmp > InstSet
void getJoinSites(const CallGraphNode *routine, InstSet &csSet)
@ TheadMHPIndirectVF
Definition VFGEdge.h:59
virtual const ICFGNode * getICFGNode() const
Return corresponding ICFG node.
Definition VFGNode.h:67
for isBitcode
Definition BasicTypes.h:70
unsigned CallSiteID
Definition GeneralType.h:78
u32_t NodeID
Definition GeneralType.h:76
llvm::IRBuilder IRBuilder
Definition BasicTypes.h:76