aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKarl Schimpf <kschimpf@google.com>2013-07-02 14:36:36 -0700
committerKarl Schimpf <kschimpf@google.com>2013-07-02 14:36:36 -0700
commit4390b1a9ac86937b9f9886119b5fa0ffc77295eb (patch)
tree0ed3cbe3018c87f90bb55627066f6bdd5cfae9e0
parentbdad02d55969b0963ac23e05578d28def5636ebd (diff)
Simplify globals in PNaCl wire format based on normalized constants.
Generates simple global variable records, followed by list of records defining byte initialization and relocations. BUG= https://code.google.com/p/nativeclient/issues/detail?id=3504 R=mseaborn@chromium.org Review URL: https://codereview.chromium.org/18111002
-rw-r--r--include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h34
-rw-r--r--lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp291
-rw-r--r--lib/Bitcode/NaCl/Reader/NaClBitcodeReader.h24
-rw-r--r--lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp257
-rw-r--r--lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp22
-rw-r--r--lib/Bitcode/NaCl/Writer/NaClValueEnumerator.h13
-rw-r--r--test/NaCl/Bitcode/globalvars.ll98
-rw-r--r--tools/pnacl-bcanalyzer/pnacl-bcanalyzer.cpp11
8 files changed, 573 insertions, 177 deletions
diff --git a/include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h b/include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h
index f4fc4b8e6f..7d270cd98c 100644
--- a/include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h
+++ b/include/llvm/Bitcode/NaCl/NaClLLVMBitCodes.h
@@ -43,7 +43,8 @@ namespace naclbitc {
TYPE_BLOCK_ID_NEW,
- USELIST_BLOCK_ID
+ USELIST_BLOCK_ID,
+ GLOBALVAR_BLOCK_ID
};
@@ -58,9 +59,7 @@ namespace naclbitc {
// FIXME: Remove DEPLIB in 4.0.
MODULE_CODE_DEPLIB = 6, // DEPLIB: [strchr x N]
- // GLOBALVAR: [pointer type, isconst, initid,
- // linkage, alignment, section, visibility, threadlocal]
- MODULE_CODE_GLOBALVAR = 7,
+ MODULE_CODE_GLOBALVAR = 7, // Not used in PNaCl.
// FUNCTION: [type, callingconv, isproto, linkage]
MODULE_CODE_FUNCTION = 8,
@@ -179,6 +178,33 @@ namespace naclbitc {
// asmdialect,asmstr,conststr]
};
+ /// GlobalVarOpcodes - These are values used in the bitcode files to
+ /// encode records defining global variables.
+ ///
+ /// The structure of global variables can be summarized as follows:
+ ///
+ /// The global variable block begins with a GLOBALVAR_COUNT, defining
+ /// the number of global variables in the bitcode file. After that,
+ /// each global variable is defined.
+ ///
+ /// Global variables are defined by a GLOBALVAR_VAR record, followed
+ /// by 1 or more records defining its initial value. Simple
+ /// variables have a single initializer. Structured variables are
+ /// defined by an initial GLOBALVAR_COMPOUND record defining the
+ /// number of fields in the structure, followed by an initializer
+ /// for each of its fields. In this context, a field is either data,
+ /// or a relocation. A data field is defined by a
+ /// GLOBALVAR_ZEROFILL or GLOBALVAR_DATA record. A relocation field
+ /// is defined by a GLOBALVAR_RELOC record.
+ enum NaClGlobalVarOpcodes {
+ GLOBALVAR_VAR = 0, // VAR: [align, isconst]
+ GLOBALVAR_COMPOUND = 1, // COMPOUND: [size]
+ GLOBALVAR_ZEROFILL = 2, // ZEROFILL: [size]
+ GLOBALVAR_DATA = 3, // DATA: [b0, b1, ...]
+ GLOBALVAR_RELOC = 4, // RELOC: [val, [addend]]
+ GLOBALVAR_COUNT = 5, // COUNT: [n]
+ };
+
/// CastOpcodes - These are values used in the bitcode files to encode which
/// cast a CST_CODE_CE_CAST or a XXX refers to. The values of these enums
/// have no fixed relation to the LLVM IR enum values. Changing these will
diff --git a/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp b/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp
index 9f36a73654..5f14a639ba 100644
--- a/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp
+++ b/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.cpp
@@ -101,17 +101,6 @@ static GlobalValue::VisibilityTypes GetDecodedVisibility(unsigned Val) {
}
}
-static GlobalVariable::ThreadLocalMode GetDecodedThreadLocalMode(unsigned Val) {
- switch (Val) {
- case 0: return GlobalVariable::NotThreadLocal;
- default: // Map unknown non-zero value to general dynamic.
- case 1: return GlobalVariable::GeneralDynamicTLSModel;
- case 2: return GlobalVariable::LocalDynamicTLSModel;
- case 3: return GlobalVariable::InitialExecTLSModel;
- case 4: return GlobalVariable::LocalExecTLSModel;
- }
-}
-
static int GetDecodedCastOpcode(unsigned Val) {
switch (Val) {
default: return -1;
@@ -236,6 +225,7 @@ struct OperandTraits<ConstantPlaceHolder> :
void NaClBitcodeReaderValueList::AssignValue(Value *V, unsigned Idx) {
+ assert(V);
if (Idx == size()) {
push_back(V);
return;
@@ -263,9 +253,35 @@ void NaClBitcodeReaderValueList::AssignValue(Value *V, unsigned Idx) {
}
}
+void NaClBitcodeReaderValueList::AssignGlobalVar(GlobalVariable *GV,
+ unsigned Idx) {
+ assert(GV);
+
+ if (Idx == size()) {
+ push_back(GV);
+ return;
+ }
+
+ if (Idx >= size())
+ resize(Idx+1);
+
+ WeakVH &OldV = ValuePtrs[Idx];
+ if (OldV == 0) {
+ OldV = GV;
+ return;
+ }
+
+ // If there was a forward reference to this value, replace it.
+ Value *PrevVal = OldV;
+ GlobalVariable *Placeholder = cast<GlobalVariable>(PrevVal);
+ Placeholder->replaceAllUsesWith(
+ ConstantExpr::getBitCast(GV, Placeholder->getType()));
+ Placeholder->eraseFromParent();
+ ValuePtrs[Idx] = GV;
+}
Constant *NaClBitcodeReaderValueList::getConstantFwdRef(unsigned Idx,
- Type *Ty) {
+ Type *Ty) {
if (Idx >= size())
resize(Idx + 1);
@@ -307,6 +323,26 @@ bool NaClBitcodeReaderValueList::createValueFwdRef(unsigned Idx, Type *Ty) {
return false;
}
+Constant *NaClBitcodeReaderValueList::getOrCreateGlobalVarRef(
+ unsigned Idx, Module *M) {
+ // First make sure the element for Idx is defined.
+ if (Idx >= size())
+ resize(Idx + 1);
+
+ // Now get its value (if applicable).
+ if (Value *V = ValuePtrs[Idx])
+ return dyn_cast<Constant>(V);
+
+ // Create a placeholder, which will later be RAUW'd.
+ Type *PlaceholderType = Type::getInt8Ty(Context);
+
+ Constant *C =
+ new GlobalVariable(*M, PlaceholderType, false,
+ GlobalValue::ExternalLinkage, 0);
+ ValuePtrs[Idx] = C;
+ return C;
+}
+
/// ResolveConstantForwardRefs - Once all constants are read, this method bulk
/// resolves any forward references. The idea behind this is that we sometimes
/// get constants (such as large arrays) which reference *many* forward ref
@@ -635,6 +671,147 @@ bool NaClBitcodeReader::ParseTypeTableBody() {
}
}
+bool NaClBitcodeReader::ParseGlobalVars() {
+ if (Stream.EnterSubBlock(naclbitc::GLOBALVAR_BLOCK_ID))
+ return Error("Malformed block record");
+
+ SmallVector<uint64_t, 64> Record;
+
+ // True when processing a global variable. Stays true until all records
+ // are processed, and the global variable is created.
+ bool ProcessingGlobal = false;
+ // The alignment value defined for the global variable.
+ unsigned VarAlignment = 0;
+ // True if the variable is read-only.
+ bool VarIsConstant = false;
+ // The initializer for the global variable.
+ SmallVector<Constant *, 10> VarInit;
+ // The number of initializers needed for the global variable.
+ unsigned VarInitializersNeeded = 0;
+ unsigned FirstValueNo = ValueList.size();
+ // The index of the next global variable.
+ unsigned NextValueNo = FirstValueNo;
+ // The number of expected global variable definitions.
+ unsigned NumGlobals = 0;
+
+ // Read all global variable records.
+ while (1) {
+ NaClBitstreamEntry Entry = Stream.advanceSkippingSubblocks();
+ switch (Entry.Kind) {
+ case NaClBitstreamEntry::SubBlock:
+ case NaClBitstreamEntry::Error:
+ return Error("Error in the global vars block");
+ case NaClBitstreamEntry::EndBlock:
+ if (ProcessingGlobal || NumGlobals != (NextValueNo - FirstValueNo))
+ return Error("Error in the global vars block");
+ return false;
+ case NaClBitstreamEntry::Record:
+ // The interesting case.
+ break;
+ }
+
+ // Read a record.
+ Record.clear();
+ unsigned Bitcode = Stream.readRecord(Entry.ID, Record);
+ switch (Bitcode) {
+ default: return Error("Unknown global variable entry");
+ case naclbitc::GLOBALVAR_VAR:
+ // Start the definition of a global variable.
+ if (ProcessingGlobal || Record.size() != 2)
+ return Error("Bad GLOBALVAR_VAR record");
+ ProcessingGlobal = true;
+ VarAlignment = (1 << Record[0]) >> 1;
+ VarIsConstant = Record[1] != 0;
+ // Assume (by default) there is a single initializer.
+ VarInitializersNeeded = 1;
+ break;
+ case naclbitc::GLOBALVAR_COMPOUND:
+ // Global variable has multiple initializers. Changes the
+ // default number of initializers to the given value in
+ // Record[0].
+ if (!ProcessingGlobal || !VarInit.empty() ||
+ VarInitializersNeeded != 1 || Record.size() != 1)
+ return Error("Bad GLOBALVAR_COMPOUND record");
+ VarInitializersNeeded = Record[0];
+ break;
+ case naclbitc::GLOBALVAR_ZEROFILL: {
+ // Define an initializer that defines a sequence of zero-filled bytes.
+ if (!ProcessingGlobal || Record.size() != 1)
+ return Error("Bad GLOBALVAR_ZEROFILL record");
+ Type *Ty = ArrayType::get(Type::getInt8Ty(Context), Record[0]);
+ Constant *Zero = ConstantAggregateZero::get(Ty);
+ VarInit.push_back(Zero);
+ break;
+ }
+ case naclbitc::GLOBALVAR_DATA: {
+ // Defines an initializer defined by a sequence of byte values.
+ if (!ProcessingGlobal || Record.size() < 1)
+ return Error("Bad GLOBALVAR_DATA record");
+ unsigned Size = Record.size();
+ uint8_t *Buf = new uint8_t[Size];
+ assert(Buf);
+ for (unsigned i = 0; i < Size; ++i)
+ Buf[i] = Record[i];
+ Constant *Init = ConstantDataArray::get(
+ Context, ArrayRef<uint8_t>(Buf, Buf + Size));
+ VarInit.push_back(Init);
+ delete[] Buf;
+ break;
+ }
+ case naclbitc::GLOBALVAR_RELOC: {
+ // Define a relocation initializer.
+ if (!ProcessingGlobal || Record.size() < 1 || Record.size() > 2)
+ return Error("Bad GLOBALVAR_RELOC record");
+ Constant *BaseVal =
+ ValueList.getOrCreateGlobalVarRef(Record[0], TheModule);
+ if (BaseVal == 0)
+ return Error("Bad base value in GLOBALVAR_RELOC record");
+ Type *IntPtrType = IntegerType::get(Context, 32);
+ Constant *Val = ConstantExpr::getPtrToInt(BaseVal, IntPtrType);
+ if (Record.size() == 2) {
+ uint32_t Addend = Record[1];
+ Val = ConstantExpr::getAdd(Val, ConstantInt::get(IntPtrType,
+ Addend));
+ }
+ VarInit.push_back(Val);
+ break;
+ }
+ case naclbitc::GLOBALVAR_COUNT:
+ if (Record.size() != 1 || NumGlobals != 0)
+ return Error("Invalid global count record");
+ NumGlobals = Record[0];
+ break;
+ }
+
+ // If more initializers needed for global variable, continue processing.
+ if (!ProcessingGlobal || VarInit.size() < VarInitializersNeeded)
+ continue;
+
+ Constant *Init = 0;
+ switch (VarInit.size()) {
+ case 0:
+ return Error("No initializer for global variable in global vars block");
+ case 1:
+ Init = VarInit[0];
+ break;
+ default:
+ Init = ConstantStruct::getAnon(Context, VarInit, true);
+ break;
+ }
+ GlobalVariable *GV = new GlobalVariable(
+ *TheModule, Init->getType(), VarIsConstant,
+ GlobalValue::InternalLinkage, Init, "");
+ GV->setAlignment(VarAlignment);
+ ValueList.AssignGlobalVar(GV, NextValueNo);
+ ++NextValueNo;
+ ProcessingGlobal = false;
+ VarAlignment = 0;
+ VarIsConstant = false;
+ VarInitializersNeeded = 0;
+ VarInit.clear();
+ }
+}
+
bool NaClBitcodeReader::ParseValueSymbolTable() {
DEBUG(dbgs() << "-> ParseValueSymbolTable\n");
if (Stream.EnterSubBlock(naclbitc::VALUE_SYMTAB_BLOCK_ID))
@@ -691,29 +868,12 @@ bool NaClBitcodeReader::ParseValueSymbolTable() {
}
}
-/// ResolveGlobalAndAliasInits - Resolve all of the initializers for global
-/// values and aliases that we can.
-bool NaClBitcodeReader::ResolveGlobalAndAliasInits() {
- std::vector<std::pair<GlobalVariable*, unsigned> > GlobalInitWorklist;
+/// ResolveAliasInits - Resolve all of the initializers for aliases that we can.
+bool NaClBitcodeReader::ResolveAliasInits() {
std::vector<std::pair<GlobalAlias*, unsigned> > AliasInitWorklist;
- GlobalInitWorklist.swap(GlobalInits);
AliasInitWorklist.swap(AliasInits);
- while (!GlobalInitWorklist.empty()) {
- unsigned ValID = GlobalInitWorklist.back().second;
- if (ValID >= ValueList.size()) {
- // Not ready to resolve this yet, it requires something later in the file.
- GlobalInits.push_back(GlobalInitWorklist.back());
- } else {
- if (Constant *C = dyn_cast<Constant>(ValueList[ValID]))
- GlobalInitWorklist.back().first->setInitializer(C);
- else
- return Error("Global variable initializer is not a constant!");
- }
- GlobalInitWorklist.pop_back();
- }
-
while (!AliasInitWorklist.empty()) {
unsigned ValID = AliasInitWorklist.back().second;
if (ValID >= ValueList.size()) {
@@ -1199,9 +1359,10 @@ bool NaClBitcodeReader::RememberAndSkipFunctionBody() {
bool NaClBitcodeReader::GlobalCleanup() {
// Patch the initializers for globals and aliases up.
- ResolveGlobalAndAliasInits();
- if (!GlobalInits.empty() || !AliasInits.empty())
- return Error("Malformed global initializer set");
+ ResolveAliasInits();
+
+ if (!AliasInits.empty())
+ return Error("Malformed Alias Initializer");
// Look for intrinsic functions which need to be upgraded at some point
for (Module::iterator FI = TheModule->begin(), FE = TheModule->end();
@@ -1216,9 +1377,6 @@ bool NaClBitcodeReader::GlobalCleanup() {
GI = TheModule->global_begin(), GE = TheModule->global_end();
GI != GE; ++GI)
UpgradeGlobalVariable(GI);
- // Force deallocation of memory for these vectors to favor the client that
- // want lazy deserialization.
- std::vector<std::pair<GlobalVariable*, unsigned> >().swap(GlobalInits);
std::vector<std::pair<GlobalAlias*, unsigned> >().swap(AliasInits);
return false;
}
@@ -1261,13 +1419,17 @@ bool NaClBitcodeReader::ParseModule(bool Resume) {
if (ParseTypeTable())
return true;
break;
+ case naclbitc::GLOBALVAR_BLOCK_ID:
+ if (ParseGlobalVars())
+ return true;
+ break;
case naclbitc::VALUE_SYMTAB_BLOCK_ID:
if (ParseValueSymbolTable())
return true;
SeenValueSymbolTable = true;
break;
case naclbitc::CONSTANTS_BLOCK_ID:
- if (ParseConstants() || ResolveGlobalAndAliasInits())
+ if (ParseConstants() || ResolveAliasInits())
return true;
break;
case naclbitc::FUNCTION_BLOCK_ID:
@@ -1359,57 +1521,8 @@ bool NaClBitcodeReader::ParseModule(bool Resume) {
// GLOBALVAR: [pointer type, isconst, initid,
// linkage, alignment, section, visibility, threadlocal,
// unnamed_addr]
- case naclbitc::MODULE_CODE_GLOBALVAR: {
- if (Record.size() < 6)
- return Error("Invalid MODULE_CODE_GLOBALVAR record");
- Type *Ty = getTypeByID(Record[0]);
- if (!Ty) return Error("Invalid MODULE_CODE_GLOBALVAR record");
- if (!Ty->isPointerTy())
- return Error("Global not a pointer type!");
- unsigned AddressSpace = cast<PointerType>(Ty)->getAddressSpace();
- Ty = cast<PointerType>(Ty)->getElementType();
-
- bool isConstant = Record[1];
- GlobalValue::LinkageTypes Linkage = GetDecodedLinkage(Record[3]);
- unsigned Alignment = (1 << Record[4]) >> 1;
- std::string Section;
- if (Record[5]) {
- if (Record[5]-1 >= SectionTable.size())
- return Error("Invalid section ID");
- Section = SectionTable[Record[5]-1];
- }
- GlobalValue::VisibilityTypes Visibility = GlobalValue::DefaultVisibility;
- if (Record.size() > 6)
- Visibility = GetDecodedVisibility(Record[6]);
-
- GlobalVariable::ThreadLocalMode TLM = GlobalVariable::NotThreadLocal;
- if (Record.size() > 7)
- TLM = GetDecodedThreadLocalMode(Record[7]);
-
- bool UnnamedAddr = false;
- if (Record.size() > 8)
- UnnamedAddr = Record[8];
-
- bool ExternallyInitialized = false;
- if (Record.size() > 9)
- ExternallyInitialized = Record[9];
-
- GlobalVariable *NewGV =
- new GlobalVariable(*TheModule, Ty, isConstant, Linkage, 0, "", 0,
- TLM, AddressSpace, ExternallyInitialized);
- NewGV->setAlignment(Alignment);
- if (!Section.empty())
- NewGV->setSection(Section);
- NewGV->setVisibility(Visibility);
- NewGV->setUnnamedAddr(UnnamedAddr);
-
- ValueList.push_back(NewGV);
-
- // Remember which value to use for the global initializer.
- if (unsigned InitID = Record[2])
- GlobalInits.push_back(std::make_pair(NewGV, InitID-1));
- break;
- }
+ case naclbitc::MODULE_CODE_GLOBALVAR:
+ return Error("Invalid MODULE_CODE_GLOBALVAR record");
// FUNCTION: [type, callingconv, isproto, linkage]
case naclbitc::MODULE_CODE_FUNCTION: {
if (Record.size() < 4)
diff --git a/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.h b/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.h
index 9320de92ea..454875796a 100644
--- a/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.h
+++ b/lib/Bitcode/NaCl/Reader/NaClBitcodeReader.h
@@ -82,13 +82,32 @@ public:
// already been declared.
bool createValueFwdRef(unsigned Idx, Type *Ty);
+ // Declares the type of the forward-referenced constant Idx. Returns
+ // 0 if an error occurred.
+ // TODO(kschimpf) Convert these to be like createValueFwdRef and
+ // getValueFwdRef.
Constant *getConstantFwdRef(unsigned Idx, Type *Ty);
// Gets the forward reference value for Idx.
Value *getValueFwdRef(unsigned Idx);
+ // Gets the corresponding constant defining the address of the
+ // corresponding global variable defined by Idx, if already defined.
+ // Otherwise, creates a forward reference for Idx, and returns the
+ // placeholder constant for the address of the corresponding global
+ // variable defined by Idx.
+ Constant *getOrCreateGlobalVarRef(unsigned Idx, Module* M);
+
+ // Assigns Idx to the given value (if new), or assigns V to Idx (if Idx
+ // was forward referenced).
void AssignValue(Value *V, unsigned Idx);
+ // Assigns Idx to the given global variable. If the Idx currently has
+ // a forward reference (built by createGlobalVarFwdRef(unsigned Idx)),
+ // replaces uses of the global variable forward reference with the
+ // value GV.
+ void AssignGlobalVar(GlobalVariable *GV, unsigned Idx);
+
/// ResolveConstantForwardRefs - Once all constants are read, this method bulk
/// resolves any forward references.
void ResolveConstantForwardRefs();
@@ -114,7 +133,6 @@ class NaClBitcodeReader : public GVMaterializer {
SmallVector<Instruction *, 64> InstructionList;
SmallVector<SmallVector<uint64_t, 64>, 64> UseListRecords;
- std::vector<std::pair<GlobalVariable*, unsigned> > GlobalInits;
std::vector<std::pair<GlobalAlias*, unsigned> > AliasInits;
/// FunctionBBs - While parsing a function body, this is a list of the basic
@@ -259,13 +277,13 @@ private:
bool ParseModule(bool Resume);
bool ParseTypeTable();
bool ParseTypeTableBody();
-
+ bool ParseGlobalVars();
bool ParseValueSymbolTable();
bool ParseConstants();
bool RememberAndSkipFunctionBody();
bool ParseFunctionBody(Function *F);
bool GlobalCleanup();
- bool ResolveGlobalAndAliasInits();
+ bool ResolveAliasInits();
bool ParseUseLists();
bool InitStream();
bool InitStreamFromBuffer();
diff --git a/lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp b/lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp
index be75af1e40..44aaeb3f0e 100644
--- a/lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp
+++ b/lib/Bitcode/NaCl/Writer/NaClBitcodeWriter.cpp
@@ -69,6 +69,15 @@ enum {
CST_CONSTANTS_CSTRING_6_ABBREV,
CST_CONSTANTS_MAX_ABBREV = CST_CONSTANTS_CSTRING_6_ABBREV,
+ // GLOBALVAR BLOCK abbrev id's.
+ GLOBALVAR_VAR_ABBREV = naclbitc::FIRST_APPLICATION_ABBREV,
+ GLOBALVAR_COMPOUND_ABBREV,
+ GLOBALVAR_ZEROFILL_ABBREV,
+ GLOBALVAR_DATA_ABBREV,
+ GLOBALVAR_RELOC_ABBREV,
+ GLOBALVAR_RELOC_WITH_ADDEND_ABBREV,
+ GLOBALVAR_MAX_ABBREV = GLOBALVAR_RELOC_WITH_ADDEND_ABBREV,
+
// FUNCTION_BLOCK abbrev id's.
FUNCTION_INST_LOAD_ABBREV = naclbitc::FIRST_APPLICATION_ABBREV,
FUNCTION_INST_BINOP_ABBREV,
@@ -89,10 +98,6 @@ enum {
TYPE_ARRAY_ABBREV,
TYPE_MAX_ABBREV = TYPE_ARRAY_ABBREV,
- // MODULE_BLOCK abbrev id's.
- MODULE_GLOBALVAR_ABBREV = naclbitc::FIRST_APPLICATION_ABBREV,
- MODULE_MAX_ABBREV = MODULE_GLOBALVAR_ABBREV,
-
// SwitchInst Magic
SWITCH_INST_MAGIC = 0x4B5 // May 2012 => 1205 => Hex
};
@@ -409,15 +414,124 @@ static unsigned getEncodedVisibility(const GlobalValue *GV) {
llvm_unreachable("Invalid visibility");
}
-static unsigned getEncodedThreadLocalMode(const GlobalVariable *GV) {
- switch (GV->getThreadLocalMode()) {
- case GlobalVariable::NotThreadLocal: return 0;
- case GlobalVariable::GeneralDynamicTLSModel: return 1;
- case GlobalVariable::LocalDynamicTLSModel: return 2;
- case GlobalVariable::InitialExecTLSModel: return 3;
- case GlobalVariable::LocalExecTLSModel: return 4;
+/// \brief Function to convert constant initializers for global
+/// variables into corresponding bitcode. Takes advantage that these
+/// global variable initializations are normalized (see
+/// lib/Transforms/NaCl/FlattenGlobals.cpp).
+void WriteGlobalInit(const Constant *C, unsigned GlobalVarID,
+ SmallVectorImpl<uint32_t> &Vals,
+ const NaClValueEnumerator &VE,
+ NaClBitstreamWriter &Stream) {
+ if (ArrayType *Ty = dyn_cast<ArrayType>(C->getType())) {
+ if (!Ty->getElementType()->isIntegerTy(8))
+ report_fatal_error("Global array initializer not i8");
+ uint32_t Size = Ty->getNumElements();
+ if (isa<ConstantAggregateZero>(C)) {
+ Vals.push_back(Size);
+ Stream.EmitRecord(naclbitc::GLOBALVAR_ZEROFILL, Vals,
+ GLOBALVAR_ZEROFILL_ABBREV);
+ Vals.clear();
+ } else {
+ const ConstantDataSequential *CD = cast<ConstantDataSequential>(C);
+ StringRef Data = CD->getRawDataValues();
+ for (size_t i = 0; i < Size; ++i) {
+ Vals.push_back(Data[i] & 0xFF);
+ }
+ Stream.EmitRecord(naclbitc::GLOBALVAR_DATA, Vals,
+ GLOBALVAR_DATA_ABBREV);
+ Vals.clear();
+ }
+ return;
+ }
+ if (C->getType()->isIntegerTy(32)) {
+ // This constant defines a relocation. Start by verifying the
+ // relocation is of the right form.
+ const ConstantExpr *CE = dyn_cast<ConstantExpr>(C);
+ if (CE == 0)
+ report_fatal_error("Global i32 initializer not constant");
+ assert(CE);
+ int32_t Addend = 0;
+ if (CE->getOpcode() == Instruction::Add) {
+ const ConstantInt *AddendConst = dyn_cast<ConstantInt>(CE->getOperand(1));
+ if (AddendConst == 0)
+ report_fatal_error("Malformed addend in global relocation initializer");
+ Addend = AddendConst->getSExtValue();
+ CE = dyn_cast<ConstantExpr>(CE->getOperand(0));
+ if (CE == 0)
+ report_fatal_error(
+ "Base of global relocation initializer not constant");
+ }
+ if (CE->getOpcode() != Instruction::PtrToInt)
+ report_fatal_error("Global relocation base doesn't contain ptrtoint");
+ GlobalValue *GV = dyn_cast<GlobalValue>(CE->getOperand(0));
+ if (GV == 0)
+ report_fatal_error(
+ "Argument of ptrtoint in global relocation no global value");
+
+ // Now generate the corresponding relocation record.
+ unsigned RelocID = VE.getValueID(GV);
+ // This is a value index.
+ unsigned AbbrevToUse = GLOBALVAR_RELOC_ABBREV;
+ Vals.push_back(RelocID);
+ if (Addend) {
+ Vals.push_back(Addend);
+ AbbrevToUse = GLOBALVAR_RELOC_WITH_ADDEND_ABBREV;
+ }
+ Stream.EmitRecord(naclbitc::GLOBALVAR_RELOC, Vals, AbbrevToUse);
+ Vals.clear();
+ return;
+ }
+ report_fatal_error("Global initializer is not a SimpleElement");
+}
+
+// Emit global variables.
+static void WriteGlobalVars(const Module *M,
+ const NaClValueEnumerator &VE,
+ NaClBitstreamWriter &Stream) {
+ Stream.EnterSubblock(naclbitc::GLOBALVAR_BLOCK_ID);
+ SmallVector<uint32_t, 32> Vals;
+ unsigned GlobalVarID = VE.getFirstGlobalVarID();
+
+ // Emit the number of global variables.
+
+ Vals.push_back(M->getGlobalList().size());
+ Stream.EmitRecord(naclbitc::GLOBALVAR_COUNT, Vals);
+ Vals.clear();
+
+ // Now emit each global variable.
+ for (Module::const_global_iterator
+ GV = M->global_begin(), E = M->global_end();
+ GV != E; ++GV, ++GlobalVarID) {
+ // Define the global variable.
+ Vals.push_back(Log2_32(GV->getAlignment()) + 1);
+ Vals.push_back(GV->isConstant());
+ Stream.EmitRecord(naclbitc::GLOBALVAR_VAR, Vals, GLOBALVAR_VAR_ABBREV);
+ Vals.clear();
+
+ // Add the field(s).
+ const Constant *C = GV->getInitializer();
+ if (C == 0)
+ report_fatal_error("Global variable initializer not a constant");
+ if (const ConstantStruct *CS = dyn_cast<ConstantStruct>(C)) {
+ if (!CS->getType()->isPacked())
+ report_fatal_error("Global variable type not packed");
+ if (CS->getType()->hasName())
+ report_fatal_error("Global variable type is named");
+ Vals.push_back(CS->getNumOperands());
+ Stream.EmitRecord(naclbitc::GLOBALVAR_COMPOUND, Vals,
+ GLOBALVAR_COMPOUND_ABBREV);
+ Vals.clear();
+ for (unsigned I = 0; I < CS->getNumOperands(); ++I) {
+ WriteGlobalInit(dyn_cast<Constant>(CS->getOperand(I)), GlobalVarID,
+ Vals, VE, Stream);
+ }
+ } else {
+ WriteGlobalInit(C, GlobalVarID, Vals, VE, Stream);
+ }
}
- llvm_unreachable("Invalid TLS model");
+
+ assert(GlobalVarID == VE.getFirstGlobalVarID() + VE.getNumGlobalVarIDs());
+ Stream.ExitBlock();
}
// Emit top-level description of module, including inline asm,
@@ -472,65 +586,10 @@ static void WriteModuleInfo(const Module *M, const NaClValueEnumerator &VE,
}
}
- // Emit abbrev for globals, now that we know # sections and max alignment.
- // Add an abbrev for common globals with no visibility or thread localness.
- // Don't bother emitting vis + thread local abbreviation.
- {
- NaClBitCodeAbbrev *Abbv = new NaClBitCodeAbbrev();
- Abbv->Add(NaClBitCodeAbbrevOp(naclbitc::MODULE_CODE_GLOBALVAR));
- Abbv->Add(NaClBitCodeAbbrevOp(
- NaClBitCodeAbbrevOp::Fixed, NaClBitsNeededForValue(MaxGlobalType)));
- Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::Fixed, 1)); // Constant.
- Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::VBR, 6)); // Initializer.
- Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::Fixed, 4)); // Linkage.
- if (MaxAlignment == 0) // Alignment.
- Abbv->Add(NaClBitCodeAbbrevOp(0));
- else {
- unsigned MaxEncAlignment = Log2_32(MaxAlignment)+1;
- Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::Fixed,
- NaClBitsNeededForValue(MaxEncAlignment)));
- }
- if (SectionMap.empty()) // Section.
- Abbv->Add(NaClBitCodeAbbrevOp(0));
- else
- Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::Fixed,
- NaClBitsNeededForValue(SectionMap.size())));
- if (MODULE_GLOBALVAR_ABBREV != Stream.EmitAbbrev(Abbv))
- llvm_unreachable("Unexpected abbrev ordering!");
- }
-
- // Emit the global variable information.
+ // Emit the function proto information. Note: We do this before
+ // global variables, so that global variable initializations can
+ // refer to the functions without a forward reference.
SmallVector<unsigned, 64> Vals;
- for (Module::const_global_iterator GV = M->global_begin(),E = M->global_end();
- GV != E; ++GV) {
- unsigned AbbrevToUse = 0;
-
- // GLOBALVAR: [type, isconst, initid,
- // linkage, alignment, section, visibility, threadlocal,
- // unnamed_addr]
- Vals.push_back(VE.getTypeID(GV->getType()));
- Vals.push_back(GV->isConstant());
- Vals.push_back(GV->isDeclaration() ? 0 :
- (VE.getValueID(GV->getInitializer()) + 1));
- Vals.push_back(getEncodedLinkage(GV));
- Vals.push_back(Log2_32(GV->getAlignment())+1);
- Vals.push_back(GV->hasSection() ? SectionMap[GV->getSection()] : 0);
- if (GV->isThreadLocal() ||
- GV->getVisibility() != GlobalValue::DefaultVisibility ||
- GV->hasUnnamedAddr() || GV->isExternallyInitialized()) {
- Vals.push_back(getEncodedVisibility(GV));
- Vals.push_back(getEncodedThreadLocalMode(GV));
- Vals.push_back(GV->hasUnnamedAddr());
- Vals.push_back(GV->isExternallyInitialized());
- } else {
- AbbrevToUse = MODULE_GLOBALVAR_ABBREV;
- }
-
- Stream.EmitRecord(naclbitc::MODULE_CODE_GLOBALVAR, Vals, AbbrevToUse);
- Vals.clear();
- }
-
- // Emit the function proto information.
for (Module::const_iterator F = M->begin(), E = M->end(); F != E; ++F) {
// FUNCTION: [type, callingconv, isproto, linkage]
Vals.push_back(VE.getTypeID(F->getType()));
@@ -543,6 +602,9 @@ static void WriteModuleInfo(const Module *M, const NaClValueEnumerator &VE,
Vals.clear();
}
+ // Emit the global variable information.
+ WriteGlobalVars(M, VE, Stream);
+
// Emit the alias information.
for (Module::const_alias_iterator AI = M->alias_begin(), E = M->alias_end();
AI != E; ++AI) {
@@ -1561,13 +1623,66 @@ static void WriteBlockInfo(const NaClValueEnumerator &VE,
llvm_unreachable("Unexpected abbrev ordering!");
}
+ { // VAR abbrev for GLOBALVAR_BLOCK.
+ NaClBitCodeAbbrev *Abbv = new NaClBitCodeAbbrev();
+ Abbv->Add(NaClBitCodeAbbrevOp(naclbitc::GLOBALVAR_VAR));
+ Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::VBR, 6));
+ Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::Fixed, 1));
+ if (Stream.EmitBlockInfoAbbrev(naclbitc::GLOBALVAR_BLOCK_ID,
+ Abbv) != GLOBALVAR_VAR_ABBREV)
+ llvm_unreachable("Unexpected abbrev ordering!");
+ }
+ { // COMPOUND abbrev for GLOBALVAR_BLOCK.
+ NaClBitCodeAbbrev *Abbv = new NaClBitCodeAbbrev();
+ Abbv->Add(NaClBitCodeAbbrevOp(naclbitc::GLOBALVAR_COMPOUND));
+ Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::VBR, 8));
+ if (Stream.EmitBlockInfoAbbrev(naclbitc::GLOBALVAR_BLOCK_ID,
+ Abbv) != GLOBALVAR_COMPOUND_ABBREV)
+ llvm_unreachable("Unexpected abbrev ordering!");
+ }
+ { // ZEROFILL abbrev for GLOBALVAR_BLOCK.
+ NaClBitCodeAbbrev *Abbv = new NaClBitCodeAbbrev();
+ Abbv->Add(NaClBitCodeAbbrevOp(naclbitc::GLOBALVAR_ZEROFILL));
+ Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::VBR, 8));
+ if (Stream.EmitBlockInfoAbbrev(naclbitc::GLOBALVAR_BLOCK_ID,
+ Abbv) != GLOBALVAR_ZEROFILL_ABBREV)
+ llvm_unreachable("Unexpected abbrev ordering!");
+ }
+ { // DATA abbrev for GLOBALVAR_BLOCK.
+ NaClBitCodeAbbrev *Abbv = new NaClBitCodeAbbrev();
+ Abbv->Add(NaClBitCodeAbbrevOp(naclbitc::GLOBALVAR_DATA));
+ Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::Array));
+ Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::Fixed, 8));
+ if (Stream.EmitBlockInfoAbbrev(naclbitc::GLOBALVAR_BLOCK_ID,
+ Abbv) != GLOBALVAR_DATA_ABBREV)
+ llvm_unreachable("Unexpected abbrev ordering!");
+ }
+ { // RELOC abbrev for GLOBALVAR_BLOCK.
+ NaClBitCodeAbbrev *Abbv = new NaClBitCodeAbbrev();
+ Abbv->Add(NaClBitCodeAbbrevOp(naclbitc::GLOBALVAR_RELOC));
+ Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::VBR, 6));
+ if (Stream.EmitBlockInfoAbbrev(naclbitc::GLOBALVAR_BLOCK_ID,
+ Abbv) != GLOBALVAR_RELOC_ABBREV)
+ llvm_unreachable("Unexpected abbrev ordering!");
+ }
+ { // RELOC_WITH_ADDEND_ABBREV abbrev for GLOBALVAR_BLOCK.
+ NaClBitCodeAbbrev *Abbv = new NaClBitCodeAbbrev();
+ Abbv->Add(NaClBitCodeAbbrevOp(naclbitc::GLOBALVAR_RELOC));
+ Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::VBR, 6));
+ Abbv->Add(NaClBitCodeAbbrevOp(NaClBitCodeAbbrevOp::VBR, 6));
+ if (Stream.EmitBlockInfoAbbrev(
+ naclbitc::GLOBALVAR_BLOCK_ID,
+ Abbv) != GLOBALVAR_RELOC_WITH_ADDEND_ABBREV)
+ llvm_unreachable("Unexpected abbrev ordering!");
+ }
+
Stream.ExitBlock();
}
/// WriteModule - Emit the specified module to the bitstream.
static void WriteModule(const Module *M, NaClBitstreamWriter &Stream) {
DEBUG(dbgs() << "-> WriteModule\n");
- Stream.EnterSubblock(naclbitc::MODULE_BLOCK_ID, MODULE_MAX_ABBREV);
+ Stream.EnterSubblock(naclbitc::MODULE_BLOCK_ID);
SmallVector<unsigned, 1> Vals;
unsigned CurVersion = 1;
diff --git a/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp b/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp
index feb14cdb0c..393c5bc934 100644
--- a/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp
+++ b/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.cpp
@@ -42,16 +42,21 @@ NaClValueEnumerator::NaClValueEnumerator(const Module *M) {
// constructor completes.
TypeCountMapType count_map;
TypeCountMap = &count_map;
- // Enumerate the global variables.
- for (Module::const_global_iterator I = M->global_begin(),
- E = M->global_end(); I != E; ++I)
- EnumerateValue(I);
- // Enumerate the functions.
+ // Enumerate the functions. Note: We do this before global
+ // variables, so that global variable initializations can refer to
+ // the functions without a forward reference.
for (Module::const_iterator I = M->begin(), E = M->end(); I != E; ++I) {
EnumerateValue(I);
}
+ // Enumerate the global variables.
+ FirstGlobalVarID = Values.size();
+ for (Module::const_global_iterator I = M->global_begin(),
+ E = M->global_end(); I != E; ++I)
+ EnumerateValue(I);
+ NumGlobalVarIDs = Values.size() - FirstGlobalVarID;
+
// Enumerate the aliases.
for (Module::const_alias_iterator I = M->alias_begin(), E = M->alias_end();
I != E; ++I)
@@ -60,11 +65,8 @@ NaClValueEnumerator::NaClValueEnumerator(const Module *M) {
// Remember what is the cutoff between globalvalue's and other constants.
unsigned FirstConstant = Values.size();
- // Enumerate the global variable initializers.
- for (Module::const_global_iterator I = M->global_begin(),
- E = M->global_end(); I != E; ++I)
- if (I->hasInitializer())
- EnumerateValue(I->getInitializer());
+ // Skip global variable initializers since they are handled within
+ // WriteGlobalVars of file NaClBitcodeWriter.cpp.
// Enumerate the aliasees.
for (Module::const_alias_iterator I = M->alias_begin(), E = M->alias_end();
diff --git a/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.h b/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.h
index 90946551f5..e98a091ba8 100644
--- a/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.h
+++ b/lib/Bitcode/NaCl/Writer/NaClValueEnumerator.h
@@ -79,6 +79,11 @@ private:
/// than necessary.
SmallSet<unsigned, 32> FnForwardTypeRefs;
+ // The index of the first global variable ID in the bitcode file.
+ unsigned FirstGlobalVarID;
+ // The number of global variable IDs defined in the bitcode file.
+ unsigned NumGlobalVarIDs;
+
NaClValueEnumerator(const NaClValueEnumerator &) LLVM_DELETED_FUNCTION;
void operator=(const NaClValueEnumerator &) LLVM_DELETED_FUNCTION;
public:
@@ -87,6 +92,14 @@ public:
void dump() const;
void print(raw_ostream &OS, const ValueMapType &Map, const char *Name) const;
+ unsigned getFirstGlobalVarID() const {
+ return FirstGlobalVarID;
+ }
+
+ unsigned getNumGlobalVarIDs() const {
+ return NumGlobalVarIDs;
+ }
+
unsigned getValueID(const Value *V) const;
unsigned getTypeID(Type *T) const {
diff --git a/test/NaCl/Bitcode/globalvars.ll b/test/NaCl/Bitcode/globalvars.ll
new file mode 100644
index 0000000000..c6e6f2f17a
--- /dev/null
+++ b/test/NaCl/Bitcode/globalvars.ll
@@ -0,0 +1,98 @@
+; RUN: llvm-as < %s | pnacl-freeze | pnacl-thaw | llvm-dis - | FileCheck %s
+; RUN: llvm-as < %s | pnacl-freeze | pnacl-bcanalyzer -dump \
+; RUN: | FileCheck %s -check-prefix=BC
+
+; Test that we generate appropriate bitcode values for global variables.
+
+; Make sure that the function declaration for function func (below)
+; appears before the global variables block.
+; BC: <FUNCTION op0=5 op1=0 op2=0 op3=0/>
+
+; Make sure we begin the globals block after function declarations.
+; BC-NEXT: <GLOBALVAR_BLOCK
+; BC-NEXT: <COUNT op0=15/>
+
+@bytes = internal global [7 x i8] c"abcdefg"
+; CHECK: @bytes = internal global [7 x i8] c"abcdefg"
+; BC-NEXT: <VAR abbrevid=4 op0=0 op1=0/>
+; BC-NEXT: <DATA abbrevid=7 op0=97 op1=98 op2=99 op3=100 op4=101 op5=102 op6=103/>
+
+
+@ptr_to_ptr = internal global i32 ptrtoint (i32* @ptr to i32)
+; CHECK: @ptr_to_ptr = internal global i32 ptrtoint (i32* @ptr to i32)
+; BC-NEXT: <VAR abbrevid=4 op0=0 op1=0/>
+; BC-NEXT: <RELOC abbrevid=8 op0=5/>
+
+@ptr_to_func = internal global i32 ptrtoint (void ()* @func to i32)
+; CHECK: @ptr_to_func = internal global i32 ptrtoint (void ()* @func to i32)
+; BC-NEXT: <VAR abbrevid=4 op0=0 op1=0/>
+; BC-NEXT: <RELOC abbrevid=8 op0=0/>
+
+@compound = internal global <{ [3 x i8], i32 }> <{ [3 x i8] c"foo", i32 ptrtoint (void ()* @func to i32) }>
+; CHECK: @compound = internal global <{ [3 x i8], i32 }> <{ [3 x i8] c"foo", i32 ptrtoint (void ()* @func to i32) }>
+; BC-NEXT: <VAR abbrevid=4 op0=0 op1=0/>
+; BC-NEXT: <COMPOUND abbrevid=5 op0=2/>
+; BC-NEXT: <DATA abbrevid=7 op0=102 op1=111 op2=111/>
+; BC-NEXT: <RELOC abbrevid=8 op0=0/>
+
+@ptr = internal global i32 ptrtoint ([7 x i8]* @bytes to i32)
+; CHECK: @ptr = internal global i32 ptrtoint ([7 x i8]* @bytes to i32)
+; BC-NEXT: <VAR abbrevid=4 op0=0 op1=0/>
+; BC-NEXT: <RELOC abbrevid=8 op0=1/>
+
+@addend_ptr = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 1)
+; CHECK: @addend_ptr = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 1)
+; BC-NEXT: <VAR abbrevid=4 op0=0 op1=0/>
+; BC-NEXT: <RELOC abbrevid=9 op0=5 op1=1/>
+
+@addend_negative = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 -1)
+; CHECK: @addend_negative = internal global i32 add (i32 ptrtoint (i32* @ptr to i32), i32 -1)
+; BC-NEXT: <VAR abbrevid=4 op0=0 op1=0/>
+; BC-NEXT: <RELOC abbrevid=9 op0=5 op1=4294967295/>
+
+@addend_array1 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 1)
+; CHECK: @addend_array1 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 1)
+; BC-NEXT: <VAR abbrevid=4 op0=0 op1=0/>
+; BC-NEXT: <RELOC abbrevid=9 op0=1 op1=1/>
+
+@addend_array2 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 7)
+; CHECK: @addend_array2 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 7)
+; BC-NEXT: <VAR abbrevid=4 op0=0 op1=0/>
+; BC-NEXT: <RELOC abbrevid=9 op0=1 op1=7/>
+
+@addend_array3 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 9)
+; CHECK: @addend_array3 = internal global i32 add (i32 ptrtoint ([7 x i8]* @bytes to i32), i32 9)
+; BC-NEXT: <VAR abbrevid=4 op0=0 op1=0/>
+; BC-NEXT: <RELOC abbrevid=9 op0=1 op1=9/>
+
+@addend_struct1 = internal global i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 1)
+; CHECK: @addend_struct1 = internal global i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 1)
+; BC-NEXT: <VAR abbrevid=4 op0=0 op1=0/>
+; BC-NEXT: <RELOC abbrevid=9 op0=4 op1=1/>
+
+@addend_struct2 = internal global i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 4)
+; CHECK: @addend_struct2 = internal global i32 add (i32 ptrtoint (<{ [3 x i8], i32 }>* @compound to i32), i32 4)
+; BC-NEXT: <VAR abbrevid=4 op0=0 op1=0/>
+; BC-NEXT: <RELOC abbrevid=9 op0=4 op1=4/>
+
+@ptr_to_func_align = internal global i32 ptrtoint (void ()* @func to i32), align 8
+; CHECK: @ptr_to_func_align = internal global i32 ptrtoint (void ()* @func to i32), align 8
+; BC-NEXT: <VAR abbrevid=4 op0=4 op1=0/>
+; BC-NEXT: <RELOC abbrevid=8 op0=0/>
+
+@char = internal constant [1 x i8] c"0"
+; CHECK: @char = internal constant [1 x i8] c"0"
+; BC-NEXT: <VAR abbrevid=4 op0=0 op1=1/>
+; BC-NEXT: <DATA abbrevid=7 op0=48/>
+
+@short = internal constant [2 x i8] zeroinitializer
+; CHECK: @short = internal constant [2 x i8] zeroinitializer
+; BC-NEXT: <VAR abbrevid=4 op0=0 op1=1/>
+; BC-NEXT: <ZEROFILL abbrevid=6 op0=2/>
+
+; BC-NEXT: </GLOBALVAR_BLOCK>
+
+define void @func() {
+ ret void
+}
+
diff --git a/tools/pnacl-bcanalyzer/pnacl-bcanalyzer.cpp b/tools/pnacl-bcanalyzer/pnacl-bcanalyzer.cpp
index ee002dd729..7a44542022 100644
--- a/tools/pnacl-bcanalyzer/pnacl-bcanalyzer.cpp
+++ b/tools/pnacl-bcanalyzer/pnacl-bcanalyzer.cpp
@@ -97,6 +97,7 @@ static const char *GetBlockName(unsigned BlockID,
case naclbitc::METADATA_BLOCK_ID: return "METADATA_BLOCK";
case naclbitc::METADATA_ATTACHMENT_ID: return "METADATA_ATTACHMENT_BLOCK";
case naclbitc::USELIST_BLOCK_ID: return "USELIST_BLOCK_ID";
+ case naclbitc::GLOBALVAR_BLOCK_ID: return "GLOBALVAR_BLOCK";
}
}
@@ -259,6 +260,16 @@ static const char *GetCodeName(unsigned CodeID, unsigned BlockID,
default:return 0;
case naclbitc::USELIST_CODE_ENTRY: return "USELIST_CODE_ENTRY";
}
+ case naclbitc::GLOBALVAR_BLOCK_ID:
+ switch (CodeID) {
+ default: return 0;
+ case naclbitc::GLOBALVAR_VAR: return "VAR";
+ case naclbitc::GLOBALVAR_COMPOUND: return "COMPOUND";
+ case naclbitc::GLOBALVAR_ZEROFILL: return "ZEROFILL";
+ case naclbitc::GLOBALVAR_DATA: return "DATA";
+ case naclbitc::GLOBALVAR_RELOC: return "RELOC";
+ case naclbitc::GLOBALVAR_COUNT: return "COUNT";
+ }
}
}