mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-01-03 13:31:05 +00:00
Add support for building the compactiontable for bytecode files. This shrinks
the bytecode file for 176.gcc by about 200K (10%), and 254.gap by about 167K, a 25% reduction. There is still a lot of room for improvement in the encoding of the compaction table. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@10913 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
af894e963b
commit
614cdcd002
@ -36,6 +36,7 @@ using namespace llvm;
|
||||
|
||||
SlotCalculator::SlotCalculator(const Module *M, bool buildBytecodeInfo) {
|
||||
BuildBytecodeInfo = buildBytecodeInfo;
|
||||
ModuleContainsAllFunctionConstants = false;
|
||||
TheModule = M;
|
||||
|
||||
// Preload table... Make sure that all of the primitive types are in the table
|
||||
@ -53,6 +54,7 @@ SlotCalculator::SlotCalculator(const Module *M, bool buildBytecodeInfo) {
|
||||
|
||||
SlotCalculator::SlotCalculator(const Function *M, bool buildBytecodeInfo) {
|
||||
BuildBytecodeInfo = buildBytecodeInfo;
|
||||
ModuleContainsAllFunctionConstants = false;
|
||||
TheModule = M ? M->getParent() : 0;
|
||||
|
||||
// Preload table... Make sure that all of the primitive types are in the table
|
||||
@ -70,6 +72,17 @@ SlotCalculator::SlotCalculator(const Function *M, bool buildBytecodeInfo) {
|
||||
incorporateFunction(M); // Start out in incorporated state
|
||||
}
|
||||
|
||||
unsigned SlotCalculator::getGlobalSlot(const Value *V) const {
|
||||
assert(!CompactionTable.empty() &&
|
||||
"This method can only be used when compaction is enabled!");
|
||||
if (const ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(V))
|
||||
V = CPR->getValue();
|
||||
std::map<const Value*, unsigned>::const_iterator I = NodeMap.find(V);
|
||||
assert(I != NodeMap.end() && "Didn't find entry!");
|
||||
return I->second;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// processModule - Process all of the module level function declarations and
|
||||
// types that are available.
|
||||
@ -127,23 +140,36 @@ void SlotCalculator::processModule() {
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// FIXME: Empirically, this causes the bytecode files to get BIGGER, because
|
||||
// it explodes the operand size numbers to be bigger than can be handled
|
||||
// compactly, which offsets the ~40% savings in constant sizes. Whoops.
|
||||
|
||||
// If we are emitting a bytecode file, scan all of the functions for their
|
||||
// constants, which allows us to emit more compact modules. This is optional,
|
||||
// and is just used to compactify the constants used by different functions
|
||||
// together.
|
||||
//
|
||||
// This functionality is completely optional for the bytecode writer, but
|
||||
// tends to produce smaller bytecode files. This should not be used in the
|
||||
// future by clients that want to, for example, build and emit functions on
|
||||
// the fly. For now, however, it is unconditionally enabled when building
|
||||
// bytecode information.
|
||||
//
|
||||
if (BuildBytecodeInfo) {
|
||||
ModuleContainsAllFunctionConstants = true;
|
||||
|
||||
SC_DEBUG("Inserting function constants:\n");
|
||||
for (Module::const_iterator F = TheModule->begin(), E = TheModule->end();
|
||||
F != E; ++F)
|
||||
for_each(constant_begin(F), constant_end(F),
|
||||
bind_obj(this, &SlotCalculator::getOrCreateSlot));
|
||||
F != E; ++F) {
|
||||
for (const_inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I){
|
||||
for (unsigned op = 0, e = I->getNumOperands(); op != e; ++op)
|
||||
if (isa<Constant>(I->getOperand(op)))
|
||||
getOrCreateSlot(I->getOperand(op));
|
||||
getOrCreateSlot(I->getType());
|
||||
if (const VANextInst *VAN = dyn_cast<VANextInst>(*I))
|
||||
getOrCreateSlot(VAN->getArgType());
|
||||
}
|
||||
processSymbolTableConstants(&F->getSymbolTable());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
// Insert constants that are named at module level into the slot pool so that
|
||||
// the module symbol table can refer to them...
|
||||
@ -220,27 +246,32 @@ void SlotCalculator::incorporateFunction(const Function *F) {
|
||||
|
||||
SC_DEBUG("begin processFunction!\n");
|
||||
|
||||
// Save the Table state before we process the function...
|
||||
for (unsigned i = 0; i < Table.size(); ++i)
|
||||
ModuleLevel.push_back(Table[i].size());
|
||||
|
||||
SC_DEBUG("Inserting function arguments\n");
|
||||
// If we emitted all of the function constants, build a compaction table.
|
||||
if (BuildBytecodeInfo && ModuleContainsAllFunctionConstants)
|
||||
buildCompactionTable(F);
|
||||
else {
|
||||
// Save the Table state before we process the function...
|
||||
for (unsigned i = 0, e = Table.size(); i != e; ++i)
|
||||
ModuleLevel.push_back(Table[i].size());
|
||||
}
|
||||
|
||||
// Iterate over function arguments, adding them to the value table...
|
||||
for(Function::const_aiterator I = F->abegin(), E = F->aend(); I != E; ++I)
|
||||
getOrCreateSlot(I);
|
||||
|
||||
// Iterate over all of the instructions in the function, looking for constant
|
||||
// values that are referenced. Add these to the value pools before any
|
||||
// nonconstant values. This will be turned into the constant pool for the
|
||||
// bytecode writer.
|
||||
//
|
||||
if (BuildBytecodeInfo) { // Assembly writer does not need this!
|
||||
// Emit all of the constants that are being used by the instructions in the
|
||||
// function...
|
||||
if (BuildBytecodeInfo && // Assembly writer does not need this!
|
||||
!ModuleContainsAllFunctionConstants) {
|
||||
// Iterate over all of the instructions in the function, looking for
|
||||
// constant values that are referenced. Add these to the value pools
|
||||
// before any nonconstant values. This will be turned into the constant
|
||||
// pool for the bytecode writer.
|
||||
//
|
||||
|
||||
// Emit all of the constants that are being used by the instructions in
|
||||
// the function...
|
||||
for_each(constant_begin(F), constant_end(F),
|
||||
bind_obj(this, &SlotCalculator::getOrCreateSlot));
|
||||
|
||||
bind_obj(this, &SlotCalculator::getOrCreateSlot));
|
||||
|
||||
// If there is a symbol table, it is possible that the user has names for
|
||||
// constants that are not being used. In this case, we will have problems
|
||||
// if we don't emit the constants now, because otherwise we will get
|
||||
@ -271,42 +302,169 @@ void SlotCalculator::purgeFunction() {
|
||||
|
||||
SC_DEBUG("begin purgeFunction!\n");
|
||||
|
||||
// First, remove values from existing type planes
|
||||
for (unsigned i = 0; i < NumModuleTypes; ++i) {
|
||||
unsigned ModuleSize = ModuleLevel[i]; // Size of plane before function came
|
||||
TypePlane &CurPlane = Table[i];
|
||||
//SC_DEBUG("Processing Plane " <<i<< " of size " << CurPlane.size() <<"\n");
|
||||
|
||||
while (CurPlane.size() != ModuleSize) {
|
||||
//SC_DEBUG(" Removing [" << i << "] Value=" << CurPlane.back() << "\n");
|
||||
std::map<const Value *, unsigned>::iterator NI =
|
||||
NodeMap.find(CurPlane.back());
|
||||
assert(NI != NodeMap.end() && "Node not in nodemap?");
|
||||
NodeMap.erase(NI); // Erase from nodemap
|
||||
CurPlane.pop_back(); // Shrink plane
|
||||
// First, free the compaction map if used.
|
||||
CompactionNodeMap.clear();
|
||||
|
||||
// Next, remove values from existing type planes
|
||||
for (unsigned i = 0; i != NumModuleTypes; ++i)
|
||||
if (i >= CompactionTable.size() || CompactionTable[i].empty()) {
|
||||
unsigned ModuleSize = ModuleLevel[i];// Size of plane before function came
|
||||
TypePlane &CurPlane = Table[i];
|
||||
|
||||
while (CurPlane.size() != ModuleSize) {
|
||||
std::map<const Value *, unsigned>::iterator NI =
|
||||
NodeMap.find(CurPlane.back());
|
||||
assert(NI != NodeMap.end() && "Node not in nodemap?");
|
||||
NodeMap.erase(NI); // Erase from nodemap
|
||||
CurPlane.pop_back(); // Shrink plane
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We don't need this state anymore, free it up.
|
||||
ModuleLevel.clear();
|
||||
|
||||
// Next, remove any type planes defined by the function...
|
||||
while (NumModuleTypes != Table.size()) {
|
||||
TypePlane &Plane = Table.back();
|
||||
SC_DEBUG("Removing Plane " << (Table.size()-1) << " of size "
|
||||
<< Plane.size() << "\n");
|
||||
while (Plane.size()) {
|
||||
NodeMap.erase(NodeMap.find(Plane.back())); // Erase from nodemap
|
||||
Plane.pop_back(); // Shrink plane
|
||||
if (!CompactionTable.empty()) {
|
||||
CompactionTable.clear();
|
||||
} else {
|
||||
// FIXME: this will require adjustment when we don't compact everything.
|
||||
|
||||
// Finally, remove any type planes defined by the function...
|
||||
while (NumModuleTypes != Table.size()) {
|
||||
TypePlane &Plane = Table.back();
|
||||
SC_DEBUG("Removing Plane " << (Table.size()-1) << " of size "
|
||||
<< Plane.size() << "\n");
|
||||
while (Plane.size()) {
|
||||
NodeMap.erase(NodeMap.find(Plane.back())); // Erase from nodemap
|
||||
Plane.pop_back(); // Shrink plane
|
||||
}
|
||||
|
||||
Table.pop_back(); // Nuke the plane, we don't like it.
|
||||
}
|
||||
|
||||
Table.pop_back(); // Nuke the plane, we don't like it.
|
||||
}
|
||||
|
||||
SC_DEBUG("end purgeFunction!\n");
|
||||
}
|
||||
|
||||
static inline bool hasNullValue(unsigned TyID) {
|
||||
return TyID != Type::LabelTyID && TyID != Type::TypeTyID &&
|
||||
TyID != Type::VoidTyID;
|
||||
}
|
||||
|
||||
/// getOrCreateCompactionTableSlot - This method is used to build up the initial
|
||||
/// approximation of the compaction table.
|
||||
unsigned SlotCalculator::getOrCreateCompactionTableSlot(const Value *V) {
|
||||
std::map<const Value*, unsigned>::iterator I =
|
||||
CompactionNodeMap.lower_bound(V);
|
||||
if (I != CompactionNodeMap.end() && I->first == V)
|
||||
return I->second; // Already exists?
|
||||
|
||||
// Make sure the type is in the table.
|
||||
unsigned Ty = getOrCreateCompactionTableSlot(V->getType());
|
||||
if (CompactionTable.size() <= Ty)
|
||||
CompactionTable.resize(Ty+1);
|
||||
|
||||
assert(!isa<Type>(V) || ModuleLevel.empty());
|
||||
|
||||
TypePlane &TyPlane = CompactionTable[Ty];
|
||||
|
||||
// Make sure to insert the null entry if the thing we are inserting is not a
|
||||
// null constant.
|
||||
if (TyPlane.empty() && hasNullValue(V->getType()->getPrimitiveID())) {
|
||||
Value *ZeroInitializer = Constant::getNullValue(V->getType());
|
||||
if (V != ZeroInitializer) {
|
||||
TyPlane.push_back(ZeroInitializer);
|
||||
CompactionNodeMap[ZeroInitializer] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SlotNo = TyPlane.size();
|
||||
TyPlane.push_back(V);
|
||||
CompactionNodeMap.insert(std::make_pair(V, SlotNo));
|
||||
return SlotNo;
|
||||
}
|
||||
|
||||
|
||||
/// buildCompactionTable - Since all of the function constants and types are
|
||||
/// stored in the module-level constant table, we don't need to emit a function
|
||||
/// constant table. Also due to this, the indices for various constants and
|
||||
/// types might be very large in large programs. In order to avoid blowing up
|
||||
/// the size of instructions in the bytecode encoding, we build a compaction
|
||||
/// table, which defines a mapping from function-local identifiers to global
|
||||
/// identifiers.
|
||||
void SlotCalculator::buildCompactionTable(const Function *F) {
|
||||
assert(CompactionNodeMap.empty() && "Compaction table already built!");
|
||||
// First step, insert the primitive types.
|
||||
CompactionTable.resize(Type::TypeTyID+1);
|
||||
for (unsigned i = 0; i != Type::FirstDerivedTyID; ++i) {
|
||||
const Type *PrimTy = Type::getPrimitiveType((Type::PrimitiveID)i);
|
||||
CompactionTable[Type::TypeTyID].push_back(PrimTy);
|
||||
CompactionNodeMap[PrimTy] = i;
|
||||
}
|
||||
|
||||
// Next, include any types used by function arguments.
|
||||
for (Function::const_aiterator I = F->abegin(), E = F->aend(); I != E; ++I)
|
||||
getOrCreateCompactionTableSlot(I->getType());
|
||||
|
||||
// Next, find all of the types and values that are referred to by the
|
||||
// instructions in the program.
|
||||
for (const_inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) {
|
||||
getOrCreateCompactionTableSlot(I->getType());
|
||||
for (unsigned op = 0, e = I->getNumOperands(); op != e; ++op)
|
||||
if (isa<Constant>(I->getOperand(op)) ||
|
||||
isa<GlobalValue>(I->getOperand(op)))
|
||||
getOrCreateCompactionTableSlot(I->getOperand(op));
|
||||
if (const VANextInst *VAN = dyn_cast<VANextInst>(*I))
|
||||
getOrCreateCompactionTableSlot(VAN->getArgType());
|
||||
}
|
||||
|
||||
const SymbolTable &ST = F->getSymbolTable();
|
||||
for (SymbolTable::const_iterator I = ST.begin(), E = ST.end(); I != E; ++I)
|
||||
for (SymbolTable::type_const_iterator TI = I->second.begin(),
|
||||
TE = I->second.end(); TI != TE; ++TI)
|
||||
if (isa<Constant>(TI->second) || isa<Type>(TI->second) ||
|
||||
isa<GlobalValue>(TI->second))
|
||||
getOrCreateCompactionTableSlot(TI->second);
|
||||
|
||||
// Now that we have all of the values in the table, and know what types are
|
||||
// referenced, make sure that there is at least the zero initializer in any
|
||||
// used type plane. Since the type was used, we will be emitting instructions
|
||||
// to the plane even if there are no constants in it.
|
||||
CompactionTable.resize(CompactionTable[Type::TypeTyID].size());
|
||||
for (unsigned i = 0, e = CompactionTable.size(); i != e; ++i)
|
||||
if (CompactionTable[i].empty() && i != Type::VoidTyID &&
|
||||
i != Type::LabelTyID) {
|
||||
const Type *Ty = cast<Type>(CompactionTable[Type::TypeTyID][i]);
|
||||
getOrCreateCompactionTableSlot(Constant::getNullValue(Ty));
|
||||
}
|
||||
|
||||
// Okay, now at this point, we have a legal compaction table. Since we want
|
||||
// to emit the smallest possible binaries, we delete planes that do not NEED
|
||||
// to be compacted, starting with the type plane.
|
||||
|
||||
|
||||
// If decided not to compact anything, do not modify ModuleLevels.
|
||||
if (CompactionTable.empty())
|
||||
// FIXME: must update ModuleLevel.
|
||||
return;
|
||||
|
||||
// Finally, for any planes that we have decided to compact, update the
|
||||
// ModuleLevel entries to be accurate.
|
||||
|
||||
// FIXME: This does not yet work for partially compacted tables.
|
||||
ModuleLevel.resize(CompactionTable.size());
|
||||
for (unsigned i = 0, e = CompactionTable.size(); i != e; ++i)
|
||||
ModuleLevel[i] = CompactionTable[i].size();
|
||||
}
|
||||
|
||||
int SlotCalculator::getSlot(const Value *V) const {
|
||||
// If there is a CompactionTable active...
|
||||
if (!CompactionNodeMap.empty()) {
|
||||
std::map<const Value*, unsigned>::const_iterator I =
|
||||
CompactionNodeMap.find(V);
|
||||
if (I != CompactionNodeMap.end())
|
||||
return (int)I->second;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::map<const Value*, unsigned>::const_iterator I = NodeMap.find(V);
|
||||
if (I != NodeMap.end())
|
||||
return (int)I->second;
|
||||
@ -327,8 +485,11 @@ int SlotCalculator::getOrCreateSlot(const Value *V) {
|
||||
if (const ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(V))
|
||||
return getOrCreateSlot(CPR->getValue());
|
||||
|
||||
if (!isa<GlobalValue>(V))
|
||||
if (!isa<GlobalValue>(V)) // Initializers for globals are handled explicitly
|
||||
if (const Constant *C = dyn_cast<Constant>(V)) {
|
||||
assert(CompactionNodeMap.empty() &&
|
||||
"All needed constants should be in the compaction map already!");
|
||||
|
||||
// If we are emitting a bytecode file, do not index the characters that
|
||||
// make up constant strings. We emit constant strings as special
|
||||
// entities that don't require their individual characters to be emitted.
|
||||
@ -358,6 +519,17 @@ int SlotCalculator::insertValue(const Value *D, bool dontIgnore) {
|
||||
assert(D && "Can't insert a null value!");
|
||||
assert(getSlot(D) == -1 && "Value is already in the table!");
|
||||
|
||||
// If we are building a compaction map, and if this plane is being compacted,
|
||||
// insert the value into the compaction map, not into the global map.
|
||||
if (!CompactionNodeMap.empty()) {
|
||||
if (D->getType() == Type::VoidTy) return -1; // Do not insert void values
|
||||
assert(!isa<Type>(D) && !isa<Constant>(D) && !isa<GlobalValue>(D) &&
|
||||
"Types, constants, and globals should be in global SymTab!");
|
||||
|
||||
// FIXME: this does not yet handle partially compacted tables yet!
|
||||
return getOrCreateCompactionTableSlot(D);
|
||||
}
|
||||
|
||||
// If this node does not contribute to a plane, or if the node has a
|
||||
// name and we don't want names, then ignore the silly node... Note that types
|
||||
// do need slot numbers so that we can keep track of where other values land.
|
||||
@ -406,11 +578,6 @@ int SlotCalculator::insertValue(const Value *D, bool dontIgnore) {
|
||||
return doInsertValue(D);
|
||||
}
|
||||
|
||||
static inline bool hasNullValue(unsigned TyID) {
|
||||
return TyID != Type::LabelTyID && TyID != Type::TypeTyID &&
|
||||
TyID != Type::VoidTyID;
|
||||
}
|
||||
|
||||
// doInsertValue - This is a small helper function to be called only
|
||||
// be insertValue.
|
||||
//
|
||||
|
@ -36,6 +36,7 @@ using namespace llvm;
|
||||
|
||||
SlotCalculator::SlotCalculator(const Module *M, bool buildBytecodeInfo) {
|
||||
BuildBytecodeInfo = buildBytecodeInfo;
|
||||
ModuleContainsAllFunctionConstants = false;
|
||||
TheModule = M;
|
||||
|
||||
// Preload table... Make sure that all of the primitive types are in the table
|
||||
@ -53,6 +54,7 @@ SlotCalculator::SlotCalculator(const Module *M, bool buildBytecodeInfo) {
|
||||
|
||||
SlotCalculator::SlotCalculator(const Function *M, bool buildBytecodeInfo) {
|
||||
BuildBytecodeInfo = buildBytecodeInfo;
|
||||
ModuleContainsAllFunctionConstants = false;
|
||||
TheModule = M ? M->getParent() : 0;
|
||||
|
||||
// Preload table... Make sure that all of the primitive types are in the table
|
||||
@ -70,6 +72,17 @@ SlotCalculator::SlotCalculator(const Function *M, bool buildBytecodeInfo) {
|
||||
incorporateFunction(M); // Start out in incorporated state
|
||||
}
|
||||
|
||||
unsigned SlotCalculator::getGlobalSlot(const Value *V) const {
|
||||
assert(!CompactionTable.empty() &&
|
||||
"This method can only be used when compaction is enabled!");
|
||||
if (const ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(V))
|
||||
V = CPR->getValue();
|
||||
std::map<const Value*, unsigned>::const_iterator I = NodeMap.find(V);
|
||||
assert(I != NodeMap.end() && "Didn't find entry!");
|
||||
return I->second;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// processModule - Process all of the module level function declarations and
|
||||
// types that are available.
|
||||
@ -127,23 +140,36 @@ void SlotCalculator::processModule() {
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// FIXME: Empirically, this causes the bytecode files to get BIGGER, because
|
||||
// it explodes the operand size numbers to be bigger than can be handled
|
||||
// compactly, which offsets the ~40% savings in constant sizes. Whoops.
|
||||
|
||||
// If we are emitting a bytecode file, scan all of the functions for their
|
||||
// constants, which allows us to emit more compact modules. This is optional,
|
||||
// and is just used to compactify the constants used by different functions
|
||||
// together.
|
||||
//
|
||||
// This functionality is completely optional for the bytecode writer, but
|
||||
// tends to produce smaller bytecode files. This should not be used in the
|
||||
// future by clients that want to, for example, build and emit functions on
|
||||
// the fly. For now, however, it is unconditionally enabled when building
|
||||
// bytecode information.
|
||||
//
|
||||
if (BuildBytecodeInfo) {
|
||||
ModuleContainsAllFunctionConstants = true;
|
||||
|
||||
SC_DEBUG("Inserting function constants:\n");
|
||||
for (Module::const_iterator F = TheModule->begin(), E = TheModule->end();
|
||||
F != E; ++F)
|
||||
for_each(constant_begin(F), constant_end(F),
|
||||
bind_obj(this, &SlotCalculator::getOrCreateSlot));
|
||||
F != E; ++F) {
|
||||
for (const_inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I){
|
||||
for (unsigned op = 0, e = I->getNumOperands(); op != e; ++op)
|
||||
if (isa<Constant>(I->getOperand(op)))
|
||||
getOrCreateSlot(I->getOperand(op));
|
||||
getOrCreateSlot(I->getType());
|
||||
if (const VANextInst *VAN = dyn_cast<VANextInst>(*I))
|
||||
getOrCreateSlot(VAN->getArgType());
|
||||
}
|
||||
processSymbolTableConstants(&F->getSymbolTable());
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
// Insert constants that are named at module level into the slot pool so that
|
||||
// the module symbol table can refer to them...
|
||||
@ -220,27 +246,32 @@ void SlotCalculator::incorporateFunction(const Function *F) {
|
||||
|
||||
SC_DEBUG("begin processFunction!\n");
|
||||
|
||||
// Save the Table state before we process the function...
|
||||
for (unsigned i = 0; i < Table.size(); ++i)
|
||||
ModuleLevel.push_back(Table[i].size());
|
||||
|
||||
SC_DEBUG("Inserting function arguments\n");
|
||||
// If we emitted all of the function constants, build a compaction table.
|
||||
if (BuildBytecodeInfo && ModuleContainsAllFunctionConstants)
|
||||
buildCompactionTable(F);
|
||||
else {
|
||||
// Save the Table state before we process the function...
|
||||
for (unsigned i = 0, e = Table.size(); i != e; ++i)
|
||||
ModuleLevel.push_back(Table[i].size());
|
||||
}
|
||||
|
||||
// Iterate over function arguments, adding them to the value table...
|
||||
for(Function::const_aiterator I = F->abegin(), E = F->aend(); I != E; ++I)
|
||||
getOrCreateSlot(I);
|
||||
|
||||
// Iterate over all of the instructions in the function, looking for constant
|
||||
// values that are referenced. Add these to the value pools before any
|
||||
// nonconstant values. This will be turned into the constant pool for the
|
||||
// bytecode writer.
|
||||
//
|
||||
if (BuildBytecodeInfo) { // Assembly writer does not need this!
|
||||
// Emit all of the constants that are being used by the instructions in the
|
||||
// function...
|
||||
if (BuildBytecodeInfo && // Assembly writer does not need this!
|
||||
!ModuleContainsAllFunctionConstants) {
|
||||
// Iterate over all of the instructions in the function, looking for
|
||||
// constant values that are referenced. Add these to the value pools
|
||||
// before any nonconstant values. This will be turned into the constant
|
||||
// pool for the bytecode writer.
|
||||
//
|
||||
|
||||
// Emit all of the constants that are being used by the instructions in
|
||||
// the function...
|
||||
for_each(constant_begin(F), constant_end(F),
|
||||
bind_obj(this, &SlotCalculator::getOrCreateSlot));
|
||||
|
||||
bind_obj(this, &SlotCalculator::getOrCreateSlot));
|
||||
|
||||
// If there is a symbol table, it is possible that the user has names for
|
||||
// constants that are not being used. In this case, we will have problems
|
||||
// if we don't emit the constants now, because otherwise we will get
|
||||
@ -271,42 +302,169 @@ void SlotCalculator::purgeFunction() {
|
||||
|
||||
SC_DEBUG("begin purgeFunction!\n");
|
||||
|
||||
// First, remove values from existing type planes
|
||||
for (unsigned i = 0; i < NumModuleTypes; ++i) {
|
||||
unsigned ModuleSize = ModuleLevel[i]; // Size of plane before function came
|
||||
TypePlane &CurPlane = Table[i];
|
||||
//SC_DEBUG("Processing Plane " <<i<< " of size " << CurPlane.size() <<"\n");
|
||||
|
||||
while (CurPlane.size() != ModuleSize) {
|
||||
//SC_DEBUG(" Removing [" << i << "] Value=" << CurPlane.back() << "\n");
|
||||
std::map<const Value *, unsigned>::iterator NI =
|
||||
NodeMap.find(CurPlane.back());
|
||||
assert(NI != NodeMap.end() && "Node not in nodemap?");
|
||||
NodeMap.erase(NI); // Erase from nodemap
|
||||
CurPlane.pop_back(); // Shrink plane
|
||||
// First, free the compaction map if used.
|
||||
CompactionNodeMap.clear();
|
||||
|
||||
// Next, remove values from existing type planes
|
||||
for (unsigned i = 0; i != NumModuleTypes; ++i)
|
||||
if (i >= CompactionTable.size() || CompactionTable[i].empty()) {
|
||||
unsigned ModuleSize = ModuleLevel[i];// Size of plane before function came
|
||||
TypePlane &CurPlane = Table[i];
|
||||
|
||||
while (CurPlane.size() != ModuleSize) {
|
||||
std::map<const Value *, unsigned>::iterator NI =
|
||||
NodeMap.find(CurPlane.back());
|
||||
assert(NI != NodeMap.end() && "Node not in nodemap?");
|
||||
NodeMap.erase(NI); // Erase from nodemap
|
||||
CurPlane.pop_back(); // Shrink plane
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We don't need this state anymore, free it up.
|
||||
ModuleLevel.clear();
|
||||
|
||||
// Next, remove any type planes defined by the function...
|
||||
while (NumModuleTypes != Table.size()) {
|
||||
TypePlane &Plane = Table.back();
|
||||
SC_DEBUG("Removing Plane " << (Table.size()-1) << " of size "
|
||||
<< Plane.size() << "\n");
|
||||
while (Plane.size()) {
|
||||
NodeMap.erase(NodeMap.find(Plane.back())); // Erase from nodemap
|
||||
Plane.pop_back(); // Shrink plane
|
||||
if (!CompactionTable.empty()) {
|
||||
CompactionTable.clear();
|
||||
} else {
|
||||
// FIXME: this will require adjustment when we don't compact everything.
|
||||
|
||||
// Finally, remove any type planes defined by the function...
|
||||
while (NumModuleTypes != Table.size()) {
|
||||
TypePlane &Plane = Table.back();
|
||||
SC_DEBUG("Removing Plane " << (Table.size()-1) << " of size "
|
||||
<< Plane.size() << "\n");
|
||||
while (Plane.size()) {
|
||||
NodeMap.erase(NodeMap.find(Plane.back())); // Erase from nodemap
|
||||
Plane.pop_back(); // Shrink plane
|
||||
}
|
||||
|
||||
Table.pop_back(); // Nuke the plane, we don't like it.
|
||||
}
|
||||
|
||||
Table.pop_back(); // Nuke the plane, we don't like it.
|
||||
}
|
||||
|
||||
SC_DEBUG("end purgeFunction!\n");
|
||||
}
|
||||
|
||||
static inline bool hasNullValue(unsigned TyID) {
|
||||
return TyID != Type::LabelTyID && TyID != Type::TypeTyID &&
|
||||
TyID != Type::VoidTyID;
|
||||
}
|
||||
|
||||
/// getOrCreateCompactionTableSlot - This method is used to build up the initial
|
||||
/// approximation of the compaction table.
|
||||
unsigned SlotCalculator::getOrCreateCompactionTableSlot(const Value *V) {
|
||||
std::map<const Value*, unsigned>::iterator I =
|
||||
CompactionNodeMap.lower_bound(V);
|
||||
if (I != CompactionNodeMap.end() && I->first == V)
|
||||
return I->second; // Already exists?
|
||||
|
||||
// Make sure the type is in the table.
|
||||
unsigned Ty = getOrCreateCompactionTableSlot(V->getType());
|
||||
if (CompactionTable.size() <= Ty)
|
||||
CompactionTable.resize(Ty+1);
|
||||
|
||||
assert(!isa<Type>(V) || ModuleLevel.empty());
|
||||
|
||||
TypePlane &TyPlane = CompactionTable[Ty];
|
||||
|
||||
// Make sure to insert the null entry if the thing we are inserting is not a
|
||||
// null constant.
|
||||
if (TyPlane.empty() && hasNullValue(V->getType()->getPrimitiveID())) {
|
||||
Value *ZeroInitializer = Constant::getNullValue(V->getType());
|
||||
if (V != ZeroInitializer) {
|
||||
TyPlane.push_back(ZeroInitializer);
|
||||
CompactionNodeMap[ZeroInitializer] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
unsigned SlotNo = TyPlane.size();
|
||||
TyPlane.push_back(V);
|
||||
CompactionNodeMap.insert(std::make_pair(V, SlotNo));
|
||||
return SlotNo;
|
||||
}
|
||||
|
||||
|
||||
/// buildCompactionTable - Since all of the function constants and types are
|
||||
/// stored in the module-level constant table, we don't need to emit a function
|
||||
/// constant table. Also due to this, the indices for various constants and
|
||||
/// types might be very large in large programs. In order to avoid blowing up
|
||||
/// the size of instructions in the bytecode encoding, we build a compaction
|
||||
/// table, which defines a mapping from function-local identifiers to global
|
||||
/// identifiers.
|
||||
void SlotCalculator::buildCompactionTable(const Function *F) {
|
||||
assert(CompactionNodeMap.empty() && "Compaction table already built!");
|
||||
// First step, insert the primitive types.
|
||||
CompactionTable.resize(Type::TypeTyID+1);
|
||||
for (unsigned i = 0; i != Type::FirstDerivedTyID; ++i) {
|
||||
const Type *PrimTy = Type::getPrimitiveType((Type::PrimitiveID)i);
|
||||
CompactionTable[Type::TypeTyID].push_back(PrimTy);
|
||||
CompactionNodeMap[PrimTy] = i;
|
||||
}
|
||||
|
||||
// Next, include any types used by function arguments.
|
||||
for (Function::const_aiterator I = F->abegin(), E = F->aend(); I != E; ++I)
|
||||
getOrCreateCompactionTableSlot(I->getType());
|
||||
|
||||
// Next, find all of the types and values that are referred to by the
|
||||
// instructions in the program.
|
||||
for (const_inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I) {
|
||||
getOrCreateCompactionTableSlot(I->getType());
|
||||
for (unsigned op = 0, e = I->getNumOperands(); op != e; ++op)
|
||||
if (isa<Constant>(I->getOperand(op)) ||
|
||||
isa<GlobalValue>(I->getOperand(op)))
|
||||
getOrCreateCompactionTableSlot(I->getOperand(op));
|
||||
if (const VANextInst *VAN = dyn_cast<VANextInst>(*I))
|
||||
getOrCreateCompactionTableSlot(VAN->getArgType());
|
||||
}
|
||||
|
||||
const SymbolTable &ST = F->getSymbolTable();
|
||||
for (SymbolTable::const_iterator I = ST.begin(), E = ST.end(); I != E; ++I)
|
||||
for (SymbolTable::type_const_iterator TI = I->second.begin(),
|
||||
TE = I->second.end(); TI != TE; ++TI)
|
||||
if (isa<Constant>(TI->second) || isa<Type>(TI->second) ||
|
||||
isa<GlobalValue>(TI->second))
|
||||
getOrCreateCompactionTableSlot(TI->second);
|
||||
|
||||
// Now that we have all of the values in the table, and know what types are
|
||||
// referenced, make sure that there is at least the zero initializer in any
|
||||
// used type plane. Since the type was used, we will be emitting instructions
|
||||
// to the plane even if there are no constants in it.
|
||||
CompactionTable.resize(CompactionTable[Type::TypeTyID].size());
|
||||
for (unsigned i = 0, e = CompactionTable.size(); i != e; ++i)
|
||||
if (CompactionTable[i].empty() && i != Type::VoidTyID &&
|
||||
i != Type::LabelTyID) {
|
||||
const Type *Ty = cast<Type>(CompactionTable[Type::TypeTyID][i]);
|
||||
getOrCreateCompactionTableSlot(Constant::getNullValue(Ty));
|
||||
}
|
||||
|
||||
// Okay, now at this point, we have a legal compaction table. Since we want
|
||||
// to emit the smallest possible binaries, we delete planes that do not NEED
|
||||
// to be compacted, starting with the type plane.
|
||||
|
||||
|
||||
// If decided not to compact anything, do not modify ModuleLevels.
|
||||
if (CompactionTable.empty())
|
||||
// FIXME: must update ModuleLevel.
|
||||
return;
|
||||
|
||||
// Finally, for any planes that we have decided to compact, update the
|
||||
// ModuleLevel entries to be accurate.
|
||||
|
||||
// FIXME: This does not yet work for partially compacted tables.
|
||||
ModuleLevel.resize(CompactionTable.size());
|
||||
for (unsigned i = 0, e = CompactionTable.size(); i != e; ++i)
|
||||
ModuleLevel[i] = CompactionTable[i].size();
|
||||
}
|
||||
|
||||
int SlotCalculator::getSlot(const Value *V) const {
|
||||
// If there is a CompactionTable active...
|
||||
if (!CompactionNodeMap.empty()) {
|
||||
std::map<const Value*, unsigned>::const_iterator I =
|
||||
CompactionNodeMap.find(V);
|
||||
if (I != CompactionNodeMap.end())
|
||||
return (int)I->second;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::map<const Value*, unsigned>::const_iterator I = NodeMap.find(V);
|
||||
if (I != NodeMap.end())
|
||||
return (int)I->second;
|
||||
@ -327,8 +485,11 @@ int SlotCalculator::getOrCreateSlot(const Value *V) {
|
||||
if (const ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(V))
|
||||
return getOrCreateSlot(CPR->getValue());
|
||||
|
||||
if (!isa<GlobalValue>(V))
|
||||
if (!isa<GlobalValue>(V)) // Initializers for globals are handled explicitly
|
||||
if (const Constant *C = dyn_cast<Constant>(V)) {
|
||||
assert(CompactionNodeMap.empty() &&
|
||||
"All needed constants should be in the compaction map already!");
|
||||
|
||||
// If we are emitting a bytecode file, do not index the characters that
|
||||
// make up constant strings. We emit constant strings as special
|
||||
// entities that don't require their individual characters to be emitted.
|
||||
@ -358,6 +519,17 @@ int SlotCalculator::insertValue(const Value *D, bool dontIgnore) {
|
||||
assert(D && "Can't insert a null value!");
|
||||
assert(getSlot(D) == -1 && "Value is already in the table!");
|
||||
|
||||
// If we are building a compaction map, and if this plane is being compacted,
|
||||
// insert the value into the compaction map, not into the global map.
|
||||
if (!CompactionNodeMap.empty()) {
|
||||
if (D->getType() == Type::VoidTy) return -1; // Do not insert void values
|
||||
assert(!isa<Type>(D) && !isa<Constant>(D) && !isa<GlobalValue>(D) &&
|
||||
"Types, constants, and globals should be in global SymTab!");
|
||||
|
||||
// FIXME: this does not yet handle partially compacted tables yet!
|
||||
return getOrCreateCompactionTableSlot(D);
|
||||
}
|
||||
|
||||
// If this node does not contribute to a plane, or if the node has a
|
||||
// name and we don't want names, then ignore the silly node... Note that types
|
||||
// do need slot numbers so that we can keep track of where other values land.
|
||||
@ -406,11 +578,6 @@ int SlotCalculator::insertValue(const Value *D, bool dontIgnore) {
|
||||
return doInsertValue(D);
|
||||
}
|
||||
|
||||
static inline bool hasNullValue(unsigned TyID) {
|
||||
return TyID != Type::LabelTyID && TyID != Type::TypeTyID &&
|
||||
TyID != Type::VoidTyID;
|
||||
}
|
||||
|
||||
// doInsertValue - This is a small helper function to be called only
|
||||
// be insertValue.
|
||||
//
|
||||
|
Loading…
Reference in New Issue
Block a user