[DebugInfo] Add calling convention support for DWARF and CodeView

Summary:
Now DISubroutineType has a 'cc' field which should be a DW_CC_ enum.  If
it is present and non-zero, the backend will emit it as a
DW_AT_calling_convention attribute. On the CodeView side, we translate
it to the appropriate enum for the LF_PROCEDURE record.

I added a new LLVM vendor specific enum to the list of DWARF calling
conventions. DWARF does not appear to attempt to standardize these, so I
assume it's OK to do this until we coordinate with GCC on how to emit
vectorcall convention functions.

Reviewers: dexonsmith, majnemer, aaboud, amccarth

Subscribers: mehdi_amini, llvm-commits

Differential Revision: http://reviews.llvm.org/D21114

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@272197 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Reid Kleckner 2016-06-08 20:34:29 +00:00
parent 2ae3d2cd86
commit 3d3aca2d97
21 changed files with 468 additions and 69 deletions

View File

@ -243,7 +243,7 @@ enum MetadataCodes {
METADATA_FILE = 16, // [distinct, filename, directory]
METADATA_DERIVED_TYPE = 17, // [distinct, ...]
METADATA_COMPOSITE_TYPE = 18, // [distinct, ...]
METADATA_SUBROUTINE_TYPE = 19, // [distinct, flags, types]
METADATA_SUBROUTINE_TYPE = 19, // [distinct, flags, types, cc]
METADATA_COMPILE_UNIT = 20, // [distinct, ...]
METADATA_SUBPROGRAM = 21, // [distinct, ...]
METADATA_LEXICAL_BLOCK = 22, // [distinct, scope, file, line, column]

View File

@ -374,8 +374,9 @@ namespace llvm {
/// includes return type at 0th index.
/// \param Flags E.g.: LValueReference.
/// These flags are used to emit dwarf attributes.
/// \param CC Calling convention, e.g. dwarf::DW_CC_normal
DISubroutineType *createSubroutineType(DITypeRefArray ParameterTypes,
unsigned Flags = 0);
unsigned Flags = 0, unsigned CC = 0);
/// Create an external type reference.
/// \param Tag Dwarf TAG.

View File

@ -920,35 +920,44 @@ class DISubroutineType : public DIType {
friend class LLVMContextImpl;
friend class MDNode;
/// The calling convention used with DW_AT_calling_convention. Actually of
/// type dwarf::CallingConvention.
uint8_t CC;
DISubroutineType(LLVMContext &C, StorageType Storage, unsigned Flags,
ArrayRef<Metadata *> Ops)
uint8_t CC, ArrayRef<Metadata *> Ops)
: DIType(C, DISubroutineTypeKind, Storage, dwarf::DW_TAG_subroutine_type,
0, 0, 0, 0, Flags, Ops) {}
0, 0, 0, 0, Flags, Ops),
CC(CC) {}
~DISubroutineType() = default;
static DISubroutineType *getImpl(LLVMContext &Context, unsigned Flags,
DITypeRefArray TypeArray,
uint8_t CC, DITypeRefArray TypeArray,
StorageType Storage,
bool ShouldCreate = true) {
return getImpl(Context, Flags, TypeArray.get(), Storage, ShouldCreate);
return getImpl(Context, Flags, CC, TypeArray.get(), Storage, ShouldCreate);
}
static DISubroutineType *getImpl(LLVMContext &Context, unsigned Flags,
Metadata *TypeArray, StorageType Storage,
uint8_t CC, Metadata *TypeArray,
StorageType Storage,
bool ShouldCreate = true);
TempDISubroutineType cloneImpl() const {
return getTemporary(getContext(), getFlags(), getTypeArray());
return getTemporary(getContext(), getFlags(), getCC(), getTypeArray());
}
public:
DEFINE_MDNODE_GET(DISubroutineType,
(unsigned Flags, DITypeRefArray TypeArray),
(Flags, TypeArray))
DEFINE_MDNODE_GET(DISubroutineType, (unsigned Flags, Metadata *TypeArray),
(Flags, TypeArray))
(unsigned Flags, uint8_t CC, DITypeRefArray TypeArray),
(Flags, CC, TypeArray))
DEFINE_MDNODE_GET(DISubroutineType,
(unsigned Flags, uint8_t CC, Metadata *TypeArray),
(Flags, CC, TypeArray))
TempDISubroutineType clone() const { return cloneImpl(); }
uint8_t getCC() const { return CC; }
DITypeRefArray getTypeArray() const {
return cast_or_null<MDTuple>(getRawTypeArray());
}

View File

