diff options
| -rw-r--r-- | llvm/lib/Target/AVR/AVR.h | 2 | ||||
| -rw-r--r-- | llvm/lib/Target/AVR/AVRInstrumentFunctions.cpp | 220 | ||||
| -rw-r--r-- | llvm/lib/Target/AVR/AVRTargetMachine.cpp | 1 | ||||
| -rw-r--r-- | llvm/lib/Target/AVR/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | llvm/test/CodeGen/AVR/instrumentation/basic.ll | 45 |
5 files changed, 269 insertions, 0 deletions
diff --git a/llvm/lib/Target/AVR/AVR.h b/llvm/lib/Target/AVR/AVR.h index 45119adb553..8e5cc5360ad 100644 --- a/llvm/lib/Target/AVR/AVR.h +++ b/llvm/lib/Target/AVR/AVR.h @@ -27,11 +27,13 @@ FunctionPass *createAVRISelDag(AVRTargetMachine &TM, CodeGenOpt::Level OptLevel); FunctionPass *createAVRExpandPseudoPass(); FunctionPass *createAVRFrameAnalyzerPass(); +FunctionPass *createAVRInstrumentFunctionsPass(); FunctionPass *createAVRRelaxMemPass(); FunctionPass *createAVRDynAllocaSRPass(); FunctionPass *createAVRBranchSelectionPass(); void initializeAVRExpandPseudoPass(PassRegistry&); +void initializeAVRInstrumentFunctionsPass(PassRegistry&); void initializeAVRRelaxMemPass(PassRegistry&); /// Contains the AVR backend. diff --git a/llvm/lib/Target/AVR/AVRInstrumentFunctions.cpp b/llvm/lib/Target/AVR/AVRInstrumentFunctions.cpp new file mode 100644 index 00000000000..ae2428e5e1e --- /dev/null +++ b/llvm/lib/Target/AVR/AVRInstrumentFunctions.cpp @@ -0,0 +1,220 @@ +//===-- AVRInstrumentFunctions.cpp - Insert instrumentation for testing ---===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This pass takes a function and inserts calls to hook functions which are +// told the name, arguments, and results of function calls. +// +// The hooks can do anything with the information given. It is possible to +// send the data through a serial connection in order to runs tests on +// bare metal. +// +//===----------------------------------------------------------------------===// + +#include "AVR.h" + +#include <llvm/IR/Function.h> +#include <llvm/IR/Module.h> + +using namespace llvm; + +#define AVR_INSTRUMENT_FUNCTIONS_NAME "AVR function instrumentation pass" + +namespace { + +// External symbols that we emit calls to. +namespace symbols { + +#define SYMBOL_PREFIX "avr_instrumentation" + + const StringRef PREFIX = SYMBOL_PREFIX; + + // void (i16 argCount); + const StringRef BEGIN_FUNCTION_SIGNATURE = SYMBOL_PREFIX "_begin_signature"; + // void(i16 argCount); + const StringRef END_FUNCTION_SIGNATURE = SYMBOL_PREFIX "_end_signature"; + +#undef SYMBOL_PREFIX +} + +class AVRInstrumentFunctions : public FunctionPass { +public: + static char ID; + + AVRInstrumentFunctions() : FunctionPass(ID) { + initializeAVRInstrumentFunctionsPass(*PassRegistry::getPassRegistry()); + } + + bool runOnFunction(Function &F) override; + + StringRef getPassName() const override { return AVR_INSTRUMENT_FUNCTIONS_NAME; } +}; + +char AVRInstrumentFunctions::ID = 0; + +/// Creates a pointer to a string. +static Value *CreateStringPtr(BasicBlock &BB, StringRef Str) { + LLVMContext &Ctx = BB.getContext(); + IntegerType *I8 = Type::getInt8Ty(Ctx); + + Constant *ConstantStr = ConstantDataArray::getString(Ctx, Str); + GlobalVariable *GlobalStr = new GlobalVariable(*BB.getParent()->getParent(), + ConstantStr->getType(), + true, /* is a constant */ + GlobalValue::PrivateLinkage, + ConstantStr); + return GetElementPtrInst::CreateInBounds(GlobalStr, + {ConstantInt::get(I8, 0), ConstantInt::get(I8, 0)}, "", &BB); +} + +/// Builds a call to one of the signature begin/end hooks. +static void BuildSignatureCall(StringRef SymName, BasicBlock &BB, Function &F) { + LLVMContext &Ctx = F.getContext(); + IntegerType *I16 = Type::getInt16Ty(Ctx); + + FunctionType *FnType = FunctionType::get(Type::getVoidTy(Ctx), + {Type::getInt8PtrTy(Ctx), I16}, false); + + Constant *Fn = F.getParent()->getOrInsertFunction(SymName, FnType); + Value *FunctionName = CreateStringPtr(BB, F.getName()); + + Value *Args[] = {FunctionName, + ConstantInt::get(I16, F.getArgumentList().size())}; + CallInst::Create(Fn, Args, "", &BB); +} + +/// Builds instructions to call into an external function to +/// notify about a function signature beginning. +static void BuildBeginSignature(BasicBlock &BB, Function &F) { + return BuildSignatureCall(symbols::BEGIN_FUNCTION_SIGNATURE, BB, F); +} + +/// Builds instructions to call into an external function to +/// notify about a function signature ending. +static void BuildEndSignature(BasicBlock &BB, Function &F) { + return BuildSignatureCall(symbols::END_FUNCTION_SIGNATURE, BB, F); +} + +/// Get the name of the external symbol that we need to call +/// to notify about this argument. +static std::string GetArgumentSymbolName(Argument &Arg) { + Type *Ty = Arg.getType(); + + if (auto *IntTy = dyn_cast<IntegerType>(Ty)) { + return (symbols::PREFIX + "_argument_i" + std::to_string(IntTy->getBitWidth())).str(); + } + + llvm_unreachable("unknown argument type"); +} + +/// Builds a call to one of the argument hooks. +static void BuildArgument(BasicBlock &BB, Argument &Arg) { + Function &F = *Arg.getParent(); + LLVMContext &Ctx = F.getContext(); + + FunctionType *FnType = FunctionType::get(Type::getVoidTy(Ctx), + {Type::getInt8PtrTy(Ctx), Arg.getType()}, false); + + Constant *Fn = F.getParent()->getOrInsertFunction( + GetArgumentSymbolName(Arg), FnType); + Value *ArgName = CreateStringPtr(BB, Arg.getName()); + + Value *Args[] = {ArgName, &Arg}; + CallInst::Create(Fn, Args, "", &BB); +} + +/// Builds a call to all of the function signature hooks. +static void BuildSignature(BasicBlock &BB, Function &F) { + BuildBeginSignature(BB, F); + for (Argument &Arg : F.args()) { BuildArgument(BB, Arg); } + BuildEndSignature(BB, F); +} + +/// Builds the instrumentation entry block. +static void BuildEntryBlock(Function &F) { + BasicBlock &EntryBlock = F.getEntryBlock(); + + // Create a new basic block at the start of the existing entry block. + BasicBlock *BB = BasicBlock::Create(F.getContext(), + "instrumentation_entry", + &F, &EntryBlock); + + BuildSignature(*BB, F); + + // Jump to the actual entry block. + BranchInst::Create(&EntryBlock, BB); +} + +static std::string GetReturnSymbolName(Value &Val) { + Type *Ty = Val.getType(); + + if (auto *IntTy = dyn_cast<IntegerType>(Ty)) { + return (symbols::PREFIX + "_result_u" + std::to_string(IntTy->getBitWidth())).str(); + } + + llvm_unreachable("unknown return type"); +} + +static void BuildExitHook(Instruction &I) { + Function &F = *I.getParent()->getParent(); + LLVMContext &Ctx = F.getContext(); + + if (auto *Ret = dyn_cast<ReturnInst>(&I)) { + Value *RetVal = Ret->getReturnValue(); + assert(RetVal && "should only be instrumenting functions with return values"); + + FunctionType *FnType = FunctionType::get(Type::getVoidTy(Ctx), + {RetVal->getType()}, false); + + Constant *Fn = F.getParent()->getOrInsertFunction( + GetReturnSymbolName(*RetVal), FnType); + + // Call the result hook just before the return. + CallInst::Create(Fn, {RetVal}, "", &I); + } +} + +/// Runs return hooks before all returns in a function. +static void BuildExitHooks(Function &F) { + for (BasicBlock &BB : F) { + auto BBI = BB.begin(), E = BB.end(); + while (BBI != E) { + auto NBBI = std::next(BBI); + + BuildExitHook(*BBI); + + // Modified |= expandMI(BB, MBBI); + BBI = NBBI; + } + } +} + +static bool ShouldInstrument(Function &F) { + // No point reporting results if there are none. + return !F.getReturnType()->isVoidTy(); +} + +bool AVRInstrumentFunctions::runOnFunction(Function &F) { + if (ShouldInstrument(F)) { + BuildEntryBlock(F); + BuildExitHooks(F); + } + + return true; +} + +} // end of anonymous namespace + +INITIALIZE_PASS(AVRInstrumentFunctions, "avr-instrument-functions", + AVR_INSTRUMENT_FUNCTIONS_NAME, false, false) + +namespace llvm { + +FunctionPass *createAVRInstrumentFunctionsPass() { return new AVRInstrumentFunctions(); } + +} // end of namespace llvm diff --git a/llvm/lib/Target/AVR/AVRTargetMachine.cpp b/llvm/lib/Target/AVR/AVRTargetMachine.cpp index 9d10ef96e11..fb3262916b4 100644 --- a/llvm/lib/Target/AVR/AVRTargetMachine.cpp +++ b/llvm/lib/Target/AVR/AVRTargetMachine.cpp @@ -80,6 +80,7 @@ extern "C" void LLVMInitializeAVRTarget() { auto &PR = *PassRegistry::getPassRegistry(); initializeAVRExpandPseudoPass(PR); + initializeAVRInstrumentFunctionsPass(PR); initializeAVRRelaxMemPass(PR); } diff --git a/llvm/lib/Target/AVR/CMakeLists.txt b/llvm/lib/Target/AVR/CMakeLists.txt index af89a2476c4..e103a60fa6f 100644 --- a/llvm/lib/Target/AVR/CMakeLists.txt +++ b/llvm/lib/Target/AVR/CMakeLists.txt @@ -21,6 +21,7 @@ add_llvm_target(AVRCodeGen AVRExpandPseudoInsts.cpp AVRFrameLowering.cpp AVRInstrInfo.cpp + AVRInstrumentFunctions.cpp AVRISelDAGToDAG.cpp AVRISelLowering.cpp AVRMCInstLower.cpp diff --git a/llvm/test/CodeGen/AVR/instrumentation/basic.ll b/llvm/test/CodeGen/AVR/instrumentation/basic.ll new file mode 100644 index 00000000000..232868a97f7 --- /dev/null +++ b/llvm/test/CodeGen/AVR/instrumentation/basic.ll @@ -0,0 +1,45 @@ +; RUN: opt -S -avr-instrument-functions < %s | FileCheck %s + +; Functions returning void should not be instrumented. +; CHECK-LABEL: do_nothing +define void @do_nothing(i8 %c) { + ; CHECK-NEXT: ret void + ret void +} + +; CHECK-LABEL: do_something +define i8 @do_something(i16 %a, i16 %b) { + ; CHECK: instrumentation_entry + ; CHECK-NEXT: %0 = getelementptr inbounds [13 x i8], [13 x i8]* @0, i8 0, i8 0 + ; CHECK-NEXT: call void @avr_instrumentation_begin_signature(i8* %0, i16 2) + + ; CHECK-NEXT: %1 = getelementptr inbounds [2 x i8], [2 x i8]* @1, i8 0, i8 0 + ; CHECK-NEXT: call void @avr_instrumentation_argument_i16(i8* %1, i16 %a) + + ; CHECK-NEXT: %2 = getelementptr inbounds [2 x i8], [2 x i8]* @2, i8 0, i8 0 + ; CHECK-NEXT: call void @avr_instrumentation_argument_i16(i8* %2, i16 %b) + + ; CHECK-NEXT: %3 = getelementptr inbounds [13 x i8], [13 x i8]* @3, i8 0, i8 0 + ; CHECK-NEXT: call void @avr_instrumentation_end_signature(i8* %3, i16 2) + + ; CHECK-NEXT: br label %4 + + ; CHECK: call void @avr_instrumentation_result_u8(i8 1) + ; CHECK-NEXT: ret i8 1 + ret i8 1 +} + +; CHECK-LABEL: foo +define i32 @foo() { + ; CHECK: instrumentation_entry: + ; CHECK-NEXT: %0 = getelementptr inbounds [4 x i8], [4 x i8]* @4, i8 0, i8 0 + ; CHECK-NEXT: call void @avr_instrumentation_begin_signature(i8* %0, i16 0) + ; CHECK-NEXT: %1 = getelementptr inbounds [4 x i8], [4 x i8]* @5, i8 0, i8 0 + ; CHECK-NEXT: call void @avr_instrumentation_end_signature(i8* %1, i16 0) + + ; CHECK-NEXT: br label %2 + + ; CHECK: call void @avr_instrumentation_result_u32(i32 50) + ; CHECK-NEXT: ret i32 50 + ret i32 50 +} |

