Static Value-Flow Analysis
Loading...
Searching...
No Matches
Public Member Functions | Protected Member Functions | Static Protected Member Functions | Protected Attributes | List of all members
SVF::MTASlicerBase Class Reference

#include <MTASlicer.h>

Inheritance diagram for SVF::MTASlicerBase:
SVF::MultiStageSlicer SVF::SingleSlicer

Public Member Functions

 MTASlicerBase (SVFIR *svfir, AndersenBase *pta, MHP *mhp, LockAnalysis *lockAnalysis, SVFG *svfg=nullptr)
 

Protected Member Functions

OrderedSet< const VFGNode * > computeDataDependenceSVFGNodes (const OrderedSet< const SVFStmt * > &seeds, SVFG *svfg)
 
OrderedSet< const ICFGNode * > svfgNodesToICFGNodes (const OrderedSet< const VFGNode * > &nodes, const OrderedSet< const SVFStmt * > &seeds)
 Project the retained VFG nodes (plus the seeds) onto their ICFG nodes.
 
OrderedSet< const CallICFGNode * > getDependentThreadCreate (const ICFGNode *node)
 
OrderedSet< const TCTNode * > getTCTNodeSetFromNode (const ICFGNode *node)
 
OrderedSet< const ICFGNode * > getLockSet (const ICFGNode *node)
 
OrderedSet< const CallICFGNode * > collectPthreadStatements (const OrderedSet< const ICFGNode * > &sourceNodes)
 
OrderedSet< const CallICFGNode * > collectMutexStatements (const OrderedSet< const ICFGNode * > &sourceNodes)
 
std::pair< OrderedSet< const CallICFGNode * >, OrderedSet< const CallICFGNode * > > collectCommonThreadStatements (const OrderedSet< const ICFGNode * > &sourceNodes)
 
void addSynchronizationDependencies (const OrderedSet< const CallICFGNode * > &pthreadCallNodes, const OrderedSet< const CallICFGNode * > &mutexCallNodes, OrderedSet< const ICFGNode * > &retainedNodes)
 
OrderedSet< const ICFGNode * > expandCallDependence (const OrderedSet< const ICFGNode * > &nodes)
 

Static Protected Member Functions

static void enqueueSVFGNode (const SVFGNode *node, OrderedSet< const SVFGNode * > &visited, std::deque< const SVFGNode * > &worklist)
 

Protected Attributes

SVFIRsvfir
 
AndersenBasepta
 
MHPmhp
 
LockAnalysislockAnalysis
 
CallGraphcallGraph
 
SVFGsvfg
 thread-aware VFG_pre (PTA/Single slicers; null for MTA)
 

Detailed Description

MTASlicerBase - Base class for program slicing.

Holds the shared helper methods and data members used by both concrete slicers (the ILA and FSPTA stages of MultiStageSlicer).

Definition at line 121 of file MTASlicer.h.

Constructor & Destructor Documentation

◆ MTASlicerBase()

SVF::MTASlicerBase::MTASlicerBase ( SVFIR svfir,
AndersenBase pta,
MHP mhp,
LockAnalysis lockAnalysis,
SVFG svfg = nullptr 
)

Definition at line 300 of file MTASlicer.cpp.

303{
305}
AndersenBase * pta
Definition MTASlicer.h:129
LockAnalysis * lockAnalysis
Definition MTASlicer.h:131
CallGraph * callGraph
Definition MTASlicer.h:132
SVFG * svfg
thread-aware VFG_pre (PTA/Single slicers; null for MTA)
Definition MTASlicer.h:133
CallGraph * getCallGraph() const
Return call graph.

Member Function Documentation

◆ addSynchronizationDependencies()

void SVF::MTASlicerBase::addSynchronizationDependencies ( const OrderedSet< const CallICFGNode * > &  pthreadCallNodes,
const OrderedSet< const CallICFGNode * > &  mutexCallNodes,
OrderedSet< const ICFGNode * > &  retainedNodes 
)
protected

Add synchronization primitives and the control-flow anchors required by the sliced MHP/lock analyses.

Definition at line 625 of file MTASlicer.cpp.

