//===-- SelectionDAGCSEMap.cpp - Implement the SelectionDAG CSE Map -------===// // // The LLVM Compiler Infrastructure // // This file was developed by Chris Lattner and is distributed under // the University of Illinois Open Source License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This implements the SelectionDAGCSEMap class. // //===----------------------------------------------------------------------===// #include "llvm/CodeGen/SelectionDAG.h" #include "llvm/CodeGen/MachineConstantPool.h" #include "llvm/Support/MathExtras.h" using namespace llvm; //===----------------------------------------------------------------------===// // SelectionDAGCSEMap::NodeID Implementation /// SetValueTypes - Value type lists are intern'd so we can represent them /// solely with their pointer. void SelectionDAGCSEMap::NodeID::SetValueTypes(SDVTList VTList) { AddPointer(VTList.VTs); } SelectionDAGCSEMap::NodeID::NodeID(SDNode *N) { SetOpcode(N->getOpcode()); // Add the return value info. SetValueTypes(N->getVTList()); // Add the operand info. SetOperands(N->op_begin(), N->getNumOperands()); // Handle SDNode leafs with special info. if (N->getNumOperands() == 0) { switch (N->getOpcode()) { default: break; // Normal nodes don't need extra info. case ISD::TargetConstant: case ISD::Constant: AddInteger(cast(N)->getValue()); break; case ISD::TargetConstantFP: case ISD::ConstantFP: AddInteger(DoubleToBits(cast(N)->getValue())); break; case ISD::TargetGlobalAddress: case ISD::GlobalAddress: AddPointer(cast(N)->getGlobal()); AddInteger(cast(N)->getOffset()); break; case ISD::BasicBlock: AddPointer(cast(N)->getBasicBlock()); break; case ISD::Register: AddInteger(cast(N)->getReg()); break; case ISD::SRCVALUE: AddPointer(cast(N)->getValue()); AddInteger(cast(N)->getOffset()); break; case ISD::FrameIndex: case ISD::TargetFrameIndex: AddInteger(cast(N)->getIndex()); break; case ISD::JumpTable: case ISD::TargetJumpTable: AddInteger(cast(N)->getIndex()); break; case ISD::ConstantPool: case ISD::TargetConstantPool: AddInteger(cast(N)->getAlignment()); AddInteger(cast(N)->getOffset()); if (cast(N)->isMachineConstantPoolEntry()) cast(N)->getMachineCPVal()-> AddSelectionDAGCSEId(this); else AddPointer(cast(N)->getConstVal()); break; } } } SelectionDAGCSEMap::NodeID::NodeID(unsigned short ID, SDVTList VTList) { SetOpcode(ID); SetValueTypes(VTList); SetOperands(); } SelectionDAGCSEMap::NodeID::NodeID(unsigned short ID, SDVTList VTList, SDOperand Op) { SetOpcode(ID); SetValueTypes(VTList); SetOperands(Op); } SelectionDAGCSEMap::NodeID::NodeID(unsigned short ID, SDVTList VTList, SDOperand Op1, SDOperand Op2) { SetOpcode(ID); SetValueTypes(VTList); SetOperands(Op1, Op2); } SelectionDAGCSEMap::NodeID::NodeID(unsigned short ID, SDVTList VTList, SDOperand Op1, SDOperand Op2, SDOperand Op3) { SetOpcode(ID); SetValueTypes(VTList); SetOperands(Op1, Op2, Op3); } SelectionDAGCSEMap::NodeID::NodeID(unsigned short ID, SDVTList VTList, const SDOperand *OpList, unsigned N) { SetOpcode(ID); SetValueTypes(VTList); SetOperands(OpList, N); } void SelectionDAGCSEMap::NodeID::AddPointer(const void *Ptr) { // Note: this adds pointers to the hash using sizes and endianness that depend // on the host. It doesn't matter however, because hashing on pointer values // in inherently unstable. Nothing in the SelectionDAG should depend on the // ordering of nodes in the CSEMap. intptr_t PtrI = (intptr_t)Ptr; Bits.push_back(unsigned(PtrI)); if (sizeof(intptr_t) > sizeof(unsigned)) Bits.push_back(unsigned(uint64_t(PtrI) >> 32)); } void SelectionDAGCSEMap::NodeID::AddOperand(SDOperand Op) { AddPointer(Op.Val); Bits.push_back(Op.ResNo); } void SelectionDAGCSEMap::NodeID::SetOperands(const SDOperand *Ops, unsigned NumOps) { for (; NumOps; --NumOps, ++Ops) AddOperand(*Ops); } /// ComputeHash - Compute a strong hash value for this NodeID, for lookup in /// the SelectionDAGCSEMap. unsigned SelectionDAGCSEMap::NodeID::ComputeHash() const { // This is adapted from SuperFastHash by Paul Hsieh. unsigned Hash = Bits.size(); for (const unsigned *BP = &Bits[0], *E = BP+Bits.size(); BP != E; ++BP) { unsigned Data = *BP; Hash += Data & 0xFFFF; unsigned Tmp = ((Data >> 16) << 11) ^ Hash; Hash = (Hash << 16) ^ Tmp; Hash += Hash >> 11; } // Force "avalanching" of final 127 bits. Hash ^= Hash << 3; Hash += Hash >> 5; Hash ^= Hash << 4; Hash += Hash >> 17; Hash ^= Hash << 25; Hash += Hash >> 6; return Hash; } bool SelectionDAGCSEMap::NodeID::operator==(const NodeID &RHS) const { if (Bits.size() != RHS.Bits.size()) return false; return memcmp(&Bits[0], &RHS.Bits[0], Bits.size()*sizeof(Bits[0])) == 0; } //===----------------------------------------------------------------------===// // SelectionDAGCSEMap Implementation SelectionDAGCSEMap::SelectionDAGCSEMap() : NumNodes(0) { NumBuckets = 64; Buckets = new void*[NumBuckets]; memset(Buckets, 0, NumBuckets*sizeof(void*)); } SelectionDAGCSEMap::~SelectionDAGCSEMap() { delete [] Buckets; } /// GetNextPtr - In order to save space, each bucket is a singly-linked-list. In /// order to make deletion more efficient, we make the list circular, so we can /// delete a node without computing its hash. The problem with this is that the /// start of the hash buckets are not SDNodes. If NextInBucketPtr is a bucket /// pointer, this method returns null: use GetBucketPtr when this happens. SDNode *SelectionDAGCSEMap::GetNextPtr(void *NextInBucketPtr) { if (NextInBucketPtr >= Buckets && NextInBucketPtr < Buckets+NumBuckets) return 0; return static_cast(NextInBucketPtr); } /// GetNextPtr - This is just like the previous GetNextPtr implementation, but /// allows a bucket array to be specified. SDNode *SelectionDAGCSEMap::GetNextPtr(void *NextInBucketPtr, void **Bucks, unsigned NumBuck) { if (NextInBucketPtr >= Bucks && NextInBucketPtr < Bucks+NumBuck) return 0; return static_cast(NextInBucketPtr); } void **SelectionDAGCSEMap::GetBucketPtr(void *NextInBucketPtr) { //assert(NextInBucketPtr >= Buckets && NextInBucketPtr < Buckets+NumBuckets && // "NextInBucketPtr is not a bucket ptr"); return static_cast(NextInBucketPtr); } /// GetBucketFor - Hash the specified node ID and return the hash bucket for the /// specified ID. void **SelectionDAGCSEMap::GetBucketFor(const NodeID &ID) const { // NumBuckets is always a power of 2. unsigned BucketNum = ID.ComputeHash() & (NumBuckets-1); return Buckets+BucketNum; } /// GrowHashTable - Double the size of the hash table and rehash everything. /// void SelectionDAGCSEMap::GrowHashTable() { void **OldBuckets = Buckets; unsigned OldNumBuckets = NumBuckets; NumBuckets <<= 1; // Reset the node count to zero: we're going to reinsert everything. NumNodes = 0; // Clear out new buckets. Buckets = new void*[NumBuckets]; memset(Buckets, 0, NumBuckets*sizeof(void*)); // Walk the old buckets, rehashing nodes into their new place. for (unsigned i = 0; i != OldNumBuckets; ++i) { void *Probe = OldBuckets[i]; if (!Probe) continue; while (SDNode *NodeInBucket = GetNextPtr(Probe, OldBuckets, OldNumBuckets)){ // Figure out the next link, remove NodeInBucket from the old link. Probe = NodeInBucket->getNextInBucket(); NodeInBucket->SetNextInBucket(0); // Insert the node into the new bucket, after recomputing the hash. InsertNode(NodeInBucket, GetBucketFor(NodeID(NodeInBucket))); } } delete[] OldBuckets; } /// FindNodeOrInsertPos - Look up the node specified by ID. If it exists, /// return it. If not, return the insertion token that will make insertion /// faster. SDNode *SelectionDAGCSEMap::FindNodeOrInsertPos(const NodeID &ID, void *&InsertPos) { void **Bucket = GetBucketFor(ID); void *Probe = *Bucket; InsertPos = 0; unsigned Opc = ID.getOpcode(); while (SDNode *NodeInBucket = GetNextPtr(Probe)) { // If we found a node with the same opcode, it might be a matching node. // Because it is in the same bucket as this one, we know the hash values // match. Compute the NodeID for the possible match and do a final compare. if (NodeInBucket->getOpcode() == Opc) { NodeID OtherID(NodeInBucket); if (OtherID == ID) return NodeInBucket; } Probe = NodeInBucket->getNextInBucket(); } // Didn't find the node, return null with the bucket as the InsertPos. InsertPos = Bucket; return 0; } /// InsertNode - Insert the specified node into the CSE Map, knowing that it /// is not already in the map. InsertPos must be obtained from /// FindNodeOrInsertPos. void SelectionDAGCSEMap::InsertNode(SDNode *N, void *InsertPos) { ++NumNodes; // Do we need to grow the hashtable? if (NumNodes > NumBuckets*2) { GrowHashTable(); InsertPos = GetBucketFor(NodeID(N)); } /// The insert position is actually a bucket pointer. void **Bucket = static_cast(InsertPos); void *Next = *Bucket; // If this is the first insertion into this bucket, its next pointer will be // null. Pretend as if it pointed to itself. if (Next == 0) Next = Bucket; // Set the nodes next pointer, and make the bucket point to the node. N->SetNextInBucket(Next); *Bucket = N; } /// RemoveNode - Remove a node from the CSE map, returning true if one was /// removed or false if the node was not in the CSE map. bool SelectionDAGCSEMap::RemoveNode(SDNode *N) { // Because each bucket is a circular list, we don't need to compute N's hash // to remove it. Chase around the list until we find the node (or bucket) // which points to N. void *Ptr = N->getNextInBucket(); if (Ptr == 0) return false; // Not in CSEMap. --NumNodes; void *NodeNextPtr = Ptr; N->SetNextInBucket(0); while (1) { if (SDNode *NodeInBucket = GetNextPtr(Ptr)) { // Advance pointer. Ptr = NodeInBucket->getNextInBucket(); // We found a node that points to N, change it to point to N's next node, // removing N from the list. if (Ptr == N) { NodeInBucket->SetNextInBucket(NodeNextPtr); return true; } } else { void **Bucket = GetBucketPtr(Ptr); Ptr = *Bucket; // If we found that the bucket points to N, update the bucket to point to // whatever is next. if (Ptr == N) { *Bucket = NodeNextPtr; return true; } } } } /// GetOrInsertSimpleNode - If there is an existing simple SDNode exactly /// equal to the specified node, return it. Otherwise, insert 'N' and it /// instead. This only works on *simple* SDNodes, not ConstantSDNode or any /// other classes derived from SDNode. SDNode *SelectionDAGCSEMap::GetOrInsertNode(SDNode *N) { SelectionDAGCSEMap::NodeID ID(N); void *IP; if (SDNode *E = FindNodeOrInsertPos(ID, IP)) return E; InsertNode(N, IP); return N; }