Skip to content

Commit 3abd1d3

Browse files
author
Dylan McKay
committedDec 14, 2016
[AVR] Add a function instrumentation pass
This will be used for an on-chip test suite. llvm-svn: 289641
1 parent 5dee8c4 commit 3abd1d3

File tree

5 files changed

+269
-0
lines changed

5 files changed

+269
-0
lines changed
 

‎llvm/lib/Target/AVR/AVR.h

+2
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,13 @@ FunctionPass *createAVRISelDag(AVRTargetMachine &TM,
2727
CodeGenOpt::Level OptLevel);
2828
FunctionPass *createAVRExpandPseudoPass();
2929
FunctionPass *createAVRFrameAnalyzerPass();
30+
FunctionPass *createAVRInstrumentFunctionsPass();
3031
FunctionPass *createAVRRelaxMemPass();
3132
FunctionPass *createAVRDynAllocaSRPass();
3233
FunctionPass *createAVRBranchSelectionPass();
3334

3435
void initializeAVRExpandPseudoPass(PassRegistry&);
36+
void initializeAVRInstrumentFunctionsPass(PassRegistry&);
3537
void initializeAVRRelaxMemPass(PassRegistry&);
3638

3739
/// Contains the AVR backend.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
//===-- AVRInstrumentFunctions.cpp - Insert instrumentation for testing ---===//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// This pass takes a function and inserts calls to hook functions which are
11+
// told the name, arguments, and results of function calls.
12+
//
13+
// The hooks can do anything with the information given. It is possible to
14+
// send the data through a serial connection in order to runs tests on
15+
// bare metal.
16+
//
17+
//===----------------------------------------------------------------------===//
18+
19+
#include "AVR.h"
20+
21+
#include <llvm/IR/Function.h>
22+
#include <llvm/IR/Module.h>
23+
24+
using namespace llvm;
25+
26+
#define AVR_INSTRUMENT_FUNCTIONS_NAME "AVR function instrumentation pass"
27+
28+
namespace {
29+
30+
// External symbols that we emit calls to.
31+
namespace symbols {
32+
33+
#define SYMBOL_PREFIX "avr_instrumentation"
34+
35+
const StringRef PREFIX = SYMBOL_PREFIX;
36+
37+
// void (i16 argCount);
38+
const StringRef BEGIN_FUNCTION_SIGNATURE = SYMBOL_PREFIX "_begin_signature";
39+
// void(i16 argCount);
40+
const StringRef END_FUNCTION_SIGNATURE = SYMBOL_PREFIX "_end_signature";
41+
42+
#undef SYMBOL_PREFIX
43+
}
44+
45+
class AVRInstrumentFunctions : public FunctionPass {
46+
public:
47+
static char ID;
48+
49+
AVRInstrumentFunctions() : FunctionPass(ID) {
50+
initializeAVRInstrumentFunctionsPass(*PassRegistry::getPassRegistry());
51+
}
52+
53+
bool runOnFunction(Function &F) override;
54+
55+
StringRef getPassName() const override { return AVR_INSTRUMENT_FUNCTIONS_NAME; }
56+
};
57+
58+
char AVRInstrumentFunctions::ID = 0;
59+
60+
/// Creates a pointer to a string.
61+
static Value *CreateStringPtr(BasicBlock &BB, StringRef Str) {
62+
LLVMContext &Ctx = BB.getContext();
63+
IntegerType *I8 = Type::getInt8Ty(Ctx);
64+
65+
Constant *ConstantStr = ConstantDataArray::getString(Ctx, Str);
66+
GlobalVariable *GlobalStr = new GlobalVariable(*BB.getParent()->getParent(),
67+
ConstantStr->getType(),
68+
true, /* is a constant */
69+
GlobalValue::PrivateLinkage,
70+
ConstantStr);
71+
return GetElementPtrInst::CreateInBounds(GlobalStr,
72+
{ConstantInt::get(I8, 0), ConstantInt::get(I8, 0)}, "", &BB);
73+
}
74+
75+
/// Builds a call to one of the signature begin/end hooks.
76+
static void BuildSignatureCall(StringRef SymName, BasicBlock &BB, Function &F) {
77+
LLVMContext &Ctx = F.getContext();
78+
IntegerType *I16 = Type::getInt16Ty(Ctx);
79+
80+
FunctionType *FnType = FunctionType::get(Type::getVoidTy(Ctx),
81+
{Type::getInt8PtrTy(Ctx), I16}, false);
82+
83+
Constant *Fn = F.getParent()->getOrInsertFunction(SymName, FnType);
84+
Value *FunctionName = CreateStringPtr(BB, F.getName());
85+
86+
Value *Args[] = {FunctionName,
87+
ConstantInt::get(I16, F.getArgumentList().size())};
88+
CallInst::Create(Fn, Args, "", &BB);
89+
}
90+
91+
/// Builds instructions to call into an external function to
92+
/// notify about a function signature beginning.
93+
static void BuildBeginSignature(BasicBlock &BB, Function &F) {
94+
return BuildSignatureCall(symbols::BEGIN_FUNCTION_SIGNATURE, BB, F);
95+
}
96+
97+
/// Builds instructions to call into an external function to
98+
/// notify about a function signature ending.
99+
static void BuildEndSignature(BasicBlock &BB, Function &F) {
100+
return BuildSignatureCall(symbols::END_FUNCTION_SIGNATURE, BB, F);
101+
}
102+
103+
/// Get the name of the external symbol that we need to call
104+
/// to notify about this argument.
105+
static std::string GetArgumentSymbolName(Argument &Arg) {
106+
Type *Ty = Arg.getType();
107+
108+
if (auto *IntTy = dyn_cast<IntegerType>(Ty)) {
109+
return (symbols::PREFIX + "_argument_i" + std::to_string(IntTy->getBitWidth())).str();
110+
}
111+
112+
llvm_unreachable("unknown argument type");
113+
}
114+
115+
/// Builds a call to one of the argument hooks.
116+
static void BuildArgument(BasicBlock &BB, Argument &Arg) {
117+
Function &F = *Arg.getParent();
118+
LLVMContext &Ctx = F.getContext();
119+
120+
FunctionType *FnType = FunctionType::get(Type::getVoidTy(Ctx),
121+
{Type::getInt8PtrTy(Ctx), Arg.getType()}, false);
122+
123+
Constant *Fn = F.getParent()->getOrInsertFunction(
124+
GetArgumentSymbolName(Arg), FnType);
125+
Value *ArgName = CreateStringPtr(BB, Arg.getName());
126+
127+
Value *Args[] = {ArgName, &Arg};
128+
CallInst::Create(Fn, Args, "", &BB);
129+
}
130+
131+
/// Builds a call to all of the function signature hooks.
132+
static void BuildSignature(BasicBlock &BB, Function &F) {
133+
BuildBeginSignature(BB, F);
134+
for (Argument &Arg : F.args()) { BuildArgument(BB, Arg); }
135+
BuildEndSignature(BB, F);
136+
}
137+
138+
/// Builds the instrumentation entry block.
139+
static void BuildEntryBlock(Function &F) {
140+
BasicBlock &EntryBlock = F.getEntryBlock();
141+
142+
// Create a new basic block at the start of the existing entry block.
143+
BasicBlock *BB = BasicBlock::Create(F.getContext(),
144+
"instrumentation_entry",
145+
&F, &EntryBlock);
146+
147+
BuildSignature(*BB, F);
148+
149+
// Jump to the actual entry block.
150+
BranchInst::Create(&EntryBlock, BB);
151+
}
152+
153+
static std::string GetReturnSymbolName(Value &Val) {
154+
Type *Ty = Val.getType();
155+
156+
if (auto *IntTy = dyn_cast<IntegerType>(Ty)) {
157+
return (symbols::PREFIX + "_result_u" + std::to_string(IntTy->getBitWidth())).str();
158+
}
159+
160+
llvm_unreachable("unknown return type");
161+
}
162+
163+
static void BuildExitHook(Instruction &I) {
164+
Function &F = *I.getParent()->getParent();
165+
LLVMContext &Ctx = F.getContext();
166+
167+
if (auto *Ret = dyn_cast<ReturnInst>(&I)) {
168+
Value *RetVal = Ret->getReturnValue();
169+
assert(RetVal && "should only be instrumenting functions with return values");
170+
171+
FunctionType *FnType = FunctionType::get(Type::getVoidTy(Ctx),
172+
{RetVal->getType()}, false);
173+
174+
Constant *Fn = F.getParent()->getOrInsertFunction(
175+
GetReturnSymbolName(*RetVal), FnType);
176+
177+
// Call the result hook just before the return.
178+
CallInst::Create(Fn, {RetVal}, "", &I);
179+
}
180+
}
181+
182+
/// Runs return hooks before all returns in a function.
183+
static void BuildExitHooks(Function &F) {
184+
for (BasicBlock &BB : F) {
185+
auto BBI = BB.begin(), E = BB.end();
186+
while (BBI != E) {
187+
auto NBBI = std::next(BBI);
188+
189+
BuildExitHook(*BBI);
190+
191+
// Modified |= expandMI(BB, MBBI);
192+
BBI = NBBI;
193+
}
194+
}
195+
}
196+
197+
static bool ShouldInstrument(Function &F) {
198+
// No point reporting results if there are none.
199+
return !F.getReturnType()->isVoidTy();
200+
}
201+
202+
bool AVRInstrumentFunctions::runOnFunction(Function &F) {
203+
if (ShouldInstrument(F)) {
204+
BuildEntryBlock(F);
205+
BuildExitHooks(F);
206+
}
207+
208+
return true;
209+
}
210+
211+
} // end of anonymous namespace
212+
213+
INITIALIZE_PASS(AVRInstrumentFunctions, "avr-instrument-functions",
214+
AVR_INSTRUMENT_FUNCTIONS_NAME, false, false)
215+
216+
namespace llvm {
217+
218+
FunctionPass *createAVRInstrumentFunctionsPass() { return new AVRInstrumentFunctions(); }
219+
220+
} // end of namespace llvm

