aboutsummaryrefslogtreecommitdiff
path: root/lib/Target/AVR/AVRInstrumentFunctions.cpp
blob: 5553dc2da31b50964cecf502fe8ae6551a25d3fb (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
//===-- 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);
}

static std::string GetTypeName(Type &Ty) {
  if (auto *IntTy = dyn_cast<IntegerType>(&Ty)) {
    return std::string("i") + std::to_string(IntTy->getBitWidth());
  }

  if (Ty.isFloatingPointTy()) {
    return std::string("f") + std::to_string(Ty.getPrimitiveSizeInBits());
  }

  llvm_unreachable("unknown return type");
}

/// 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) {
  return (symbols::PREFIX + "_argument_" + GetTypeName(*Arg.getType())).str();
}

/// Builds a call to one of the argument hooks.
static void BuildArgument(BasicBlock &BB, Argument &Arg) {
  Function &F = *Arg.getParent();
  LLVMContext &Ctx = F.getContext();

  Type *I8 = Type::getInt8Ty(Ctx);

  FunctionType *FnType = FunctionType::get(Type::getVoidTy(Ctx),
    {Type::getInt8PtrTy(Ctx), I8, Arg.getType()}, false);

  Constant *Fn = F.getParent()->getOrInsertFunction(
    GetArgumentSymbolName(Arg), FnType);
  Value *ArgName = CreateStringPtr(BB, Arg.getName());

  Value *Args[] = {ArgName, ConstantInt::get(I8, Arg.getArgNo()), &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) {
  return (symbols::PREFIX + "_result_" + GetTypeName(*Val.getType())).str();
}

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