mirror of
https://github.com/RPCS3/llvm.git
synced 2025-04-02 13:21:43 +00:00
[AVR] Add a function instrumentation pass
This will be used for an on-chip test suite. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@289641 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
b1003083d7
commit
732b1bee27
@ -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.
|
||||
|
220
lib/Target/AVR/AVRInstrumentFunctions.cpp
Normal file
220
lib/Target/AVR/AVRInstrumentFunctions.cpp
Normal file
@ -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
|
@ -80,6 +80,7 @@ extern "C" void LLVMInitializeAVRTarget() {
|
||||
|
||||
auto &PR = *PassRegistry::getPassRegistry();
|
||||
initializeAVRExpandPseudoPass(PR);
|
||||
initializeAVRInstrumentFunctionsPass(PR);
|
||||
initializeAVRRelaxMemPass(PR);
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ add_llvm_target(AVRCodeGen
|
||||
AVRExpandPseudoInsts.cpp
|
||||
AVRFrameLowering.cpp
|
||||
AVRInstrInfo.cpp
|
||||
AVRInstrumentFunctions.cpp
|
||||
AVRISelDAGToDAG.cpp
|
||||
AVRISelLowering.cpp
|
||||
AVRMCInstLower.cpp
|
||||
|
45
test/CodeGen/AVR/instrumentation/basic.ll
Normal file
45
test/CodeGen/AVR/instrumentation/basic.ll
Normal file
@ -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
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user