mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-06-18 11:24:01 +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:
@ -36,6 +36,7 @@ using namespace llvm;
|
|||||||
|
|
||||||
SlotCalculator::SlotCalculator(const Module *M, bool buildBytecodeInfo) {
|
SlotCalculator::SlotCalculator(const Module *M, bool buildBytecodeInfo) {
|
||||||
BuildBytecodeInfo = buildBytecodeInfo;
|
BuildBytecodeInfo = buildBytecodeInfo;
|
||||||
|
ModuleContainsAllFunctionConstants = false;
|
||||||
TheModule = M;
|
TheModule = M;
|
||||||
|
|
||||||
// Preload table... Make sure that all of the primitive types are in the table
|
// 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) {
|
SlotCalculator::SlotCalculator(const Function *M, bool buildBytecodeInfo) {
|
||||||
BuildBytecodeInfo = buildBytecodeInfo;
|
BuildBytecodeInfo = buildBytecodeInfo;
|
||||||
|
ModuleContainsAllFunctionConstants = false;
|
||||||
TheModule = M ? M->getParent() : 0;
|
TheModule = M ? M->getParent() : 0;
|
||||||
|
|
||||||
// Preload table... Make sure that all of the primitive types are in the table
|
// 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
|
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
|
// processModule - Process all of the module level function declarations and
|
||||||
// types that are available.
|
// 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
|
// 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,
|
// constants, which allows us to emit more compact modules. This is optional,
|
||||||
// and is just used to compactify the constants used by different functions
|
// and is just used to compactify the constants used by different functions
|
||||||
// together.
|
// 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) {
|
if (BuildBytecodeInfo) {
|
||||||
|
ModuleContainsAllFunctionConstants = true;
|
||||||
|
|
||||||
SC_DEBUG("Inserting function constants:\n");
|
SC_DEBUG("Inserting function constants:\n");
|
||||||
for (Module::const_iterator F = TheModule->begin(), E = TheModule->end();
|
for (Module::const_iterator F = TheModule->begin(), E = TheModule->end();
|
||||||
F != E; ++F)
|
F != E; ++F) {
|
||||||
for_each(constant_begin(F), constant_end(F),
|
for (const_inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I){
|
||||||
bind_obj(this, &SlotCalculator::getOrCreateSlot));
|
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
|
// Insert constants that are named at module level into the slot pool so that
|
||||||
// the module symbol table can refer to them...
|
// the module symbol table can refer to them...
|
||||||
@ -220,24 +246,29 @@ void SlotCalculator::incorporateFunction(const Function *F) {
|
|||||||
|
|
||||||
SC_DEBUG("begin processFunction!\n");
|
SC_DEBUG("begin processFunction!\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...
|
// Save the Table state before we process the function...
|
||||||
for (unsigned i = 0; i < Table.size(); ++i)
|
for (unsigned i = 0, e = Table.size(); i != e; ++i)
|
||||||
ModuleLevel.push_back(Table[i].size());
|
ModuleLevel.push_back(Table[i].size());
|
||||||
|
}
|
||||||
SC_DEBUG("Inserting function arguments\n");
|
|
||||||
|
|
||||||
// Iterate over function arguments, adding them to the value table...
|
// Iterate over function arguments, adding them to the value table...
|
||||||
for(Function::const_aiterator I = F->abegin(), E = F->aend(); I != E; ++I)
|
for(Function::const_aiterator I = F->abegin(), E = F->aend(); I != E; ++I)
|
||||||
getOrCreateSlot(I);
|
getOrCreateSlot(I);
|
||||||
|
|
||||||
// Iterate over all of the instructions in the function, looking for constant
|
if (BuildBytecodeInfo && // Assembly writer does not need this!
|
||||||
// values that are referenced. Add these to the value pools before any
|
!ModuleContainsAllFunctionConstants) {
|
||||||
// nonconstant values. This will be turned into the constant pool for the
|
// Iterate over all of the instructions in the function, looking for
|
||||||
// bytecode writer.
|
// 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
|
// Emit all of the constants that are being used by the instructions in
|
||||||
// function...
|
// the function...
|
||||||
for_each(constant_begin(F), constant_end(F),
|
for_each(constant_begin(F), constant_end(F),
|
||||||
bind_obj(this, &SlotCalculator::getOrCreateSlot));
|
bind_obj(this, &SlotCalculator::getOrCreateSlot));
|
||||||
|
|
||||||
@ -271,14 +302,16 @@ void SlotCalculator::purgeFunction() {
|
|||||||
|
|
||||||
SC_DEBUG("begin purgeFunction!\n");
|
SC_DEBUG("begin purgeFunction!\n");
|
||||||
|
|
||||||
// First, remove values from existing type planes
|
// First, free the compaction map if used.
|
||||||
for (unsigned i = 0; i < NumModuleTypes; ++i) {
|
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
|
unsigned ModuleSize = ModuleLevel[i];// Size of plane before function came
|
||||||
TypePlane &CurPlane = Table[i];
|
TypePlane &CurPlane = Table[i];
|
||||||
//SC_DEBUG("Processing Plane " <<i<< " of size " << CurPlane.size() <<"\n");
|
|
||||||
|
|
||||||
while (CurPlane.size() != ModuleSize) {
|
while (CurPlane.size() != ModuleSize) {
|
||||||
//SC_DEBUG(" Removing [" << i << "] Value=" << CurPlane.back() << "\n");
|
|
||||||
std::map<const Value *, unsigned>::iterator NI =
|
std::map<const Value *, unsigned>::iterator NI =
|
||||||
NodeMap.find(CurPlane.back());
|
NodeMap.find(CurPlane.back());
|
||||||
assert(NI != NodeMap.end() && "Node not in nodemap?");
|
assert(NI != NodeMap.end() && "Node not in nodemap?");
|
||||||
@ -290,7 +323,12 @@ void SlotCalculator::purgeFunction() {
|
|||||||
// We don't need this state anymore, free it up.
|
// We don't need this state anymore, free it up.
|
||||||
ModuleLevel.clear();
|
ModuleLevel.clear();
|
||||||
|
|
||||||
// Next, remove any type planes defined by the function...
|
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()) {
|
while (NumModuleTypes != Table.size()) {
|
||||||
TypePlane &Plane = Table.back();
|
TypePlane &Plane = Table.back();
|
||||||
SC_DEBUG("Removing Plane " << (Table.size()-1) << " of size "
|
SC_DEBUG("Removing Plane " << (Table.size()-1) << " of size "
|
||||||
@ -302,11 +340,131 @@ void SlotCalculator::purgeFunction() {
|
|||||||
|
|
||||||
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");
|
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 {
|
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);
|
std::map<const Value*, unsigned>::const_iterator I = NodeMap.find(V);
|
||||||
if (I != NodeMap.end())
|
if (I != NodeMap.end())
|
||||||
return (int)I->second;
|
return (int)I->second;
|
||||||
@ -327,8 +485,11 @@ int SlotCalculator::getOrCreateSlot(const Value *V) {
|
|||||||
if (const ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(V))
|
if (const ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(V))
|
||||||
return getOrCreateSlot(CPR->getValue());
|
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)) {
|
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
|
// If we are emitting a bytecode file, do not index the characters that
|
||||||
// make up constant strings. We emit constant strings as special
|
// make up constant strings. We emit constant strings as special
|
||||||
// entities that don't require their individual characters to be emitted.
|
// 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(D && "Can't insert a null value!");
|
||||||
assert(getSlot(D) == -1 && "Value is already in the table!");
|
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
|
// 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
|
// 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.
|
// 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);
|
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
|
// doInsertValue - This is a small helper function to be called only
|
||||||
// be insertValue.
|
// be insertValue.
|
||||||
//
|
//
|
||||||
|
@ -36,6 +36,7 @@ using namespace llvm;
|
|||||||
|
|
||||||
SlotCalculator::SlotCalculator(const Module *M, bool buildBytecodeInfo) {
|
SlotCalculator::SlotCalculator(const Module *M, bool buildBytecodeInfo) {
|
||||||
BuildBytecodeInfo = buildBytecodeInfo;
|
BuildBytecodeInfo = buildBytecodeInfo;
|
||||||
|
ModuleContainsAllFunctionConstants = false;
|
||||||
TheModule = M;
|
TheModule = M;
|
||||||
|
|
||||||
// Preload table... Make sure that all of the primitive types are in the table
|
// 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) {
|
SlotCalculator::SlotCalculator(const Function *M, bool buildBytecodeInfo) {
|
||||||
BuildBytecodeInfo = buildBytecodeInfo;
|
BuildBytecodeInfo = buildBytecodeInfo;
|
||||||
|
ModuleContainsAllFunctionConstants = false;
|
||||||
TheModule = M ? M->getParent() : 0;
|
TheModule = M ? M->getParent() : 0;
|
||||||
|
|
||||||
// Preload table... Make sure that all of the primitive types are in the table
|
// 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
|
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
|
// processModule - Process all of the module level function declarations and
|
||||||
// types that are available.
|
// 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
|
// 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,
|
// constants, which allows us to emit more compact modules. This is optional,
|
||||||
// and is just used to compactify the constants used by different functions
|
// and is just used to compactify the constants used by different functions
|
||||||
// together.
|
// 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) {
|
if (BuildBytecodeInfo) {
|
||||||
|
ModuleContainsAllFunctionConstants = true;
|
||||||
|
|
||||||
SC_DEBUG("Inserting function constants:\n");
|
SC_DEBUG("Inserting function constants:\n");
|
||||||
for (Module::const_iterator F = TheModule->begin(), E = TheModule->end();
|
for (Module::const_iterator F = TheModule->begin(), E = TheModule->end();
|
||||||
F != E; ++F)
|
F != E; ++F) {
|
||||||
for_each(constant_begin(F), constant_end(F),
|
for (const_inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I){
|
||||||
bind_obj(this, &SlotCalculator::getOrCreateSlot));
|
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
|
// Insert constants that are named at module level into the slot pool so that
|
||||||
// the module symbol table can refer to them...
|
// the module symbol table can refer to them...
|
||||||
@ -220,24 +246,29 @@ void SlotCalculator::incorporateFunction(const Function *F) {
|
|||||||
|
|
||||||
SC_DEBUG("begin processFunction!\n");
|
SC_DEBUG("begin processFunction!\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...
|
// Save the Table state before we process the function...
|
||||||
for (unsigned i = 0; i < Table.size(); ++i)
|
for (unsigned i = 0, e = Table.size(); i != e; ++i)
|
||||||
ModuleLevel.push_back(Table[i].size());
|
ModuleLevel.push_back(Table[i].size());
|
||||||
|
}
|
||||||
SC_DEBUG("Inserting function arguments\n");
|
|
||||||
|
|
||||||
// Iterate over function arguments, adding them to the value table...
|
// Iterate over function arguments, adding them to the value table...
|
||||||
for(Function::const_aiterator I = F->abegin(), E = F->aend(); I != E; ++I)
|
for(Function::const_aiterator I = F->abegin(), E = F->aend(); I != E; ++I)
|
||||||
getOrCreateSlot(I);
|
getOrCreateSlot(I);
|
||||||
|
|
||||||
// Iterate over all of the instructions in the function, looking for constant
|
if (BuildBytecodeInfo && // Assembly writer does not need this!
|
||||||
// values that are referenced. Add these to the value pools before any
|
!ModuleContainsAllFunctionConstants) {
|
||||||
// nonconstant values. This will be turned into the constant pool for the
|
// Iterate over all of the instructions in the function, looking for
|
||||||
// bytecode writer.
|
// 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
|
// Emit all of the constants that are being used by the instructions in
|
||||||
// function...
|
// the function...
|
||||||
for_each(constant_begin(F), constant_end(F),
|
for_each(constant_begin(F), constant_end(F),
|
||||||
bind_obj(this, &SlotCalculator::getOrCreateSlot));
|
bind_obj(this, &SlotCalculator::getOrCreateSlot));
|
||||||
|
|
||||||
@ -271,14 +302,16 @@ void SlotCalculator::purgeFunction() {
|
|||||||
|
|
||||||
SC_DEBUG("begin purgeFunction!\n");
|
SC_DEBUG("begin purgeFunction!\n");
|
||||||
|
|
||||||
// First, remove values from existing type planes
|
// First, free the compaction map if used.
|
||||||
for (unsigned i = 0; i < NumModuleTypes; ++i) {
|
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
|
unsigned ModuleSize = ModuleLevel[i];// Size of plane before function came
|
||||||
TypePlane &CurPlane = Table[i];
|
TypePlane &CurPlane = Table[i];
|
||||||
//SC_DEBUG("Processing Plane " <<i<< " of size " << CurPlane.size() <<"\n");
|
|
||||||
|
|
||||||
while (CurPlane.size() != ModuleSize) {
|
while (CurPlane.size() != ModuleSize) {
|
||||||
//SC_DEBUG(" Removing [" << i << "] Value=" << CurPlane.back() << "\n");
|
|
||||||
std::map<const Value *, unsigned>::iterator NI =
|
std::map<const Value *, unsigned>::iterator NI =
|
||||||
NodeMap.find(CurPlane.back());
|
NodeMap.find(CurPlane.back());
|
||||||
assert(NI != NodeMap.end() && "Node not in nodemap?");
|
assert(NI != NodeMap.end() && "Node not in nodemap?");
|
||||||
@ -290,7 +323,12 @@ void SlotCalculator::purgeFunction() {
|
|||||||
// We don't need this state anymore, free it up.
|
// We don't need this state anymore, free it up.
|
||||||
ModuleLevel.clear();
|
ModuleLevel.clear();
|
||||||
|
|
||||||
// Next, remove any type planes defined by the function...
|
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()) {
|
while (NumModuleTypes != Table.size()) {
|
||||||
TypePlane &Plane = Table.back();
|
TypePlane &Plane = Table.back();
|
||||||
SC_DEBUG("Removing Plane " << (Table.size()-1) << " of size "
|
SC_DEBUG("Removing Plane " << (Table.size()-1) << " of size "
|
||||||
@ -302,11 +340,131 @@ void SlotCalculator::purgeFunction() {
|
|||||||
|
|
||||||
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");
|
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 {
|
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);
|
std::map<const Value*, unsigned>::const_iterator I = NodeMap.find(V);
|
||||||
if (I != NodeMap.end())
|
if (I != NodeMap.end())
|
||||||
return (int)I->second;
|
return (int)I->second;
|
||||||
@ -327,8 +485,11 @@ int SlotCalculator::getOrCreateSlot(const Value *V) {
|
|||||||
if (const ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(V))
|
if (const ConstantPointerRef *CPR = dyn_cast<ConstantPointerRef>(V))
|
||||||
return getOrCreateSlot(CPR->getValue());
|
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)) {
|
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
|
// If we are emitting a bytecode file, do not index the characters that
|
||||||
// make up constant strings. We emit constant strings as special
|
// make up constant strings. We emit constant strings as special
|
||||||
// entities that don't require their individual characters to be emitted.
|
// 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(D && "Can't insert a null value!");
|
||||||
assert(getSlot(D) == -1 && "Value is already in the table!");
|
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
|
// 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
|
// 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.
|
// 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);
|
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
|
// doInsertValue - This is a small helper function to be called only
|
||||||
// be insertValue.
|
// be insertValue.
|
||||||
//
|
//
|
||||||
|
Reference in New Issue
Block a user