summaryrefslogtreecommitdiffstats
path: root/llvm/examples/TracingBrainF/BrainFTraceRecorder.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'llvm/examples/TracingBrainF/BrainFTraceRecorder.cpp')
-rw-r--r--llvm/examples/TracingBrainF/BrainFTraceRecorder.cpp155
1 files changed, 155 insertions, 0 deletions
diff --git a/llvm/examples/TracingBrainF/BrainFTraceRecorder.cpp b/llvm/examples/TracingBrainF/BrainFTraceRecorder.cpp
new file mode 100644
index 00000000000..cd8babd1991
--- /dev/null
+++ b/llvm/examples/TracingBrainF/BrainFTraceRecorder.cpp
@@ -0,0 +1,155 @@
+//===-- BrainFTraceRecorder.cpp - BrainF trace recorder ------------------==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===--------------------------------------------------------------------===//
+//
+// This class observes the execution trace of the interpreter, identifying
+// hot traces and eventually compiling them to native code.
+//
+// The operation of the recorder can be divided into four parts:
+// 1) Interation Counting - To identify hot traces, we track the execution
+// counts of all loop headers ('[' instructions). We use a fixed-size
+// array of counters for this, since lack of precision does not affect
+// correctness.
+//
+// 2) Trace Buffering - Once a header has passed a hotness threshold, we
+// begin buffering the execution trace beginning from that header the
+// next time it is executed. This buffer is of a fixed length, though
+// that choice can be tuned for performance. If the end of the buffer
+// is reached without execution returning to the header, we throw out
+// the trace.
+//
+// 3) Trace Commit - If the buffered trace returns to the header before
+// the buffer limit is reached, that trace is commited to form a trace
+// tree. This tree aggregates all execution traces that have been
+// observed originating from the header since it passed the hotness
+// threshold. The buffer is then cleared to allow a new trace to be
+// recorded.
+//
+// 4) Trace Compilation - Once a secondary hotness threshold is reached,
+// trace recording is terminated and the set of observed traces encoded
+// in the trace tree are compiled to native code, and a function pointer
+// to that trace is installed into the bytecode array in place of one of
+// the normal opcode functions. Details of this compilation are in
+// BrainFCodeGen.cpp
+//===--------------------------------------------------------------------===//
+
+#include "BrainF.h"
+#include "BrainFVM.h"
+#include "llvm/Support/raw_ostream.h"
+
+#define ITERATION_BUF_SIZE 1024
+#define TRACE_BUF_SIZE 256
+#define TRACE_THRESHOLD 50
+#define COMPILE_THRESHOLD 200
+
+void BrainFTraceRecorder::BrainFTraceNode::dump(unsigned lvl) {
+ for (unsigned i = 0; i < lvl; ++i)
+ outs() << '.';
+ outs() << opcode << " : " << pc << "\n";
+ if (left && left != (BrainFTraceNode*)~0ULL) left->dump(lvl+1);
+ if (right && right != (BrainFTraceNode*)~0ULL) right->dump(lvl+1);
+}
+
+BrainFTraceRecorder::BrainFTraceRecorder()
+ : prev_opcode('+'), iteration_count(new uint8_t[ITERATION_BUF_SIZE]),
+ trace_begin(new std::pair<uint8_t, size_t>[TRACE_BUF_SIZE]),
+ trace_end(trace_begin + TRACE_BUF_SIZE),
+ trace_tail(trace_begin),
+ module(new Module("BrainF", getGlobalContext())) {
+ memset(iteration_count, 0, ITERATION_BUF_SIZE);
+ memset(trace_begin, 0, sizeof(std::pair<uint8_t, size_t>) * TRACE_BUF_SIZE);
+
+ initialize_module();
+}
+
+BrainFTraceRecorder::~BrainFTraceRecorder() {
+ delete[] iteration_count;
+ delete[] trace_begin;
+ delete FPM;
+ delete EE;
+}
+
+void BrainFTraceRecorder::commit() {
+ BrainFTraceNode *&Head = trace_map[trace_begin->second];
+ if (!Head)
+ Head = new BrainFTraceNode(trace_begin->first, trace_begin->second);
+
+ BrainFTraceNode *Parent = Head;
+ std::pair<uint8_t, size_t> *trace_iter = trace_begin+1;
+ while (trace_iter != trace_tail) {
+ BrainFTraceNode *Child = 0;
+
+ if (trace_iter->second == Parent->pc+1) {
+ if (Parent->left) Child = Parent->left;
+ else Child = Parent->left =
+ new BrainFTraceNode(trace_iter->first, trace_iter->second);
+ } else {
+ if (Parent->right) Child = Parent->right;
+ else Child = Parent->right =
+ new BrainFTraceNode(trace_iter->first, trace_iter->second);
+ }
+
+ Parent = Child;
+ ++trace_iter;
+ }
+
+ if (Parent->pc+1 == Head->pc)
+ Parent->left = (BrainFTraceNode*)~0ULL;
+ else
+ Parent->right = (BrainFTraceNode*)~0ULL;
+}
+
+void BrainFTraceRecorder::record_simple(size_t pc, uint8_t opcode) {
+ if (executed) {
+ executed = false;
+ trace_tail = trace_begin;
+ }
+
+ if (trace_tail != trace_begin) {
+ if (trace_tail == trace_end) {
+ trace_tail = trace_begin;
+ } else {
+ trace_tail->first = opcode;
+ trace_tail->second = pc;
+ ++trace_tail;
+ }
+ }
+ prev_opcode = opcode;
+}
+
+void BrainFTraceRecorder::record(size_t pc, uint8_t opcode) {
+ if (executed) {
+ executed = false;
+ trace_tail = trace_begin;
+ }
+
+ if (trace_tail != trace_begin) {
+ if (pc == trace_begin->second) {
+ commit();
+ trace_tail = trace_begin;
+ } else if (trace_tail == trace_end) {
+ trace_tail = trace_begin;
+ } else {
+ trace_tail->first = opcode;
+ trace_tail->second = pc;
+ ++trace_tail;
+ }
+ } else if (opcode == '[' && prev_opcode == ']'){
+ size_t hash = pc % ITERATION_BUF_SIZE;
+ if (iteration_count[hash] == 255) iteration_count[hash] = 254;
+ if (++iteration_count[hash] > COMPILE_THRESHOLD && trace_map.count(pc)) {
+ compile(trace_map[pc]);
+ } else if (++iteration_count[hash] > TRACE_THRESHOLD) {
+ trace_tail->first = opcode;
+ trace_tail->second = pc;
+ ++trace_tail;
+ }
+ }
+
+ prev_opcode = opcode;
+} \ No newline at end of file
OpenPOWER on IntegriCloud