@ -14,7 +14,7 @@
// TODO: Add other DW-based macros.
#if !(defined HANDLE_DW_TAG || defined HANDLE_DW_OP || \
defined HANDLE_DW_LANG || defined HANDLE_DW_ATE || \
defined HANDLE_DW_VIRTUALITY)
defined HANDLE_DW_VIRTUALITY || defined HANDLE_DW_CC)
#error "Missing macro definition of HANDLE_DW*"
#endif
@ -38,6 +38,11 @@
#define HANDLE_DW_VIRTUALITY(ID, NAME)
#endif
#ifndef HANDLE_DW_CC
#define HANDLE_DW_CC(ID, NAME)
#endif
HANDLE_DW_TAG(0x0001, array_type)
HANDLE_DW_TAG(0x0002, class_type)
HANDLE_DW_TAG(0x0003, entry_point)
@ -346,8 +351,23 @@ HANDLE_DW_VIRTUALITY(0x00, none)
HANDLE_DW_VIRTUALITY(0x01, virtual)
HANDLE_DW_VIRTUALITY(0x02, pure_virtual)
// DWARF calling convention codes.
HANDLE_DW_CC(0x01, normal)
HANDLE_DW_CC(0x02, program)
HANDLE_DW_CC(0x03, nocall)
HANDLE_DW_CC(0x41, GNU_borland_fastcall_i386)
HANDLE_DW_CC(0xb0, BORLAND_safecall)
HANDLE_DW_CC(0xb1, BORLAND_stdcall)
HANDLE_DW_CC(0xb2, BORLAND_pascal)
HANDLE_DW_CC(0xb3, BORLAND_msfastcall)
HANDLE_DW_CC(0xb4, BORLAND_msreturn)
HANDLE_DW_CC(0xb5, BORLAND_thiscall)
HANDLE_DW_CC(0xb6, BORLAND_fastcall)
HANDLE_DW_CC(0xc0, LLVM_vectorcall)
#undef HANDLE_DW_TAG
#undef HANDLE_DW_OP
#undef HANDLE_DW_LANG
#undef HANDLE_DW_ATE
#undef HANDLE_DW_VIRTUALITY
#undef HANDLE_DW_CC

View File

