blob: bfd2f9eb29ae24bf594971133a6792a4c4e975a9 [file] [log] [blame]
Chris Lattnera5682852006-08-07 23:03:03 +00001//===-- SelectionDAGCSEMap.cpp - Implement the SelectionDAG CSE Map -------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file was developed by Chris Lattner and is distributed under
6// the University of Illinois Open Source License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This implements the SelectionDAGCSEMap class.
11//
12//===----------------------------------------------------------------------===//
13
14#include "llvm/CodeGen/SelectionDAG.h"
15using namespace llvm;
16
17//===----------------------------------------------------------------------===//
18// SelectionDAGCSEMap::NodeID Implementation
19
20SelectionDAGCSEMap::NodeID::NodeID(SDNode *N) {
21 SetOpcode(N->getOpcode());
22 // Add the return value info.
23 SetValueTypes(N->value_begin());
24 // Add the operand info.
25 SetOperands(N->op_begin(), N->getNumOperands());
Chris Lattner61b09412006-08-11 21:01:22 +000026
27 // Handle SDNode leafs with special info.
28 if (N->getNumOperands() == 0) {
29 switch (N->getOpcode()) {
30 default: break; // Normal nodes don't need extra info.
31 case ISD::TargetConstant:
32 case ISD::Constant:
33 AddInteger(cast<ConstantSDNode>(N)->getValue());
34 break;
35 case ISD::TargetGlobalAddress:
36 case ISD::GlobalAddress:
37 AddPointer(cast<GlobalAddressSDNode>(N)->getGlobal());
38 AddInteger(cast<GlobalAddressSDNode>(N)->getOffset());
39 break;
40 case ISD::BasicBlock:
41 AddPointer(cast<BasicBlockSDNode>(N)->getBasicBlock());
42 break;
43 case ISD::Register:
44 AddInteger(cast<RegisterSDNode>(N)->getReg());
45 break;
46 case ISD::SRCVALUE:
47 AddPointer(cast<SrcValueSDNode>(N)->getValue());
48 AddInteger(cast<SrcValueSDNode>(N)->getOffset());
49 break;
50 }
51 }
Chris Lattnera5682852006-08-07 23:03:03 +000052}
53
54SelectionDAGCSEMap::NodeID::NodeID(unsigned short ID, const void *VTList) {
55 SetOpcode(ID);
56 SetValueTypes(VTList);
57 SetOperands();
58}
59SelectionDAGCSEMap::NodeID::NodeID(unsigned short ID, const void *VTList,
60 SDOperand Op) {
61 SetOpcode(ID);
62 SetValueTypes(VTList);
63 SetOperands(Op);
64}
65SelectionDAGCSEMap::NodeID::NodeID(unsigned short ID, const void *VTList,
66 SDOperand Op1, SDOperand Op2) {
67 SetOpcode(ID);
68 SetValueTypes(VTList);
69 SetOperands(Op1, Op2);
70}
71SelectionDAGCSEMap::NodeID::NodeID(unsigned short ID, const void *VTList,
72 SDOperand Op1, SDOperand Op2,
73 SDOperand Op3) {
74 SetOpcode(ID);
75 SetValueTypes(VTList);
76 SetOperands(Op1, Op2, Op3);
77}
78SelectionDAGCSEMap::NodeID::NodeID(unsigned short ID, const void *VTList,
79 const SDOperand *OpList, unsigned N) {
80 SetOpcode(ID);
81 SetValueTypes(VTList);
82 SetOperands(OpList, N);
83}
84
85void SelectionDAGCSEMap::NodeID::AddPointer(const void *Ptr) {
86 // Note: this adds pointers to the hash using sizes and endianness that depend
87 // on the host. It doesn't matter however, because hashing on pointer values
88 // in inherently unstable. Nothing in the SelectionDAG should depend on the
89 // ordering of nodes in the CSEMap.
90 union {
91 intptr_t PtrI;
92 unsigned char PtrA[sizeof(intptr_t)];
93 };
94 PtrI = (intptr_t)Ptr;
95 Bits.append(PtrA, PtrA+sizeof(intptr_t));
96}
97
98void SelectionDAGCSEMap::NodeID::AddOperand(SDOperand Op) {
99 AddPointer(Op.Val);
100 // 2 bytes of resno might be too small, three should certainly be enough. :)
101 assert(Op.ResNo < (1 << 24) && "ResNo too large for CSE Map to handle!");
102 Bits.push_back((Op.ResNo >> 0) & 0xFF);
103 Bits.push_back((Op.ResNo >> 8) & 0xFF);
104 Bits.push_back((Op.ResNo >> 16) & 0xFF);
105}
106
107void SelectionDAGCSEMap::NodeID::SetOperands(const SDOperand *Ops,
108 unsigned NumOps) {
109 for (; NumOps; --NumOps, ++Ops)
110 AddOperand(*Ops);
111}
112
113
114/// ComputeHash - Compute a strong hash value for this NodeID, for lookup in
115/// the SelectionDAGCSEMap.
116unsigned SelectionDAGCSEMap::NodeID::ComputeHash() const {
117 // FIXME: this hash function sucks.
118 unsigned Hash = 0;
119 for (unsigned i = 0, e = Bits.size(); i != e; ++i)
120 Hash += Bits[i];
121 return Hash;
122}
123
124bool SelectionDAGCSEMap::NodeID::operator==(const NodeID &RHS) const {
125 if (Bits.size() != RHS.Bits.size()) return false;
126 return memcmp(&Bits[0], &RHS.Bits[0], Bits.size()) == 0;
127}
128
129
130//===----------------------------------------------------------------------===//
131// SelectionDAGCSEMap Implementation
132
133SelectionDAGCSEMap::SelectionDAGCSEMap() {
134 NumBuckets = 256;
135 Buckets = new void*[NumBuckets];
136 memset(Buckets, 0, NumBuckets*sizeof(void*));
137}
138SelectionDAGCSEMap::~SelectionDAGCSEMap() {
139 delete [] Buckets;
140}
141
142/// GetNextPtr - In order to save space, each bucket is a singly-linked-list. In
143/// order to make deletion more efficient, we make the list circular, so we can
144/// delete a node without computing its hash. The problem with this is that the
145/// start of the hash buckets are not SDNodes. If NextInBucketPtr is a bucket
146/// pointer, this method returns null: use GetBucketPtr when this happens.
147SDNode *SelectionDAGCSEMap::GetNextPtr(void *NextInBucketPtr) {
148 if (NextInBucketPtr >= Buckets && NextInBucketPtr < Buckets+NumBuckets)
149 return 0;
150 return static_cast<SDNode*>(NextInBucketPtr);
151}
152
153void **SelectionDAGCSEMap::GetBucketPtr(void *NextInBucketPtr) {
154 assert(NextInBucketPtr >= Buckets && NextInBucketPtr < Buckets+NumBuckets &&
155 "NextInBucketPtr is not a bucket ptr");
156 return static_cast<void**>(NextInBucketPtr);
157}
158
159/// GetBucketFor - Hash the specified node ID and return the hash bucket for the
160/// specified ID.
161void **SelectionDAGCSEMap::GetBucketFor(const NodeID &ID) const {
162 // TODO: if load is high, resize hash table.
163
164 // NumBuckets is always a power of 2.
165 unsigned BucketNum = ID.ComputeHash() & (NumBuckets-1);
166 return Buckets+BucketNum;
167}
168
169/// FindNodeOrInsertPos - Look up the node specified by ID. If it exists,
170/// return it. If not, return the insertion token that will make insertion
171/// faster.
172SDNode *SelectionDAGCSEMap::FindNodeOrInsertPos(const NodeID &ID,
173 void *&InsertPos) {
174 void **Bucket = GetBucketFor(ID);
175 void *Probe = *Bucket;
176
177 InsertPos = 0;
178
179 unsigned Opc = ID.getOpcode();
180 while (SDNode *NodeInBucket = GetNextPtr(Probe)) {
181 // If we found a node with the same opcode, it might be a matching node.
182 // Because it is in the same bucket as this one, we know the hash values
183 // match. Compute the NodeID for the possible match and do a final compare.
184 if (NodeInBucket->getOpcode() == Opc) {
185 NodeID OtherID(NodeInBucket);
186 if (OtherID == ID)
187 return NodeInBucket;
188 }
189
190 Probe = NodeInBucket->getNextInBucket();
191 }
192
193 // Didn't find the node, return null with the bucket as the InsertPos.
194 InsertPos = Bucket;
195 return 0;
196}
197
198/// InsertNode - Insert the specified node into the CSE Map, knowing that it
199/// is not already in the map. InsertPos must be obtained from
200/// FindNodeOrInsertPos.
201void SelectionDAGCSEMap::InsertNode(SDNode *N, void *InsertPos) {
202 /// The insert position is actually a bucket pointer.
203 void **Bucket = static_cast<void**>(InsertPos);
204
205 void *Next = *Bucket;
206
207 // If this is the first insertion into this bucket, its next pointer will be
208 // null. Pretend as if it pointed to itself.
209 if (Next == 0)
210 Next = Bucket;
211
212 // Set the nodes next pointer, and make the bucket point to the node.
213 N->SetNextInBucket(Next);
214 *Bucket = N;
215}
216
217
218/// RemoveNode - Remove a node from the CSE map, returning true if one was
219/// removed or false if the node was not in the CSE map.
220bool SelectionDAGCSEMap::RemoveNode(SDNode *N) {
221 // Because each bucket is a circular list, we don't need to compute N's hash
222 // to remove it. Chase around the list until we find the node (or bucket)
223 // which points to N.
224 void *Ptr = N->getNextInBucket();
225 if (Ptr == 0) return false; // Not in CSEMap.
226
227 void *NodeNextPtr = Ptr;
228 N->SetNextInBucket(0);
229 while (1) {
230 if (SDNode *NodeInBucket = GetNextPtr(Ptr)) {
231 // Advance pointer.
232 Ptr = NodeInBucket->getNextInBucket();
233
234 // We found a node that points to N, change it to point to N's next node,
235 // removing N from the list.
236 if (Ptr == N) {
237 NodeInBucket->SetNextInBucket(NodeNextPtr);
238 return true;
239 }
240 } else {
241 void **Bucket = GetBucketPtr(Ptr);
242 Ptr = *Bucket;
243
244 // If we found that the bucket points to N, update the bucket to point to
245 // whatever is next.
246 if (Ptr == N) {
247 *Bucket = NodeNextPtr;
248 return true;
249 }
250 }
251 }
252}
253
254/// GetOrInsertSimpleNode - If there is an existing simple SDNode exactly
255/// equal to the specified node, return it. Otherwise, insert 'N' and it
256/// instead. This only works on *simple* SDNodes, not ConstantSDNode or any
257/// other classes derived from SDNode.
258SDNode *SelectionDAGCSEMap::GetOrInsertNode(SDNode *N) {
259 SelectionDAGCSEMap::NodeID ID(N);
260 void *IP;
261 if (SDNode *E = FindNodeOrInsertPos(ID, IP))
262 return E;
263 InsertNode(N, IP);
264 return N;
265}