629{
631 {
632 sliceResult.insert(callNode);
633 if (callNode->getRetICFGNode() != nullptr)
634 sliceResult.insert(callNode->getRetICFGNode());
635 }
637 {
638 sliceResult.insert(callNode);
639 if (callNode->getRetICFGNode() != nullptr)
640 sliceResult.insert(callNode->getRetICFGNode());
641 }
642
644 {
645 const FunObjVar* fun = mutexCallNode->getFun();
646 if (fun == nullptr)
647 continue;
648 if (const SVFBasicBlock* entry = fun->getEntryBlock())
649 {
650 sliceResult.insert(entry->front());
651 sliceResult.insert(entry->back());
652 }
653 if (const SVFBasicBlock* exit = fun->getExitBB())
654 sliceResult.insert(exit->back());
655 }
656
659 {
660 if (!threadAPI->isTDJoin(callNode) || callNode->getBB() == nullptr)
661 continue;
662 std::vector<const SVFBasicBlock*> exitBlocks;
663 callNode->getFun()->getExitBlocksOfLoop(callNode->getBB(), exitBlocks);
664 for (const SVFBasicBlock* exitBlock : exitBlocks)
665 if (!exitBlock->getICFGNodeList().empty())
666 sliceResult.insert(exitBlock->front());
667 }
668}
if(prebuffer< 0)
Definition cJSON.cpp:1269
const SVFBasicBlock * getEntryBlock() const
const SVFBasicBlock * getExitBB() const
ThreadCallGraph * getThreadCallGraph() const
Get ThreadCallGraph.
Definition MHP.h:104
ThreadAPI * getThreadAPI() const
Thread API.
llvm::IRBuilder IRBuilder
Definition BasicTypes.h:76

◆ collectCommonThreadStatements()

std::pair< OrderedSet< const CallICFGNode * >, OrderedSet< const CallICFGNode * > > SVF::MTASlicerBase::collectCommonThreadStatements ( const OrderedSet< const ICFGNode * > &  sourceNodes)
protected

Collect common pthread and mutex statements (shared by PTA and MTA slicing).

Parameters
sourceNodesComplete ILA source set ([INIT] union [THREAD-VF])
Returns
Pair of (pthreadCallNodes, mutexCallNodes)

Definition at line 595 of file MTASlicer.cpp.

597{
598 // Step 1: Collect pthread-related statements, i.e., pthread_create and pthread_join
601
602 // Step 2: Collect mutex-related statements
605
606 return std::make_pair(pthreadCallNodes, mutexCallNodes);
607}
OrderedSet< const CallICFGNode * > collectMutexStatements(const OrderedSet< const ICFGNode * > &sourceNodes)
OrderedSet< const CallICFGNode * > collectPthreadStatements(const OrderedSet< const ICFGNode * > &sourceNodes)

◆ collectMutexStatements()

OrderedSet< const CallICFGNode * > SVF::MTASlicerBase::collectMutexStatements ( const OrderedSet< const ICFGNode * > &  sourceNodes)
protected

Definition at line 534 of file MTASlicer.cpp.

536{
538
541
542 // Map mutex_lock nodes to their corresponding mutex_unlock nodes
544
545 // First pass: collect all mutex_lock nodes from lock sets
546 for (const ICFGNode* sourceNode : sourceNodes)
547 {
549 for (const ICFGNode* lockNode : lockSet)
550 {
552 SVFUtil::dyn_cast<CallICFGNode>(lockNode);
553 if (lockCallNode != nullptr &&
554 threadAPI->isTDAcquire(lockCallNode))
555 {
558 }
559 }
560 }
561
562 // Second pass: find corresponding mutex_unlock nodes
563 ICFG* icfg = svfir->getICFG();
564 for (ICFG::iterator it = icfg->begin(), eit = icfg->end();
565 it != eit; ++it)
566 {
567 const ICFGNode* node = it->second;
568 const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node);
569 if (callNode != nullptr && threadAPI->isTDRelease(callNode))
570 {
571 const SVFVar* unlockVar = threadAPI->getLockVal(callNode);
572 if (unlockVar != nullptr)
573 {
575 {
576 if (lockCallNode != nullptr)
577 {
578 const SVFVar* lockVar = threadAPI->getLockVal(lockCallNode);
579 if (lockVar != nullptr &&
580 pta->alias(unlockVar->getId(), lockVar->getId()))
581 {
582 mutexCallNodes.insert(callNode);
583 }
584 }
585 }
586 }
587 }
588 }
589
590 return mutexCallNodes;
591}
AliasResult alias(const SVFVar *V1, const SVFVar *V2) override
Interface expose to users of our pointer analysis, given Value infos.
iterator begin()
Iterators.
ICFGNodeIDToNodeMapTy::iterator iterator
Definition ICFG.h:58
OrderedSet< const ICFGNode * > getLockSet(const ICFGNode *node)
ICFG * getICFG() const
Definition SVFIR.h:231

