//===-- 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[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) * 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 *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; }