diff --git a/llvm/include/llvm/CodeGen/MachineFunction.h b/llvm/include/llvm/CodeGen/MachineFunction.h --- a/llvm/include/llvm/CodeGen/MachineFunction.h +++ b/llvm/include/llvm/CodeGen/MachineFunction.h @@ -28,6 +28,7 @@ #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineMemOperand.h" +#include "llvm/IR/Instructions.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/ArrayRecycler.h" #include "llvm/Support/AtomicOrdering.h" @@ -421,6 +422,38 @@ /// Callee type id. ConstantInt *TypeId = nullptr; + + CallSiteInfo() {} + + /// Extracts the numeric type id from the CallBase's type operand bundle, + /// and sets TypeId. This is used as type id for the indirect call in the + /// call graph section. + CallSiteInfo(const CallBase &CB) { + // Call graph section needs numeric type id only for indirect calls. + if (!CB.isIndirectCall()) + return; + + auto Opt = CB.getOperandBundle(LLVMContext::OB_type); + if (!Opt.hasValue()) { + errs() << "warning: cannot find indirect call type operand bundle for " + "call graph section\n"; + return; + } + + // Get generalized type id string + auto OB = Opt.getValue(); + assert(OB.Inputs.size() == 1 && "invalid input size"); + auto *OBVal = OB.Inputs.front().get(); + auto *TypeIdMD = cast(OBVal)->getMetadata(); + auto *TypeIdStr = cast(TypeIdMD); + assert(TypeIdStr->getString().endswith(".generalized") && + "invalid type identifier"); + + // Compute numeric type id from generalized type id string + uint64_t TypeIdVal = llvm::MD5Hash(TypeIdStr->getString()); + IntegerType *Int64Ty = Type::getInt64Ty(CB.getContext()); + TypeId = ConstantInt::get(Int64Ty, TypeIdVal, /*IsSigned=*/false); + } }; private: diff --git a/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp b/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp --- a/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/ScheduleDAGSDNodes.cpp @@ -887,7 +887,8 @@ } if (MI->isCandidateForCallSiteEntry() && - DAG->getTarget().Options.EmitCallSiteInfo) + (DAG->getTarget().Options.EmitCallSiteInfo || + DAG->getTarget().Options.EmitCallGraphSection)) MF.addCallSiteInfo(MI, DAG->getSDCallSiteInfo(Node)); if (DAG->getNoMergeSiteInfo(Node)) { diff --git a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp --- a/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/llvm/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -5676,6 +5676,7 @@ bool &IsTailCall = CLI.IsTailCall; CallingConv::ID CallConv = CLI.CallConv; bool IsVarArg = CLI.IsVarArg; + const auto *CB = CLI.CB; MachineFunction &MF = DAG.getMachineFunction(); MachineFunction::CallSiteInfo CSInfo; @@ -5686,6 +5687,10 @@ bool IsSibCall = false; bool IsCalleeWin64 = Subtarget->isCallingConvWin64(CallConv); + // Set type id for call site info. + if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall()) + CSInfo = MachineFunction::CallSiteInfo(*CB); + // Check callee args/returns for SVE registers and set calling convention // accordingly. if (CallConv == CallingConv::C || CallConv == CallingConv::Fast) { diff --git a/llvm/lib/Target/ARM/ARMISelLowering.cpp b/llvm/lib/Target/ARM/ARMISelLowering.cpp --- a/llvm/lib/Target/ARM/ARMISelLowering.cpp +++ b/llvm/lib/Target/ARM/ARMISelLowering.cpp @@ -2287,6 +2287,7 @@ CallingConv::ID CallConv = CLI.CallConv; bool doesNotRet = CLI.DoesNotReturn; bool isVarArg = CLI.IsVarArg; + const auto *CB = CLI.CB; MachineFunction &MF = DAG.getMachineFunction(); ARMFunctionInfo *AFI = MF.getInfo(); @@ -2297,6 +2298,10 @@ bool isSibCall = false; bool PreferIndirect = false; + // Set type id for call site info. + if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall()) + CSInfo = MachineFunction::CallSiteInfo(*CB); + // Determine whether this is a non-secure function call. if (CLI.CB && CLI.CB->getAttributes().hasFnAttribute("cmse_nonsecure_call")) isCmseNSCall = true; diff --git a/llvm/lib/Target/Mips/MipsISelLowering.cpp b/llvm/lib/Target/Mips/MipsISelLowering.cpp --- a/llvm/lib/Target/Mips/MipsISelLowering.cpp +++ b/llvm/lib/Target/Mips/MipsISelLowering.cpp @@ -3152,6 +3152,7 @@ bool &IsTailCall = CLI.IsTailCall; CallingConv::ID CallConv = CLI.CallConv; bool IsVarArg = CLI.IsVarArg; + const auto *CB = CLI.CB; MachineFunction &MF = DAG.getMachineFunction(); MachineFrameInfo &MFI = MF.getFrameInfo(); @@ -3209,8 +3210,11 @@ // Get a count of how many bytes are to be pushed on the stack. unsigned NextStackOffset = CCInfo.getNextStackOffset(); - // Call site info for function parameters tracking. + // Call site info for function parameters tracking and call base type info. MachineFunction::CallSiteInfo CSInfo; + // Set type id for call site info. + if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall()) + CSInfo = MachineFunction::CallSiteInfo(*CB); // Check if it's really possible to do a tail call. Restrict it to functions // that are part of this compilation unit. diff --git a/llvm/lib/Target/X86/X86FastISel.cpp b/llvm/lib/Target/X86/X86FastISel.cpp --- a/llvm/lib/Target/X86/X86FastISel.cpp +++ b/llvm/lib/Target/X86/X86FastISel.cpp @@ -3579,6 +3579,12 @@ CLI.NumResultRegs = RVLocs.size(); CLI.Call = MIB; + // Add call site info for call graph section. + if (TM.Options.EmitCallGraphSection && CB && CB->isIndirectCall()) { + MachineFunction::CallSiteInfo CSInfo(*CB); + MF->addCallSiteInfo(CLI.Call, std::move(CSInfo)); + } + return true; } @@ -3969,6 +3975,8 @@ MO.setReg(IndexReg); } + if (MI->isCall()) + FuncInfo.MF->moveCallSiteInfo(MI, Result); Result->addMemOperand(*FuncInfo.MF, createMachineMemOperandFor(LI)); Result->cloneInstrSymbols(*FuncInfo.MF, *MI); MachineBasicBlock::iterator I(MI); diff --git a/llvm/lib/Target/X86/X86ISelLowering.cpp b/llvm/lib/Target/X86/X86ISelLowering.cpp --- a/llvm/lib/Target/X86/X86ISelLowering.cpp +++ b/llvm/lib/Target/X86/X86ISelLowering.cpp @@ -3937,6 +3937,10 @@ if (CallConv == CallingConv::X86_INTR) report_fatal_error("X86 interrupts may not be called directly"); + // Set type id for call site info. + if (MF.getTarget().Options.EmitCallGraphSection && CB && CB->isIndirectCall()) + CSInfo = MachineFunction::CallSiteInfo(*CB); + bool IsMustTail = CLI.CB && CLI.CB->isMustTailCall(); if (Subtarget.isPICStyleGOT() && !IsGuaranteeTCO && !IsMustTail) { // If we are using a GOT, disable tail calls to external symbols with diff --git a/llvm/test/CodeGen/AArch64/call-site-info-typeid.ll b/llvm/test/CodeGen/AArch64/call-site-info-typeid.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/call-site-info-typeid.ll @@ -0,0 +1,39 @@ +; Tests that call site type ids can be extracted and set from type operand +; bundles. + +; Verify the exact typeId value to ensure it is not garbage but the value +; computed as the type id from the type operand bundle. +; RUN: llc --call-graph-section -mtriple aarch64-linux-gnu %s -stop-before=finalize-isel -o - | FileCheck %s + +; ModuleID = 'test.c' +source_filename = "test.c" +target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128" +target triple = "aarch64-unknown-linux-gnu" + +define dso_local void @foo(i8 signext %a) !type !3 { +entry: + ret void +} + +; CHECK: name: main +define dso_local i32 @main() !type !4 { +entry: + %retval = alloca i32, align 4 + %fp = alloca void (i8)*, align 8 + store i32 0, i32* %retval, align 4 + store void (i8)* @foo, void (i8)** %fp, align 8 + %0 = load void (i8)*, void (i8)** %fp, align 8 + ; CHECK: callSites: + ; CHECK-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: [], typeId: + ; CHECK-NEXT: 7854600665770582568 } + call void %0(i8 signext 97) [ "type"(metadata !"_ZTSFvcE.generalized") ] + ret i32 0 +} + +!llvm.module.flags = !{!0, !1, !2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"uwtable", i32 1} +!2 = !{i32 7, !"frame-pointer", i32 2} +!3 = !{i64 0, !"_ZTSFvcE.generalized"} +!4 = !{i64 0, !"_ZTSFiE.generalized"} diff --git a/llvm/test/CodeGen/ARM/call-site-info-typeid.ll b/llvm/test/CodeGen/ARM/call-site-info-typeid.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/ARM/call-site-info-typeid.ll @@ -0,0 +1,39 @@ +; Tests that call site type ids can be extracted and set from type operand +; bundles. + +; Verify the exact typeId value to ensure it is not garbage but the value +; computed as the type id from the type operand bundle. +; RUN: llc --call-graph-section -mtriple arm-linux-gnu %s -stop-before=finalize-isel -o - | FileCheck %s + +; ModuleID = 'test.c' +source_filename = "test.c" +target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" +target triple = "armv4t-unknown-linux-gnu" + +define dso_local void @foo(i8 signext %a) !type !3 { +entry: + ret void +} + +; CHECK: name: main +define dso_local i32 @main() !type !4 { +entry: + %retval = alloca i32, align 4 + %fp = alloca void (i8)*, align 8 + store i32 0, i32* %retval, align 4 + store void (i8)* @foo, void (i8)** %fp, align 8 + %0 = load void (i8)*, void (i8)** %fp, align 8 + ; CHECK: callSites: + ; CHECK-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: [], typeId: + ; CHECK-NEXT: 7854600665770582568 } + call void %0(i8 signext 97) [ "type"(metadata !"_ZTSFvcE.generalized") ] + ret i32 0 +} + +!llvm.module.flags = !{!0, !1, !2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"uwtable", i32 1} +!2 = !{i32 7, !"frame-pointer", i32 2} +!3 = !{i64 0, !"_ZTSFvcE.generalized"} +!4 = !{i64 0, !"_ZTSFiE.generalized"} diff --git a/llvm/test/CodeGen/MIR/X86/call-site-info-typeid.ll b/llvm/test/CodeGen/MIR/X86/call-site-info-typeid.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/MIR/X86/call-site-info-typeid.ll @@ -0,0 +1,112 @@ +; Test MIR printer and parser for type id field in call site info. Test that +; it works well with/without --emit-call-site-info. + +; Multiplex --call-graph-section and -emit-call-site-info as both utilize +; CallSiteInfo and callSites. + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Test printer and parser with --call-graph-section only. + +; Test printer. +; Verify that fwdArgRegs is not set, typeId is set. +; Verify the exact typeId value to ensure it is not garbage but the value +; computed as the type id from the type operand bundle. +; RUN: llc --call-graph-section %s -stop-before=finalize-isel -o %t1.mir +; RUN: cat %t1.mir | FileCheck %s --check-prefix=PRINTER_CGS +; PRINTER_CGS: name: main +; PRINTER_CGS: callSites: +; PRINTER_CGS-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: [], typeId: +; PRINTER_CGS-NEXT: 7854600665770582568 } + + +; Test parser. +; Verify that we get the same result. +; RUN: llc --call-graph-section %t1.mir -run-pass=finalize-isel -o - \ +; RUN: | FileCheck %s --check-prefix=PARSER_CGS +; PARSER_CGS: name: main +; PARSER_CGS: callSites: +; PARSER_CGS-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: [], typeId: +; PARSER_CGS-NEXT: 7854600665770582568 } + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Test printer and parser with -emit-call-site-info only. + +; Test printer. +; Verify that fwdArgRegs is set, typeId is not set. +; RUN: llc -emit-call-site-info %s -stop-before=finalize-isel -o %t2.mir +; RUN: cat %t2.mir | FileCheck %s --check-prefix=PRINTER_CSI +; PRINTER_CSI: name: main +; PRINTER_CSI: callSites: +; PRINTER_CSI-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: +; PRINTER_CSI-NEXT: { arg: 0, reg: '$edi' } +; PRINTER_CSI-NOT: typeId: + + +; Test parser. +; Verify that we get the same result. +; RUN: llc -emit-call-site-info %t2.mir -run-pass=finalize-isel -o - \ +; RUN: | FileCheck %s --check-prefix=PARSER_CSI +; PARSER_CSI: name: main +; PARSER_CSI: callSites: +; PARSER_CSI-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: +; PARSER_CSI-NEXT: { arg: 0, reg: '$edi' } +; PARSER_CSI-NOT: typeId: + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; Test printer and parser with both -emit-call-site-info and --call-graph-section. + +; Test printer. +; Verify both fwdArgRegs and typeId are set. +; Verify the exact typeId value to ensure it is not garbage but the value +; computed as the type id from the type operand bundle. +; RUN: llc --call-graph-section -emit-call-site-info %s -stop-before=finalize-isel -o %t2.mir +; RUN: cat %t2.mir | FileCheck %s --check-prefix=PRINTER_CGS_CSI +; PRINTER_CGS_CSI: name: main +; PRINTER_CGS_CSI: callSites: +; PRINTER_CGS_CSI-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: +; PRINTER_CGS_CSI-NEXT: { arg: 0, reg: '$edi' }, typeId: +; PRINTER_CGS_CSI-NEXT: 7854600665770582568 } + + +; Test parser. +; Verify that we get the same result. +; RUN: llc --call-graph-section -emit-call-site-info %t2.mir -run-pass=finalize-isel -o - \ +; RUN: | FileCheck %s --check-prefix=PARSER_CGS_CSI +; PARSER_CGS_CSI: name: main +; PARSER_CGS_CSI: callSites: +; PARSER_CGS_CSI-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: +; PARSER_CGS_CSI-NEXT: { arg: 0, reg: '$edi' }, typeId: +; PARSER_CGS_CSI-NEXT: 7854600665770582568 } + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; ModuleID = 'test.c' +source_filename = "test.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local void @foo(i8 signext %a) !type !3 { +entry: + ret void +} + +; Function Attrs: noinline nounwind optnone uwtable +define dso_local i32 @main() !type !4 { +entry: + %retval = alloca i32, align 4 + %fp = alloca void (i8)*, align 8 + store i32 0, i32* %retval, align 4 + store void (i8)* @foo, void (i8)** %fp, align 8 + %0 = load void (i8)*, void (i8)** %fp, align 8 + call void %0(i8 signext 97) [ "type"(metadata !"_ZTSFvcE.generalized") ] + ret i32 0 +} + +!llvm.module.flags = !{!0, !1, !2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"uwtable", i32 1} +!2 = !{i32 7, !"frame-pointer", i32 2} +!3 = !{i64 0, !"_ZTSFvcE.generalized"} +!4 = !{i64 0, !"_ZTSFiE.generalized"} diff --git a/llvm/test/CodeGen/Mips/call-site-info-typeid.ll b/llvm/test/CodeGen/Mips/call-site-info-typeid.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/Mips/call-site-info-typeid.ll @@ -0,0 +1,39 @@ +; Tests that call site type ids can be extracted and set from type operand +; bundles. + +; Verify the exact typeId value to ensure it is not garbage but the value +; computed as the type id from the type operand bundle. +; RUN: llc --call-graph-section -mtriple=mips-linux-gnu %s -stop-before=finalize-isel -o - | FileCheck %s + +; ModuleID = 'test.c' +source_filename = "test.c" +target datalayout = "E-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64" +target triple = "mips-unknown-linux-gnu" + +define dso_local void @foo(i8 signext %a) !type !3 { +entry: + ret void +} + +; CHECK: name: main +define dso_local i32 @main() !type !4 { +entry: + %retval = alloca i32, align 4 + %fp = alloca void (i8)*, align 8 + store i32 0, i32* %retval, align 4 + store void (i8)* @foo, void (i8)** %fp, align 8 + %0 = load void (i8)*, void (i8)** %fp, align 8 + ; CHECK: callSites: + ; CHECK-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: [], typeId: + ; CHECK-NEXT: 7854600665770582568 } + call void %0(i8 signext 97) [ "type"(metadata !"_ZTSFvcE.generalized") ] + ret i32 0 +} + +!llvm.module.flags = !{!0, !1, !2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"uwtable", i32 1} +!2 = !{i32 7, !"frame-pointer", i32 2} +!3 = !{i64 0, !"_ZTSFvcE.generalized"} +!4 = !{i64 0, !"_ZTSFiE.generalized"} diff --git a/llvm/test/CodeGen/X86/call-site-info-typeid.ll b/llvm/test/CodeGen/X86/call-site-info-typeid.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/X86/call-site-info-typeid.ll @@ -0,0 +1,39 @@ +; Tests that call site type ids can be extracted and set from type operand +; bundles. + +; Verify the exact typeId value to ensure it is not garbage but the value +; computed as the type id from the type operand bundle. +; RUN: llc --call-graph-section -mtriple=x86_64-unknown-linux %s -stop-before=finalize-isel -o - | FileCheck %s + +; ModuleID = 'test.c' +source_filename = "test.c" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-unknown-linux-gnu" + +define dso_local void @foo(i8 signext %a) !type !3 { +entry: + ret void +} + +; CHECK: name: main +define dso_local i32 @main() !type !4 { +entry: + %retval = alloca i32, align 4 + %fp = alloca void (i8)*, align 8 + store i32 0, i32* %retval, align 4 + store void (i8)* @foo, void (i8)** %fp, align 8 + %0 = load void (i8)*, void (i8)** %fp, align 8 + ; CHECK: callSites: + ; CHECK-NEXT: - { bb: {{.*}}, offset: {{.*}}, fwdArgRegs: [], typeId: + ; CHECK-NEXT: 7854600665770582568 } + call void %0(i8 signext 97) [ "type"(metadata !"_ZTSFvcE.generalized") ] + ret i32 0 +} + +!llvm.module.flags = !{!0, !1, !2} + +!0 = !{i32 1, !"wchar_size", i32 4} +!1 = !{i32 7, !"uwtable", i32 1} +!2 = !{i32 7, !"frame-pointer", i32 2} +!3 = !{i64 0, !"_ZTSFvcE.generalized"} +!4 = !{i64 0, !"_ZTSFiE.generalized"}