◆ collectPthreadStatements()

OrderedSet< const CallICFGNode * > SVF::MTASlicerBase::collectPthreadStatements ( const OrderedSet< const ICFGNode * > &  sourceNodes)
protected

Definition at line 481 of file MTASlicer.cpp.

483{
485
488
489 // Map pthread_create nodes to their corresponding pthread_join nodes
491
492 // First pass: collect all pthread_create nodes
493 for (const ICFGNode* sourceNode : sourceNodes)
494 {
497 for (const CallICFGNode* forkCallNode : forkSites)
498 {
501 }
502 }
503
504 // Second pass: find corresponding pthread_join nodes
505 ICFG* icfg = svfir->getICFG();
506 for (ICFG::iterator it = icfg->begin(), eit = icfg->end();
507 it != eit; ++it)
508 {
509 const ICFGNode* node = it->second;
510 const CallICFGNode* callNode = SVFUtil::dyn_cast<CallICFGNode>(node);
511 if (callNode != nullptr && threadAPI->isTDJoin(callNode))
512 {
513 const SVFVar* joinThread = threadAPI->getJoinedThread(callNode);
514 if (joinThread != nullptr)
515 {
517 {
518 const SVFVar* forkedThread = threadAPI->getForkedThread(createCallNode);
519 if (forkedThread != nullptr &&
520 threadAPI->isAliasedForkJoin(
522 {
524 }
525 }
526 }
527 }
528 }
529
530 return pthreadCallNodes;
531}
OrderedSet< const CallICFGNode * > getDependentThreadCreate(const ICFGNode *node)

◆ computeDataDependenceSVFGNodes()

OrderedSet< const SVFGNode * > SVF::MTASlicerBase::computeDataDependenceSVFGNodes ( const OrderedSet< const SVFStmt * > &  seeds,
SVFG svfg 
)
protected

The SVFG-node granularity of the data-dependence slice above: the set of VFG nodes reachable backward from the seeds. ThreadVF(VFG'_pre) is exactly the thread-aware edges whose both endpoints lie in this set, so ILA slicing uses it to restrict the [THREAD-VF] sources to surviving edges.

Definition at line 405 of file MTASlicer.cpp.

407{
408
409 assert(svfg != nullptr && "data-dependence slice requires the thread-aware VFG_pre");
410
412 std::deque<const SVFGNode*> worklist;
413
414 // Seed from the value-flow nodes of the given (e.g. race target) statements.
415 // VFG_pre is a pointer-only SVFG, so a load/store of a NON-pointer value (the
416 // usual case -- a race on an int/float field) has no statement node. For those
417 // we must still preserve the points-to of the dereferenced address pointer, or
418 // the sliced flow-sensitive solve sees an empty slice for it, computes empty
419 // points-to, and drops the race (a soundness bug). So additionally seed from the
420 // definition of each load/store's address pointer (always a pointer, hence
421 // always in the pointer-only SVFG); its backward closure keeps the pointer's
422 // def chain regardless of the value type.
423 for (const SVFStmt* stmt : seeds)
424 {
425 if (svfg->hasStmtVFGNode(stmt))
426 enqueueSVFGNode(svfg->getStmtVFGNode(stmt), visited, worklist);
427
428 NodeID addrPtr = 0;
429 if (const LoadStmt* load = SVFUtil::dyn_cast<LoadStmt>(stmt))
430 addrPtr = load->getRHSVarID();
431 else if (const StoreStmt* store = SVFUtil::dyn_cast<StoreStmt>(stmt))
432 addrPtr = store->getLHSVarID();
433 if (addrPtr != 0)
434 {
435 // getDefSVFGNode takes a ValVar (the address pointer is a top-level
436 // value variable).
437 const ValVar* ptrNode = SVFUtil::dyn_cast<ValVar>(svfir->getGNode(addrPtr));
438 if (ptrNode != nullptr && svfg->hasDefSVFGNode(ptrNode))
439 enqueueSVFGNode(svfg->getDefSVFGNode(ptrNode), visited, worklist);
440 }
441 }
442
443 // Backward over every value-flow edge.
444 while (!worklist.empty())
445 {
446 const SVFGNode* node = worklist.front();
447 worklist.pop_front();
448 for (const VFGEdge* edge : node->getInEdges())
449 enqueueSVFGNode(edge->getSrcNode(), visited, worklist);
450 }
451
452 return visited;
453}
NodeType * getGNode(NodeID id) const
Get a node.
static void enqueueSVFGNode(const SVFGNode *node, OrderedSet< const SVFGNode * > &visited, std::deque< const SVFGNode * > &worklist)
bool hasDefSVFGNode(const ValVar *valVar) const
Given a valVar, return whether it has definition site.
Definition SVFG.h:177
const SVFGNode * getDefSVFGNode(const ValVar *valVar) const
Given a valVar, return its definition site.
Definition SVFG.h:171
bool hasStmtVFGNode(const SVFStmt *svfStmt) const
Existence checks for VFGNodes.
Definition VFG.h:217
StmtVFGNode * getStmtVFGNode(const SVFStmt *svfStmt) const
Get an VFGNode.
Definition VFG.h:261
u32_t NodeID
Definition GeneralType.h:76

◆ enqueueSVFGNode()

void SVF::MTASlicerBase::enqueueSVFGNode ( const SVFGNode node,
OrderedSet< const SVFGNode * > &  visited,
std::deque< const SVFGNode * > &  worklist 
)
staticprotected

Definition at line 455 of file MTASlicer.cpp.

458{
459 if (node != nullptr && visited.insert(node).second)
460 worklist.push_back(node);
461}

◆ expandCallDependence()

OrderedSet< const ICFGNode * > SVF::MTASlicerBase::expandCallDependence ( const OrderedSet< const ICFGNode * > &  nodes)
protected

Call-dependence expansion (used by MultiStageSlicer): take the kept functions of the given nodes, close upward over the call graph (every transitive caller), then add each kept function's entry/exit nodes and the call/ret nodes of every call site targeting it.

Parameters
nodesCurrent set of ICFG nodes
Returns
The input nodes plus the call/ret and entry/exit nodes above

Definition at line 671 of file MTASlicer.cpp.

673{
674
675 // Determine keptFunctions from the given nodes
677 for (const ICFGNode* node : nodes)
678 {
679 if (node != nullptr && node->getFun() != nullptr)
680 {
681 keptFunctions.insert(node->getFun());
682 }
683 }
684
685 // Build ancestor closure (upward traversal in call graph)
686 std::queue<const FunObjVar*> functionWorklist;
687 for (const FunObjVar* fun : keptFunctions)
688 functionWorklist.push(fun);
689
691 for (auto it = callGraph->begin(), eit = callGraph->end();
692 it != eit; ++it)
693 {
694 const CallGraphNode* node = it->second;
695 if (node != nullptr && node->getFunction() != nullptr)
696 functionToNode[node->getFunction()] = node;
697 }
698
700 while (!functionWorklist.empty())
701 {
702 const FunObjVar* target = functionWorklist.front();
703 functionWorklist.pop();
704 const auto nodeIt = functionToNode.find(target);
705 if (nodeIt == functionToNode.end())
706 continue;
707
708 const CallGraphNode* node = nodeIt->second;
709 for (const CallGraphEdge* inEdge : node->getInEdges())
710 {
711 if (inEdge == nullptr)
712 continue;
713 const CallGraphNode* callerNode = inEdge->getSrcNode();
714 if (callerNode != nullptr && callerNode->getFunction() != nullptr)
715 {
718 {
719 keptFunctions.insert(callerFun);
722 }
723 }
724 }
725 }
726
727 // For each keptFunction, add call/ret nodes and entry/exit nodes
728 ICFG* icfg = svfir->getICFG();
730 for (const FunObjVar* fun : keptFunctions)
731 {
732 if (fun == nullptr)
733 continue;
734
735 // Add function entry/exit nodes
736 if (fun->hasBasicBlock())
737 {
738 if (FunEntryICFGNode* entry = icfg->getFunEntryICFGNode(fun))
739 expandedNodes.insert(entry);
740 if (FunExitICFGNode* exit = icfg->getFunExitICFGNode(fun))
741 expandedNodes.insert(exit);
742 }
743
744 // Find all call/ret nodes that call this function
745 const auto funNodeIt = functionToNode.find(fun);
746 if (funNodeIt != functionToNode.end())
747 {
748 const CallGraphNode* calleeNode = funNodeIt->second;
749
750 // Traverse all edges that call this function
751 for (const CallGraphEdge* inEdge : calleeNode->getInEdges())
752 {
753 if (inEdge == nullptr)
754 continue;
755
756 const CallGraphEdge::CallInstSet& directCalls =
757 inEdge->getDirectCalls();
758 const CallGraphEdge::CallInstSet& indirectCalls =
759 inEdge->getIndirectCalls();
760
761 for (const CallICFGNode* callNode : directCalls)
762 {
763 if (callNode != nullptr)
764 {
765 expandedNodes.insert(callNode);
766 const RetICFGNode* retNode = callNode->getRetICFGNode();
767 if (retNode != nullptr)
768 expandedNodes.insert(retNode);
769 }
770 }
771
772 for (const CallICFGNode* callNode : indirectCalls)
773 {
774 if (callNode != nullptr)
775 {
776 expandedNodes.insert(callNode);
777 const RetICFGNode* retNode = callNode->getRetICFGNode();
778 if (retNode != nullptr)
779 expandedNodes.insert(retNode);
780 }
781 }
782 }
783 }
784 }
785
786 return expandedNodes;
787}
Set< const CallICFGNode * > CallInstSet
Definition CallGraph.h:55
const FunObjVar * getFunction() const
Get function of this call node.
Definition CallGraph.h:191
virtual const FunObjVar * getFunction() const
Get containing function, or null for globals/constants.
const SVFBasicBlock * front() const
FunExitICFGNode * getFunExitICFGNode(const FunObjVar *fun)
Add a function exit node.
Definition ICFG.cpp:250
FunEntryICFGNode * getFunEntryICFGNode(const FunObjVar *fun)
Add a function entry node.
Definition ICFG.cpp:243
iter_range< typename GenericGraphTraits< GraphType >::nodes_iterator > nodes(const GraphType &G)

◆ getDependentThreadCreate()

OrderedSet< const CallICFGNode * > SVF::MTASlicerBase::getDependentThreadCreate ( const ICFGNode node)
protected

Definition at line 375 of file MTASlicer.cpp.

377{
381
382 TCT* tct = mhp->getTCT();
383 for (const TCTNode* tctNode : tctNodeSet)
384 {
386 dependentThreads.set(tctNode->getId());
387 for (NodeID tid : dependentThreads)
388 {
389 const ICFGNode* forkSite =
390 tct->getTCTNode(tid)->getCxtThread().getThread();
391 const CallICFGNode* forkCall =
392 SVFUtil::dyn_cast<CallICFGNode>(forkSite);
393 if (forkCall != nullptr && threadAPI->isTDFork(forkCall))
394 forkSites.insert(forkCall);
395 }
396 }
397
398 return forkSites;
399}
const ICFGNode * getThread() const
Return forksite.
Definition CxtStmt.h:209
TCT * getTCT() const
Get Thread Creation Tree.
Definition MHP.h:110
OrderedSet< const TCTNode * > getTCTNodeSetFromNode(const ICFGNode *node)
void set(unsigned Idx)
const CxtThread & getCxtThread() const
Get thread creation context, <fork site, call string context>
Definition TCT.h:103
const NodeBS getAncestorThreads(NodeID tid) const
Get all ancestor threads.
Definition TCT.h:336
TCTNode * getTCTNode(NodeID id) const
Get TCT node.
Definition TCT.h:209

◆ getLockSet()

OrderedSet< const ICFGNode * > SVF::MTASlicerBase::getLockSet ( const ICFGNode node)
protected

Definition at line 308 of file MTASlicer.cpp.

309{
311
312 // Synchronization dependence is based on may-lock spans. Retain both the
313 // unconditional locks used to prove mutual exclusion and conditional locks
314 // whose spans can affect the sliced lock analysis' classification.
315 if (lockAnalysis->hasIntraLockSet(node))
316 {
318 for (const ICFGNode* lockSite : intraLocks)
319 {
320 allLockSites.insert(lockSite);
321 }
322 }
324 {
327 for (const ICFGNode* lockSite : conditionalLocks)
328 {
329 allLockSites.insert(lockSite);
330 }
331 }
332
333 // Get context-sensitive locks
335 {
337 for (const CxtStmt& cxtStmt : cxtStmts)
338 {
340 {
344 {
345 allLockSites.insert(cxtLock.getStmt());
346 }
347 }
348 }
349 }
350
351 return allLockSites;
352}
Set< CxtLock > CxtLockSet
bool hasIntraLockSet(const ICFGNode *stmt) const
Set< CxtStmt > CxtStmtSet
bool isInsideCondIntraLock(const ICFGNode *stmt) const
Return true if a statement is inside a partial lock/unlock pair (conditional lock with unconditional ...
const InstSet & getIntraLockSet(const ICFGNode *stmt) const
const CxtLockSet & getCxtLockFromCxtStmt(const CxtStmt &cts) const
bool hasCxtStmtFromInst(const ICFGNode *inst) const
Context-sensitive statement and lock spans.
Set< const ICFGNode * > InstSet
const CxtStmtSet & getCxtStmtsFromInst(const ICFGNode *inst) const
bool hasCxtLockFromCxtStmt(const CxtStmt &cts) const
const InstSet & getCondIntraLockSet(const ICFGNode *stmt) const

◆ getTCTNodeSetFromNode()

OrderedSet< const TCTNode * > SVF::MTASlicerBase::getTCTNodeSetFromNode ( const ICFGNode node)
protected

Definition at line 355 of file MTASlicer.cpp.

357{
359
360 if (mhp->hasThreadStmtSet(node))
361 {
362 for (const CxtThreadStmt& cts : mhp->getThreadStmtSet(node))
363 {
364 if (mhp->getTCT()->hasGNode(cts.getTid()))
365 {
366 tctNodeSet.insert(mhp->getTCT()->getTCTNode(cts.getTid()));
367 }
368 }
369 }
370
371 return tctNodeSet;
372}
bool hasGNode(NodeID id) const
Has a node.
bool hasThreadStmtSet(const ICFGNode *inst) const
Definition MHP.h:148

◆ svfgNodesToICFGNodes()

OrderedSet< const ICFGNode * > SVF::MTASlicerBase::svfgNodesToICFGNodes ( const OrderedSet< const VFGNode * > &  nodes,
const OrderedSet< const SVFStmt * > &  seeds 
)
protected

Project the retained VFG nodes (plus the seeds) onto their ICFG nodes.

Definition at line 464 of file MTASlicer.cpp.

467{
469 for (const SVFGNode* node : nodes)
471 SVFUtil::dyn_cast<StmtVFGNode>(node))
472 if (statementNode->getICFGNode() != nullptr)
473 result.insert(statementNode->getICFGNode());
474 for (const SVFStmt* stmt : seeds)
475 if (stmt != nullptr && stmt->getICFGNode() != nullptr)
476 result.insert(stmt->getICFGNode());
477 return result;
478}
LLVM_NODISCARD std::enable_if_t<!is_simple_type< Y >::value, typename cast_retty< X, const Y >::ret_type > dyn_cast(const Y &Val)
Definition Casting.h:405

Member Data Documentation

◆ callGraph

CallGraph* SVF::MTASlicerBase::callGraph
protected

Definition at line 132 of file MTASlicer.h.

◆ lockAnalysis

LockAnalysis* SVF::MTASlicerBase::lockAnalysis
protected

Definition at line 131 of file MTASlicer.h.

◆ mhp

MHP* SVF::MTASlicerBase::mhp
protected

Definition at line 130 of file MTASlicer.h.

◆ pta

AndersenBase* SVF::MTASlicerBase::pta
protected

Definition at line 129 of file MTASlicer.h.

◆ svfg

SVFG* SVF::MTASlicerBase::svfg
protected

thread-aware VFG_pre (PTA/Single slicers; null for MTA)

Definition at line 133 of file MTASlicer.h.

◆ svfir

SVFIR* SVF::MTASlicerBase::svfir
protected

Definition at line 128 of file MTASlicer.h.


The documentation for this class was generated from the following files: