1.3.3.4.1.2. 导入全局常量
全局对象是程序范围内唯一的,但在DSA中为了效率上的考虑,分为了全局图与函数图。函数通常会访问(部分的)全局对象,因此函数图中也会有相应的DSNode来表示。但出于不同的角度,同一个全局对象在两者看来可能是不一样的,因此我们需要一个操作,使得同一个全局对象对于两者有相同的DSNode。这正是下面要做的,不过只是第一步——针对常量。后面还会有其他的操作。
我们知道DSGraph的ScalarMap记录在一个函数中所引用的所有DSNode。它的ValueMap域专门记录引用的所有对象及其关联的DSNodeHandler,GlobalSet则记录了该图引用的全局对象。185行的global_begin就是返回GlobalSet的第一条记录。
GraphBuilder:: GraphBuilder(续)
179 // If there areany constant globals referenced in this function, merge
180 // theirinitializers into the local graph from the globals graph.
181 // Thisresolves indirect calls in some common cases
182 // Only mergeinfo for nodes that already exist in the local pass
183 // otherwiseleaf functions could contain less collapsing than the globals
184 // graph
185 if (g.getScalarMap().global_begin() !=g.getScalarMap().global_end()) {
186 ReachabilityCloner RC(&g,g.getGlobalsGraph(), 0);
187 for(DSScalarMap::global_iterator I = g.getScalarMap().global_begin(),
188 E = g.getScalarMap().global_end();I != E; ++I) {
189 if (constGlobalVariable * GV = dyn_cast<GlobalVariable > (*I))
190 if (GV->isConstant())
191 RC.merge(g.getNodeForValue(GV),g.getGlobalsGraph()->getNodeForValue(GV));
192 }
193 }
这里,我们没有选择让全局图及函数图共享DSNode,而是让它们持有独立但相同的DSNode。这涉及DSNode的克隆及克隆节点间的简并,ReachabilityCloner提供这样的操作。其最重要的成员是NodeMap,类型为std::map<const DSNode*, DSNodeHandle>,第一部分保存源图的DSNode地址,第二部分保存目标图中援引与第一部分保存的DSNode对应的简并后DSNode的DSNodeHandle。在186行首先构建它的一个实例。
658 ReachabilityCloner(DSGraph* dest, const DSGraph* src, unsigned cloneFlags,
659 bool _createDest = true)
660 : Dest(dest), Src(src),CloneFlags(cloneFlags), createDest(_createDest) {
661 assert(Dest!= Src && "Cannot clone from graph to same graph!");
662 BitsToKeep = ~DSNode::DeadNode;
663 if (CloneFlags &DSGraph::StripAllocaBit)
664 BitsToKeep &= ~DSNode::AllocaNode;
665 if (CloneFlags &DSGraph::StripModRefBits)
666 BitsToKeep &= ~(DSNode::ModifiedNode| DSNode::ReadNode);
667 if (CloneFlags &DSGraph::StripIncompleteBit)
668 BitsToKeep &=~DSNode::IncompleteNode;
669 }
在186行ReachabilityCloner的构造函数调用中,源图是全局对象的图GlobalsGraph,目标图是在LocalDataStructures::runOnModule的1483行构建的当前处理函数的图。下面函数的参数NH及SrcNH分别是目标图及源图中对应同一个全局对象的DSNodeHandle(即它们都指向相同的DSNode)。对于全局对象,函数图不拥有对应的DSNode节点,这些节点由全局图在1.3.3.1.全局对象声明的处理等章节根据全局对象的声明及定义来创建。那么在第一次遇到这些DSNode时,必然满足843行条件,846行的getClonedNH克隆1个DSNode(后面看其实现),通过mergeWith丢给NH。
840 void ReachabilityCloner::merge(const DSNodeHandle &NH,
841 const DSNodeHandle &SrcNH) {
842 if (SrcNH.isNull()) return; // Noop
843 if (NH.isNull()) {
844 // If there isno destination node, just clone the source and assign the
845 // destinationnode to be it.
846 NH.mergeWith(getClonedNH(SrcNH));
847 return;
848 }
849
850 // Okay, at thispoint, we know that we have both a destination and a source
851 // node that needto be merged. Check to see if the sourcenode has already
852 // been cloned.
853 const DSNode*SN = SrcNH.getNode();
854 DSNodeHandle &SCNH = NodeMap[SN]; //SourceClonedNodeHandle
855 if (!SCNH.isNull()) { // Node alreadycloned?
856 DSNode *SCNHN = SCNH.getNode();
857 NH.mergeWith(DSNodeHandle(SCNHN,
858 SCNH.getOffset()+SrcNH.getOffset()));
859 return; // Nothing to do!
860 }
来到在854行,NH是源图里某一个DSNode的克隆。如果SrcNH所指向的DSNode还未克隆,从NodeMap得到的SCNH是由DSNodeHandle的缺省构造函数构造的,它不满足855行的条件。否则,它已被克隆,简并节点即可(简并的结果是合二为一)。
ReachabilityCloner::merge(续)
862 // Okay, so thesource node has not already been cloned. Instead of creating
863 // a new DSNode,only to merge it into the one we already have, try to perform
864 // the mergein-place. The only case we cannot handlehere is when the offset
865 // into theexisting node is less than the offset into the virtual node we are
866 // mergingin. In this case, we have to extend theexisting node, which
867 // requires anallocation anyway.
868 DSNode *DN = NH.getNode(); // Make sure the Offset is up-to-date
869 if (NH.getOffset() >= SrcNH.getOffset()) {
870 if(!DN->isNodeCompletelyFolded()) {
871 // Make surethe destination node is folded if the source node is folded.
872 if (SN->isNodeCompletelyFolded()) {
873 DN->foldNodeCompletely();
874 DN = NH.getNode();
875 } else if (SN->getSize() !=DN->getSize()) {
876 // If thetwo nodes are of different size, and the smaller node has the
877 // arraybit set, collapse!
878 #if COLLAPSE_ARRAYS_AGGRESSIVELY
879 if (SN->getSize() < DN->getSize()) {
880 if (SN->isArrayNode()) {
881 DN->foldNodeCompletely();
882 DN = NH.getNode();
883 }
884 } else if (DN->isArrayNode()) {
885 DN->foldNodeCompletely();
886 DN = NH.getNode();
887 }
888 #endif
889 }
890
891
892 // FIXME:Addcomments.
893 if(!DN->isArrayNode() &&SN->isArrayNode()) {
894 if(DN->getSize() != 0 &&SN->getSize() != 0) {
895 if((DN->getSize() !=SN->getSize() &&
896 (NH.getOffset() != 0 ||SrcNH.getOffset() != 0)
897 && DN->getSize() >SN->getSize())) {
898 DN->foldNodeCompletely();
899 DN = NH.getNode();
900 }
901 }
902 }
903 if(!SN->isArrayNode() &&DN->isArrayNode()) {
904 if(DN->getSize() != 0 &&SN->getSize() != 0) {
905 if((DN->getSize() !=SN->getSize() &&
906 (NH.getOffset() != 0 ||SrcNH.getOffset() != 0)
907 && DN->getSize() <SN->getSize())) {
908 DN->foldNodeCompletely();
909 DN = NH.getNode();
910 }
911 }
912 }
913
914 if (SN->isArrayNode() &&DN->isArrayNode()) {
915 if((SN->getSize() !=DN->getSize()) && (SN->getSize() != 0)
916 && DN->getSize() != 0) {
917 DN->foldNodeCompletely();
918 DN = NH.getNode();
919 }
920 }
921 if (!DN->isNodeCompletelyFolded()&& DN->getSize() < SN->getSize())
922 DN->growSize(SN->getSize());
923
924
925 // Merge thetype entries of the two nodes together...
926 if (!DN->isNodeCompletelyFolded())
927 DN->mergeTypeInfo(SN,NH.getOffset() - SrcNH.getOffset());
928 }
929
930 assert(!DN->isDeadNode());
如果目标图还未克隆SN,也不一定非要克隆SN,前提是目标图所指向的偏移超过源图(这样我们可以使用已经克隆的NH作为这个节点的克隆)。869行的if直到958行才结束,而870行的if结束在927行。870~927行首先确定DN是否需要缩合。缩合的原因无非就是要算法正确处理计算量太大、太困难,这些都涉及数组。如果DN逃过了缩合的命运,在921~928行根据SN调整DN,这几行可以放在下面。927行对offset的调整,NH.getOffset() -SrcNH.getOffset()是目标节点相对源节点的偏移差,因为927行是简并源节点与目标节点的类型信息进入目标节点,这个偏移差是必须的。
ReachabilityCloner::merge(续)
932 // Merge theNodeType information.
933 DN->mergeNodeFlags(SN->getNodeFlags()& BitsToKeep);
934
935 // Before westart merging outgoing links and updating the scalar map, make
936 // sure it isknown that this is the representative node for the src node.
937 SCNH = DSNodeHandle(DN,NH.getOffset()-SrcNH.getOffset());
938
939 // If thesource node contains any globals, make sure they end up in the
940 // scalar mapwith the correct offset.
941 if (SN->globals_begin() !=SN->globals_end()) {
942 // Update theglobals in the destination node itself.
943 DN->mergeGlobals(*SN);
944
945 // Update thescalar map for the graph we are merging the source node
946 // into.
947 for(DSNode::globals_iterator I = SN->globals_begin(),
948 E = SN->globals_end(); I != E;++I) {
949 constGlobalValue *GV = *I;
950 const DSNodeHandle &SrcGNH = Src->getNodeForValue(GV);
951 DSNodeHandle &DestGNH =NodeMap[SrcGNH.getNode()];
952 assert(DestGNH.getNode()==NH.getNode()&&"Global mapping inconsistent");
953 Dest->getNodeForValue(GV).mergeWith(DSNodeHandle(DestGNH.getNode(),
954 DestGNH.getOffset()+SrcGNH.getOffset()));
955 }
956 NH.getNode()->mergeGlobals(*SN);
957 }
在937行,SCNH现在被更新为指向DN了(原来它不指向任何DSNode),而且由于它是个引用,这个改变发生在NodeMap里。另外,这里还调整了相对DSNode的偏移,这是为了方便下面953行的处理。947行的DSNode的globals_begin返回DSNode中Globals的第一个元素,Globals记录了该DSNode代表的所有全局对象。
前面看到,formGlobalECs的效果是生成同类集,并让同类集的Leader在GlobalGraph的ScalarMap及对应的DSNode的Globals中替代这个同类集的全局对象(LocalDataStructures::runOnModule的1472行),因此此时Globals里只有一个Leader元素,另外对应的DSNodeHandle是唯一的。
另一方面,在DSNode简并时,多个DSNode的Globals会合并在一起,其中每个元素代表一个同类集(但在后面的调用中,formGlobalECs会再次把它们合并为一个同类集),而且简并后只有一个DSNode,这些Globals成员共享这个DSNode(通过各自的DSNodeHandle)。
因此,在953行对Globals的每个成员构建DSNodeHandle,因为DestGNH来自NodeMap,它的偏移减去了SrcNH的偏移(927行),因此只要加上SrcGNH的偏移即可,而不是DestGNH.getOffset() +SrcGNH.getOffset()-SrcNH.getOffset()。注意,因为NodeMap只能(需要)映射一对DSNode与DSNodeHandle,获取其他DSNodeHandle的方法就是通过getNodeForValue,这会生成一个空的DSNodeHandle,而mergeWith把生成的DSNodeHandle直接拷贝给这个DSNodeHandle。
956行与943行是重复的。
ReachabilityCloner::merge(续)
958 } else {
959 // We cannothandle this case without allocating a temporary node. Fall
960 // back onbeing simple.
961 DSNode *NewDN = newDSNode(*SN, Dest, true /* Null out all links */);
962 NewDN->maskNodeTypes(BitsToKeep);
963
964 #ifndef NDEBUG
965 unsigned NHOffset = NH.getOffset();
966 #endif
967 NH.mergeWith(DSNodeHandle(NewDN,SrcNH.getOffset()));
968
969 #ifndef NDEBUG
970 assert(NH.getNode()&&
971 (NH.getOffset() > NHOffset ||
972 (NH.getOffset() == 0 &&NH.getNode()->isNodeCompletelyFolded())) &&
973 "Merging did not adjust theoffset!");
974 #endif
975
976 // Before westart merging outgoing links and updating the scalar map, make
977 // sure it isknown that this is the representative node for the src node.
978 SCNH = DSNodeHandle(NH.getNode(),NH.getOffset()-SrcNH.getOffset());
979
980 // If thesource node contained any globals, make sure to create entries
981 // in thescalar map for them!
982 for(DSNode::globals_iterator I = SN->globals_begin(),
983 E = SN->globals_end(); I != E; ++I){
984 const GlobalValue*GV = *I;
985 constDSNodeHandle &SrcGNH = Src->getNodeForValue(GV);
986 DSNodeHandle &DestGNH =NodeMap[SrcGNH.getNode()];
987 assert(DestGNH.getNode()==NH.getNode()&&"Global mapping inconsistent");
988 assert(SrcGNH.getNode()== SN && "Global mapping inconsistent");
989 Dest->getNodeForValue(GV).mergeWith(DSNodeHandle(DestGNH.getNode(),
990 DestGNH.getOffset()+SrcGNH.getOffset()));
991 }
992 }
958行开始的else块面对的是目标节点的指向偏移小于源图节点的情形。961行DSNode的构造函数从SN拷贝大部分的内容(但Links除外),并加入图Dest。967行的mergeWith合并了NH与NewDN的Globals,TyMap(类型映射),在989行简并相关的Globals节点。987~988行的断言的原理跟前面是类似的。
ReachabilityCloner::merge(续)
994 // DOUT << "LLVA: mergeWith: "<< SN << " becomes " << DN << "\n";
995
996 // Next,recursively merge all outgoing links as necessary. Note that
997 // adding theselinks can cause the destination node to collapse itself at
998 // any time, andthe current node may be merged with arbitrary other nodes.
999 // For thisreason, we must always go through NH.
1000 DN = 0;
1001 for(DSNode::const_edge_iterator ii = SN->edge_begin(), ee = SN->edge_end();
1002 ii != ee; ++ii) {
1003 constDSNodeHandle &SrcEdge = ii->second;
1004 if (!SrcEdge.isNull()) {
1005 // Computethe offset into the current node at which to
1006 // merge thislink. In the common case, this is alinear
1007 // relationto the offset in the original node (with
1008 // wrapping),but if the current node gets collapsed due to
1009 // recursivemerging, we must make sure to merge in all remaining
1010 // links atoffset zero.
1011 DSNode *CN = SCNH.getNode();
1012 unsigned MergeOffset =(ii->first+SCNH.getOffset()) % CN->getSize();
1013
1014 DSNodeHandle Tmp =CN->getLink(MergeOffset);
1015 if (!Tmp.isNull()) {
1016 // Performthe recursive merging. Make sure tocreate a temporary NH,
1017 // becausethe Link can disappear in the process of recursive merging.
1018 merge(Tmp, SrcEdge);
1019 } else {
1020 Tmp.mergeWith(getClonedNH(SrcEdge));
1021 // Mergingthis could cause all kinds of recursive things to happen,
1022 //culminating in the current node being eliminated. Since this is
1023 //possible, make sure to reaquire the link from 'CN'.
1024
1025 unsigned MergeOffset = 0;
1026 CN = SCNH.getNode();
1027 MergeOffset = (ii->first +SCNH.getOffset()) % CN->getSize();
1028 CN->getLink(MergeOffset).mergeWith(Tmp);
1029 }
1030 }
1031 }
1032 }
前面处理了当前DSNode/DSNodeHandle所代表的全局对象。现在处理这个全局对象所指向的对象。这个指向关系由DSNode中的Links成员表示,Links[offset]即表示该对象偏移offset处所指向的对象。同样,这个指向关系由一个DSNodeHandle来表示。
在1011行,必须从SCNH获取DSNode节点,因为前面众多的mergeWith操作会不停地改变相关的DSNode,只有SCNH才拥有最新的DSNode节点。我们知道DSNode代表一个对象或一块内存,DSNodeHandle指向其中的对象(可能是基类对象或数组元素),Offset表示这个对象在DSNode所代表对象(内存)中的偏移。1012行的ii->first则是在DSNodeHandle表示的对象中的偏移位置,因此MergeOffset就是该位置在DSNode所代表内存中的偏移。
SCNH的DSNode节点保存在NodeMap中,因此1014行的Tmp代表这个DSNode表示对象里的指针,与它简并的源也必须来自NodeMap。因此有在1020行对getClonedNH的调用。
750 DSNodeHandle ReachabilityCloner::getClonedNH(const DSNodeHandle &SrcNH) {
751 if (SrcNH.isNull()) returnDSNodeHandle();
752 const DSNode*SN = SrcNH.getNode();
753
754 DSNodeHandle &NH = NodeMap[SN];
755 if (!NH.isNull()) { //Node already mapped?
756 DSNode *NHN = NH.getNode();
757 unsigned NewOffset = NH.getOffset() +SrcNH.getOffset();
758 if (NHN) {
759 NHN->checkOffsetFoldIfNeeded(NewOffset);
760 NHN = NH.getNode();
761 }
762 returnDSNodeHandle(NHN, NewOffset);
763 }
764
765 // If SrcNH hasglobals and the destination graph has one of the same globals,
766 // merge this nodewith the destination node, which is much more efficient.
767 if (SN->globals_begin() != SN->globals_end()){
768 DSScalarMap &DestSM =Dest->getScalarMap();
769 for(DSNode::globals_iterator I = SN->globals_begin(),E = SN->globals_end();
770 I != E; ++I) {
771 constGlobalValue *GV = *I;
772 DSScalarMap::iterator GI = DestSM.find(GV);
773 if (GI != DestSM.end() &&!GI->second.isNull()) {
774 // We foundone, use merge instead!
775 merge(GI->second,Src->getNodeForValue(GV));
776 assert(!NH.isNull()&& "Didn't merge node!");
777 DSNode *NHN = NH.getNode();
778 unsigned NewOffset = NH.getOffset() +SrcNH.getOffset();
779 if (NHN) {
780 NHN->checkOffsetFoldIfNeeded(NewOffset);
781 NHN = NH.getNode();
782 }
783 returnDSNodeHandle(NHN, NewOffset);
784 }
785 }
786 }
787
788 if (!createDest) returnDSNodeHandle(0,0);
同样,如果SrcNH所指向的DSNode已经映射到NodeMap(755行条件),由于映射来的DSNodeHandle里的偏移是减去源DSNodeHandle节点偏移的,因此在757行补回这个差值来得到这个映射DSNodeHandle节点的真正偏移。
如果SrcNH所指向的DSNode还没有映射(实际上不一定是没有映射,因为NodeMap的类型是std::map<const DSNode*, DSNodeHandle>,当存在多个DSNodeHandle指向同一个DSNode时,即DSNode的Globals有多个元素,只有其中一个的DSNodeHandle被NodeMap记录,通过NodeMap是找不到其他元素的DSNodeHandle),769行遍历该DSNode所代表的内存对象,如果其中之一的DSNodeHandle已经记录在NodeMap中,说明这个DSNode实际已经映射了,那么在775行把这对DSNodeHandle节点克隆、简并起来就好了。因为merge会产生Globals中所有元素的DSNodeHandle。注释说到,这样做会效率更高。776行的NH是NodeMap的一个引用,因此775行的merge调用的结果会在其中反映出来。
788行的createDest由ReachabilityCloner的构造函数设置,默认为true。
ReachabilityCloner::getClonedNH(续)
790 DSNode *DN = newDSNode(*SN, Dest, true /* Null out all links */);
791 DN->maskNodeTypes(BitsToKeep);
792 NH = DN;
793
794 // Next,recursively clone all outgoing links as necessary. Note that
795 // adding theselinks can cause the node to collapse itself at any time, and
796 // the current nodemay be merged with arbitrary other nodes. For this
797 // reason, we mustalways go through NH.
798 DN = 0;
799 for(DSNode::const_edge_iterator ii = SN->edge_begin(), ee = SN->edge_end();
800 ii != ee; ++ii) {
801 constDSNodeHandle &SrcEdge = ii->second;
802 if (!SrcEdge.isNull()) {
803 constDSNodeHandle &DestEdge = getClonedNH(SrcEdge);
804 // Compute theoffset into the current node at which to
805 // merge thislink. In the common case, this is alinear
806 // relation tothe offset in the original node (with
807 // wrapping),but if the current node gets collapsed due to
808 // recursivemerging, we must make sure to merge in all remaining
809 // links atoffset zero.
810 unsigned MergeOffset = 0;
811 DSNode *CN = NH.getNode();
812 if (CN->getSize() != 1)
813 MergeOffset = (ii->first +NH.getOffset()) % CN->getSize();
814 CN->addEdgeTo(MergeOffset,DestEdge);
815 }
816 }
817
818 // If this nodecontains any globals, make sure they end up in the scalar
819 // map with thecorrect offset.
820 for(DSNode::globals_iterator I = SN->globals_begin(), E = SN->globals_end();
821 I != E; ++I) {
823 constGlobalValue *GV = *I;
824 constDSNodeHandle &SrcGNH = Src->getNodeForValue(GV);
825 DSNodeHandle &DestGNH =NodeMap[SrcGNH.getNode()];
826 assert(DestGNH.getNode()== NH.getNode() &&"Global mapping inconsistent");
827 Dest->getNodeForValue(GV).mergeWith(DSNodeHandle(DestGNH.getNode(),
828 DestGNH.getOffset()+SrcGNH.getOffset()));
829 }
830 NH.getNode()->mergeGlobals(*SN);
831
832 DSNode* NHN = NH.getNode();
833 unsigned NewOffset = NH.getOffset() +SrcNH.getOffset();
834 if (NHN) {
835 NHN->checkOffsetFoldIfNeeded(NewOffset);
836 NHN = NH.getNode();
837 }
838 returnDSNodeHandle(NHN, NewOffset);
839 }
来到这里,目标图确实没有克隆源DSNode,因此首先创建一个DSNode节点。在792行,NH实际被赋予DSNodeHandle(DN, 0)。接着在799行遍历源节点所代表的对象所指向的对象,使目标节点指向对应的映射后的DSNode。最后,在820行,更新目标节点所代表的所有全局对象的引用(DSNodeHandle)。