forked from OSchip/llvm-project
Add support for embedded metadata to LLVM. This introduces two new types of
Constant, MDString and MDNode which can only be used by globals with a name that starts with "llvm." or as arguments to a function with the same naming restriction. llvm-svn: 68420
This commit is contained in:
parent
734dee287d
commit
49f891958f
|
@ -65,6 +65,7 @@
|
||||||
<li><a href="#globalconstants">Global Variable and Function Addresses</a></li>
|
<li><a href="#globalconstants">Global Variable and Function Addresses</a></li>
|
||||||
<li><a href="#undefvalues">Undefined Values</a></li>
|
<li><a href="#undefvalues">Undefined Values</a></li>
|
||||||
<li><a href="#constantexprs">Constant Expressions</a></li>
|
<li><a href="#constantexprs">Constant Expressions</a></li>
|
||||||
|
<li><a href="#metadata">Embedded Metadata</a></li>
|
||||||
</ol>
|
</ol>
|
||||||
</li>
|
</li>
|
||||||
<li><a href="#othervalues">Other Values</a>
|
<li><a href="#othervalues">Other Values</a>
|
||||||
|
@ -1847,6 +1848,14 @@ constants and smaller complex constants.</p>
|
||||||
large arrays) and is always exactly equivalent to using explicit zero
|
large arrays) and is always exactly equivalent to using explicit zero
|
||||||
initializers.
|
initializers.
|
||||||
</dd>
|
</dd>
|
||||||
|
|
||||||
|
<dt><b>Metadata node</b></dt>
|
||||||
|
|
||||||
|
<dd>A metadata node is a structure-like constant with the type of an empty
|
||||||
|
struct. For example: "<tt>{ } !{ i32 0, { } !"test" }</tt>". Unlike other
|
||||||
|
constants that are meant to be interpreted as part of the instruction stream,
|
||||||
|
metadata is a place to attach additional information such as debug info.
|
||||||
|
</dd>
|
||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -2015,6 +2024,35 @@ following is the syntax for constant expressions:</p>
|
||||||
</dl>
|
</dl>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- ======================================================================= -->
|
||||||
|
<div class="doc_subsection"><a name="metadata">Embedded Metadata</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="doc_text">
|
||||||
|
|
||||||
|
<p>Embedded metadata provides a way to attach arbitrary data to the
|
||||||
|
instruction stream without affecting the behaviour of the program. There are
|
||||||
|
two metadata primitives, strings and nodes. All metadata has the type of an
|
||||||
|
empty struct and is identified in syntax by a preceding exclamation point
|
||||||
|
('<tt>!</tt>').
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>A metadata string is a string surrounded by double quotes. It can contain
|
||||||
|
any character by escaping non-printable characters with "\xx" where "xx" is
|
||||||
|
the two digit hex code. For example: "<tt>!"test\00"</tt>".
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Metadata nodes are represented with notation similar to structure constants
|
||||||
|
(a comma separated list of elements, surrounded by braces and preceeded by an
|
||||||
|
exclamation point). For example: "<tt>!{ { } !"test\00", i32 10}</tt>".
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>Optimizations may rely on metadata to provide additional information about
|
||||||
|
the program that isn't available in the instructions, or that isn't easily
|
||||||
|
computable. Similarly, the code generator may expect a certain metadata format
|
||||||
|
to be used to express debugging information.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- *********************************************************************** -->
|
<!-- *********************************************************************** -->
|
||||||
<div class="doc_section"> <a name="othervalues">Other Values</a> </div>
|
<div class="doc_section"> <a name="othervalues">Other Values</a> </div>
|
||||||
<!-- *********************************************************************** -->
|
<!-- *********************************************************************** -->
|
||||||
|
|
|
@ -126,7 +126,9 @@ namespace bitc {
|
||||||
CST_CODE_CE_SHUFFLEVEC = 16, // CE_SHUFFLEVEC: [opval, opval, opval]
|
CST_CODE_CE_SHUFFLEVEC = 16, // CE_SHUFFLEVEC: [opval, opval, opval]
|
||||||
CST_CODE_CE_CMP = 17, // CE_CMP: [opty, opval, opval, pred]
|
CST_CODE_CE_CMP = 17, // CE_CMP: [opty, opval, opval, pred]
|
||||||
CST_CODE_INLINEASM = 18, // INLINEASM: [sideeffect,asmstr,conststr]
|
CST_CODE_INLINEASM = 18, // INLINEASM: [sideeffect,asmstr,conststr]
|
||||||
CST_CODE_CE_SHUFVEC_EX = 19 // SHUFVEC_EX: [opty, opval, opval, opval]
|
CST_CODE_CE_SHUFVEC_EX = 19, // SHUFVEC_EX: [opty, opval, opval, opval]
|
||||||
|
CST_CODE_MDSTRING = 20, // MDSTRING: [values]
|
||||||
|
CST_CODE_MDNODE = 21 // MDNODE: [n x (type num, value num)]
|
||||||
};
|
};
|
||||||
|
|
||||||
/// CastOpcodes - These are values used in the bitcode files to encode which
|
/// CastOpcodes - These are values used in the bitcode files to encode which
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
#include "llvm/OperandTraits.h"
|
#include "llvm/OperandTraits.h"
|
||||||
#include "llvm/ADT/APInt.h"
|
#include "llvm/ADT/APInt.h"
|
||||||
#include "llvm/ADT/APFloat.h"
|
#include "llvm/ADT/APFloat.h"
|
||||||
|
#include "llvm/ADT/FoldingSet.h"
|
||||||
#include "llvm/ADT/SmallVector.h"
|
#include "llvm/ADT/SmallVector.h"
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
@ -813,6 +814,109 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
/// MDString - a single uniqued string.
|
||||||
|
/// These are used to efficiently contain a byte sequence for metadata.
|
||||||
|
///
|
||||||
|
class MDString : public Constant {
|
||||||
|
MDString(const MDString &); // DO NOT IMPLEMENT
|
||||||
|
void *operator new(size_t, unsigned); // DO NOT IMPLEMENT
|
||||||
|
MDString(const char *begin, const char *end);
|
||||||
|
|
||||||
|
const char *StrBegin, *StrEnd;
|
||||||
|
protected:
|
||||||
|
// allocate space for exactly zero operands
|
||||||
|
void *operator new(size_t s) {
|
||||||
|
return User::operator new(s, 0);
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
/// get() - Static factory methods - Return objects of the specified value.
|
||||||
|
///
|
||||||
|
static MDString *get(const char *StrBegin, const char *StrEnd);
|
||||||
|
|
||||||
|
/// size() - The length of this string.
|
||||||
|
///
|
||||||
|
unsigned size() const { return StrEnd - StrBegin; }
|
||||||
|
|
||||||
|
/// begin() - Pointer to the first byte of the string.
|
||||||
|
///
|
||||||
|
const char *begin() const { return StrBegin; }
|
||||||
|
|
||||||
|
/// end() - Pointer to one byte past the end of the string.
|
||||||
|
///
|
||||||
|
const char *end() const { return StrEnd; }
|
||||||
|
|
||||||
|
/// getType() specialization - Type is always an empty struct.
|
||||||
|
///
|
||||||
|
inline const Type *getType() const {
|
||||||
|
return Type::EmptyStructTy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// isNullValue - Return true if this is the value that would be returned by
|
||||||
|
/// getNullValue. This always returns false because getNullValue will never
|
||||||
|
/// produce metadata.
|
||||||
|
virtual bool isNullValue() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void destroyConstant();
|
||||||
|
|
||||||
|
/// Methods for support type inquiry through isa, cast, and dyn_cast:
|
||||||
|
static inline bool classof(const MDString *) { return true; }
|
||||||
|
static bool classof(const Value *V) {
|
||||||
|
return V->getValueID() == MDStringVal;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
/// MDNode - a tuple of other values.
|
||||||
|
/// These contain a list of the Constants that represent the metadata.
|
||||||
|
///
|
||||||
|
class MDNode : public Constant, public FoldingSetNode {
|
||||||
|
MDNode(const MDNode &); // DO NOT IMPLEMENT
|
||||||
|
protected:
|
||||||
|
explicit MDNode(Constant*const* Vals, unsigned NumVals);
|
||||||
|
public:
|
||||||
|
/// get() - Static factory methods - Return objects of the specified value.
|
||||||
|
///
|
||||||
|
static MDNode *get(Constant*const* Vals, unsigned NumVals);
|
||||||
|
|
||||||
|
// Transparently provide more efficient getOperand methods.
|
||||||
|
DECLARE_TRANSPARENT_OPERAND_ACCESSORS(Constant);
|
||||||
|
|
||||||
|
/// getType() specialization - Type is always an empty struct.
|
||||||
|
///
|
||||||
|
inline const Type *getType() const {
|
||||||
|
return Type::EmptyStructTy;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// isNullValue - Return true if this is the value that would be returned by
|
||||||
|
/// getNullValue. This always returns false because getNullValue will never
|
||||||
|
/// produce metadata.
|
||||||
|
virtual bool isNullValue() const {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Profile - calculate a unique identifier for this MDNode to collapse
|
||||||
|
/// duplicates
|
||||||
|
void Profile(FoldingSetNodeID &ID);
|
||||||
|
|
||||||
|
virtual void destroyConstant();
|
||||||
|
virtual void replaceUsesOfWithOnConstant(Value *From, Value *To, Use *U);
|
||||||
|
|
||||||
|
/// Methods for support type inquiry through isa, cast, and dyn_cast:
|
||||||
|
static inline bool classof(const MDNode *) { return true; }
|
||||||
|
static bool classof(const Value *V) {
|
||||||
|
return V->getValueID() == MDNodeVal;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct OperandTraits<MDNode> : VariadicOperandTraits<> {
|
||||||
|
};
|
||||||
|
|
||||||
|
DEFINE_TRANSPARENT_CASTED_OPERAND_ACCESSORS(MDNode, Constant)
|
||||||
|
|
||||||
} // End llvm namespace
|
} // End llvm namespace
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -326,7 +326,7 @@ public:
|
||||||
//===--------------------------------------------------------------------===//
|
//===--------------------------------------------------------------------===//
|
||||||
// These are the builtin types that are always available...
|
// These are the builtin types that are always available...
|
||||||
//
|
//
|
||||||
static const Type *VoidTy, *LabelTy, *FloatTy, *DoubleTy;
|
static const Type *VoidTy, *LabelTy, *FloatTy, *DoubleTy, *EmptyStructTy;
|
||||||
static const Type *X86_FP80Ty, *FP128Ty, *PPC_FP128Ty;
|
static const Type *X86_FP80Ty, *FP128Ty, *PPC_FP128Ty;
|
||||||
static const IntegerType *Int1Ty, *Int8Ty, *Int16Ty, *Int32Ty, *Int64Ty;
|
static const IntegerType *Int1Ty, *Int8Ty, *Int16Ty, *Int32Ty, *Int64Ty;
|
||||||
|
|
||||||
|
|
|
@ -203,13 +203,15 @@ public:
|
||||||
ConstantStructVal, // This is an instance of ConstantStruct
|
ConstantStructVal, // This is an instance of ConstantStruct
|
||||||
ConstantVectorVal, // This is an instance of ConstantVector
|
ConstantVectorVal, // This is an instance of ConstantVector
|
||||||
ConstantPointerNullVal, // This is an instance of ConstantPointerNull
|
ConstantPointerNullVal, // This is an instance of ConstantPointerNull
|
||||||
|
MDStringVal, // This is an instance of MDString
|
||||||
|
MDNodeVal, // This is an instance of MDNode
|
||||||
InlineAsmVal, // This is an instance of InlineAsm
|
InlineAsmVal, // This is an instance of InlineAsm
|
||||||
PseudoSourceValueVal, // This is an instance of PseudoSourceValue
|
PseudoSourceValueVal, // This is an instance of PseudoSourceValue
|
||||||
InstructionVal, // This is an instance of Instruction
|
InstructionVal, // This is an instance of Instruction
|
||||||
|
|
||||||
// Markers:
|
// Markers:
|
||||||
ConstantFirstVal = FunctionVal,
|
ConstantFirstVal = FunctionVal,
|
||||||
ConstantLastVal = ConstantPointerNullVal
|
ConstantLastVal = MDNodeVal
|
||||||
};
|
};
|
||||||
|
|
||||||
/// getValueID - Return an ID for the concrete type of this object. This is
|
/// getValueID - Return an ID for the concrete type of this object. This is
|
||||||
|
|
|
@ -265,6 +265,7 @@ lltok::Kind LLLexer::LexToken() {
|
||||||
case ';':
|
case ';':
|
||||||
SkipLineComment();
|
SkipLineComment();
|
||||||
return LexToken();
|
return LexToken();
|
||||||
|
case '!': return lltok::Metadata;
|
||||||
case '0': case '1': case '2': case '3': case '4':
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
case '5': case '6': case '7': case '8': case '9':
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
case '-':
|
case '-':
|
||||||
|
|
|
@ -1561,6 +1561,29 @@ bool LLParser::ParseValID(ValID &ID) {
|
||||||
ID.StrVal = Lex.getStrVal();
|
ID.StrVal = Lex.getStrVal();
|
||||||
ID.Kind = ValID::t_LocalName;
|
ID.Kind = ValID::t_LocalName;
|
||||||
break;
|
break;
|
||||||
|
case lltok::Metadata: { // !{...} MDNode, !"foo" MDString
|
||||||
|
ID.Kind = ValID::t_Constant;
|
||||||
|
Lex.Lex();
|
||||||
|
if (Lex.getKind() == lltok::lbrace) {
|
||||||
|
// MDNode:
|
||||||
|
// ::= '!' '{' TypeAndValue (',' TypeAndValue)* '}'
|
||||||
|
SmallVector<Constant*, 16> Elts;
|
||||||
|
if (ParseMDNodeVector(Elts) ||
|
||||||
|
ParseToken(lltok::rbrace, "expected end of metadata node"))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
ID.ConstantVal = MDNode::get(&Elts[0], Elts.size());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MDString:
|
||||||
|
// ::= '!' STRINGCONSTANT
|
||||||
|
std::string Str;
|
||||||
|
if (ParseStringConstant(Str)) return true;
|
||||||
|
|
||||||
|
ID.ConstantVal = MDString::get(Str.data(), Str.data() + Str.size());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
case lltok::APSInt:
|
case lltok::APSInt:
|
||||||
ID.APSIntVal = Lex.getAPSIntVal();
|
ID.APSIntVal = Lex.getAPSIntVal();
|
||||||
ID.Kind = ValID::t_APSInt;
|
ID.Kind = ValID::t_APSInt;
|
||||||
|
@ -3221,3 +3244,21 @@ bool LLParser::ParseInsertValue(Instruction *&Inst, PerFunctionState &PFS) {
|
||||||
Inst = InsertValueInst::Create(Val0, Val1, Indices.begin(), Indices.end());
|
Inst = InsertValueInst::Create(Val0, Val1, Indices.begin(), Indices.end());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
// Embedded metadata.
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
/// ParseMDNodeVector
|
||||||
|
/// ::= TypeAndValue (',' TypeAndValue)*
|
||||||
|
bool LLParser::ParseMDNodeVector(SmallVectorImpl<Constant*> &Elts) {
|
||||||
|
assert(Lex.getKind() == lltok::lbrace);
|
||||||
|
Lex.Lex();
|
||||||
|
do {
|
||||||
|
Constant *C;
|
||||||
|
if (ParseGlobalTypeAndValue(C)) return true;
|
||||||
|
Elts.push_back(C);
|
||||||
|
} while (EatIfPresent(lltok::comma));
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
|
@ -27,6 +27,8 @@ namespace llvm {
|
||||||
class Instruction;
|
class Instruction;
|
||||||
class Constant;
|
class Constant;
|
||||||
class GlobalValue;
|
class GlobalValue;
|
||||||
|
class MDString;
|
||||||
|
class MDNode;
|
||||||
struct ValID;
|
struct ValID;
|
||||||
|
|
||||||
class LLParser {
|
class LLParser {
|
||||||
|
@ -156,6 +158,7 @@ namespace llvm {
|
||||||
bool ParseGlobalValue(const Type *Ty, Constant *&V);
|
bool ParseGlobalValue(const Type *Ty, Constant *&V);
|
||||||
bool ParseGlobalTypeAndValue(Constant *&V);
|
bool ParseGlobalTypeAndValue(Constant *&V);
|
||||||
bool ParseGlobalValueVector(SmallVectorImpl<Constant*> &Elts);
|
bool ParseGlobalValueVector(SmallVectorImpl<Constant*> &Elts);
|
||||||
|
bool ParseMDNodeVector(SmallVectorImpl<Constant*> &);
|
||||||
|
|
||||||
|
|
||||||
// Function Semantic Analysis.
|
// Function Semantic Analysis.
|
||||||
|
|
|
@ -115,6 +115,9 @@ namespace lltok {
|
||||||
LocalVar, // %foo %"foo"
|
LocalVar, // %foo %"foo"
|
||||||
StringConstant, // "foo"
|
StringConstant, // "foo"
|
||||||
|
|
||||||
|
// Metadata valued tokens.
|
||||||
|
Metadata, // !"foo" !{i8 42}
|
||||||
|
|
||||||
// Type valued tokens (TyVal).
|
// Type valued tokens (TyVal).
|
||||||
Type,
|
Type,
|
||||||
|
|
||||||
|
|
|
@ -286,10 +286,12 @@ void BitcodeReaderValueList::ResolveConstantForwardRefs() {
|
||||||
UserCS->getType()->isPacked());
|
UserCS->getType()->isPacked());
|
||||||
} else if (isa<ConstantVector>(UserC)) {
|
} else if (isa<ConstantVector>(UserC)) {
|
||||||
NewC = ConstantVector::get(&NewOps[0], NewOps.size());
|
NewC = ConstantVector::get(&NewOps[0], NewOps.size());
|
||||||
} else {
|
} else if (isa<ConstantExpr>(UserC)) {
|
||||||
// Must be a constant expression.
|
|
||||||
NewC = cast<ConstantExpr>(UserC)->getWithOperands(&NewOps[0],
|
NewC = cast<ConstantExpr>(UserC)->getWithOperands(&NewOps[0],
|
||||||
NewOps.size());
|
NewOps.size());
|
||||||
|
} else {
|
||||||
|
assert(isa<MDNode>(UserC) && "Must be a metadata node.");
|
||||||
|
NewC = MDNode::get(&NewOps[0], NewOps.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
UserC->replaceAllUsesWith(NewC);
|
UserC->replaceAllUsesWith(NewC);
|
||||||
|
@ -999,6 +1001,29 @@ bool BitcodeReader::ParseConstants() {
|
||||||
AsmStr, ConstrStr, HasSideEffects);
|
AsmStr, ConstrStr, HasSideEffects);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case bitc::CST_CODE_MDSTRING: {
|
||||||
|
if (Record.size() < 2) return Error("Invalid MDSTRING record");
|
||||||
|
unsigned MDStringLength = Record.size();
|
||||||
|
SmallString<8> String;
|
||||||
|
String.resize(MDStringLength);
|
||||||
|
for (unsigned i = 0; i != MDStringLength; ++i)
|
||||||
|
String[i] = Record[i];
|
||||||
|
V = MDString::get(String.c_str(), String.c_str() + MDStringLength);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case bitc::CST_CODE_MDNODE: {
|
||||||
|
if (Record.empty() || Record.size() % 2 == 1)
|
||||||
|
return Error("Invalid CST_MDNODE record");
|
||||||
|
|
||||||
|
unsigned Size = Record.size();
|
||||||
|
SmallVector<Constant*, 8> Elts;
|
||||||
|
for (unsigned i = 0; i != Size; i += 2) {
|
||||||
|
const Type *Ty = getTypeByID(Record[i], false);
|
||||||
|
Elts.push_back(ValueList.getConstantFwdRef(Record[i+1], Ty));
|
||||||
|
}
|
||||||
|
V = MDNode::get(&Elts[0], Elts.size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ValueList.AssignValue(V, NextCstNo);
|
ValueList.AssignValue(V, NextCstNo);
|
||||||
|
|
|
@ -458,6 +458,8 @@ static void WriteConstants(unsigned FirstVal, unsigned LastVal,
|
||||||
unsigned String8Abbrev = 0;
|
unsigned String8Abbrev = 0;
|
||||||
unsigned CString7Abbrev = 0;
|
unsigned CString7Abbrev = 0;
|
||||||
unsigned CString6Abbrev = 0;
|
unsigned CString6Abbrev = 0;
|
||||||
|
unsigned MDString8Abbrev = 0;
|
||||||
|
unsigned MDString6Abbrev = 0;
|
||||||
// If this is a constant pool for the module, emit module-specific abbrevs.
|
// If this is a constant pool for the module, emit module-specific abbrevs.
|
||||||
if (isGlobal) {
|
if (isGlobal) {
|
||||||
// Abbrev for CST_CODE_AGGREGATE.
|
// Abbrev for CST_CODE_AGGREGATE.
|
||||||
|
@ -485,6 +487,19 @@ static void WriteConstants(unsigned FirstVal, unsigned LastVal,
|
||||||
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));
|
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));
|
||||||
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Char6));
|
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Char6));
|
||||||
CString6Abbrev = Stream.EmitAbbrev(Abbv);
|
CString6Abbrev = Stream.EmitAbbrev(Abbv);
|
||||||
|
|
||||||
|
// Abbrev for CST_CODE_MDSTRING.
|
||||||
|
Abbv = new BitCodeAbbrev();
|
||||||
|
Abbv->Add(BitCodeAbbrevOp(bitc::CST_CODE_MDSTRING));
|
||||||
|
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));
|
||||||
|
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 8));
|
||||||
|
MDString8Abbrev = Stream.EmitAbbrev(Abbv);
|
||||||
|
// Abbrev for CST_CODE_MDSTRING.
|
||||||
|
Abbv = new BitCodeAbbrev();
|
||||||
|
Abbv->Add(BitCodeAbbrevOp(bitc::CST_CODE_MDSTRING));
|
||||||
|
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Array));
|
||||||
|
Abbv->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Char6));
|
||||||
|
MDString6Abbrev = Stream.EmitAbbrev(Abbv);
|
||||||
}
|
}
|
||||||
|
|
||||||
SmallVector<uint64_t, 64> Record;
|
SmallVector<uint64_t, 64> Record;
|
||||||
|
@ -678,6 +693,22 @@ static void WriteConstants(unsigned FirstVal, unsigned LastVal,
|
||||||
Record.push_back(CE->getPredicate());
|
Record.push_back(CE->getPredicate());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else if (const MDString *S = dyn_cast<MDString>(C)) {
|
||||||
|
Code = bitc::CST_CODE_MDSTRING;
|
||||||
|
AbbrevToUse = MDString6Abbrev;
|
||||||
|
for (unsigned i = 0, e = S->size(); i != e; ++i) {
|
||||||
|
char V = S->begin()[i];
|
||||||
|
Record.push_back(V);
|
||||||
|
|
||||||
|
if (!BitCodeAbbrevOp::isChar6(V))
|
||||||
|
AbbrevToUse = MDString8Abbrev;
|
||||||
|
}
|
||||||
|
} else if (const MDNode *N = dyn_cast<MDNode>(C)) {
|
||||||
|
Code = bitc::CST_CODE_MDNODE;
|
||||||
|
for (unsigned i = 0, e = N->getNumOperands(); i != e; ++i) {
|
||||||
|
Record.push_back(VE.getTypeID(N->getOperand(i)->getType()));
|
||||||
|
Record.push_back(VE.getValueID(N->getOperand(i)));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
assert(0 && "Unknown constant!");
|
assert(0 && "Unknown constant!");
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "llvm/InlineAsm.h"
|
#include "llvm/InlineAsm.h"
|
||||||
#include "llvm/Instruction.h"
|
#include "llvm/Instruction.h"
|
||||||
#include "llvm/Instructions.h"
|
#include "llvm/Instructions.h"
|
||||||
|
#include "llvm/Metadata.h"
|
||||||
#include "llvm/Module.h"
|
#include "llvm/Module.h"
|
||||||
#include "llvm/ValueSymbolTable.h"
|
#include "llvm/ValueSymbolTable.h"
|
||||||
#include "llvm/TypeSymbolTable.h"
|
#include "llvm/TypeSymbolTable.h"
|
||||||
|
@ -361,8 +362,8 @@ namespace {
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If this is a structure or opaque type, add a name for the type.
|
// If this is a structure or opaque type, add a name for the type.
|
||||||
if ((isa<StructType>(Ty) || isa<OpaqueType>(Ty))
|
if (((isa<StructType>(Ty) && cast<StructType>(Ty)->getNumElements())
|
||||||
&& !TP.hasTypeName(Ty)) {
|
|| isa<OpaqueType>(Ty)) && !TP.hasTypeName(Ty)) {
|
||||||
TP.addTypeName(Ty, "%"+utostr(unsigned(NumberedTypes.size())));
|
TP.addTypeName(Ty, "%"+utostr(unsigned(NumberedTypes.size())));
|
||||||
NumberedTypes.push_back(Ty);
|
NumberedTypes.push_back(Ty);
|
||||||
}
|
}
|
||||||
|
@ -936,6 +937,26 @@ static void WriteConstantInt(raw_ostream &Out, const Constant *CV,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (const MDString *S = dyn_cast<MDString>(CV)) {
|
||||||
|
Out << "!\"";
|
||||||
|
PrintEscapedString(S->begin(), S->size(), Out);
|
||||||
|
Out << '"';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (const MDNode *N = dyn_cast<MDNode>(CV)) {
|
||||||
|
Out << "!{";
|
||||||
|
for (MDNode::const_op_iterator I = N->op_begin(), E = N->op_end(); I != E;){
|
||||||
|
TypePrinter.print((*I)->getType(), Out);
|
||||||
|
Out << ' ';
|
||||||
|
WriteAsOperandInternal(Out, *I, TypePrinter, Machine);
|
||||||
|
if (++I != E)
|
||||||
|
Out << ", ";
|
||||||
|
}
|
||||||
|
Out << "}";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(CV)) {
|
if (const ConstantExpr *CE = dyn_cast<ConstantExpr>(CV)) {
|
||||||
Out << CE->getOpcodeName();
|
Out << CE->getOpcodeName();
|
||||||
if (CE->isCompare())
|
if (CE->isCompare())
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
#include "llvm/GlobalValue.h"
|
#include "llvm/GlobalValue.h"
|
||||||
#include "llvm/Instructions.h"
|
#include "llvm/Instructions.h"
|
||||||
#include "llvm/Module.h"
|
#include "llvm/Module.h"
|
||||||
|
#include "llvm/ADT/FoldingSet.h"
|
||||||
#include "llvm/ADT/StringExtras.h"
|
#include "llvm/ADT/StringExtras.h"
|
||||||
|
#include "llvm/ADT/StringMap.h"
|
||||||
#include "llvm/Support/Compiler.h"
|
#include "llvm/Support/Compiler.h"
|
||||||
#include "llvm/Support/Debug.h"
|
#include "llvm/Support/Debug.h"
|
||||||
#include "llvm/Support/ManagedStatic.h"
|
#include "llvm/Support/ManagedStatic.h"
|
||||||
|
@ -1657,6 +1659,63 @@ void UndefValue::destroyConstant() {
|
||||||
destroyConstantImpl();
|
destroyConstantImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//---- MDString::get() implementation
|
||||||
|
//
|
||||||
|
|
||||||
|
MDString::MDString(const char *begin, const char *end)
|
||||||
|
: Constant(Type::EmptyStructTy, MDStringVal, 0, 0),
|
||||||
|
StrBegin(begin), StrEnd(end) {}
|
||||||
|
|
||||||
|
static ManagedStatic<StringMap<MDString*> > MDStringCache;
|
||||||
|
|
||||||
|
MDString *MDString::get(const char *StrBegin, const char *StrEnd) {
|
||||||
|
StringMapEntry<MDString *> &Entry = MDStringCache->GetOrCreateValue(StrBegin,
|
||||||
|
StrEnd);
|
||||||
|
MDString *&S = Entry.getValue();
|
||||||
|
if (!S) S = new MDString(Entry.getKeyData(),
|
||||||
|
Entry.getKeyData() + Entry.getKeyLength());
|
||||||
|
return S;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDString::destroyConstant() {
|
||||||
|
MDStringCache->erase(MDStringCache->find(StrBegin, StrEnd));
|
||||||
|
destroyConstantImpl();
|
||||||
|
}
|
||||||
|
|
||||||
|
//---- MDNode::get() implementation
|
||||||
|
//
|
||||||
|
|
||||||
|
static ManagedStatic<FoldingSet<MDNode> > MDNodeSet;
|
||||||
|
|
||||||
|
MDNode::MDNode(Constant*const* Vals, unsigned NumVals)
|
||||||
|
: Constant(Type::EmptyStructTy, MDNodeVal,
|
||||||
|
OperandTraits<MDNode>::op_end(this) - NumVals, NumVals) {
|
||||||
|
std::copy(Vals, Vals + NumVals, OperandList);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNode::Profile(FoldingSetNodeID &ID) {
|
||||||
|
for (op_iterator I = op_begin(), E = op_end(); I != E; ++I)
|
||||||
|
ID.AddPointer(*I);
|
||||||
|
}
|
||||||
|
|
||||||
|
MDNode *MDNode::get(Constant*const* Vals, unsigned NumVals) {
|
||||||
|
FoldingSetNodeID ID;
|
||||||
|
for (unsigned i = 0; i != NumVals; ++i)
|
||||||
|
ID.AddPointer(Vals[i]);
|
||||||
|
|
||||||
|
void *InsertPoint;
|
||||||
|
if (MDNode *N = MDNodeSet->FindNodeOrInsertPos(ID, InsertPoint))
|
||||||
|
return N;
|
||||||
|
|
||||||
|
// InsertPoint will have been set by the FindNodeOrInsertPos call.
|
||||||
|
MDNode *N = new(NumVals) MDNode(Vals, NumVals);
|
||||||
|
MDNodeSet->InsertNode(N, InsertPoint);
|
||||||
|
return N;
|
||||||
|
}
|
||||||
|
|
||||||
|
void MDNode::destroyConstant() {
|
||||||
|
destroyConstantImpl();
|
||||||
|
}
|
||||||
|
|
||||||
//---- ConstantExpr::get() implementations...
|
//---- ConstantExpr::get() implementations...
|
||||||
//
|
//
|
||||||
|
@ -2741,3 +2800,24 @@ void ConstantExpr::replaceUsesOfWithOnConstant(Value *From, Value *ToV,
|
||||||
// Delete the old constant!
|
// Delete the old constant!
|
||||||
destroyConstant();
|
destroyConstant();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MDNode::replaceUsesOfWithOnConstant(Value *From, Value *To, Use *U) {
|
||||||
|
assert(isa<Constant>(To) && "Cannot make Constant refer to non-constant!");
|
||||||
|
|
||||||
|
SmallVector<Constant*, 8> Values;
|
||||||
|
Values.reserve(getNumOperands()); // Build replacement array...
|
||||||
|
for (unsigned i = 0, e = getNumOperands(); i != e; ++i) {
|
||||||
|
Constant *Val = getOperand(i);
|
||||||
|
if (Val == From) Val = cast<Constant>(To);
|
||||||
|
Values.push_back(Val);
|
||||||
|
}
|
||||||
|
|
||||||
|
Constant *Replacement = MDNode::get(&Values[0], Values.size());
|
||||||
|
assert(Replacement != this && "I didn't contain From!");
|
||||||
|
|
||||||
|
// Everyone using this now uses the replacement.
|
||||||
|
uncheckedReplaceAllUsesWith(Replacement);
|
||||||
|
|
||||||
|
// Delete the old constant!
|
||||||
|
destroyConstant();
|
||||||
|
}
|
||||||
|
|
|
@ -288,6 +288,8 @@ const IntegerType *Type::Int16Ty = new BuiltinIntegerType(16);
|
||||||
const IntegerType *Type::Int32Ty = new BuiltinIntegerType(32);
|
const IntegerType *Type::Int32Ty = new BuiltinIntegerType(32);
|
||||||
const IntegerType *Type::Int64Ty = new BuiltinIntegerType(64);
|
const IntegerType *Type::Int64Ty = new BuiltinIntegerType(64);
|
||||||
|
|
||||||
|
const Type *Type::EmptyStructTy = StructType::get(NULL, NULL);
|
||||||
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Derived Type Constructors
|
// Derived Type Constructors
|
||||||
|
|
|
@ -338,6 +338,36 @@ static RegisterPass<Verifier> X("verify", "Module Verifier");
|
||||||
#define Assert4(C, M, V1, V2, V3, V4) \
|
#define Assert4(C, M, V1, V2, V3, V4) \
|
||||||
do { if (!(C)) { CheckFailed(M, V1, V2, V3, V4); return; } } while (0)
|
do { if (!(C)) { CheckFailed(M, V1, V2, V3, V4); return; } } while (0)
|
||||||
|
|
||||||
|
/// Check whether or not a Value is metadata or made up of a constant
|
||||||
|
/// expression involving metadata.
|
||||||
|
static bool isMetadata(Value *X) {
|
||||||
|
SmallPtrSet<Value *, 8> Visited;
|
||||||
|
SmallVector<Value *, 8> Queue;
|
||||||
|
Queue.push_back(X);
|
||||||
|
|
||||||
|
while (!Queue.empty()) {
|
||||||
|
Value *V = Queue.back();
|
||||||
|
Queue.pop_back();
|
||||||
|
if (!Visited.insert(V))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (isa<MDString>(V) || isa<MDNode>(V))
|
||||||
|
return true;
|
||||||
|
if (!isa<ConstantExpr>(V))
|
||||||
|
continue;
|
||||||
|
ConstantExpr *CE = cast<ConstantExpr>(V);
|
||||||
|
|
||||||
|
if (CE->getType() != Type::EmptyStructTy)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// The only constant expression that works on metadata type is select.
|
||||||
|
if (CE->getOpcode() != Instruction::Select) return false;
|
||||||
|
|
||||||
|
Queue.push_back(CE->getOperand(1));
|
||||||
|
Queue.push_back(CE->getOperand(2));
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void Verifier::visit(Instruction &I) {
|
void Verifier::visit(Instruction &I) {
|
||||||
for (unsigned i = 0, e = I.getNumOperands(); i != e; ++i)
|
for (unsigned i = 0, e = I.getNumOperands(); i != e; ++i)
|
||||||
|
@ -649,6 +679,7 @@ void Verifier::visitReturnInst(ReturnInst &RI) {
|
||||||
"Found return instr that returns non-void in Function of void "
|
"Found return instr that returns non-void in Function of void "
|
||||||
"return type!", &RI, F->getReturnType());
|
"return type!", &RI, F->getReturnType());
|
||||||
else if (N == 1 && F->getReturnType() == RI.getOperand(0)->getType()) {
|
else if (N == 1 && F->getReturnType() == RI.getOperand(0)->getType()) {
|
||||||
|
Assert1(!isMetadata(RI.getOperand(0)), "Invalid use of metadata!", &RI);
|
||||||
// Exactly one return value and it matches the return type. Good.
|
// Exactly one return value and it matches the return type. Good.
|
||||||
} else if (const StructType *STy = dyn_cast<StructType>(F->getReturnType())) {
|
} else if (const StructType *STy = dyn_cast<StructType>(F->getReturnType())) {
|
||||||
// The return type is a struct; check for multiple return values.
|
// The return type is a struct; check for multiple return values.
|
||||||
|
@ -696,6 +727,8 @@ void Verifier::visitSelectInst(SelectInst &SI) {
|
||||||
|
|
||||||
Assert1(SI.getTrueValue()->getType() == SI.getType(),
|
Assert1(SI.getTrueValue()->getType() == SI.getType(),
|
||||||
"Select values must have same type as select instruction!", &SI);
|
"Select values must have same type as select instruction!", &SI);
|
||||||
|
Assert1(!isMetadata(SI.getOperand(1)) && !isMetadata(SI.getOperand(2)),
|
||||||
|
"Invalid use of metadata!", &SI);
|
||||||
visitInstruction(SI);
|
visitInstruction(SI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -951,6 +984,13 @@ void Verifier::visitPHINode(PHINode &PN) {
|
||||||
Assert1(PN.getType() == PN.getIncomingValue(i)->getType(),
|
Assert1(PN.getType() == PN.getIncomingValue(i)->getType(),
|
||||||
"PHI node operands are not the same type as the result!", &PN);
|
"PHI node operands are not the same type as the result!", &PN);
|
||||||
|
|
||||||
|
// Check that it's not a PHI of metadata.
|
||||||
|
if (PN.getType() == Type::EmptyStructTy) {
|
||||||
|
for (unsigned i = 0, e = PN.getNumIncomingValues(); i != e; ++i)
|
||||||
|
Assert1(!isMetadata(PN.getIncomingValue(i)),
|
||||||
|
"Invalid use of metadata!", &PN);
|
||||||
|
}
|
||||||
|
|
||||||
// All other PHI node constraints are checked in the visitBasicBlock method.
|
// All other PHI node constraints are checked in the visitBasicBlock method.
|
||||||
|
|
||||||
visitInstruction(PN);
|
visitInstruction(PN);
|
||||||
|
@ -981,6 +1021,14 @@ void Verifier::VerifyCallSite(CallSite CS) {
|
||||||
"Call parameter type does not match function signature!",
|
"Call parameter type does not match function signature!",
|
||||||
CS.getArgument(i), FTy->getParamType(i), I);
|
CS.getArgument(i), FTy->getParamType(i), I);
|
||||||
|
|
||||||
|
if (CS.getCalledValue()->getNameLen() < 5 ||
|
||||||
|
strncmp(CS.getCalledValue()->getNameStart(), "llvm.", 5) != 0) {
|
||||||
|
// Verify that none of the arguments are metadata...
|
||||||
|
for (unsigned i = 0, e = FTy->getNumParams(); i != e; ++i)
|
||||||
|
Assert2(!isMetadata(CS.getArgument(i)), "Invalid use of metadata!",
|
||||||
|
CS.getArgument(i), I);
|
||||||
|
}
|
||||||
|
|
||||||
const AttrListPtr &Attrs = CS.getAttributes();
|
const AttrListPtr &Attrs = CS.getAttributes();
|
||||||
|
|
||||||
Assert1(VerifyAttributeCount(Attrs, CS.arg_size()),
|
Assert1(VerifyAttributeCount(Attrs, CS.arg_size()),
|
||||||
|
@ -1152,6 +1200,7 @@ void Verifier::visitStoreInst(StoreInst &SI) {
|
||||||
cast<PointerType>(SI.getOperand(1)->getType())->getElementType();
|
cast<PointerType>(SI.getOperand(1)->getType())->getElementType();
|
||||||
Assert2(ElTy == SI.getOperand(0)->getType(),
|
Assert2(ElTy == SI.getOperand(0)->getType(),
|
||||||
"Stored value type does not match pointer operand type!", &SI, ElTy);
|
"Stored value type does not match pointer operand type!", &SI, ElTy);
|
||||||
|
Assert1(!isMetadata(SI.getOperand(0)), "Invalid use of metadata!", &SI);
|
||||||
visitInstruction(SI);
|
visitInstruction(SI);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1481,7 +1530,7 @@ bool Verifier::PerformTypeCheck(Intrinsic::ID ID, Function *F, const Type *Ty,
|
||||||
if (EltTy != Ty)
|
if (EltTy != Ty)
|
||||||
Suffix += "v" + utostr(NumElts);
|
Suffix += "v" + utostr(NumElts);
|
||||||
|
|
||||||
Suffix += "i" + utostr(GotBits);;
|
Suffix += "i" + utostr(GotBits);
|
||||||
|
|
||||||
// Check some constraints on various intrinsics.
|
// Check some constraints on various intrinsics.
|
||||||
switch (ID) {
|
switch (ID) {
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
; RUN: llvm-as < %s | llvm-dis | not grep undef
|
||||||
|
|
||||||
|
declare i8 @llvm.something({ } %a)
|
||||||
|
|
||||||
|
@llvm.foo = internal constant { } !{i17 123, { } !"foobar"}
|
||||||
|
|
||||||
|
define void @foo() {
|
||||||
|
%x = call i8 @llvm.something({ } !{{ } !"f\00oa", i42 123})
|
||||||
|
ret void
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
//===- llvm/unittest/VMCore/Metadata.cpp - Metadata unit tests ------------===//
|
||||||
|
//
|
||||||
|
// The LLVM Compiler Infrastructure
|
||||||
|
//
|
||||||
|
// This file is distributed under the University of Illinois Open Source
|
||||||
|
// License. See LICENSE.TXT for details.
|
||||||
|
//
|
||||||
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
#include "gtest/gtest.h"
|
||||||
|
#include "llvm/Constants.h"
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
using namespace llvm;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// Test that construction of MDString with different value produces different
|
||||||
|
// MDString objects, even with the same string pointer and nulls in the string.
|
||||||
|
TEST(MDStringTest, CreateDifferent) {
|
||||||
|
char x[3] = { 'f', 0, 'A' };
|
||||||
|
MDString *s1 = MDString::get(&x[0], &x[3]);
|
||||||
|
x[2] = 'B';
|
||||||
|
MDString *s2 = MDString::get(&x[0], &x[3]);
|
||||||
|
EXPECT_NE(s1, s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that creation of MDStrings with the same string contents produces the
|
||||||
|
// same MDString object, even with different pointers.
|
||||||
|
TEST(MDStringTest, CreateSame) {
|
||||||
|
char x[4] = { 'a', 'b', 'c', 'X' };
|
||||||
|
char y[4] = { 'a', 'b', 'c', 'Y' };
|
||||||
|
|
||||||
|
MDString *s1 = MDString::get(&x[0], &x[3]);
|
||||||
|
MDString *s2 = MDString::get(&y[0], &y[3]);
|
||||||
|
EXPECT_EQ(s1, s2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that MDString prints out the string we fed it.
|
||||||
|
TEST(MDStringTest, PrintingSimple) {
|
||||||
|
char *str = new char[13];
|
||||||
|
strncpy(str, "testing 1 2 3", 13);
|
||||||
|
MDString *s = MDString::get(str, str+13);
|
||||||
|
strncpy(str, "aaaaaaaaaaaaa", 13);
|
||||||
|
delete[] str;
|
||||||
|
|
||||||
|
std::ostringstream oss;
|
||||||
|
s->print(oss);
|
||||||
|
EXPECT_STREQ("{ } !\"testing 1 2 3\"", oss.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test printing of MDString with non-printable characters.
|
||||||
|
TEST(MDStringTest, PrintingComplex) {
|
||||||
|
char str[5] = {0, '\n', '"', '\\', -1};
|
||||||
|
MDString *s = MDString::get(str+0, str+5);
|
||||||
|
std::ostringstream oss;
|
||||||
|
s->print(oss);
|
||||||
|
EXPECT_STREQ("{ } !\"\\00\\0A\\22\\5C\\FF\"", oss.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test the two constructors, and containing other Constants.
|
||||||
|
TEST(MDNodeTest, Everything) {
|
||||||
|
char x[3] = { 'a', 'b', 'c' };
|
||||||
|
char y[3] = { '1', '2', '3' };
|
||||||
|
|
||||||
|
MDString *s1 = MDString::get(&x[0], &x[3]);
|
||||||
|
MDString *s2 = MDString::get(&y[0], &y[3]);
|
||||||
|
ConstantInt *CI = ConstantInt::get(APInt(8, 0));
|
||||||
|
|
||||||
|
std::vector<Constant *> V;
|
||||||
|
V.push_back(s1);
|
||||||
|
V.push_back(CI);
|
||||||
|
V.push_back(s2);
|
||||||
|
|
||||||
|
MDNode *n1 = MDNode::get(&V[0], 3);
|
||||||
|
MDNode *n2 = MDNode::get((Constant**)&n1, 1);
|
||||||
|
MDNode *n3 = MDNode::get(&V[0], 3);
|
||||||
|
EXPECT_NE(n1, n2);
|
||||||
|
EXPECT_EQ(n1, n3);
|
||||||
|
|
||||||
|
EXPECT_EQ(3u, n1->getNumOperands());
|
||||||
|
EXPECT_EQ(s1, n1->getOperand(0));
|
||||||
|
EXPECT_EQ(CI, n1->getOperand(1));
|
||||||
|
EXPECT_EQ(s2, n1->getOperand(2));
|
||||||
|
|
||||||
|
EXPECT_EQ(1u, n2->getNumOperands());
|
||||||
|
EXPECT_EQ(n1, n2->getOperand(0));
|
||||||
|
|
||||||
|
std::ostringstream oss1, oss2;
|
||||||
|
n1->print(oss1);
|
||||||
|
n2->print(oss2);
|
||||||
|
EXPECT_STREQ("{ } !{{ } !\"abc\", i8 0, { } !\"123\"}", oss1.str().c_str());
|
||||||
|
EXPECT_STREQ("{ } !{{ } !{{ } !\"abc\", i8 0, { } !\"123\"}}",
|
||||||
|
oss2.str().c_str());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue