-
Notifications
You must be signed in to change notification settings - Fork 12.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[AVR] Add a function instrumentation pass
This will be used for an on-chip test suite. llvm-svn: 289641
- Loading branch information
Dylan McKay
committed
Dec 14, 2016
1 parent
5dee8c4
commit 3abd1d3
Showing
5 changed files
with
269 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 | ||
| } |