135 changes: 135 additions & 0 deletions llvm/lib/IR/AbstractCallSite.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
//===-- AbstractCallSite.cpp - Implementation of abstract call sites ------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file implements abstract call sites which unify the interface for
// direct, indirect, and callback call sites.
//
// For more information see:
// https://llvm.org/devmtg/2018-10/talk-abstracts.html#talk20
//
//===----------------------------------------------------------------------===//

#include "llvm/ADT/Statistic.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/IR/CallSite.h"
#include "llvm/Support/Debug.h"

using namespace llvm;

#define DEBUG_TYPE "abstract-call-sites"

STATISTIC(NumCallbackCallSites, "Number of callback call sites created");
STATISTIC(NumDirectAbstractCallSites,
"Number of direct abstract call sites created");
STATISTIC(NumInvalidAbstractCallSitesUnknownUse,
"Number of invalid abstract call sites created (unknown use)");
STATISTIC(NumInvalidAbstractCallSitesUnknownCallee,
"Number of invalid abstract call sites created (unknown callee)");
STATISTIC(NumInvalidAbstractCallSitesNoCallback,
"Number of invalid abstract call sites created (no callback)");

/// Create an abstract call site from a use.
AbstractCallSite::AbstractCallSite(const Use *U) : CS(U->getUser()) {

// First handle unknown users.
if (!CS) {

// If the use is actually in a constant cast expression which itself
// has only one use, we look through the constant cast expression.
// This happens by updating the use @p U to the use of the constant
// cast expression and afterwards re-initializing CS accordingly.
if (ConstantExpr *CE = dyn_cast<ConstantExpr>(U->getUser()))
if (CE->getNumUses() == 1 && CE->isCast()) {
U = &*CE->use_begin();
CS = CallSite(U->getUser());
}

if (!CS) {
NumInvalidAbstractCallSitesUnknownUse++;
return;
}
}

// Then handle direct or indirect calls. Thus, if U is the callee of the
// call site CS it is not a callback and we are done.
if (CS.isCallee(U)) {
NumDirectAbstractCallSites++;
return;
}

// If we cannot identify the broker function we cannot create a callback and
// invalidate the abstract call site.
Function *Callee = CS.getCalledFunction();
if (!Callee) {
NumInvalidAbstractCallSitesUnknownCallee++;
CS = CallSite();
return;
}

MDNode *CallbackMD = Callee->getMetadata(LLVMContext::MD_callback);
if (!CallbackMD) {
NumInvalidAbstractCallSitesNoCallback++;
CS = CallSite();
return;
}

unsigned UseIdx = CS.getArgumentNo(U);
MDNode *CallbackEncMD = nullptr;
for (const MDOperand &Op : CallbackMD->operands()) {
MDNode *OpMD = cast<MDNode>(Op.get());
auto *CBCalleeIdxAsCM = cast<ConstantAsMetadata>(OpMD->getOperand(0));
uint64_t CBCalleeIdx =
cast<ConstantInt>(CBCalleeIdxAsCM->getValue())->getZExtValue();
if (CBCalleeIdx != UseIdx)
continue;
CallbackEncMD = OpMD;
break;
}

if (!CallbackEncMD) {
NumInvalidAbstractCallSitesNoCallback++;
CS = CallSite();
return;
}

NumCallbackCallSites++;

assert(CallbackEncMD->getNumOperands() >= 2 && "Incomplete !callback metadata");

unsigned NumCallOperands = CS.getNumArgOperands();
// Skip the var-arg flag at the end when reading the metadata.
for (unsigned u = 0, e = CallbackEncMD->getNumOperands() - 1; u < e; u++) {
Metadata *OpAsM = CallbackEncMD->getOperand(u).get();
auto *OpAsCM = cast<ConstantAsMetadata>(OpAsM);
assert(OpAsCM->getType()->isIntegerTy(64) &&
"Malformed !callback metadata");

int64_t Idx = cast<ConstantInt>(OpAsCM->getValue())->getSExtValue();
assert(-1 <= Idx && Idx <= NumCallOperands &&
"Out-of-bounds !callback metadata index");

CI.ParameterEncoding.push_back(Idx);
}

if (!Callee->isVarArg())
return;

Metadata *VarArgFlagAsM =
CallbackEncMD->getOperand(CallbackEncMD->getNumOperands() - 1).get();
auto *VarArgFlagAsCM = cast<ConstantAsMetadata>(VarArgFlagAsM);
assert(VarArgFlagAsCM->getType()->isIntegerTy(1) &&
"Malformed !callback metadata var-arg flag");

if (VarArgFlagAsCM->getValue()->isNullValue())
return;

// Add all variadic arguments at the end.
for (unsigned u = Callee->arg_size(); u < NumCallOperands; u++)
CI.ParameterEncoding.push_back(u);
}
1 change: 1 addition & 0 deletions llvm/lib/IR/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ tablegen(LLVM AttributesCompatFunc.inc -gen-attrs)
add_public_tablegen_target(AttributeCompatFuncTableGen)

