From e8fdde179d610ad4452b0cbed67d17b78ace4e38 Mon Sep 17 00:00:00 2001 From: Chris Lattner Date: Fri, 7 Sep 2001 16:39:41 +0000 Subject: [PATCH] * Emit bytecode using a deque instead of a vector to be faster * Internal rep no longer has a constant pool * Support emission of recursive types * Don't output a constant pool for an external method * The bytecode writer is no longer a module analyzer git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@449 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Bytecode/Writer/ConstantWriter.cpp | 8 +- lib/Bytecode/Writer/InstructionWriter.cpp | 23 ++-- lib/Bytecode/Writer/Writer.cpp | 131 ++++++++++++++-------- lib/Bytecode/Writer/WriterInternals.h | 25 +++-- 4 files changed, 114 insertions(+), 73 deletions(-) diff --git a/lib/Bytecode/Writer/ConstantWriter.cpp b/lib/Bytecode/Writer/ConstantWriter.cpp index bac5a4c0502..4940ea671be 100644 --- a/lib/Bytecode/Writer/ConstantWriter.cpp +++ b/lib/Bytecode/Writer/ConstantWriter.cpp @@ -81,10 +81,10 @@ void BytecodeWriter::outputType(const Type *T) { } case Type::ModuleTyID: - case Type::PackedTyID: + //case Type::PackedTyID: default: cerr << __FILE__ << ":" << __LINE__ << ": Don't know how to serialize" - << " Type '" << T->getName() << "'\n"; + << " Type '" << T->getDescription() << "'\n"; break; } } @@ -113,7 +113,7 @@ bool BytecodeWriter::outputConstant(const ConstPoolVal *CPV) { break; case Type::TypeTyID: // Serialize type type - outputType(((const ConstPoolType*)CPV)->getValue()); + assert(0 && "Types should not be in the ConstPool!"); break; case Type::ArrayTyID: { @@ -123,7 +123,7 @@ bool BytecodeWriter::outputConstant(const ConstPoolVal *CPV) { output_vbr(size, Out); // Not for sized arrays!!! for (unsigned i = 0; i < size; i++) { - int Slot = Table.getValSlot(CPA->getValues()[i]); + int Slot = Table.getValSlot(CPA->getOperand(i)); assert(Slot != -1 && "Constant used but not available!!"); output_vbr((unsigned)Slot, Out); } diff --git a/lib/Bytecode/Writer/InstructionWriter.cpp b/lib/Bytecode/Writer/InstructionWriter.cpp index 3ab882fa7b9..3859ea83030 100644 --- a/lib/Bytecode/Writer/InstructionWriter.cpp +++ b/lib/Bytecode/Writer/InstructionWriter.cpp @@ -26,7 +26,7 @@ typedef unsigned char uchar; // static void outputInstructionFormat0(const Instruction *I, const SlotCalculator &Table, - unsigned Type, vector &Out) { + unsigned Type, deque &Out) { // Opcode must have top two bits clear... output_vbr(I->getOpcode(), Out); // Instruction Opcode ID output_vbr(Type, Out); // Result type @@ -54,7 +54,7 @@ static void outputInstructionFormat0(const Instruction *I, // static void outputInstrVarArgsCall(const Instruction *I, const SlotCalculator &Table, unsigned Type, - vector &Out) { + deque &Out) { assert(I->getOpcode() == Instruction::Call /*|| I->getOpcode() == Instruction::ICall */); // Opcode must have top two bits clear... @@ -94,7 +94,7 @@ static void outputInstrVarArgsCall(const Instruction *I, // static void outputInstructionFormat1(const Instruction *I, const SlotCalculator &Table, int *Slots, - unsigned Type, vector &Out) { + unsigned Type, deque &Out) { unsigned IType = I->getOpcode(); // Instruction Opcode ID // bits Instruction format: @@ -115,7 +115,7 @@ static void outputInstructionFormat1(const Instruction *I, // static void outputInstructionFormat2(const Instruction *I, const SlotCalculator &Table, int *Slots, - unsigned Type, vector &Out) { + unsigned Type, deque &Out) { unsigned IType = I->getOpcode(); // Instruction Opcode ID // bits Instruction format: @@ -139,7 +139,7 @@ static void outputInstructionFormat2(const Instruction *I, // static void outputInstructionFormat3(const Instruction *I, const SlotCalculator &Table, int *Slots, - unsigned Type, vector &Out) { + unsigned Type, deque &Out) { unsigned IType = I->getOpcode(); // Instruction Opcode ID // bits Instruction format: @@ -158,7 +158,9 @@ static void outputInstructionFormat3(const Instruction *I, output(Opcode, Out); } -bool BytecodeWriter::processInstruction(const Instruction *I) { +#include "llvm/Assembly/Writer.h" + +void BytecodeWriter::processInstruction(const Instruction *I) { assert(I->getOpcode() < 64 && "Opcode too big???"); unsigned NumOperands = I->getNumOperands(); @@ -215,7 +217,7 @@ bool BytecodeWriter::processInstruction(const Instruction *I) { } else if (I->getOpcode() == Instruction::Call && // Handle VarArg calls I->getOperand(0)->getType()->isMethodType()->isVarArg()) { outputInstrVarArgsCall(I, Table, Type, Out); - return false; + return; } // Decide which instruction encoding to use. This is determined primarily by @@ -228,21 +230,21 @@ bool BytecodeWriter::processInstruction(const Instruction *I) { case 1: if (MaxOpSlot < (1 << 12)-1) { // -1 because we use 4095 to indicate 0 ops outputInstructionFormat1(I, Table, Slots, Type, Out); - return false; + return; } break; case 2: if (MaxOpSlot < (1 << 8)) { outputInstructionFormat2(I, Table, Slots, Type, Out); - return false; + return; } break; case 3: if (MaxOpSlot < (1 << 6)) { outputInstructionFormat3(I, Table, Slots, Type, Out); - return false; + return; } break; } @@ -250,5 +252,4 @@ bool BytecodeWriter::processInstruction(const Instruction *I) { // If we weren't handled before here, we either have a large number of // operands or a large operand index that we are refering to. outputInstructionFormat0(I, Table, Type, Out); - return false; } diff --git a/lib/Bytecode/Writer/Writer.cpp b/lib/Bytecode/Writer/Writer.cpp index 32ea04009cd..42351458229 100644 --- a/lib/Bytecode/Writer/Writer.cpp +++ b/lib/Bytecode/Writer/Writer.cpp @@ -6,13 +6,15 @@ // variables in the method tables... // // Note that this file uses an unusual technique of outputting all the bytecode -// to a vector of unsigned char's, then copies the vector to an ostream. The +// to a deque of unsigned char's, then copies the deque to an ostream. The // reason for this is that we must do "seeking" in the stream to do back- // patching, and some very important ostreams that we want to support (like // pipes) do not support seeking. :( :( :( // -// The choice of the vector data structure is influenced by the extremely fast -// "append" speed, plus the free "seek"/replace in the middle of the stream. +// The choice of the deque data structure is influenced by the extremely fast +// "append" speed, plus the free "seek"/replace in the middle of the stream. I +// didn't use a vector because the stream could end up very large and copying +// the whole thing to reallocate would be kinda silly. // // Note that the performance of this library is not terribly important, because // it shouldn't be used by JIT type applications... so it is not a huge focus @@ -27,10 +29,11 @@ #include "llvm/ConstPoolVals.h" #include "llvm/SymbolTable.h" #include "llvm/DerivedTypes.h" +#include "llvm/Support/STLExtras.h" #include #include -BytecodeWriter::BytecodeWriter(vector &o, const Module *M) +BytecodeWriter::BytecodeWriter(deque &o, const Module *M) : Out(o), Table(M, false) { outputSignature(); @@ -38,14 +41,21 @@ BytecodeWriter::BytecodeWriter(vector &o, const Module *M) // Emit the top level CLASS block. BytecodeBlock ModuleBlock(BytecodeFormat::Module, Out); - // Output largest ID of first "primitive" type: + // Output the ID of first "derived" type: output_vbr((unsigned)Type::FirstDerivedTyID, Out); align32(Out); - // Do the whole module now! - processModule(M); + // Output module level constants, including types used by the method protos + outputConstants(false); - // If needed, output the symbol table for the class... + // The ModuleInfoBlock follows directly after the Module constant pool + outputModuleInfoBlock(M); + + // Do the whole module now! Process each method at a time... + for_each(M->begin(), M->end(), + bind_obj(this, &BytecodeWriter::processMethod)); + + // If needed, output the symbol table for the module... if (M->hasSymbolTable()) outputSymbolTable(*M->getSymbolTable()); } @@ -53,53 +63,55 @@ BytecodeWriter::BytecodeWriter(vector &o, const Module *M) // TODO: REMOVE #include "llvm/Assembly/Writer.h" -bool BytecodeWriter::processConstPool(const ConstantPool &CP, bool isMethod) { - BytecodeBlock *CPool = new BytecodeBlock(BytecodeFormat::ConstantPool, Out); +void BytecodeWriter::outputConstants(bool isMethod) { + BytecodeBlock CPool(BytecodeFormat::ConstantPool, Out); unsigned NumPlanes = Table.getNumPlanes(); - for (unsigned pno = 0; pno < NumPlanes; pno++) { const vector &Plane = Table.getPlane(pno); - if (Plane.empty()) continue; // Skip empty type planes... + if (Plane.empty()) continue; // Skip empty type planes... - unsigned ValNo = 0; // Don't reemit module constants - if (isMethod) ValNo = Table.getModuleLevel(pno); + unsigned ValNo = 0; + if (isMethod) // Don't reemit module constants + ValNo = Table.getModuleLevel(pno); + else if (pno == Type::TypeTyID) + ValNo = Type::FirstDerivedTyID; // Start emitting at the derived types... - unsigned NumConstants = 0; - for (unsigned vn = ValNo; vn < Plane.size(); vn++) - if (Plane[vn]->isConstant()) - NumConstants++; + // Scan through and ignore method arguments... + for (; ValNo < Plane.size() && Plane[ValNo]->isMethodArgument(); ValNo++) + /*empty*/; - if (NumConstants == 0) continue; // Skip empty type planes... + unsigned NC = ValNo; // Number of constants + for (; NC < Plane.size() && + (Plane[NC]->isConstant() || Plane[NC]->isType()); NC++) /*empty*/; + NC -= ValNo; // Convert from index into count + if (NC == 0) continue; // Skip empty type planes... // Output type header: [num entries][type id number] // - output_vbr(NumConstants, Out); + output_vbr(NC, Out); // Output the Type ID Number... int Slot = Table.getValSlot(Plane.front()->getType()); assert (Slot != -1 && "Type in constant pool but not in method!!"); output_vbr((unsigned)Slot, Out); - //cerr << "NC: " << NumConstants << " Slot = " << hex << Slot << endl; + //cout << "Emitting " << NC << " constants of type '" + // << Plane.front()->getType()->getName() << "' = Slot #" << Slot << endl; - for (; ValNo < Plane.size(); ValNo++) { - const Value *V = Plane[ValNo]; + for (unsigned i = ValNo; i < ValNo+NC; ++i) { + const Value *V = Plane[i]; if (const ConstPoolVal *CPV = V->castConstant()) { //cerr << "Serializing value: <" << V->getType() << ">: " // << ((const ConstPoolVal*)V)->getStrValue() << ":" // << Out.size() << "\n"; outputConstant(CPV); + } else { + const Type *Ty = V->castTypeAsserting(); + outputType(Ty); } } } - - delete CPool; // End bytecode block section! - - if (!isMethod) // The ModuleInfoBlock follows directly after the c-pool - outputModuleInfoBlock(CP.getParentV()->castModuleAsserting()); - - return false; } void BytecodeWriter::outputModuleInfoBlock(const Module *M) { @@ -116,25 +128,35 @@ void BytecodeWriter::outputModuleInfoBlock(const Module *M) { align32(Out); } -bool BytecodeWriter::processMethod(const Method *M) { +void BytecodeWriter::processMethod(const Method *M) { BytecodeBlock MethodBlock(BytecodeFormat::Method, Out); - Table.incorporateMethod(M); + // Only output the constant pool and other goodies if needed... + if (!M->isExternal()) { + // Get slot information about the method... + Table.incorporateMethod(M); - if (ModuleAnalyzer::processMethod(M)) return true; - - // If needed, output the symbol table for the method... - if (M->hasSymbolTable()) - outputSymbolTable(*M->getSymbolTable()); + // Output information about the constants in the method... + outputConstants(true); - Table.purgeMethod(); - return false; + // Output basic block nodes... + for_each(M->begin(), M->end(), + bind_obj(this, &BytecodeWriter::processBasicBlock)); + + // If needed, output the symbol table for the method... + if (M->hasSymbolTable()) + outputSymbolTable(*M->getSymbolTable()); + + Table.purgeMethod(); + } } -bool BytecodeWriter::processBasicBlock(const BasicBlock *BB) { +void BytecodeWriter::processBasicBlock(const BasicBlock *BB) { BytecodeBlock MethodBlock(BytecodeFormat::BasicBlock, Out); - return ModuleAnalyzer::processBasicBlock(BB); + // Process all the instructions in the bb... + for_each(BB->begin(), BB->end(), + bind_obj(this, &BytecodeWriter::processInstruction)); } void BytecodeWriter::outputSymbolTable(const SymbolTable &MST) { @@ -157,7 +179,7 @@ void BytecodeWriter::outputSymbolTable(const SymbolTable &MST) { for (; I != End; ++I) { // Symtab entry: [def slot #][name] Slot = Table.getValSlot(I->second); - assert (Slot != -1 && "Value in symtab but not in method!!"); + assert(Slot != -1 && "Value in symtab but has no slot number!!"); output_vbr((unsigned)Slot, Out); output(I->first, Out, false); // Don't force alignment... } @@ -165,14 +187,31 @@ void BytecodeWriter::outputSymbolTable(const SymbolTable &MST) { } void WriteBytecodeToFile(const Module *C, ostream &Out) { - assert(C && "You can't write a null class!!"); + assert(C && "You can't write a null module!!"); - vector Buffer; + deque Buffer; // This object populates buffer for us... BytecodeWriter BCW(Buffer, C); - // Okay, write the vector out to the ostream now... - Out.write(&Buffer[0], Buffer.size()); + // Okay, write the deque out to the ostream now... the deque is not + // sequential in memory, however, so write out as much as possible in big + // chunks, until we're done. + // + deque::const_iterator I = Buffer.begin(), E = Buffer.end(); + while (I != E) { // Loop until it's all written + // Scan to see how big this chunk is... + const unsigned char *ChunkPtr = &*I; + const unsigned char *LastPtr = ChunkPtr; + while (I != E) { + const unsigned char *ThisPtr = &*++I; + if (LastPtr+1 != ThisPtr) break;// Advanced by more than a byte of memory? + LastPtr = ThisPtr; + } + + // Write out the chunk... + Out.write(ChunkPtr, LastPtr-ChunkPtr+(I != E)); + } + Out.flush(); } diff --git a/lib/Bytecode/Writer/WriterInternals.h b/lib/Bytecode/Writer/WriterInternals.h index 41f0b4de753..ff49c1d7fbb 100644 --- a/lib/Bytecode/Writer/WriterInternals.h +++ b/lib/Bytecode/Writer/WriterInternals.h @@ -14,22 +14,22 @@ #include "llvm/Bytecode/Writer.h" #include "llvm/Bytecode/Format.h" -#include "llvm/Bytecode/Primitives.h" #include "llvm/Analysis/SlotCalculator.h" -#include "llvm/Support/DataTypes.h" +#include "llvm/Bytecode/Primitives.h" #include "llvm/Instruction.h" +#include -class BytecodeWriter : public ModuleAnalyzer { - vector &Out; +class BytecodeWriter { + deque &Out; SlotCalculator Table; public: - BytecodeWriter(vector &o, const Module *M); + BytecodeWriter(deque &o, const Module *M); protected: - virtual bool processConstPool(const ConstantPool &CP, bool isMethod); - virtual bool processMethod(const Method *M); - virtual bool processBasicBlock(const BasicBlock *BB); - virtual bool processInstruction(const Instruction *I); + void outputConstants(bool isMethod); + void processMethod(const Method *M); + void processBasicBlock(const BasicBlock *BB); + void processInstruction(const Instruction *I); private : inline void outputSignature() { @@ -51,12 +51,12 @@ private : // class BytecodeBlock { unsigned Loc; - vector &Out; + deque &Out; BytecodeBlock(const BytecodeBlock &); // do not implement void operator=(const BytecodeBlock &); // do not implement public: - inline BytecodeBlock(unsigned ID, vector &o) : Out(o) { + inline BytecodeBlock(unsigned ID, deque &o) : Out(o) { output(ID, Out); output((unsigned)0, Out); // Reserve the space for the block size... Loc = Out.size(); @@ -64,7 +64,8 @@ public: inline ~BytecodeBlock() { // Do backpatch when block goes out // of scope... - // cerr << "OldLoc = " << Loc << " NewLoc = " << NewLoc << " diff = " << (NewLoc-Loc) << endl; + //cerr << "OldLoc = " << Loc << " NewLoc = " << NewLoc << " diff = " + // << (NewLoc-Loc) << endl; output((unsigned)(Out.size()-Loc), Out, (int)Loc-4); align32(Out); // Blocks must ALWAYS be aligned }