‎llvm/lib/Target/AVR/AVRTargetMachine.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ extern "C" void LLVMInitializeAVRTarget() {
8080

8181
auto &PR = *PassRegistry::getPassRegistry();
8282
initializeAVRExpandPseudoPass(PR);
83+
initializeAVRInstrumentFunctionsPass(PR);
8384
initializeAVRRelaxMemPass(PR);
8485
}
8586

‎llvm/lib/Target/AVR/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ add_llvm_target(AVRCodeGen
2121
AVRExpandPseudoInsts.cpp
2222
AVRFrameLowering.cpp
2323
AVRInstrInfo.cpp
24+
AVRInstrumentFunctions.cpp
2425
AVRISelDAGToDAG.cpp
2526
AVRISelLowering.cpp
2627
AVRMCInstLower.cpp
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
; RUN: opt -S -avr-instrument-functions < %s | FileCheck %s
2+
3+
; Functions returning void should not be instrumented.
4+
; CHECK-LABEL: do_nothing
5+
define void @do_nothing(i8 %c) {
6+
; CHECK-NEXT: ret void
7+
ret void
8+
}
9+
10+
; CHECK-LABEL: do_something
11+
define i8 @do_something(i16 %a, i16 %b) {
12+
; CHECK: instrumentation_entry
13+
; CHECK-NEXT: %0 = getelementptr inbounds [13 x i8], [13 x i8]* @0, i8 0, i8 0
14+
; CHECK-NEXT: call void @avr_instrumentation_begin_signature(i8* %0, i16 2)
15+
16+
; CHECK-NEXT: %1 = getelementptr inbounds [2 x i8], [2 x i8]* @1, i8 0, i8 0
17+
; CHECK-NEXT: call void @avr_instrumentation_argument_i16(i8* %1, i16 %a)
18+
19+
; CHECK-NEXT: %2 = getelementptr inbounds [2 x i8], [2 x i8]* @2, i8 0, i8 0
20+
; CHECK-NEXT: call void @avr_instrumentation_argument_i16(i8* %2, i16 %b)
21+
22+
; CHECK-NEXT: %3 = getelementptr inbounds [13 x i8], [13 x i8]* @3, i8 0, i8 0
23+
; CHECK-NEXT: call void @avr_instrumentation_end_signature(i8* %3, i16 2)
24+
25+
; CHECK-NEXT: br label %4
26+
27+
; CHECK: call void @avr_instrumentation_result_u8(i8 1)
28+
; CHECK-NEXT: ret i8 1
29+
ret i8 1
30+
}
31+
32+
; CHECK-LABEL: foo
33+
define i32 @foo() {
34+
; CHECK: instrumentation_entry:
35+
; CHECK-NEXT: %0 = getelementptr inbounds [4 x i8], [4 x i8]* @4, i8 0, i8 0
36+
; CHECK-NEXT: call void @avr_instrumentation_begin_signature(i8* %0, i16 0)
37+
; CHECK-NEXT: %1 = getelementptr inbounds [4 x i8], [4 x i8]* @5, i8 0, i8 0
38+
; CHECK-NEXT: call void @avr_instrumentation_end_signature(i8* %1, i16 0)
39+
40+
; CHECK-NEXT: br label %2
41+
42+
; CHECK: call void @avr_instrumentation_result_u32(i32 50)
43+
; CHECK-NEXT: ret i32 50
44+
ret i32 50
45+
}

0 commit comments

Comments
 (0)
Please sign in to comment.