diff --git a/llvm/include/llvm/InitializePasses.h b/llvm/include/llvm/InitializePasses.h index bab49b7675512f..fc3d06cf603432 100644 --- a/llvm/include/llvm/InitializePasses.h +++ b/llvm/include/llvm/InitializePasses.h @@ -344,6 +344,7 @@ void initializeXRayInstrumentationPass(PassRegistry&); void initializeYkStackmapsPass(PassRegistry&); void initializeYkSplitBlocksAfterCallsPass(PassRegistry&); void initializeYkBasicBlockTracerPass(PassRegistry&); +void initializeYkModuleClonePass(PassRegistry&); } // end namespace llvm #endif // LLVM_INITIALIZEPASSES_H diff --git a/llvm/include/llvm/Transforms/Yk/ControlPoint.h b/llvm/include/llvm/Transforms/Yk/ControlPoint.h index ea67a0259483fe..7d046b38507f9e 100644 --- a/llvm/include/llvm/Transforms/Yk/ControlPoint.h +++ b/llvm/include/llvm/Transforms/Yk/ControlPoint.h @@ -18,7 +18,7 @@ #define CP_PPNAME "llvm.experimental.patchpoint.void" namespace llvm { -ModulePass *createYkControlPointPass(); +ModulePass *createYkControlPointPass(uint64_t controlPointCount); } // namespace llvm #endif diff --git a/llvm/include/llvm/Transforms/Yk/ModuleClone.h b/llvm/include/llvm/Transforms/Yk/ModuleClone.h new file mode 100644 index 00000000000000..c2da8961400f4e --- /dev/null +++ b/llvm/include/llvm/Transforms/Yk/ModuleClone.h @@ -0,0 +1,13 @@ +#ifndef LLVM_TRANSFORMS_YK_MODULE_CLONE_H +#define LLVM_TRANSFORMS_YK_MODULE_CLONE_H + +#include "llvm/Pass.h" + +#define YK_CLONE_PREFIX "__yk_clone_" +#define YK_CLONE_MODULE_CP_COUNT 2 + +namespace llvm { +ModulePass *createYkModuleClonePass(); +} // namespace llvm + +#endif diff --git a/llvm/include/llvm/Transforms/Yk/Stackmaps.h b/llvm/include/llvm/Transforms/Yk/Stackmaps.h index 91a3276b5c7316..cb5012c0d600f8 100644 --- a/llvm/include/llvm/Transforms/Yk/Stackmaps.h +++ b/llvm/include/llvm/Transforms/Yk/Stackmaps.h @@ -4,7 +4,7 @@ #include "llvm/Pass.h" namespace llvm { -ModulePass *createYkStackmapsPass(); +ModulePass *createYkStackmapsPass(uint64_t stackmapIDStart); } // namespace llvm #endif diff --git a/llvm/lib/CodeGen/CodeGen.cpp b/llvm/lib/CodeGen/CodeGen.cpp index 77d7b6fefa7e39..eb51081c6ceaee 100644 --- a/llvm/lib/CodeGen/CodeGen.cpp +++ b/llvm/lib/CodeGen/CodeGen.cpp @@ -153,4 +153,5 @@ void llvm::initializeCodeGen(PassRegistry &Registry) { initializeYkStackmapsPass(Registry); initializeYkSplitBlocksAfterCallsPass(Registry); initializeYkBasicBlockTracerPass(Registry); + initializeYkModuleClonePass(Registry); } diff --git a/llvm/lib/CodeGen/TargetPassConfig.cpp b/llvm/lib/CodeGen/TargetPassConfig.cpp index 2fcfa3cb59180c..c6b3e1058421dc 100644 --- a/llvm/lib/CodeGen/TargetPassConfig.cpp +++ b/llvm/lib/CodeGen/TargetPassConfig.cpp @@ -56,6 +56,7 @@ #include "llvm/Transforms/Yk/Stackmaps.h" #include "llvm/Transforms/Yk/NoCallsInEntryBlocks.h" #include "llvm/Transforms/Yk/BasicBlockTracer.h" +#include "llvm/Transforms/Yk/ModuleClone.h" #include #include #include @@ -297,6 +298,9 @@ static cl::opt YkBasicBlockTracer("yk-basicblock-tracer", cl::init(false), cl::NotHidden, cl::desc("Enables YK Software Tracer capability")); +static cl::opt + YkModuleClone("yk-module-clone", cl::init(false), cl::NotHidden, + cl::desc("Enables YK Module Cloning capability")); /// Allow standard passes to be disabled by command line options. This supports /// simple binary flags that either suppress the pass or do nothing. @@ -1139,6 +1143,15 @@ bool TargetPassConfig::addISelPasses() { addIRPasses(); addCodeGenPrepare(); addPassesToHandleExceptions(); + + // Default number of control points in a module. + int numberOfControlPoints = 1; + + if (YkModuleClone) { + numberOfControlPoints = YK_CLONE_MODULE_CP_COUNT; + addPass(createYkModuleClonePass()); + } + if (YkBlockDisambiguate) addPass(createYkBlockDisambiguatePass()); @@ -1166,7 +1179,7 @@ bool TargetPassConfig::addISelPasses() { // decomposed into scalar arguments. The JIT runtime relies on the interface // *not* being changed. if (YkPatchCtrlPoint) { - addPass(createYkControlPointPass()); + addPass(createYkControlPointPass(numberOfControlPoints)); } if (YkLinkage) { @@ -1184,12 +1197,12 @@ bool TargetPassConfig::addISelPasses() { } if (YkInsertStackMaps) { - addPass(createYkStackmapsPass()); + addPass(createYkStackmapsPass(numberOfControlPoints)); } if (YkBasicBlockTracer) { addPass(createYkBasicBlockTracerPass()); - } + } addISelPrepare(); return addCoreISelPasses(); diff --git a/llvm/lib/Transforms/Yk/BasicBlockTracer.cpp b/llvm/lib/Transforms/Yk/BasicBlockTracer.cpp index 24851bb8ef6546..32975eadc097fe 100644 --- a/llvm/lib/Transforms/Yk/BasicBlockTracer.cpp +++ b/llvm/lib/Transforms/Yk/BasicBlockTracer.cpp @@ -8,6 +8,7 @@ #include "llvm/IR/Module.h" #include "llvm/InitializePasses.h" #include "llvm/Pass.h" +#include "llvm/Transforms/Yk/ModuleClone.h" #define DEBUG_TYPE "yk-basicblock-tracer-pass" @@ -42,6 +43,9 @@ struct YkBasicBlockTracer : public ModulePass { uint32_t FunctionIndex = 0; for (auto &F : M) { uint32_t BlockIndex = 0; + if (F.getName().startswith(YK_CLONE_PREFIX)) { + continue; + } for (auto &BB : F) { builder.SetInsertPoint(&*BB.getFirstInsertionPt()); builder.CreateCall(TraceFunc, {builder.getInt32(FunctionIndex), diff --git a/llvm/lib/Transforms/Yk/CMakeLists.txt b/llvm/lib/Transforms/Yk/CMakeLists.txt index 3e5fba85082c29..7911cad4dfc592 100644 --- a/llvm/lib/Transforms/Yk/CMakeLists.txt +++ b/llvm/lib/Transforms/Yk/CMakeLists.txt @@ -8,6 +8,7 @@ add_llvm_component_library(LLVMYkPasses NoCallsInEntryBlocks.cpp SplitBlocksAfterCalls.cpp BasicBlockTracer.cpp + ModuleClone.cpp DEPENDS intrinsics_gen @@ -16,4 +17,6 @@ add_llvm_component_library(LLVMYkPasses Analysis Core Support + TransformUtils # Module Clone + Linker # Module Linker ) diff --git a/llvm/lib/Transforms/Yk/ControlPoint.cpp b/llvm/lib/Transforms/Yk/ControlPoint.cpp index 037003b5d7f02a..c71fa9b29ae7f4 100644 --- a/llvm/lib/Transforms/Yk/ControlPoint.cpp +++ b/llvm/lib/Transforms/Yk/ControlPoint.cpp @@ -61,11 +61,6 @@ #define YK_CONTROL_POINT_ARG_VARS_IDX 2 #define YK_CONTROL_POINT_NUM_ARGS 3 -// Stackmap ID zero is reserved for the control point. -// -// This will need to change when we support >1 control point. -const unsigned CPStackMapID = 0; - // The number of shadow bytes required for the control point's patchpoint. // // This must be large enough to accommodate the call to patchpoint target @@ -86,22 +81,28 @@ const unsigned CPShadow = 13; using namespace llvm; -/// Find the call to the dummy control point that we want to patch. -/// Returns either a pointer the call instruction, or `nullptr` if the call -/// could not be found. -/// YKFIXME: For now assumes there's only one control point. -CallInst *findControlPointCall(Module &M) { +/// @brief Locate calls to the dummy control point that we want to patch. +/// +/// This function searches for all instances of `YK_DUMMY_CONTROL_POINT` +/// calls within the LLVM module. +/// +/// @return A vector of pointers to the `YK_DUMMY_CONTROL_POINT` call +/// instructions, or an empty vector if no calls are found. +std::vector findControlPointCalls(Module &M) { + std::vector controlPointCalls; + // Find the declaration of `yk_mt_control_point()`. Function *CtrlPoint = M.getFunction(YK_DUMMY_CONTROL_POINT); if (CtrlPoint == nullptr) - return nullptr; + return controlPointCalls; - // Find the call site of `yk_mt_control_point()`. - const Value::user_iterator U = CtrlPoint->user_begin(); - if (U == CtrlPoint->user_end()) - return nullptr; - - return cast(*U); + // Find all call sites of `yk_mt_control_point()`. + for (User *U : CtrlPoint->users()) { + if (CallInst *CI = dyn_cast(U)) { + controlPointCalls.insert(controlPointCalls.begin(), CI); + } + } + return controlPointCalls; } namespace llvm { @@ -110,17 +111,22 @@ void initializeYkControlPointPass(PassRegistry &); namespace { class YkControlPoint : public ModulePass { +private: + uint64_t controlPointCount; + public: static char ID; - YkControlPoint() : ModulePass(ID) { + YkControlPoint(uint64_t controlPointCount) + : ModulePass(ID), controlPointCount(controlPointCount) { initializeYkControlPointPass(*PassRegistry::getPassRegistry()); } bool runOnModule(Module &M) override { LLVMContext &Context = M.getContext(); + std::vector ControlPointCalls = findControlPointCalls(M); + // Locate the "dummy" control point provided by the user. - CallInst *OldCtrlPointCall = findControlPointCall(M); - if (OldCtrlPointCall == nullptr) { + if (ControlPointCalls.empty()) { // This program doesn't have a control point. We can't do any // transformations on it, but we do still want to compile it. Context.diagnose(DiagnosticInfoInlineAsm( @@ -128,75 +134,86 @@ class YkControlPoint : public ModulePass { DS_Warning)); return false; } - - // Get function containing the control point. - Function *Caller = OldCtrlPointCall->getFunction(); - - // Check that the control point is inside a loop. - DominatorTree DT(*Caller); - const LoopInfo Loops(DT); - if (!std::any_of(Loops.begin(), Loops.end(), [OldCtrlPointCall](Loop *L) { - return L->contains(OldCtrlPointCall); - })) { - ; - Context.emitError("yk_mt_control_point() must be called inside a loop."); - return false; - } - - // The old control point should be of the form: - // yk_mt_control_point(YkMT*, YkLocation*) - assert(OldCtrlPointCall->arg_size() == YK_OLD_CONTROL_POINT_NUM_ARGS); - Type *YkMTTy = - OldCtrlPointCall->getArgOperand(YK_CONTROL_POINT_ARG_MT_IDX)->getType(); - Type *YkLocTy = - OldCtrlPointCall->getArgOperand(YK_CONTROL_POINT_ARG_LOC_IDX) - ->getType(); - - // Create a call to the "new" (patched) control point, but do so via a - // patchpoint so that we can capture the live variables at exactly the - // moment before the call. - Type *Int64Ty = Type::getInt64Ty(Context); - FunctionType *FType = FunctionType::get(Type::getVoidTy(Context), - {YkMTTy, YkLocTy, Int64Ty}, false); - Function *NF = Function::Create(FType, GlobalVariable::ExternalLinkage, - YK_NEW_CONTROL_POINT, M); - - IRBuilder<> Builder(OldCtrlPointCall); - - const Intrinsic::ID SMFuncID = Function::lookupIntrinsicID(CP_PPNAME); - if (SMFuncID == Intrinsic::not_intrinsic) { - Context.emitError("can't find stackmap()"); - return false; - } - Function *SMFunc = Intrinsic::getDeclaration(&M, SMFuncID); - assert(SMFunc != nullptr); - - // Get live variables. - LivenessAnalysis LA(Caller); - auto Lives = LA.getLiveVarsBefore(OldCtrlPointCall); - - Value *SMID = ConstantInt::get(Type::getInt64Ty(Context), CPStackMapID); - Value *Shadow = ConstantInt::get(Type::getInt32Ty(Context), CPShadow); - std::vector Args = { - SMID, - Shadow, - NF, - ConstantInt::get(Type::getInt32Ty(Context), 3), - OldCtrlPointCall->getArgOperand(YK_CONTROL_POINT_ARG_MT_IDX), - OldCtrlPointCall->getArgOperand(YK_CONTROL_POINT_ARG_LOC_IDX), - SMID, - }; - - for (auto *Live : Lives) { - Args.push_back(Live); + assert(ControlPointCalls.size() == controlPointCount && + "Unexpected number of control point calls"); + + unsigned CPStackMapID = 0; + Function *NF = nullptr; + + for (CallInst *OldCtrlPointCall : ControlPointCalls) { + // Get function containing the control point. + Function *Caller = OldCtrlPointCall->getFunction(); + + // Check that the control point is inside a loop. + DominatorTree DT(*Caller); + const LoopInfo Loops(DT); + if (!std::any_of(Loops.begin(), Loops.end(), [OldCtrlPointCall](Loop *L) { + return L->contains(OldCtrlPointCall); + })) { + ; + Context.emitError( + "yk_mt_control_point() must be called inside a loop."); + return false; + } + + // The old control point should be of the form: + // yk_mt_control_point(YkMT*, YkLocation*) + assert(OldCtrlPointCall->arg_size() == YK_OLD_CONTROL_POINT_NUM_ARGS); + Type *YkMTTy = + OldCtrlPointCall->getArgOperand(YK_CONTROL_POINT_ARG_MT_IDX) + ->getType(); + Type *YkLocTy = + OldCtrlPointCall->getArgOperand(YK_CONTROL_POINT_ARG_LOC_IDX) + ->getType(); + + // Create a call to the "new" (patched) control point, but do so via a + // patchpoint so that we can capture the live variables at exactly the + // moment before the call. + if (NF == nullptr) { + Type *Int64Ty = Type::getInt64Ty(Context); + FunctionType *FType = FunctionType::get( + Type::getVoidTy(Context), {YkMTTy, YkLocTy, Int64Ty}, false); + NF = Function::Create(FType, GlobalVariable::ExternalLinkage, + YK_NEW_CONTROL_POINT, M); + } + + IRBuilder<> Builder(OldCtrlPointCall); + + const Intrinsic::ID SMFuncID = Function::lookupIntrinsicID(CP_PPNAME); + if (SMFuncID == Intrinsic::not_intrinsic) { + Context.emitError("can't find stackmap()"); + return false; + } + Function *SMFunc = Intrinsic::getDeclaration(&M, SMFuncID); + assert(SMFunc != nullptr); + + // Get live variables. + LivenessAnalysis LA(Caller); + auto Lives = LA.getLiveVarsBefore(OldCtrlPointCall); + + Value *SMID = ConstantInt::get(Type::getInt64Ty(Context), CPStackMapID); + Value *Shadow = ConstantInt::get(Type::getInt32Ty(Context), CPShadow); + std::vector Args = { + SMID, + Shadow, + NF, + ConstantInt::get(Type::getInt32Ty(Context), 3), + OldCtrlPointCall->getArgOperand(YK_CONTROL_POINT_ARG_MT_IDX), + OldCtrlPointCall->getArgOperand(YK_CONTROL_POINT_ARG_LOC_IDX), + SMID, + }; + + for (auto *Live : Lives) { + Args.push_back(Live); + } + + Builder.CreateCall(SMFunc->getFunctionType(), SMFunc, + ArrayRef(Args)); + + // Replace the call to the dummy control point. + OldCtrlPointCall->eraseFromParent(); + ++CPStackMapID; } - - Builder.CreateCall(SMFunc->getFunctionType(), SMFunc, - ArrayRef(Args)); - - // Replace the call to the dummy control point. - OldCtrlPointCall->eraseFromParent(); - #ifndef NDEBUG // Our pass runs after LLVM normally does its verify pass. In debug builds // we run it again to check that our pass is generating valid IR. @@ -213,4 +230,6 @@ class YkControlPoint : public ModulePass { char YkControlPoint::ID = 0; INITIALIZE_PASS(YkControlPoint, DEBUG_TYPE, "yk control point", false, false) -ModulePass *llvm::createYkControlPointPass() { return new YkControlPoint(); } +ModulePass *llvm::createYkControlPointPass(uint64_t controlPointCount) { + return new YkControlPoint(controlPointCount); +} diff --git a/llvm/lib/Transforms/Yk/ModuleClone.cpp b/llvm/lib/Transforms/Yk/ModuleClone.cpp new file mode 100644 index 00000000000000..de32f8653eea17 --- /dev/null +++ b/llvm/lib/Transforms/Yk/ModuleClone.cpp @@ -0,0 +1,106 @@ +//===- ModuleClone.cpp - Yk Module Cloning Pass ---------------------------===// +// +// This pass duplicates functions within the module, producing both the original +// and new versions of functions. the process is as follows: +// +// - **Cloning Criteria:** +// - Functions **without** their address taken are cloned. This results in two +// versions of such functions in the module: the original and the cloned +// version. +// - Functions **with** their address taken remain only in their original form +// and are not cloned. +// +// - **Cloned Function Naming:** +// - The cloned functions are renamed by adding the prefix `__yk_clone_` to +// their original names. This distinguishes them from the original +// functions. +// +// - **Clone Process:** +// - 1. The original module is cloned, creating two copies of the module. +// - 2. The functions in the cloned module that satisfy the cloning criteria +// are renamed. +// - 3. The cloned module is linked back into the original module. +// +// - **Optimisation Intent:** +// - The **cloned functions** (those with the `__yk_clone_` prefix) are +// intended to be the **optimised versions** of the functions. +// - The **original functions** remain **unoptimised**. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Transforms/Yk/ModuleClone.h" +#include "llvm/ADT/Twine.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Verifier.h" +#include "llvm/InitializePasses.h" +#include "llvm/Linker/Linker.h" +#include "llvm/Pass.h" +#include "llvm/Transforms/Utils/Cloning.h" + +#define DEBUG_TYPE "yk-module-clone-pass" + +using namespace llvm; + +namespace llvm { +void initializeYkModuleClonePass(PassRegistry &); +} // namespace llvm + +namespace { +struct YkModuleClone : public ModulePass { + static char ID; + + YkModuleClone() : ModulePass(ID) { + initializeYkModuleClonePass(*PassRegistry::getPassRegistry()); + } + void updateClonedFunctions(Module &M) { + for (llvm::Function &F : M) { + if (F.hasExternalLinkage() && F.isDeclaration()) { + continue; + } + // Skip functions that are address taken + if (!F.hasAddressTaken()) { + F.setName(Twine(YK_CLONE_PREFIX) + F.getName()); + } + } + } + + bool runOnModule(Module &M) override { + std::unique_ptr Cloned = CloneModule(M); + if (!Cloned) { + llvm::report_fatal_error("Error cloning the module"); + return false; + } + updateClonedFunctions(*Cloned); + + // The `OverrideFromSrc` flag instructs the linker to prioritise + // definitions from the source module (the second argument) when + // conflicts arise. This means that if two global variables, functions, + // or constants have the same name in both the original and cloned modules, + // the version from the cloned module will overwrite the original. + if (Linker::linkModules(M, std::move(Cloned), + Linker::Flags::OverrideFromSrc)) { + llvm::report_fatal_error("Error linking the modules"); + return false; + } + + if (verifyModule(M, &errs())) { + errs() << "Module verification failed!"; + return false; + } + + return true; + } +}; +} // namespace + +char YkModuleClone::ID = 0; + +INITIALIZE_PASS(YkModuleClone, DEBUG_TYPE, "yk module clone", false, false) + +ModulePass *llvm::createYkModuleClonePass() { return new YkModuleClone(); } diff --git a/llvm/lib/Transforms/Yk/StackMaps.cpp b/llvm/lib/Transforms/Yk/StackMaps.cpp index b88cc1f0af75f3..924b8f060cd0d3 100644 --- a/llvm/lib/Transforms/Yk/StackMaps.cpp +++ b/llvm/lib/Transforms/Yk/StackMaps.cpp @@ -18,18 +18,11 @@ #include "llvm/Pass.h" #include "llvm/Transforms/Yk/ControlPoint.h" #include "llvm/Transforms/Yk/LivenessAnalysis.h" +#include "llvm/Transforms/Yk/ModuleClone.h" #include #define DEBUG_TYPE "yk-stackmaps" -// The first stackmap ID available for use by this pass. -// -// It's 1 because 0 is reserved for the control point. -// See llvm/lib/Transforms/Yk/ControlPoint.cpp -// -// This will have to change when we support >1 control point. -const int NonCPStackmapIDStart = 1; - using namespace llvm; namespace llvm { @@ -39,9 +32,13 @@ void initializeYkStackmapsPass(PassRegistry &); namespace { class YkStackmaps : public ModulePass { +private: + uint64_t StackmapIDStart; + public: static char ID; - YkStackmaps() : ModulePass(ID) { + YkStackmaps(uint64_t stackmapIDStart = 1) + : ModulePass(ID), StackmapIDStart(stackmapIDStart) { initializeYkStackmapsPass(*PassRegistry::getPassRegistry()); } @@ -59,6 +56,9 @@ class YkStackmaps : public ModulePass { for (Function &F : M) { if (F.empty()) // skip declarations. continue; + if (F.getName().startswith(YK_CLONE_PREFIX)) // skip cloned functions + continue; + LivenessAnalysis LA(&F); for (BasicBlock &BB : F) { for (Instruction &I : BB) { @@ -96,7 +96,7 @@ class YkStackmaps : public ModulePass { Function *SMFunc = Intrinsic::getDeclaration(&M, SMFuncID); assert(SMFunc != nullptr); - uint64_t Count = NonCPStackmapIDStart; + uint64_t Count = StackmapIDStart; Value *Shadow = ConstantInt::get(Type::getInt32Ty(Context), 0); for (auto It : SMCalls) { Instruction *I = cast(It.first); @@ -135,4 +135,13 @@ class YkStackmaps : public ModulePass { char YkStackmaps::ID = 0; INITIALIZE_PASS(YkStackmaps, DEBUG_TYPE, "yk stackmaps", false, false) -ModulePass *llvm::createYkStackmapsPass() { return new YkStackmaps(); } +/** + * @brief Creates a new YkStackmaps pass. + * + * @param stackmapIDStart The first stackmap ID available for use by this pass. + * Defaults to 1 if not specified. + * @return ModulePass* A pointer to the newly created YkStackmaps pass. + */ +ModulePass *llvm::createYkStackmapsPass(uint64_t stackmapIDStart = 1) { + return new YkStackmaps(stackmapIDStart); +} diff --git a/llvm/lib/YkIR/YkIRWriter.cpp b/llvm/lib/YkIR/YkIRWriter.cpp index 8d329d67e9c766..28bc8fccb3b89f 100644 --- a/llvm/lib/YkIR/YkIRWriter.cpp +++ b/llvm/lib/YkIR/YkIRWriter.cpp @@ -21,6 +21,7 @@ #include "llvm/MC/MCStreamer.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Transforms/Yk/ControlPoint.h" +#include "llvm/Transforms/Yk/ModuleClone.h" using namespace llvm; using namespace std; @@ -304,7 +305,10 @@ class YkIRWriter { vector Types; vector Constants; vector Globals; - + // Maps a function to its index. This is not the same as the index in + // the module's function list because we skip cloned functions in + // serialisation. + std::unordered_map FunctionIndexMap; // File paths. vector Paths; @@ -364,9 +368,13 @@ class YkIRWriter { } size_t functionIndex(llvm::Function *F) { - // FIXME: For now we assume that function indicies in LLVM IR and our IR - // are the same. - return getIndex(&M, F); + auto it = FunctionIndexMap.find(F); + if (it != FunctionIndexMap.end()) { + return it->second; + } + llvm::errs() << "Function not found in function index map: " << F->getName() + << "\n"; + llvm::report_fatal_error("Function not found in function index map"); } // Serialises a null-terminated string. @@ -1750,9 +1758,23 @@ class YkIRWriter { OutStreamer.emitInt8(IdxBitWidth); // num_funcs: - OutStreamer.emitSizeT(M.size()); + // Count functions for serilaisation and populate functions map + int functionCount = 0; + for (llvm::Function &F : M) { + // Skip cloned functions + if (!StringRef(F.getName()).startswith(YK_CLONE_PREFIX)) { + FunctionIndexMap[&F] = functionCount; + functionCount++; + } + } + // Emit the number of functions + OutStreamer.emitSizeT(functionCount); // funcs: for (llvm::Function &F : M) { + // Skip cloned functions + if (StringRef(F.getName()).startswith(YK_CLONE_PREFIX)) { + continue; + } serialiseFunc(F); } diff --git a/llvm/test/Transforms/Yk/ModuleClone.ll b/llvm/test/Transforms/Yk/ModuleClone.ll new file mode 100644 index 00000000000000..5ea725ff9414f1 --- /dev/null +++ b/llvm/test/Transforms/Yk/ModuleClone.ll @@ -0,0 +1,116 @@ +; RUN: llc -stop-after=yk-basicblock-tracer-pass --yk-module-clone --yk-basicblock-tracer < %s | FileCheck %s + +source_filename = "ModuleClone.c" +target triple = "x86_64-pc-linux-gnu" + +@.str = private unnamed_addr constant [13 x i8] c"Hello, world\00", align 1 +@my_global = global i32 42, align 4 + +declare i32 @printf(ptr, ...) +declare dso_local ptr @yk_mt_new(ptr noundef) +declare dso_local void @yk_mt_hot_threshold_set(ptr noundef, i32 noundef) +declare dso_local i64 @yk_location_new() +declare dso_local void @yk_mt_control_point(ptr noundef, ptr noundef) +declare dso_local i32 @fprintf(ptr noundef, ptr noundef, ...) +declare dso_local void @yk_location_drop(i64) +declare dso_local void @yk_mt_shutdown(ptr noundef) + +define dso_local i32 @func_inc_with_address_taken(i32 %x) { +entry: + %0 = add i32 %x, 1 + ret i32 %0 +} + +define dso_local i32 @my_func(i32 %x) { +entry: + %0 = add i32 %x, 1 + %func_ptr = alloca ptr, align 8 + store ptr @func_inc_with_address_taken, ptr %func_ptr, align 8 + %1 = load ptr, ptr %func_ptr, align 8 + %2 = call i32 %1(i32 42) + ret i32 %2 +} + +define dso_local i32 @main() { +entry: + %0 = call i32 @my_func(i32 10) + %1 = load i32, ptr @my_global + %2 = call i32 (ptr, ...) @printf(ptr @.str, i32 %1) + ret i32 0 +} + +; ====================================================================== +; Original functions - should have trace calls +; ====================================================================== +; File header checks +; CHECK: source_filename = "ModuleClone.c" +; CHECK: target triple = "x86_64-pc-linux-gnu" + +; Global variable and string checks +; CHECK: @.str = private unnamed_addr constant [13 x i8] c"Hello, world\00", align 1 +; CHECK: @my_global = global i32 42, align 4 + +; Declaration checks +; CHECK: declare i32 @printf(ptr, ...) +; CHECK: declare dso_local ptr @yk_mt_new(ptr noundef) +; CHECK: declare dso_local void @yk_mt_hot_threshold_set(ptr noundef, i32 noundef) +; CHECK: declare dso_local i64 @yk_location_new() +; CHECK: declare dso_local void @yk_mt_control_point(ptr noundef, ptr noundef) +; CHECK: declare dso_local i32 @fprintf(ptr noundef, ptr noundef, ...) +; CHECK: declare dso_local void @yk_location_drop(i64) +; CHECK: declare dso_local void @yk_mt_shutdown(ptr noundef) + +; Check original function: my_func +; CHECK-LABEL: define dso_local i32 @my_func(i32 %x) +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @__yk_trace_basicblock({{.*}}) +; CHECK-NEXT: %0 = add i32 %x, 1 +; CHECK-NEXT: %func_ptr = alloca ptr, align 8 +; CHECK-NEXT: store ptr @func_inc_with_address_taken, ptr %func_ptr, align 8 +; CHECK-NEXT: %1 = load ptr, ptr %func_ptr, align 8 +; CHECK-NEXT: %2 = call i32 %1(i32 42) +; CHECK-NEXT: ret i32 %2 + +; Check original function: main +; CHECK-LABEL: define dso_local i32 @main() +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @__yk_trace_basicblock({{.*}}) +; CHECK-NEXT: %0 = call i32 @my_func(i32 10) +; CHECK-NEXT: %1 = load i32, ptr @my_global +; CHECK-NEXT: %2 = call i32 (ptr, ...) @printf + +; Check that func_inc_with_address_taken is present in its original form +; CHECK-LABEL: define dso_local i32 @func_inc_with_address_taken(i32 %x) +; CHECK-NEXT: entry: +; CHECK-NEXT: call void @__yk_trace_basicblock({{.*}}) +; CHECK-NEXT: %0 = add i32 %x, 1 +; CHECK-NEXT: ret i32 %0 + +; ====================================================================== +; Functions whose addresses are taken should not be cloned +; ====================================================================== +; Functions with their addresses taken should not be cloned. +; `func_inc_with_address_taken` is used by pointer and thus remains unaltered. +; CHECK-NOT: define dso_local i32 @__yk_clone_func_inc_with_address_taken + +; ====================================================================== +; Cloned functions - should have no trace calls +; ====================================================================== +; Check cloned function: __yk_clone_my_func +; CHECK-LABEL: define dso_local i32 @__yk_clone_my_func(i32 %x) +; CHECK-NEXT: entry: +; CHECK-NOT: call void @__yk_trace_basicblock({{.*}}) +; CHECK-NEXT: %0 = add i32 %x, 1 +; CHECK-NEXT: %func_ptr = alloca ptr, align 8 +; CHECK-NEXT: store ptr @func_inc_with_address_taken, ptr %func_ptr, align 8 +; CHECK-NEXT: %1 = load ptr, ptr %func_ptr, align 8 +; CHECK-NEXT: %2 = call i32 %1(i32 42) +; CHECK-NEXT: ret i32 %2 + +; Check cloned function: __yk_clone_main +; CHECK-LABEL: define dso_local i32 @__yk_clone_main() +; CHECK-NEXT: entry: +; CHECK-NOT: call void @__yk_trace_basicblock({{.*}}) +; CHECK-NEXT: %0 = call i32 @__yk_clone_my_func(i32 10) +; CHECK-NEXT: %1 = load i32, ptr @my_global +; CHECK-NEXT: %2 = call i32 (ptr, ...) @printf