aboutsummaryrefslogtreecommitdiffstats
path: root/lib/Bytecode/Writer
diff options
context:
space:
mode:
authorChris Lattner <sabre@nondot.org>2004-01-18 21:07:07 +0000
committerChris Lattner <sabre@nondot.org>2004-01-18 21:07:07 +0000
commit614cdcd0023ed382afb6183c38dbb1ee772e05ee (patch)
tree3c32b90918d3c999115c5f87b117ed7145041493 /lib/Bytecode/Writer
parentaf894e963bb3b98605e161b662c66b2286eef140 (diff)
downloadexternal_llvm-614cdcd0023ed382afb6183c38dbb1ee772e05ee.zip
external_llvm-614cdcd0023ed382afb6183c38dbb1ee772e05ee.tar.gz
external_llvm-614cdcd0023ed382afb6183c38dbb1ee772e05ee.tar.bz2
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
Diffstat (limited to 'lib/Bytecode/Writer')
-rw-r--r--lib/Bytecode/Writer/SlotCalculator.cpp275
1 files changed, 221 insertions, 54 deletions
diff --git a/lib/Bytecode/Writer/SlotCalculator.cpp b/lib/Bytecode/Writer/SlotCalculator.cpp
index 2c6dd5e..1ba895e 100644
--- a/lib/Bytecode/Writer/SlotCalculator.cpp
+++ b/lib/Bytecode/Writer/SlotCalculator.cpp
@@ -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.
+ }
+ }
+ 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;
+}
+
- Table.pop_back(); // Nuke the plane, we don't like it.
+/// 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;
}
- SC_DEBUG("end purgeFunction!\n");
+ // 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.
//