add_llvm_library(LLVMCore
AbstractCallSite.cpp
AsmWriter.cpp
Attributes.cpp
AutoUpgrade.cpp
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/IR/LLVMContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ LLVMContext::LLVMContext() : pImpl(new LLVMContextImpl(*this)) {
{MD_callees, "callees"},
{MD_irr_loop, "irr_loop"},
{MD_access_group, "llvm.access.group"},
{MD_callback, "callback"},
};

for (auto &MDKind : MDKinds) {
Expand Down
44 changes: 44 additions & 0 deletions llvm/lib/IR/MDBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,50 @@ MDNode *MDBuilder::createCallees(ArrayRef<Function *> Callees) {
return MDNode::get(Context, Ops);
}

MDNode *MDBuilder::createCallbackEncoding(unsigned CalleeArgNo,
ArrayRef<int> Arguments,
bool VarArgArePassed) {
SmallVector<Metadata *, 4> Ops;

Type *Int64 = Type::getInt64Ty(Context);
Ops.push_back(createConstant(ConstantInt::get(Int64, CalleeArgNo)));

for (int ArgNo : Arguments)
Ops.push_back(createConstant(ConstantInt::get(Int64, ArgNo, true)));

Type *Int1 = Type::getInt1Ty(Context);
Ops.push_back(createConstant(ConstantInt::get(Int1, VarArgArePassed)));

return MDNode::get(Context, Ops);
}

MDNode *MDBuilder::mergeCallbackEncodings(MDNode *ExistingCallbacks,
MDNode *NewCB) {
if (!ExistingCallbacks)
return MDNode::get(Context, {NewCB});

auto *NewCBCalleeIdxAsCM = cast<ConstantAsMetadata>(NewCB->getOperand(0));
uint64_t NewCBCalleeIdx =
cast<ConstantInt>(NewCBCalleeIdxAsCM->getValue())->getZExtValue();

SmallVector<Metadata *, 4> Ops;
unsigned NumExistingOps = ExistingCallbacks->getNumOperands();
Ops.resize(NumExistingOps + 1);

for (unsigned u = 0; u < NumExistingOps; u++) {
Ops[u] = ExistingCallbacks->getOperand(u);

auto *OldCBCalleeIdxAsCM = cast<ConstantAsMetadata>(Ops[u]);
uint64_t OldCBCalleeIdx =
cast<ConstantInt>(OldCBCalleeIdxAsCM->getValue())->getZExtValue();
assert(NewCBCalleeIdx != OldCBCalleeIdx &&
"Cannot map a callback callee index twice!");
}

Ops[NumExistingOps] = NewCB;
return MDNode::get(Context, Ops);
}

MDNode *MDBuilder::createAnonymousAARoot(StringRef Name, MDNode *Extra) {
// To ensure uniqueness the root node is self-referential.
auto Dummy = MDNode::getTemporary(Context, None);
Expand Down
4 changes: 2 additions & 2 deletions llvm/test/ThinLTO/X86/lazyload_metadata.ll
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@
; RUN: llvm-lto -thinlto-action=import %t2.bc -thinlto-index=%t3.bc \
; RUN: -o /dev/null -stats \
; RUN: 2>&1 | FileCheck %s -check-prefix=LAZY
; LAZY: 57 bitcode-reader - Number of Metadata records loaded
; LAZY: 59 bitcode-reader - Number of Metadata records loaded
; LAZY: 2 bitcode-reader - Number of MDStrings loaded

; RUN: llvm-lto -thinlto-action=import %t2.bc -thinlto-index=%t3.bc \
; RUN: -o /dev/null -disable-ondemand-mds-loading -stats \
; RUN: 2>&1 | FileCheck %s -check-prefix=NOTLAZY
; NOTLAZY: 66 bitcode-reader - Number of Metadata records loaded
; NOTLAZY: 68 bitcode-reader - Number of Metadata records loaded
; NOTLAZY: 7 bitcode-reader - Number of MDStrings loaded


Expand Down