@ -389,18 +389,9 @@ enum CaseSensitivity {
enum CallingConvention {
// Calling convention codes
DW_CC_normal = 0x01,
DW_CC_program = 0x02,
DW_CC_nocall = 0x03,
#define HANDLE_DW_CC(ID, NAME) DW_CC_##NAME = ID,
#include "llvm/Support/Dwarf.def"
DW_CC_lo_user = 0x40,
DW_CC_GNU_borland_fastcall_i386 = 0x41,
DW_CC_BORLAND_safecall = 0xb0,
DW_CC_BORLAND_stdcall = 0xb1,
DW_CC_BORLAND_pascal = 0xb2,
DW_CC_BORLAND_msfastcall = 0xb3,
DW_CC_BORLAND_msreturn = 0xb4,
DW_CC_BORLAND_thiscall = 0xb5,
DW_CC_BORLAND_fastcall = 0xb6,
DW_CC_hi_user = 0xff
};
@ -652,6 +643,7 @@ unsigned getTag(StringRef TagString);
unsigned getOperationEncoding(StringRef OperationEncodingString);
unsigned getVirtuality(StringRef VirtualityString);
unsigned getLanguage(StringRef LanguageString);
unsigned getCallingConvention(StringRef LanguageString);
unsigned getAttributeEncoding(StringRef EncodingString);
unsigned getMacinfo(StringRef MacinfoString);
/// @}

View File

@ -793,6 +793,7 @@ lltok::Kind LLLexer::LexIdentifier() {
DWKEYWORD(ATE, DwarfAttEncoding);
DWKEYWORD(VIRTUALITY, DwarfVirtuality);
DWKEYWORD(LANG, DwarfLang);
DWKEYWORD(CC, DwarfCC);
DWKEYWORD(OP, DwarfOp);
DWKEYWORD(MACINFO, DwarfMacinfo);
#undef DWKEYWORD

View File

@ -3341,6 +3341,9 @@ struct DwarfVirtualityField : public MDUnsignedField {
struct DwarfLangField : public MDUnsignedField {
DwarfLangField() : MDUnsignedField(0, dwarf::DW_LANG_hi_user) {}
};
struct DwarfCCField : public MDUnsignedField {
DwarfCCField() : MDUnsignedField(0, dwarf::DW_CC_hi_user) {}
};
struct EmissionKindField : public MDUnsignedField {
EmissionKindField() : MDUnsignedField(0, DICompileUnit::LastEmissionKind) {}
};
@ -3483,6 +3486,24 @@ bool LLParser::ParseMDField(LocTy Loc, StringRef Name, DwarfLangField &Result) {
return false;
}
template <>
bool LLParser::ParseMDField(LocTy Loc, StringRef Name, DwarfCCField &Result) {
if (Lex.getKind() == lltok::APSInt)
return ParseMDField(Loc, Name, static_cast<MDUnsignedField &>(Result));
if (Lex.getKind() != lltok::DwarfCC)
return TokError("expected DWARF calling convention");
unsigned CC = dwarf::getCallingConvention(Lex.getStrVal());
if (!CC)
return TokError("invalid DWARF calling convention" + Twine(" '") + Lex.getStrVal() +
"'");
assert(CC <= Result.Max && "Expected valid DWARF calling convention");
Result.assign(CC);
Lex.Lex();
return false;
}
template <>
bool LLParser::ParseMDField(LocTy Loc, StringRef Name, EmissionKindField &Result) {
if (Lex.getKind() == lltok::APSInt)
@ -3863,11 +3884,13 @@ bool LLParser::ParseDICompositeType(MDNode *&Result, bool IsDistinct) {
bool LLParser::ParseDISubroutineType(MDNode *&Result, bool IsDistinct) {
#define VISIT_MD_FIELDS(OPTIONAL, REQUIRED) \
OPTIONAL(flags, DIFlagField, ); \
OPTIONAL(cc, DwarfCCField, ); \
REQUIRED(types, MDField, );
PARSE_MD_FIELDS();
#undef VISIT_MD_FIELDS
Result = GET_OR_DISTINCT(DISubroutineType, (Context, flags.Val, types.Val));
Result = GET_OR_DISTINCT(DISubroutineType,
(Context, flags.Val, cc.Val, types.Val));
return false;
}

View File

@ -344,6 +344,7 @@ enum Kind {
DwarfAttEncoding, // DW_ATE_foo
DwarfVirtuality, // DW_VIRTUALITY_foo
DwarfLang, // DW_LANG_foo
DwarfCC, // DW_CC_foo
EmissionKind, // lineTablesOnly
DwarfOp, // DW_OP_foo
DIFlag, // DIFlagFoo

View File

@ -2409,17 +2409,18 @@ std::error_code BitcodeReader::parseMetadata(bool ModuleLevel) {
break;
}
case bitc::METADATA_SUBROUTINE_TYPE: {
if (Record.size() != 3)
if (Record.size() < 3 || Record.size() > 4)
return error("Invalid record");
bool IsOldTypeRefArray = Record[0] < 2;
unsigned CC = (Record.size() > 3) ? Record[3] : 0;
IsDistinct = Record[0] & 0x1;
bool IsOldTypeRefArray = Record[0] < 2;
Metadata *Types = getMDOrNull(Record[2]);
if (LLVM_UNLIKELY(IsOldTypeRefArray))
Types = MetadataList.upgradeTypeRefArray(Types);
MetadataList.assignValue(
GET_OR_DISTINCT(DISubroutineType, (Context, Record[1], Types)),
GET_OR_DISTINCT(DISubroutineType, (Context, Record[1], CC, Types)),
NextMetadataNo++);
break;
}

View File

@ -1467,6 +1467,7 @@ void ModuleBitcodeWriter::writeDISubroutineType(
Record.push_back(HasNoOldTypeRefs | (unsigned)N->isDistinct());
Record.push_back(N->getFlags());
Record.push_back(VE.getMetadataOrNullID(N->getTypeArray().get()));
Record.push_back(N->getCC());
Stream.EmitRecord(bitc::METADATA_SUBROUTINE_TYPE, Record, Abbrev);
Record.clear();

View File

@ -926,6 +926,20 @@ TypeIndex CodeViewDebug::lowerTypeMemberPointer(const DIDerivedType *Ty) {
return TypeTable.writePointer(PR);
}
/// Given a DWARF calling convention, get the CodeView equivalent. If we don't
/// have a translation, use the NearC convention.
static CallingConvention dwarfCCToCodeView(unsigned DwarfCC) {
switch (DwarfCC) {
case dwarf::DW_CC_normal: return CallingConvention::NearC;
case dwarf::DW_CC_BORLAND_msfastcall: return CallingConvention::NearFast;
case dwarf::DW_CC_BORLAND_thiscall: return CallingConvention::ThisCall;
case dwarf::DW_CC_BORLAND_stdcall: return CallingConvention::NearStdCall;
case dwarf::DW_CC_BORLAND_pascal: return CallingConvention::NearPascal;
case dwarf::DW_CC_LLVM_vectorcall: return CallingConvention::NearVector;
}
return CallingConvention::NearC;
}
TypeIndex CodeViewDebug::lowerTypeModifier(const DIDerivedType *Ty) {
ModifierOptions Mods = ModifierOptions::None;
bool IsModifier = true;
@ -967,13 +981,12 @@ TypeIndex CodeViewDebug::lowerTypeFunction(const DISubroutineType *Ty) {
ArgListRecord ArgListRec(TypeRecordKind::ArgList, ArgTypeIndices);
TypeIndex ArgListIndex = TypeTable.writeArgList(ArgListRec);
// TODO: We should use DW_AT_calling_convention to determine what CC this
// procedure record should have.
CallingConvention CC = dwarfCCToCodeView(Ty->getCC());
// TODO: Some functions are member functions, we should use a more appropriate
// record for those.
ProcedureRecord Procedure(ReturnTypeIndex, CallingConvention::NearC,
FunctionOptions::None, ArgTypeIndices.size(),
ArgListIndex);
ProcedureRecord Procedure(ReturnTypeIndex, CC, FunctionOptions::None,
ArgTypeIndices.size(), ArgListIndex);
return TypeTable.writeProcedure(Procedure);
}

View File

@ -901,6 +901,11 @@ void DwarfUnit::constructTypeDIE(DIE &Buffer, const DISubroutineType *CTy) {
Language == dwarf::DW_LANG_ObjC))
addFlag(Buffer, dwarf::DW_AT_prototyped);
// Add a DW_AT_calling_convention if this has an explicit convention.
if (CTy->getCC() && CTy->getCC() != dwarf::DW_CC_normal)
addUInt(Buffer, dwarf::DW_AT_calling_convention, dwarf::DW_FORM_data1,
CTy->getCC());
if (CTy->isLValueReference())
addFlag(Buffer, dwarf::DW_AT_reference);
@ -1207,9 +1212,16 @@ void DwarfUnit::applySubprogramAttributes(const DISubprogram *SP, DIE &SPDie,
Language == dwarf::DW_LANG_ObjC))
addFlag(SPDie, dwarf::DW_AT_prototyped);
unsigned CC = 0;
DITypeRefArray Args;
if (const DISubroutineType *SPTy = SP->getType())
if (const DISubroutineType *SPTy = SP->getType()) {
Args = SPTy->getTypeArray();
CC = SPTy->getCC();
}
// Add a DW_AT_calling_convention if this has an explicit convention.
if (CC && CC != dwarf::DW_CC_normal)
addUInt(SPDie, dwarf::DW_AT_calling_convention, dwarf::DW_FORM_data1, CC);
// Add a return type. If this is a type like a C/C++ void type we don't add a
// return type.

View File

@ -1659,6 +1659,7 @@ static void writeDISubroutineType(raw_ostream &Out, const DISubroutineType *N,
Out << "!DISubroutineType(";
MDFieldPrinter Printer(Out, TypePrinter, Machine, Context);
Printer.printDIFlags("flags", N->getFlags());
Printer.printDwarfEnum("cc", N->getCC(), dwarf::ConventionString);
Printer.printMetadata("types", N->getRawTypeArray(),
/* ShouldSkipNull */ false);
Out << ")";

View File

@ -420,8 +420,8 @@ DICompositeType *DIBuilder::createUnionType(
}
DISubroutineType *DIBuilder::createSubroutineType(DITypeRefArray ParameterTypes,
unsigned Flags) {
return DISubroutineType::get(VMContext, Flags, ParameterTypes);
unsigned Flags, unsigned CC) {
return DISubroutineType::get(VMContext, Flags, CC, ParameterTypes);
}
DICompositeType *DIBuilder::createExternalTypeRef(unsigned Tag, DIFile *File,

View File

@ -327,12 +327,13 @@ DICompositeType *DICompositeType::getODRTypeIfExists(LLVMContext &Context,
}
DISubroutineType *DISubroutineType::getImpl(LLVMContext &Context,
unsigned Flags, Metadata *TypeArray,
unsigned Flags, uint8_t CC,
Metadata *TypeArray,
StorageType Storage,
bool ShouldCreate) {
DEFINE_GETIMPL_LOOKUP(DISubroutineType, (Flags, TypeArray));
DEFINE_GETIMPL_LOOKUP(DISubroutineType, (Flags, CC, TypeArray));
Metadata *Ops[] = {nullptr, nullptr, nullptr, TypeArray};
DEFINE_GETIMPL_STORE(DISubroutineType, (Flags), Ops);
DEFINE_GETIMPL_STORE(DISubroutineType, (Flags, CC), Ops);
}
DIFile *DIFile::getImpl(LLVMContext &Context, MDString *Filename,

View File

@ -484,17 +484,19 @@ template <> struct MDNodeKeyImpl<DICompositeType> {
template <> struct MDNodeKeyImpl<DISubroutineType> {
unsigned Flags;
uint8_t CC;
Metadata *TypeArray;
MDNodeKeyImpl(int64_t Flags, Metadata *TypeArray)
: Flags(Flags), TypeArray(TypeArray) {}
MDNodeKeyImpl(unsigned Flags, uint8_t CC, Metadata *TypeArray)
: Flags(Flags), CC(CC), TypeArray(TypeArray) {}
MDNodeKeyImpl(const DISubroutineType *N)
: Flags(N->getFlags()), TypeArray(N->getRawTypeArray()) {}
: Flags(N->getFlags()), CC(N->getCC()), TypeArray(N->getRawTypeArray()) {}
bool isKeyOf(const DISubroutineType *RHS) const {
return Flags == RHS->getFlags() && TypeArray == RHS->getRawTypeArray();
return Flags == RHS->getFlags() && CC == RHS->getCC() &&
TypeArray == RHS->getRawTypeArray();
}
unsigned getHashValue() const { return hash_combine(Flags, TypeArray); }
unsigned getHashValue() const { return hash_combine(Flags, CC, TypeArray); }
};
template <> struct MDNodeKeyImpl<DIFile> {

View File

@ -384,23 +384,22 @@ const char *llvm::dwarf::CaseString(unsigned Case) {
return nullptr;
}
const char *llvm::dwarf::ConventionString(unsigned Convention) {
switch (Convention) {
case DW_CC_normal: return "DW_CC_normal";
case DW_CC_program: return "DW_CC_program";
case DW_CC_nocall: return "DW_CC_nocall";
case DW_CC_lo_user: return "DW_CC_lo_user";
case DW_CC_hi_user: return "DW_CC_hi_user";
case DW_CC_GNU_borland_fastcall_i386: return "DW_CC_GNU_borland_fastcall_i386";
case DW_CC_BORLAND_safecall: return "DW_CC_BORLAND_safecall";
case DW_CC_BORLAND_stdcall: return "DW_CC_BORLAND_stdcall";
case DW_CC_BORLAND_pascal: return "DW_CC_BORLAND_pascal";
case DW_CC_BORLAND_msfastcall: return "DW_CC_BORLAND_msfastcall";
case DW_CC_BORLAND_msreturn: return "DW_CC_BORLAND_msreturn";
case DW_CC_BORLAND_thiscall: return "DW_CC_BORLAND_thiscall";
case DW_CC_BORLAND_fastcall: return "DW_CC_BORLAND_fastcall";
const char *llvm::dwarf::ConventionString(unsigned CC) {
switch (CC) {
default:
return nullptr;
#define HANDLE_DW_CC(ID, NAME) \
case DW_CC_##NAME: \
return "DW_CC_" #NAME;
#include "llvm/Support/Dwarf.def"
}
return nullptr;
}
unsigned llvm::dwarf::getCallingConvention(StringRef CCString) {
return StringSwitch<unsigned>(CCString)
#define HANDLE_DW_CC(ID, NAME) .Case("DW_CC_" #NAME, DW_CC_##NAME)
#include "llvm/Support/Dwarf.def"
.Default(0);
}
const char *llvm::dwarf::InlineCodeString(unsigned Code) {

View File

@ -1,8 +1,8 @@
; RUN: llvm-as < %s | llvm-dis | llvm-as | llvm-dis | FileCheck %s
; RUN: verify-uselistorder %s
; CHECK: !named = !{!0, !1, !2, !3, !4, !5, !6, !7, !8}
!named = !{!0, !1, !2, !3, !4, !5, !6, !7, !8}
; CHECK: !named = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10, !11, !12}
!named = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10, !11, !12}
!0 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed)
!1 = !{null}
@ -21,3 +21,12 @@
; CHECK: !8 = !DISubroutineType(types: null)
!8 = !DISubroutineType(types: null)
; CHECK: !9 = !DISubroutineType(cc: DW_CC_normal, types: null)
; CHECK: !10 = !DISubroutineType(cc: DW_CC_BORLAND_msfastcall, types: null)
; CHECK: !11 = !DISubroutineType(cc: DW_CC_BORLAND_stdcall, types: null)
; CHECK: !12 = !DISubroutineType(cc: DW_CC_LLVM_vectorcall, types: null)
!9 = !DISubroutineType(cc: DW_CC_normal, types: null)
!10 = !DISubroutineType(cc: DW_CC_BORLAND_msfastcall, types: null)
!11 = !DISubroutineType(cc: DW_CC_BORLAND_stdcall, types: null)
!12 = !DISubroutineType(cc: DW_CC_LLVM_vectorcall, types: null)

View File

@ -0,0 +1,212 @@
; RUN: llc < %s -filetype=obj -o - | llvm-readobj - -codeview | FileCheck %s
; C++ source to regenerate:
; $ cat t.cpp
; struct A {
; void thiscallcc();
; };
; void A::thiscallcc() {}
; void cdeclcc() {}
; void __fastcall fastcallcc() {}
; void __stdcall stdcallcc() {}
; void __vectorcall vectorcallcc() {}
; $ clang -g -gcodeview t.cpp -emit-llvm -S -o t.ll -O1
; CHECK: CodeViewTypes [
; CHECK: Section: .debug$T (5)
; CHECK: Magic: 0x4
; CHECK: Struct (0x1000) {
; CHECK: TypeLeafKind: LF_STRUCTURE (0x1505)
; CHECK: MemberCount: 0
; CHECK: Properties [ (0x80)
; CHECK: ForwardReference (0x80)
; CHECK: ]
; CHECK: FieldList: 0x0
; CHECK: DerivedFrom: 0x0
; CHECK: VShape: 0x0
; CHECK: SizeOf: 0
; CHECK: Name: A
; CHECK: }
; CHECK: Pointer (0x1001) {
; CHECK: TypeLeafKind: LF_POINTER (0x1002)
; CHECK: PointeeType: A (0x1000)
; CHECK: PointerAttributes: 0x800A
; CHECK: PtrType: Near32 (0xA)
; CHECK: PtrMode: Pointer (0x0)
; CHECK: IsFlat: 0
; CHECK: IsConst: 0
; CHECK: IsVolatile: 0
; CHECK: IsUnaligned: 0
; CHECK: }
; CHECK: ArgList (0x1002) {
; CHECK: TypeLeafKind: LF_ARGLIST (0x1201)
; CHECK: NumArgs: 1
; CHECK: Arguments [
; CHECK: ArgType: A* (0x1001)
; CHECK: ]
; CHECK: }
; CHECK: Procedure (0x1003) {
; CHECK: TypeLeafKind: LF_PROCEDURE (0x1008)
; CHECK: ReturnType: void (0x3)
; CHECK: CallingConvention: ThisCall (0xB)
; CHECK: FunctionOptions [ (0x0)
; CHECK: ]
; CHECK: NumParameters: 1
; CHECK: ArgListType: (A*) (0x1002)
; CHECK: }
; CHECK: FuncId (0x1004) {
; CHECK: TypeLeafKind: LF_FUNC_ID (0x1601)
; CHECK: ParentScope: 0x0
; CHECK: FunctionType: void (A*) (0x1003)
; CHECK: Name: A::thiscallcc
; CHECK: }
; CHECK: ArgList (0x1005) {
; CHECK: TypeLeafKind: LF_ARGLIST (0x1201)
; CHECK: NumArgs: 0
; CHECK: Arguments [
; CHECK: ]
; CHECK: }
; CHECK: Procedure (0x1006) {
; CHECK: TypeLeafKind: LF_PROCEDURE (0x1008)
; CHECK: ReturnType: void (0x3)
; CHECK: CallingConvention: NearC (0x0)
; CHECK: FunctionOptions [ (0x0)
; CHECK: ]
; CHECK: NumParameters: 0
; CHECK: ArgListType: () (0x1005)
; CHECK: }
; CHECK: FuncId (0x1007) {
; CHECK: TypeLeafKind: LF_FUNC_ID (0x1601)
; CHECK: ParentScope: 0x0
; CHECK: FunctionType: void () (0x1006)
; CHECK: Name: cdeclcc
; CHECK: }
; CHECK: Procedure (0x1008) {
; CHECK: TypeLeafKind: LF_PROCEDURE (0x1008)
; CHECK: ReturnType: void (0x3)
; CHECK: CallingConvention: NearFast (0x4)
; CHECK: FunctionOptions [ (0x0)
; CHECK: ]
; CHECK: NumParameters: 0
; CHECK: ArgListType: () (0x1005)
; CHECK: }
; CHECK: FuncId (0x1009) {
; CHECK: TypeLeafKind: LF_FUNC_ID (0x1601)
; CHECK: ParentScope: 0x0
; CHECK: FunctionType: void () (0x1008)
; CHECK: Name: fastcallcc
; CHECK: }
; CHECK: Procedure (0x100A) {
; CHECK: TypeLeafKind: LF_PROCEDURE (0x1008)
; CHECK: ReturnType: void (0x3)
; CHECK: CallingConvention: NearStdCall (0x7)
; CHECK: FunctionOptions [ (0x0)
; CHECK: ]
; CHECK: NumParameters: 0
; CHECK: ArgListType: () (0x1005)
; CHECK: }
; CHECK: FuncId (0x100B) {
; CHECK: TypeLeafKind: LF_FUNC_ID (0x1601)
; CHECK: ParentScope: 0x0
; CHECK: FunctionType: void () (0x100A)
; CHECK: Name: stdcallcc
; CHECK: }
; CHECK: Procedure (0x100C) {
; CHECK: TypeLeafKind: LF_PROCEDURE (0x1008)
; CHECK: ReturnType: void (0x3)
; CHECK: CallingConvention: NearVector (0x18)
; CHECK: FunctionOptions [ (0x0)
; CHECK: ]
; CHECK: NumParameters: 0
; CHECK: ArgListType: () (0x1005)
; CHECK: }
; CHECK: FuncId (0x100D) {
; CHECK: TypeLeafKind: LF_FUNC_ID (0x1601)
; CHECK: ParentScope: 0x0
; CHECK: FunctionType: void () (0x100C)
; CHECK: Name: vectorcallcc
; CHECK: }
; CHECK: ]
; ModuleID = 't.cpp'
source_filename = "t.cpp"
target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"
target triple = "i386-pc-windows-msvc19.0.23918"
%struct.A = type { i8 }
; Function Attrs: nounwind readnone
define x86_thiscallcc void @"\01?thiscallcc@A@@QAEXXZ"(%struct.A* nocapture %this) #0 align 2 !dbg !6 {
entry:
tail call void @llvm.dbg.value(metadata %struct.A* %this, i64 0, metadata !14, metadata !16), !dbg !17
ret void, !dbg !18
}
; Function Attrs: norecurse nounwind readnone
define void @"\01?cdeclcc@@YAXXZ"() #1 !dbg !19 {
entry:
ret void, !dbg !22
}
; Function Attrs: norecurse nounwind readnone
define x86_fastcallcc void @"\01?fastcallcc@@YIXXZ"() #1 !dbg !23 {
entry:
ret void, !dbg !24
}
; Function Attrs: norecurse nounwind readnone
define x86_stdcallcc void @"\01?stdcallcc@@YGXXZ"() #1 !dbg !25 {
entry:
ret void, !dbg !26
}
; Function Attrs: norecurse nounwind readnone
define x86_vectorcallcc void @"\01?vectorcallcc@@YQXXZ"() #1 !dbg !27 {
entry:
ret void, !dbg !28
}
; Function Attrs: nounwind readnone
declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #2
attributes #0 = { nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { norecurse nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind readnone }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4}
!llvm.ident = !{!5}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.9.0 (trunk 272067)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "t.cpp", directory: "D:\5Csrc\5Cllvm\5Cbuild")
!2 = !{}
!3 = !{i32 2, !"CodeView", i32 1}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{!"clang version 3.9.0 (trunk 272067)"}
!6 = distinct !DISubprogram(name: "A::thiscallcc", linkageName: "\01?thiscallcc@A@@QAEXXZ", scope: !7, file: !1, line: 4, type: !10, isLocal: false, isDefinition: true, scopeLine: 4, flags: DIFlagPrototyped, isOptimized: true, unit: !0, declaration: !9, variables: !13)
!7 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "A", file: !1, line: 1, size: 8, align: 8, elements: !8)
!8 = !{!9}
!9 = !DISubprogram(name: "A::thiscallcc", linkageName: "\01?thiscallcc@A@@QAEXXZ", scope: !7, file: !1, line: 2, type: !10, isLocal: false, isDefinition: false, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true)
!10 = !DISubroutineType(cc: DW_CC_BORLAND_thiscall, types: !11)
!11 = !{null, !12}
!12 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 32, align: 32, flags: DIFlagArtificial | DIFlagObjectPointer)
!13 = !{!14}
!14 = !DILocalVariable(name: "this", arg: 1, scope: !6, type: !15, flags: DIFlagArtificial | DIFlagObjectPointer)
!15 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !7, size: 32, align: 32)
!16 = !DIExpression()
!17 = !DILocation(line: 0, scope: !6)
!18 = !DILocation(line: 4, column: 23, scope: !6)
!19 = distinct !DISubprogram(name: "cdeclcc", linkageName: "\01?cdeclcc@@YAXXZ", scope: !1, file: !1, line: 5, type: !20, isLocal: false, isDefinition: true, scopeLine: 5, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !2)
!21 = !{null}
!22 = !DILocation(line: 5, column: 17, scope: !19)
!23 = distinct !DISubprogram(name: "fastcallcc", linkageName: "\01?fastcallcc@@YIXXZ", scope: !1, file: !1, line: 6, type: !29, isLocal: false, isDefinition: true, scopeLine: 6, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !2)
!24 = !DILocation(line: 6, column: 31, scope: !23)
!25 = distinct !DISubprogram(name: "stdcallcc", linkageName: "\01?stdcallcc@@YGXXZ", scope: !1, file: !1, line: 7, type: !30, isLocal: false, isDefinition: true, scopeLine: 7, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !2)
!26 = !DILocation(line: 7, column: 29, scope: !25)
!27 = distinct !DISubprogram(name: "vectorcallcc", linkageName: "\01?vectorcallcc@@YQXXZ", scope: !1, file: !1, line: 8, type: !31, isLocal: false, isDefinition: true, scopeLine: 8, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !2)
!28 = !DILocation(line: 8, column: 35, scope: !27)
!20 = !DISubroutineType(cc: DW_CC_normal, types: !21)
!29 = !DISubroutineType(cc: DW_CC_BORLAND_msfastcall, types: !21)
!30 = !DISubroutineType(cc: DW_CC_BORLAND_stdcall, types: !21)
!31 = !DISubroutineType(cc: DW_CC_LLVM_vectorcall, types: !21)

View File

@ -0,0 +1,86 @@
; RUN: llc < %s -filetype=obj -o %t
; RUN: llvm-dwarfdump %t | FileCheck %s
; C++ source to regenerate:
; $ cat t.cpp
; struct A {
; void thiscallcc();
; };
; void A::thiscallcc() {}
; void cdeclcc() {}
; void __fastcall fastcallcc() {}
; void __stdcall stdcallcc() {}
; void __vectorcall vectorcallcc() {}
; $ clang -g t.cpp -emit-llvm -S -o t.ll -O1
; CHECK: .debug_abbrev contents:
; CHECK: [[subroutine_abbrev:\[[0-9]+\]]] DW_TAG_subroutine_type DW_CHILDREN_yes
; CHECK-NEXT: DW_AT_type DW_FORM_ref4
; CHECK-NEXT: DW_AT_calling_convention DW_FORM_data1
; CHECK: .debug_info contents:
; CHECK: DW_TAG_subroutine_type [[subroutine_abbrev]] *
; CHECK-NEXT: DW_AT_type [DW_FORM_ref4] {{.*}}
; CHECK-NEXT: DW_AT_calling_convention [DW_FORM_data1] (DW_CC_BORLAND_msfastcall)
; CHECK: DW_TAG_subprogram [{{.*}}] *
; CHECK: DW_AT_low_pc
; CHECK: DW_AT_high_pc
; CHECK: DW_AT_frame_base
; CHECK: DW_AT_linkage_name
; CHECK: DW_AT_name
; CHECK: DW_AT_decl_file
; CHECK: DW_AT_decl_line
; CHECK: DW_AT_calling_convention [DW_FORM_data1] (DW_CC_BORLAND_msfastcall)
; CHECK: DW_AT_type
; CHECK: DW_AT_external
; ModuleID = 't.cpp'
source_filename = "t.cpp"
target datalayout = "e-m:x-p:32:32-i64:64-f80:32-n8:16:32-a:0:32-S32"
target triple = "i386-pc-windows-msvc19.0.23918"
@"\01?fptr@@3P6IHHH@ZA" = global i32 (i32, i32)* @"\01?f@@YIHHH@Z", align 4
; Function Attrs: nounwind readnone
define x86_fastcallcc i32 @"\01?f@@YIHHH@Z"(i32 inreg %a, i32 inreg %b) #0 !dbg !12 {
entry:
tail call void @llvm.dbg.value(metadata i32 %b, i64 0, metadata !14, metadata !16), !dbg !17
tail call void @llvm.dbg.value(metadata i32 %a, i64 0, metadata !15, metadata !16), !dbg !18
%add = add nsw i32 %b, %a, !dbg !19
ret i32 %add, !dbg !20
}
; Function Attrs: nounwind readnone
declare void @llvm.dbg.value(metadata, i64, metadata, metadata) #1
attributes #0 = { nounwind readnone "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="pentium4" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { nounwind readnone }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!9, !10}
!llvm.ident = !{!11}
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus, file: !1, producer: "clang version 3.9.0 (trunk 272067)", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2, globals: !3)
!1 = !DIFile(filename: "t.cpp", directory: "D:\5Csrc\5Cllvm\5Cbuild")
!2 = !{}
!3 = !{!4}
!4 = distinct !DIGlobalVariable(name: "fptr", linkageName: "\01?fptr@@3P6IHHH@ZA", scope: !0, file: !1, line: 2, type: !5, isLocal: false, isDefinition: true, variable: i32 (i32, i32)** @"\01?fptr@@3P6IHHH@ZA")
!5 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 32, align: 32)
!6 = !DISubroutineType(cc: DW_CC_BORLAND_msfastcall, types: !7)
!7 = !{!8, !8, !8}
!8 = !DIBasicType(name: "int", size: 32, align: 32, encoding: DW_ATE_signed)
!9 = !{i32 2, !"Dwarf Version", i32 4}
!10 = !{i32 2, !"Debug Info Version", i32 3}
!11 = !{!"clang version 3.9.0 (trunk 272067)"}
!12 = distinct !DISubprogram(name: "f", linkageName: "\01?f@@YIHHH@Z", scope: !1, file: !1, line: 1, type: !6, isLocal: false, isDefinition: true, scopeLine: 1, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !13)
!13 = !{!14, !15}
!14 = !DILocalVariable(name: "b", arg: 2, scope: !12, file: !1, line: 1, type: !8)
!15 = !DILocalVariable(name: "a", arg: 1, scope: !12, file: !1, line: 1, type: !8)
!16 = !DIExpression()
!17 = !DILocation(line: 1, column: 29, scope: !12)
!18 = !DILocation(line: 1, column: 22, scope: !12)
!19 = !DILocation(line: 1, column: 43, scope: !12)
!20 = !DILocation(line: 1, column: 34, scope: !12)

