forked from OSchip/llvm-project
Removing the pool allocator from the main CVS tree.
Use the poolalloc module in CVS from now on. llvm-svn: 7810
This commit is contained in:
parent
d07283a2ad
commit
35364c8408
|
@ -1,156 +0,0 @@
|
||||||
//===-- PoolAllocate.h - Pool allocation pass -------------------*- C++ -*-===//
|
|
||||||
//
|
|
||||||
// This transform changes programs so that disjoint data structures are
|
|
||||||
// allocated out of different pools of memory, increasing locality. This header
|
|
||||||
// file exposes information about the pool allocation itself so that follow-on
|
|
||||||
// passes may extend or use the pool allocation for analysis.
|
|
||||||
//
|
|
||||||
//===----------------------------------------------------------------------===//
|
|
||||||
|
|
||||||
#ifndef LLVM_TRANSFORMS_POOLALLOCATE_H
|
|
||||||
#define LLVM_TRANSFORMS_POOLALLOCATE_H
|
|
||||||
|
|
||||||
#include "llvm/Pass.h"
|
|
||||||
#include "Support/hash_set"
|
|
||||||
#include "Support/EquivalenceClasses.h"
|
|
||||||
class BUDataStructures;
|
|
||||||
class TDDataStructures;
|
|
||||||
class DSNode;
|
|
||||||
class DSGraph;
|
|
||||||
class CallInst;
|
|
||||||
|
|
||||||
namespace PA {
|
|
||||||
/// FuncInfo - Represent the pool allocation information for one function in
|
|
||||||
/// the program. Note that many functions must actually be cloned in order
|
|
||||||
/// for pool allocation to add arguments to the function signature. In this
|
|
||||||
/// case, the Clone and NewToOldValueMap information identify how the clone
|
|
||||||
/// maps to the original function...
|
|
||||||
///
|
|
||||||
struct FuncInfo {
|
|
||||||
/// MarkedNodes - The set of nodes which are not locally pool allocatable in
|
|
||||||
/// the current function.
|
|
||||||
///
|
|
||||||
hash_set<DSNode*> MarkedNodes;
|
|
||||||
|
|
||||||
/// Clone - The cloned version of the function, if applicable.
|
|
||||||
Function *Clone;
|
|
||||||
|
|
||||||
/// ArgNodes - The list of DSNodes which have pools passed in as arguments.
|
|
||||||
///
|
|
||||||
std::vector<DSNode*> ArgNodes;
|
|
||||||
|
|
||||||
/// In order to handle indirect functions, the start and end of the
|
|
||||||
/// arguments that are useful to this function.
|
|
||||||
/// The pool arguments useful to this function are PoolArgFirst to
|
|
||||||
/// PoolArgLast not inclusive.
|
|
||||||
int PoolArgFirst, PoolArgLast;
|
|
||||||
|
|
||||||
/// PoolDescriptors - The Value* (either an argument or an alloca) which
|
|
||||||
/// defines the pool descriptor for this DSNode. Pools are mapped one to
|
|
||||||
/// one with nodes in the DSGraph, so this contains a pointer to the node it
|
|
||||||
/// corresponds to. In addition, the pool is initialized by calling the
|
|
||||||
/// "poolinit" library function with a chunk of memory allocated with an
|
|
||||||
/// alloca instruction. This entry contains a pointer to that alloca if the
|
|
||||||
/// pool is locally allocated or the argument it is passed in through if
|
|
||||||
/// not.
|
|
||||||
/// Note: Does not include pool arguments that are passed in because of
|
|
||||||
/// indirect function calls that are not used in the function.
|
|
||||||
std::map<DSNode*, Value*> PoolDescriptors;
|
|
||||||
|
|
||||||
/// NewToOldValueMap - When and if a function needs to be cloned, this map
|
|
||||||
/// contains a mapping from all of the values in the new function back to
|
|
||||||
/// the values they correspond to in the old function.
|
|
||||||
///
|
|
||||||
std::map<Value*, const Value*> NewToOldValueMap;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// PoolAllocate - The main pool allocation pass
|
|
||||||
///
|
|
||||||
class PoolAllocate : public Pass {
|
|
||||||
Module *CurModule;
|
|
||||||
BUDataStructures *BU;
|
|
||||||
|
|
||||||
TDDataStructures *TDDS;
|
|
||||||
|
|
||||||
hash_set<Function*> InlinedFuncs;
|
|
||||||
|
|
||||||
std::map<Function*, PA::FuncInfo> FunctionInfo;
|
|
||||||
|
|
||||||
void buildIndirectFunctionSets(Module &M);
|
|
||||||
|
|
||||||
void FindFunctionPoolArgs(Function &F);
|
|
||||||
|
|
||||||
// Debug function to print the FuncECs
|
|
||||||
void printFuncECs();
|
|
||||||
|
|
||||||
public:
|
|
||||||
Function *PoolInit, *PoolDestroy, *PoolAlloc, *PoolAllocArray, *PoolFree;
|
|
||||||
|
|
||||||
// Equivalence class where functions that can potentially be called via
|
|
||||||
// the same function pointer are in the same class.
|
|
||||||
EquivalenceClasses<Function *> FuncECs;
|
|
||||||
|
|
||||||
// Map from an Indirect CallInst to the set of Functions that it can point to
|
|
||||||
std::multimap<CallInst *, Function *> CallInstTargets;
|
|
||||||
|
|
||||||
// This maps an equivalence class to the last pool argument number for that
|
|
||||||
// class. This is used because the pool arguments for all functions within
|
|
||||||
// an equivalence class is passed to all the functions in that class.
|
|
||||||
// If an equivalence class does not require pool arguments, it is not
|
|
||||||
// on this map.
|
|
||||||
std::map<Function *, int> EqClass2LastPoolArg;
|
|
||||||
|
|
||||||
// Exception flags
|
|
||||||
// CollapseFlag set if all data structures are not pool allocated, due to
|
|
||||||
// collapsing of nodes in the DS graph
|
|
||||||
unsigned CollapseFlag;
|
|
||||||
|
|
||||||
public:
|
|
||||||
bool run(Module &M);
|
|
||||||
|
|
||||||
virtual void getAnalysisUsage(AnalysisUsage &AU) const;
|
|
||||||
|
|
||||||
BUDataStructures &getBUDataStructures() const { return *BU; }
|
|
||||||
|
|
||||||
PA::FuncInfo *getFuncInfo(Function &F) {
|
|
||||||
std::map<Function*, PA::FuncInfo>::iterator I = FunctionInfo.find(&F);
|
|
||||||
return I != FunctionInfo.end() ? &I->second : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Module *getCurModule() { return CurModule; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
/// AddPoolPrototypes - Add prototypes for the pool functions to the
|
|
||||||
/// specified module and update the Pool* instance variables to point to
|
|
||||||
/// them.
|
|
||||||
///
|
|
||||||
void AddPoolPrototypes();
|
|
||||||
|
|
||||||
/// MakeFunctionClone - If the specified function needs to be modified for
|
|
||||||
/// pool allocation support, make a clone of it, adding additional arguments
|
|
||||||
/// as neccesary, and return it. If not, just return null.
|
|
||||||
///
|
|
||||||
Function *MakeFunctionClone(Function &F);
|
|
||||||
|
|
||||||
/// ProcessFunctionBody - Rewrite the body of a transformed function to use
|
|
||||||
/// pool allocation where appropriate.
|
|
||||||
///
|
|
||||||
void ProcessFunctionBody(Function &Old, Function &New);
|
|
||||||
|
|
||||||
/// CreatePools - This creates the pool initialization and destruction code
|
|
||||||
/// for the DSNodes specified by the NodesToPA list. This adds an entry to
|
|
||||||
/// the PoolDescriptors map for each DSNode.
|
|
||||||
///
|
|
||||||
void CreatePools(Function &F, const std::vector<DSNode*> &NodesToPA,
|
|
||||||
std::map<DSNode*, Value*> &PoolDescriptors);
|
|
||||||
|
|
||||||
void TransformFunctionBody(Function &F, Function &OldF,
|
|
||||||
DSGraph &G, PA::FuncInfo &FI);
|
|
||||||
|
|
||||||
void InlineIndirectCalls(Function &F, DSGraph &G,
|
|
||||||
hash_set<Function*> &visited);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +0,0 @@
|
||||||
LEVEL = ../../..
|
|
||||||
LIBNAME = poolalloc
|
|
||||||
include ../Makefile.libs
|
|
||||||
|
|
|
@ -1,461 +0,0 @@
|
||||||
#include <assert.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
#undef assert
|
|
||||||
#define assert(X)
|
|
||||||
|
|
||||||
|
|
||||||
/* In the current implementation, each slab in the pool has NODES_PER_SLAB
|
|
||||||
* nodes unless the isSingleArray flag is set in which case it contains a
|
|
||||||
* single array of size ArraySize. Small arrays (size <= NODES_PER_SLAB) are
|
|
||||||
* still allocated in the slabs of size NODES_PER_SLAB
|
|
||||||
*/
|
|
||||||
#define NODES_PER_SLAB 512
|
|
||||||
|
|
||||||
typedef struct PoolTy {
|
|
||||||
void *Data;
|
|
||||||
unsigned NodeSize;
|
|
||||||
unsigned FreeablePool; /* Set to false if the memory from this pool cannot be
|
|
||||||
freed before destroy*/
|
|
||||||
|
|
||||||
} PoolTy;
|
|
||||||
|
|
||||||
/* PoolSlab Structure - Hold NODES_PER_SLAB objects of the current node type.
|
|
||||||
* Invariants: FirstUnused <= LastUsed+1
|
|
||||||
*/
|
|
||||||
typedef struct PoolSlab {
|
|
||||||
unsigned FirstUnused; /* First empty node in slab */
|
|
||||||
int LastUsed; /* Last allocated node in slab */
|
|
||||||
struct PoolSlab *Next;
|
|
||||||
unsigned char AllocatedBitVector[NODES_PER_SLAB/8];
|
|
||||||
unsigned char StartOfAllocation[NODES_PER_SLAB/8];
|
|
||||||
|
|
||||||
unsigned isSingleArray; /* If this slab is used for exactly one array */
|
|
||||||
/* The array is allocated from the start to the end of the slab */
|
|
||||||
unsigned ArraySize; /* The size of the array allocated */
|
|
||||||
|
|
||||||
char Data[1]; /* Buffer to hold data in this slab... variable sized */
|
|
||||||
|
|
||||||
} PoolSlab;
|
|
||||||
|
|
||||||
#define NODE_ALLOCATED(POOLSLAB, NODENUM) \
|
|
||||||
((POOLSLAB)->AllocatedBitVector[(NODENUM) >> 3] & (1 << ((NODENUM) & 7)))
|
|
||||||
#define MARK_NODE_ALLOCATED(POOLSLAB, NODENUM) \
|
|
||||||
(POOLSLAB)->AllocatedBitVector[(NODENUM) >> 3] |= 1 << ((NODENUM) & 7)
|
|
||||||
#define MARK_NODE_FREE(POOLSLAB, NODENUM) \
|
|
||||||
(POOLSLAB)->AllocatedBitVector[(NODENUM) >> 3] &= ~(1 << ((NODENUM) & 7))
|
|
||||||
#define ALLOCATION_BEGINS(POOLSLAB, NODENUM) \
|
|
||||||
((POOLSLAB)->StartOfAllocation[(NODENUM) >> 3] & (1 << ((NODENUM) & 7)))
|
|
||||||
#define SET_START_BIT(POOLSLAB, NODENUM) \
|
|
||||||
(POOLSLAB)->StartOfAllocation[(NODENUM) >> 3] |= 1 << ((NODENUM) & 7)
|
|
||||||
#define CLEAR_START_BIT(POOLSLAB, NODENUM) \
|
|
||||||
(POOLSLAB)->StartOfAllocation[(NODENUM) >> 3] &= ~(1 << ((NODENUM) & 7))
|
|
||||||
|
|
||||||
|
|
||||||
/* poolinit - Initialize a pool descriptor to empty
|
|
||||||
*/
|
|
||||||
void poolinit(PoolTy *Pool, unsigned NodeSize) {
|
|
||||||
if (!Pool) {
|
|
||||||
printf("Null pool pointer passed into poolinit!\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Pool->NodeSize = NodeSize;
|
|
||||||
Pool->Data = 0;
|
|
||||||
|
|
||||||
Pool->FreeablePool = 1;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void poolmakeunfreeable(PoolTy *Pool) {
|
|
||||||
if (!Pool) {
|
|
||||||
printf("Null pool pointer passed in to poolmakeunfreeable!\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Pool->FreeablePool = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* pooldestroy - Release all memory allocated for a pool
|
|
||||||
*/
|
|
||||||
void pooldestroy(PoolTy *Pool) {
|
|
||||||
PoolSlab *PS;
|
|
||||||
if (!Pool) {
|
|
||||||
printf("Null pool pointer passed in to pooldestroy!\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
PS = (PoolSlab*)Pool->Data;
|
|
||||||
while (PS) {
|
|
||||||
PoolSlab *Next = PS->Next;
|
|
||||||
free(PS);
|
|
||||||
PS = Next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static void *FindSlabEntry(PoolSlab *PS, unsigned NodeSize) {
|
|
||||||
/* Loop through all of the slabs looking for one with an opening */
|
|
||||||
for (; PS; PS = PS->Next) {
|
|
||||||
|
|
||||||
/* If the slab is a single array, go on to the next slab */
|
|
||||||
/* Don't allocate single nodes in a SingleArray slab */
|
|
||||||
if (PS->isSingleArray)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
/* Check to see if there are empty entries at the end of the slab... */
|
|
||||||
if (PS->LastUsed < NODES_PER_SLAB-1) {
|
|
||||||
/* Mark the returned entry used */
|
|
||||||
MARK_NODE_ALLOCATED(PS, PS->LastUsed+1);
|
|
||||||
SET_START_BIT(PS, PS->LastUsed+1);
|
|
||||||
|
|
||||||
/* If we are allocating out the first unused field, bump its index also */
|
|
||||||
if (PS->FirstUnused == PS->LastUsed+1)
|
|
||||||
PS->FirstUnused++;
|
|
||||||
|
|
||||||
/* Return the entry, increment LastUsed field. */
|
|
||||||
return &PS->Data[0] + ++PS->LastUsed * NodeSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If not, check to see if this node has a declared "FirstUnused" value that
|
|
||||||
* is less than the number of nodes allocated...
|
|
||||||
*/
|
|
||||||
if (PS->FirstUnused < NODES_PER_SLAB) {
|
|
||||||
/* Successfully allocate out the first unused node */
|
|
||||||
unsigned Idx = PS->FirstUnused;
|
|
||||||
|
|
||||||
MARK_NODE_ALLOCATED(PS, Idx);
|
|
||||||
SET_START_BIT(PS, Idx);
|
|
||||||
|
|
||||||
/* Increment FirstUnused to point to the new first unused value... */
|
|
||||||
do {
|
|
||||||
++PS->FirstUnused;
|
|
||||||
} while (PS->FirstUnused < NODES_PER_SLAB &&
|
|
||||||
NODE_ALLOCATED(PS, PS->FirstUnused));
|
|
||||||
|
|
||||||
return &PS->Data[0] + Idx*NodeSize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No empty nodes available, must grow # slabs! */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char *poolalloc(PoolTy *Pool) {
|
|
||||||
unsigned NodeSize;
|
|
||||||
PoolSlab *PS;
|
|
||||||
void *Result;
|
|
||||||
|
|
||||||
if (!Pool) {
|
|
||||||
printf("Null pool pointer passed in to poolalloc!\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeSize = Pool->NodeSize;
|
|
||||||
// Return if this pool has size 0
|
|
||||||
if (NodeSize == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
PS = (PoolSlab*)Pool->Data;
|
|
||||||
|
|
||||||
if ((Result = FindSlabEntry(PS, NodeSize)))
|
|
||||||
return Result;
|
|
||||||
|
|
||||||
/* Otherwise we must allocate a new slab and add it to the list */
|
|
||||||
PS = (PoolSlab*)malloc(sizeof(PoolSlab)+NodeSize*NODES_PER_SLAB-1);
|
|
||||||
|
|
||||||
if (!PS) {
|
|
||||||
printf("poolalloc: Could not allocate memory!");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize the slab to indicate that the first element is allocated */
|
|
||||||
PS->FirstUnused = 1;
|
|
||||||
PS->LastUsed = 0;
|
|
||||||
/* This is not a single array */
|
|
||||||
PS->isSingleArray = 0;
|
|
||||||
PS->ArraySize = 0;
|
|
||||||
|
|
||||||
MARK_NODE_ALLOCATED(PS, 0);
|
|
||||||
SET_START_BIT(PS, 0);
|
|
||||||
|
|
||||||
/* Add the slab to the list... */
|
|
||||||
PS->Next = (PoolSlab*)Pool->Data;
|
|
||||||
Pool->Data = PS;
|
|
||||||
return &PS->Data[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
void poolfree(PoolTy *Pool, char *Node) {
|
|
||||||
unsigned NodeSize, Idx;
|
|
||||||
PoolSlab *PS;
|
|
||||||
PoolSlab **PPS;
|
|
||||||
unsigned idxiter;
|
|
||||||
|
|
||||||
if (!Pool) {
|
|
||||||
printf("Null pool pointer passed in to poolfree!\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeSize = Pool->NodeSize;
|
|
||||||
|
|
||||||
// Return if this pool has size 0
|
|
||||||
if (NodeSize == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
PS = (PoolSlab*)Pool->Data;
|
|
||||||
PPS = (PoolSlab**)&Pool->Data;
|
|
||||||
|
|
||||||
/* Search for the slab that contains this node... */
|
|
||||||
while (&PS->Data[0] > Node || &PS->Data[NodeSize*NODES_PER_SLAB-1] < Node) {
|
|
||||||
if (!PS) {
|
|
||||||
printf("poolfree: node being free'd not found in allocation pool specified!\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
PPS = &PS->Next;
|
|
||||||
PS = PS->Next;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* PS now points to the slab where Node is */
|
|
||||||
|
|
||||||
Idx = (Node-&PS->Data[0])/NodeSize;
|
|
||||||
assert(Idx < NODES_PER_SLAB && "Pool slab searching loop broken!");
|
|
||||||
|
|
||||||
if (PS->isSingleArray) {
|
|
||||||
|
|
||||||
/* If this slab is a SingleArray */
|
|
||||||
|
|
||||||
if (Idx != 0) {
|
|
||||||
printf("poolfree: Attempt to free middle of allocated array\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
if (!NODE_ALLOCATED(PS,0)) {
|
|
||||||
printf("poolfree: Attempt to free node that is already freed\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
/* Mark this SingleArray slab as being free by just marking the first
|
|
||||||
entry as free*/
|
|
||||||
MARK_NODE_FREE(PS, 0);
|
|
||||||
} else {
|
|
||||||
|
|
||||||
/* If this slab is not a SingleArray */
|
|
||||||
|
|
||||||
if (!ALLOCATION_BEGINS(PS, Idx)) {
|
|
||||||
printf("poolfree: Attempt to free middle of allocated array\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free the first node */
|
|
||||||
if (!NODE_ALLOCATED(PS, Idx)) {
|
|
||||||
printf("poolfree: Attempt to free node that is already freed\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
CLEAR_START_BIT(PS, Idx);
|
|
||||||
MARK_NODE_FREE(PS, Idx);
|
|
||||||
|
|
||||||
// Free all nodes
|
|
||||||
idxiter = Idx + 1;
|
|
||||||
while (idxiter < NODES_PER_SLAB && (!ALLOCATION_BEGINS(PS,idxiter)) &&
|
|
||||||
(NODE_ALLOCATED(PS, idxiter))) {
|
|
||||||
MARK_NODE_FREE(PS, idxiter);
|
|
||||||
++idxiter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update the first free field if this node is below the free node line */
|
|
||||||
if (Idx < PS->FirstUnused) PS->FirstUnused = Idx;
|
|
||||||
|
|
||||||
/* If we are not freeing the last element in a slab... */
|
|
||||||
if (idxiter - 1 != PS->LastUsed) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Otherwise we are freeing the last element in a slab... shrink the
|
|
||||||
* LastUsed marker down to last used node.
|
|
||||||
*/
|
|
||||||
PS->LastUsed = Idx;
|
|
||||||
do {
|
|
||||||
--PS->LastUsed;
|
|
||||||
/* Fixme, this should scan the allocated array an entire byte at a time
|
|
||||||
* for performance!
|
|
||||||
*/
|
|
||||||
} while (PS->LastUsed >= 0 && (!NODE_ALLOCATED(PS, PS->LastUsed)));
|
|
||||||
|
|
||||||
assert(PS->FirstUnused <= PS->LastUsed+1 &&
|
|
||||||
"FirstUnused field was out of date!");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ok, if this slab is empty, we unlink it from the of slabs and either move
|
|
||||||
* it to the head of the list, or free it, depending on whether or not there
|
|
||||||
* is already an empty slab at the head of the list.
|
|
||||||
*/
|
|
||||||
/* Do this only if the pool is freeable */
|
|
||||||
if (Pool->FreeablePool) {
|
|
||||||
if (PS->isSingleArray) {
|
|
||||||
/* If it is a SingleArray, just free it */
|
|
||||||
*PPS = PS->Next;
|
|
||||||
free(PS);
|
|
||||||
} else if (PS->LastUsed == -1) { /* Empty slab? */
|
|
||||||
PoolSlab *HeadSlab;
|
|
||||||
*PPS = PS->Next; /* Unlink from the list of slabs... */
|
|
||||||
|
|
||||||
HeadSlab = (PoolSlab*)Pool->Data;
|
|
||||||
if (HeadSlab && HeadSlab->LastUsed == -1){/*List already has empty slab?*/
|
|
||||||
free(PS); /*Free memory for slab */
|
|
||||||
} else {
|
|
||||||
PS->Next = HeadSlab; /*No empty slab yet, add this*/
|
|
||||||
Pool->Data = PS; /*one to the head of the list */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
/* Pool is not freeable for safety reasons */
|
|
||||||
/* Leave it in the list of PoolSlabs as an empty PoolSlab */
|
|
||||||
if (!PS->isSingleArray)
|
|
||||||
if (PS->LastUsed == -1) {
|
|
||||||
PS->FirstUnused = 0;
|
|
||||||
|
|
||||||
/* Do not free the pool, but move it to the head of the list if there is
|
|
||||||
no empty slab there already */
|
|
||||||
PoolSlab *HeadSlab;
|
|
||||||
HeadSlab = (PoolSlab*)Pool->Data;
|
|
||||||
if (HeadSlab && HeadSlab->LastUsed != -1) {
|
|
||||||
PS->Next = HeadSlab;
|
|
||||||
Pool->Data = PS;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* The poolallocarray version of FindSlabEntry */
|
|
||||||
static void *FindSlabEntryArray(PoolSlab *PS, unsigned NodeSize,
|
|
||||||
unsigned Size) {
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
/* Loop through all of the slabs looking for one with an opening */
|
|
||||||
for (; PS; PS = PS->Next) {
|
|
||||||
|
|
||||||
/* For large array allocation */
|
|
||||||
if (Size > NODES_PER_SLAB) {
|
|
||||||
/* If this slab is a SingleArray that is free with size > Size, use it */
|
|
||||||
if (PS->isSingleArray && !NODE_ALLOCATED(PS,0) && PS->ArraySize >= Size) {
|
|
||||||
/* Allocate the array in this slab */
|
|
||||||
MARK_NODE_ALLOCATED(PS,0); /* In a single array, only the first node
|
|
||||||
needs to be marked */
|
|
||||||
return &PS->Data[0];
|
|
||||||
} else
|
|
||||||
continue;
|
|
||||||
} else if (PS->isSingleArray)
|
|
||||||
continue; /* Do not allocate small arrays in SingleArray slabs */
|
|
||||||
|
|
||||||
/* For small array allocation */
|
|
||||||
/* Check to see if there are empty entries at the end of the slab... */
|
|
||||||
if (PS->LastUsed < NODES_PER_SLAB-Size) {
|
|
||||||
/* Mark the returned entry used and set the start bit*/
|
|
||||||
SET_START_BIT(PS, PS->LastUsed + 1);
|
|
||||||
for (i = PS->LastUsed + 1; i <= PS->LastUsed + Size; ++i)
|
|
||||||
MARK_NODE_ALLOCATED(PS, i);
|
|
||||||
|
|
||||||
/* If we are allocating out the first unused field, bump its index also */
|
|
||||||
if (PS->FirstUnused == PS->LastUsed+1)
|
|
||||||
PS->FirstUnused += Size;
|
|
||||||
|
|
||||||
/* Increment LastUsed */
|
|
||||||
PS->LastUsed += Size;
|
|
||||||
|
|
||||||
/* Return the entry */
|
|
||||||
return &PS->Data[0] + (PS->LastUsed - Size + 1) * NodeSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If not, check to see if this node has a declared "FirstUnused" value
|
|
||||||
* starting which Size nodes can be allocated
|
|
||||||
*/
|
|
||||||
if (PS->FirstUnused < NODES_PER_SLAB - Size + 1 &&
|
|
||||||
(PS->LastUsed < PS->FirstUnused ||
|
|
||||||
PS->LastUsed - PS->FirstUnused >= Size)) {
|
|
||||||
unsigned Idx = PS->FirstUnused, foundArray;
|
|
||||||
|
|
||||||
/* Check if there is a continuous array of Size nodes starting
|
|
||||||
FirstUnused */
|
|
||||||
foundArray = 1;
|
|
||||||
for (i = Idx; (i < Idx + Size) && foundArray; ++i)
|
|
||||||
if (NODE_ALLOCATED(PS, i))
|
|
||||||
foundArray = 0;
|
|
||||||
|
|
||||||
if (foundArray) {
|
|
||||||
/* Successfully allocate starting from the first unused node */
|
|
||||||
SET_START_BIT(PS, Idx);
|
|
||||||
for (i = Idx; i < Idx + Size; ++i)
|
|
||||||
MARK_NODE_ALLOCATED(PS, i);
|
|
||||||
|
|
||||||
PS->FirstUnused += Size;
|
|
||||||
while (PS->FirstUnused < NODES_PER_SLAB &&
|
|
||||||
NODE_ALLOCATED(PS, PS->FirstUnused)) {
|
|
||||||
++PS->FirstUnused;
|
|
||||||
}
|
|
||||||
return &PS->Data[0] + Idx*NodeSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* No empty nodes available, must grow # slabs! */
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
char* poolallocarray(PoolTy* Pool, unsigned Size) {
|
|
||||||
unsigned NodeSize;
|
|
||||||
PoolSlab *PS;
|
|
||||||
void *Result;
|
|
||||||
unsigned i;
|
|
||||||
|
|
||||||
if (!Pool) {
|
|
||||||
printf("Null pool pointer passed to poolallocarray!\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
NodeSize = Pool->NodeSize;
|
|
||||||
|
|
||||||
// Return if this pool has size 0
|
|
||||||
if (NodeSize == 0)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
PS = (PoolSlab*)Pool->Data;
|
|
||||||
|
|
||||||
if ((Result = FindSlabEntryArray(PS, NodeSize,Size)))
|
|
||||||
return Result;
|
|
||||||
|
|
||||||
/* Otherwise we must allocate a new slab and add it to the list */
|
|
||||||
if (Size > NODES_PER_SLAB) {
|
|
||||||
/* Allocate a new slab of size Size */
|
|
||||||
PS = (PoolSlab*)malloc(sizeof(PoolSlab)+NodeSize*Size-1);
|
|
||||||
if (!PS) {
|
|
||||||
printf("poolallocarray: Could not allocate memory!\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
PS->isSingleArray = 1;
|
|
||||||
PS->ArraySize = Size;
|
|
||||||
MARK_NODE_ALLOCATED(PS, 0);
|
|
||||||
} else {
|
|
||||||
PS = (PoolSlab*)malloc(sizeof(PoolSlab)+NodeSize*NODES_PER_SLAB-1);
|
|
||||||
if (!PS) {
|
|
||||||
printf("poolallocarray: Could not allocate memory!\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Initialize the slab to indicate that the first element is allocated */
|
|
||||||
PS->FirstUnused = Size;
|
|
||||||
PS->LastUsed = Size - 1;
|
|
||||||
|
|
||||||
PS->isSingleArray = 0;
|
|
||||||
PS->ArraySize = 0;
|
|
||||||
|
|
||||||
SET_START_BIT(PS, 0);
|
|
||||||
for (i = 0; i < Size; ++i) {
|
|
||||||
MARK_NODE_ALLOCATED(PS, i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Add the slab to the list... */
|
|
||||||
PS->Next = (PoolSlab*)Pool->Data;
|
|
||||||
Pool->Data = PS;
|
|
||||||
return &PS->Data[0];
|
|
||||||
}
|
|
Loading…
Reference in New Issue