Add llvm.codeview.annotation to implement MSVC __annotation

Summary:
This intrinsic represents a label with a list of associated metadata
strings. It is modelled as reading and writing inaccessible memory so
that it won't be removed as dead code. I think the intention is that the
annotation strings should appear at most once in the debug info, so I
marked it noduplicate. We are allowed to inline code with annotations as
long as we strip the annotation, but that can be done later.

Reviewers: majnemer

Subscribers: eraman, llvm-commits, hiraditya

Differential Revision: https://reviews.llvm.org/D36904

llvm-svn: 312569
This commit is contained in:
Reid Kleckner 2017-09-05 20:14:58 +00:00
parent 3f0e4ad833
commit e33c94f1b0
18 changed files with 226 additions and 8 deletions

View File

@ -13626,6 +13626,27 @@ with arbitrary strings. This can be useful for special purpose
optimizations that want to look for these annotations. These have no
other defined use; they are ignored by code generation and optimization.
'``llvm.codeview.annotation``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Syntax:
"""""""
This annotation emits a label at its program point and an associated
``S_ANNOTATION`` codeview record with some additional string metadata. This is
used to implement MSVC's ``__annotation`` intrinsic. It is marked
``noduplicate``, so calls to this intrinsic prevent inlining and should be
considered expensive.
::
declare void @llvm.codeview.annotation(metadata)
Arguments:
""""""""""
The argument should be an MDTuple containing any number of MDStrings.
'``llvm.trap``' Intrinsic
^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -638,6 +638,12 @@ namespace ISD {
/// take a chain as input and return a chain.
EH_LABEL,
/// ANNOTATION_LABEL - Represents a mid basic block label used by
/// annotations. This should remain within the basic block and be ordered
/// with respect to other call instructions, but loads and stores may float
/// past it.
ANNOTATION_LABEL,
/// CATCHPAD - Represents a catchpad instruction.
CATCHPAD,

View File

@ -314,6 +314,9 @@ class MachineFunction {
/// Map of invoke call site index values to associated begin EH_LABEL.
DenseMap<MCSymbol*, unsigned> CallSiteMap;
/// CodeView label annotations.
std::vector<std::pair<MCSymbol *, MDNode *>> CodeViewAnnotations;
bool CallsEHReturn = false;
bool CallsUnwindInit = false;
bool HasEHFunclets = false;
@ -832,6 +835,15 @@ public:
return CallSiteMap.count(BeginLabel);
}
/// Record annotations associated with a particular label.
void addCodeViewAnnotation(MCSymbol *Label, MDNode *MD) {
CodeViewAnnotations.push_back({Label, MD});
}
ArrayRef<std::pair<MCSymbol *, MDNode *>> getCodeViewAnnotations() const {
return CodeViewAnnotations;
}
/// Return a reference to the C++ typeinfo for the current function.
const std::vector<const GlobalValue *> &getTypeInfos() const {
return TypeInfos;

View File

@ -635,6 +635,8 @@ public:
SDValue getRegister(unsigned Reg, EVT VT);
SDValue getRegisterMask(const uint32_t *RegMask);
SDValue getEHLabel(const SDLoc &dl, SDValue Root, MCSymbol *Label);
SDValue getLabelNode(unsigned Opcode, const SDLoc &dl, SDValue Root,
MCSymbol *Label);
SDValue getBlockAddress(const BlockAddress *BA, EVT VT,
int64_t Offset = 0, bool isTarget = false,
unsigned char TargetFlags = 0);

View File

@ -1848,19 +1848,20 @@ public:
}
};
class EHLabelSDNode : public SDNode {
class LabelSDNode : public SDNode {
friend class SelectionDAG;
MCSymbol *Label;
EHLabelSDNode(unsigned Order, const DebugLoc &dl, MCSymbol *L)
LabelSDNode(unsigned Order, const DebugLoc &dl, MCSymbol *L)
: SDNode(ISD::EH_LABEL, Order, dl, getSDVTList(MVT::Other)), Label(L) {}
public:
MCSymbol *getLabel() const { return Label; }
static bool classof(const SDNode *N) {
return N->getOpcode() == ISD::EH_LABEL;
return N->getOpcode() == ISD::EH_LABEL ||
N->getOpcode() == ISD::ANNOTATION_LABEL;
}
};

View File

@ -942,6 +942,8 @@ public:
uint32_t RecordOffset;
};
// S_ANNOTATION
using CVSymbol = CVRecord<SymbolKind>;
using CVSymbolArray = VarStreamArray<CVSymbol>;

View File

@ -641,6 +641,13 @@ def int_annotation : Intrinsic<[llvm_anyint_ty],
llvm_ptr_ty, llvm_i32_ty],
[], "llvm.annotation">;
// Annotates the current program point with metadata strings which are emitted
// as CodeView debug info records. This is expensive, as it disables inlining
// and is modelled as having side effects.
def int_codeview_annotation : Intrinsic<[], [llvm_metadata_ty],
[IntrInaccessibleMemOnly, IntrNoDuplicate],
"llvm.codeview.annotation">;
//===------------------------ Trampoline Intrinsics -----------------------===//
//
def int_init_trampoline : Intrinsic<[],

View File

@ -840,6 +840,13 @@ def GC_LABEL : Instruction {
let hasCtrlDep = 1;
let isNotDuplicable = 1;
}
def ANNOTATION_LABEL : Instruction {
let OutOperandList = (outs);
let InOperandList = (ins i32imm:$id);
let AsmString = "";
let hasCtrlDep = 1;
let isNotDuplicable = 1;
}
def KILL : Instruction {
let OutOperandList = (outs);
let InOperandList = (ins variable_ops);

View File

@ -32,6 +32,7 @@ HANDLE_TARGET_OPCODE(INLINEASM)
HANDLE_TARGET_OPCODE(CFI_INSTRUCTION)
HANDLE_TARGET_OPCODE(EH_LABEL)
HANDLE_TARGET_OPCODE(GC_LABEL)
HANDLE_TARGET_OPCODE(ANNOTATION_LABEL)
/// KILL - This instruction is a noop that is used only to adjust the
/// liveness of registers. This can be useful when dealing with

View File

@ -860,6 +860,30 @@ void CodeViewDebug::emitDebugInfoForFunction(const Function *GV,
emitInlinedCallSite(FI, InlinedAt, I->second);
}
for (auto Annot : FI.Annotations) {
MCSymbol *Label = Annot.first;
MDTuple *Strs = cast<MDTuple>(Annot.second);
MCSymbol *AnnotBegin = MMI->getContext().createTempSymbol(),
*AnnotEnd = MMI->getContext().createTempSymbol();
OS.AddComment("Record length");
OS.emitAbsoluteSymbolDiff(AnnotEnd, AnnotBegin, 2);
OS.EmitLabel(AnnotBegin);
OS.AddComment("Record kind: S_ANNOTATION");
OS.EmitIntValue(SymbolKind::S_ANNOTATION, 2);
OS.EmitCOFFSecRel32(Label, /*Offset=*/0);
// FIXME: Make sure we don't overflow the max record size.
OS.EmitCOFFSectionIndex(Label);
OS.EmitIntValue(Strs->getNumOperands(), 2);
for (Metadata *MD : Strs->operands()) {
// MDStrings are null terminated, so we can do EmitBytes and get the
// nice .asciz directive.
StringRef Str = cast<MDString>(MD)->getString();
assert(Str.data()[Str.size()] == '\0' && "non-nullterminated MDString");
OS.EmitBytes(StringRef(Str.data(), Str.size() + 1));
}
OS.EmitLabel(AnnotEnd);
}
if (SP != nullptr)
emitDebugInfoForUDTs(LocalUDTs);
@ -2195,6 +2219,8 @@ void CodeViewDebug::endFunctionImpl(const MachineFunction *MF) {
return;
}
CurFn->Annotations = MF->getCodeViewAnnotations();
CurFn->End = Asm->getFunctionEnd();
CurFn = nullptr;

View File

@ -119,6 +119,8 @@ class LLVM_LIBRARY_VISIBILITY CodeViewDebug : public DebugHandlerBase {
SmallVector<LocalVariable, 1> Locals;
std::vector<std::pair<MCSymbol *, MDNode *>> Annotations;
const MCSymbol *Begin = nullptr;
const MCSymbol *End = nullptr;
unsigned FuncId = 0;

View File

@ -166,6 +166,7 @@ void MachineFunction::clear() {
InstructionRecycler.clear(Allocator);
OperandRecycler.clear(Allocator);
BasicBlockRecycler.clear(Allocator);
CodeViewAnnotations.clear();
VariableDbgInfos.clear();
if (RegInfo) {
RegInfo->~MachineRegisterInfo();

View File

@ -935,10 +935,14 @@ EmitSpecialNode(SDNode *Node, bool IsClone, bool IsCloned,
EmitCopyFromReg(Node, 0, IsClone, IsCloned, SrcReg, VRBaseMap);
break;
}
case ISD::EH_LABEL: {
MCSymbol *S = cast<EHLabelSDNode>(Node)->getLabel();
case ISD::EH_LABEL:
case ISD::ANNOTATION_LABEL: {
unsigned Opc = (Node->getOpcode() == ISD::EH_LABEL)
? TargetOpcode::EH_LABEL
: TargetOpcode::ANNOTATION_LABEL;
MCSymbol *S = cast<LabelSDNode>(Node)->getLabel();
BuildMI(*MBB, InsertPos, Node->getDebugLoc(),
TII->get(TargetOpcode::EH_LABEL)).addSym(S);
TII->get(Opc)).addSym(S);
break;
}

View File

@ -1680,15 +1680,20 @@ SDValue SelectionDAG::getRegisterMask(const uint32_t *RegMask) {
SDValue SelectionDAG::getEHLabel(const SDLoc &dl, SDValue Root,
MCSymbol *Label) {
return getLabelNode(ISD::EH_LABEL, dl, Root, Label);
}
SDValue SelectionDAG::getLabelNode(unsigned Opcode, const SDLoc &dl,
SDValue Root, MCSymbol *Label) {
FoldingSetNodeID ID;
SDValue Ops[] = { Root };
AddNodeIDNode(ID, ISD::EH_LABEL, getVTList(MVT::Other), Ops);
AddNodeIDNode(ID, Opcode, getVTList(MVT::Other), Ops);
ID.AddPointer(Label);
void *IP = nullptr;
if (SDNode *E = FindNodeOrInsertPos(ID, IP))
return SDValue(E, 0);
auto *N = newSDNode<EHLabelSDNode>(dl.getIROrder(), dl.getDebugLoc(), Label);
auto *N = newSDNode<LabelSDNode>(dl.getIROrder(), dl.getDebugLoc(), Label);
createOperands(N, Ops);
CSEMap.InsertNode(N, IP);

View File

@ -5651,6 +5651,18 @@ SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, unsigned Intrinsic) {
// Discard annotate attributes and assumptions
return nullptr;
case Intrinsic::codeview_annotation: {
// Emit a label associated with this metadata.
MachineFunction &MF = DAG.getMachineFunction();
MCSymbol *Label =
MF.getMMI().getContext().createTempSymbol("annotation", true);
Metadata *MD = cast<MetadataAsValue>(I.getArgOperand(0))->getMetadata();
MF.addCodeViewAnnotation(Label, cast<MDNode>(MD));
Res = DAG.getLabelNode(ISD::ANNOTATION_LABEL, sdl, getRoot(), Label);
DAG.setRoot(Res);
return nullptr;
}
case Intrinsic::init_trampoline: {
const Function *F = cast<Function>(I.getArgOperand(1)->stripPointerCasts());

View File

@ -2911,6 +2911,7 @@ void SelectionDAGISel::SelectCodeCommon(SDNode *NodeToMatch,
case ISD::CopyFromReg:
case ISD::CopyToReg:
case ISD::EH_LABEL:
case ISD::ANNOTATION_LABEL:
case ISD::LIFETIME_START:
case ISD::LIFETIME_END:
NodeToMatch->setNodeId(-1); // Mark selected.

View File

@ -0,0 +1,73 @@
; RUN: llc < %s | FileCheck %s
; FIXME: fastisel screws up the order here.
; RUNX: llc -O0 < %s | FileCheck %s
; Source to regenerate:
; $ clang --target=x86_64-windows-msvc -S annotation.c -g -gcodeview -o t.ll \
; -emit-llvm -O1 -Xclang -disable-llvm-passes -fms-extensions
; void g(void);
; void f(void) {
; g();
; __annotation(L"a1", L"a2");
; g();
; }
; ModuleID = 'annotation.c'
source_filename = "annotation.c"
target datalayout = "e-m:w-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-windows-msvc19.0.24215"
; Function Attrs: nounwind uwtable
define void @f() #0 !dbg !8 {
entry:
call void @g(), !dbg !11
call void @llvm.codeview.annotation(metadata !12), !dbg !13
call void @g(), !dbg !14
ret void, !dbg !15
}
; CHECK-LABEL: f: # @f
; CHECK: callq g
; CHECK: .Lannotation0:
; CHECK: callq g
; CHECK: retq
; CHECK-LABEL: .short 4423 # Record kind: S_GPROC32_ID
; CHECK: .short 4121 # Record kind: S_ANNOTATION
; CHECK-NEXT: .secrel32 .Lannotation0
; CHECK-NEXT: .secidx .Lannotation0
; CHECK-NEXT: .short 2
; CHECK-NEXT: .asciz "a1"
; CHECK-NEXT: .asciz "a2"
; CHECK-LABEL: .short 4431 # Record kind: S_PROC_ID_END
declare void @g() #1
; Function Attrs: nounwind
declare void @llvm.codeview.annotation(metadata) #2
attributes #0 = { nounwind uwtable "correctly-rounded-divide-sqrt-fp-math"="false" "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" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+fxsr,+mmx,+sse,+sse2,+x87" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #2 = { nounwind }
!llvm.dbg.cu = !{!0}
!llvm.module.flags = !{!3, !4, !5, !6}
!llvm.ident = !{!7}
!0 = distinct !DICompileUnit(language: DW_LANG_C99, file: !1, producer: "clang version 6.0.0 ", isOptimized: true, runtimeVersion: 0, emissionKind: FullDebug, enums: !2)
!1 = !DIFile(filename: "annotation.c", directory: "C:\5Csrc\5Cllvm-project\5Cbuild", checksumkind: CSK_MD5, checksum: "51164221112d8a5baa55a995027e4ba5")
!2 = !{}
!3 = !{i32 2, !"CodeView", i32 1}
!4 = !{i32 2, !"Debug Info Version", i32 3}
!5 = !{i32 1, !"wchar_size", i32 2}
!6 = !{i32 7, !"PIC Level", i32 2}
!7 = !{!"clang version 6.0.0 "}
!8 = distinct !DISubprogram(name: "f", scope: !1, file: !1, line: 2, type: !9, isLocal: false, isDefinition: true, scopeLine: 2, flags: DIFlagPrototyped, isOptimized: true, unit: !0, variables: !2)
!9 = !DISubroutineType(types: !10)
!10 = !{null}
!11 = !DILocation(line: 3, column: 3, scope: !8)
!12 = !{!"a1", !"a2"}
!13 = !DILocation(line: 4, column: 3, scope: !8)
!14 = !DILocation(line: 5, column: 3, scope: !8)
!15 = !DILocation(line: 6, column: 1, scope: !8)

View File

@ -0,0 +1,35 @@
; Inlining should not clone label annotations.
; Currently we block all duplication for simplicity.
; RUN: opt < %s -S -inline | FileCheck %s
@the_global = global i32 0
declare void @llvm.codeview.annotation(metadata)
define void @inlinee() {
entry:
store i32 42, i32* @the_global
call void @llvm.codeview.annotation(metadata !0)
ret void
}
define void @caller() {
entry:
call void @inlinee()
ret void
}
!0 = !{!"annotation"}
; CHECK-LABEL: define void @inlinee()
; CHECK: store i32 42, i32* @the_global
; CHECK: call void @llvm.codeview.annotation(metadata !0)
; CHECK: ret void
; CHECK-LABEL: define void @caller()
; MSVC can inline this. If we ever do, check for the store but make sure
; there is no annotation.
; CHECK: call void @inlinee()
; CHECK-NOT: call void @llvm.codeview.annotation
; CHECK: ret void