View File

@ -80,7 +80,7 @@ protected:
MDTuple *getTuple() { return MDTuple::getDistinct(Context, None); }
DISubroutineType *getSubroutineType() {
return DISubroutineType::getDistinct(Context, 0, getNode(nullptr));
return DISubroutineType::getDistinct(Context, 0, 0, getNode(nullptr));
}
DISubprogram *getSubprogram() {
return DISubprogram::getDistinct(Context, nullptr, "", "", nullptr, 0,
@ -969,14 +969,14 @@ TEST_F(DITypeTest, setFlags) {
Metadata *TypesOps[] = {nullptr};
Metadata *Types = MDTuple::get(Context, TypesOps);
DIType *D = DISubroutineType::getDistinct(Context, 0u, Types);
DIType *D = DISubroutineType::getDistinct(Context, 0u, 0, Types);
EXPECT_EQ(0u, D->getFlags());
D->setFlags(DINode::FlagRValueReference);
EXPECT_EQ(DINode::FlagRValueReference, D->getFlags());
D->setFlags(0u);
EXPECT_EQ(0u, D->getFlags());
TempDIType T = DISubroutineType::getTemporary(Context, 0u, Types);
TempDIType T = DISubroutineType::getTemporary(Context, 0u, 0, Types);
EXPECT_EQ(0u, T->getFlags());
T->setFlags(DINode::FlagRValueReference);
EXPECT_EQ(DINode::FlagRValueReference, T->getFlags());
@ -1254,14 +1254,29 @@ TEST_F(DISubroutineTypeTest, get) {
unsigned Flags = 1;
MDTuple *TypeArray = getTuple();
auto *N = DISubroutineType::get(Context, Flags, TypeArray);
auto *N = DISubroutineType::get(Context, Flags, 0, TypeArray);
EXPECT_EQ(dwarf::DW_TAG_subroutine_type, N->getTag());
EXPECT_EQ(Flags, N->getFlags());
EXPECT_EQ(TypeArray, N->getTypeArray().get());
EXPECT_EQ(N, DISubroutineType::get(Context, Flags, TypeArray));
EXPECT_EQ(N, DISubroutineType::get(Context, Flags, 0, TypeArray));
EXPECT_NE(N, DISubroutineType::get(Context, Flags + 1, TypeArray));
EXPECT_NE(N, DISubroutineType::get(Context, Flags, getTuple()));
EXPECT_NE(N, DISubroutineType::get(Context, Flags + 1, 0, TypeArray));
EXPECT_NE(N, DISubroutineType::get(Context, Flags, 0, getTuple()));
// Test the hashing of calling conventions.
auto *Fast = DISubroutineType::get(
Context, Flags, dwarf::DW_CC_BORLAND_msfastcall, TypeArray);
auto *Std = DISubroutineType::get(Context, Flags,
dwarf::DW_CC_BORLAND_stdcall, TypeArray);
EXPECT_EQ(Fast,
DISubroutineType::get(Context, Flags,
dwarf::DW_CC_BORLAND_msfastcall, TypeArray));
EXPECT_EQ(Std, DISubroutineType::get(
Context, Flags, dwarf::DW_CC_BORLAND_stdcall, TypeArray));
EXPECT_NE(N, Fast);
EXPECT_NE(N, Std);
EXPECT_NE(Fast, Std);
TempDISubroutineType Temp = N->clone();
EXPECT_EQ(N, MDNode::replaceWithUniqued(std::move(Temp)));