PageRenderTime 428ms CodeModel.GetById 70ms app.highlight 204ms RepoModel.GetById 148ms app.codeStats 0ms

/JIT/opcodes/control.cc

http://unladen-swallow.googlecode.com/
C++ | 382 lines | 283 code | 52 blank | 47 comment | 16 complexity | e6caf182f35ec63734add901c3b6c543 MD5 | raw file
  1#include "Python.h"
  2
  3#include "JIT/opcodes/control.h"
  4#include "JIT/llvm_fbuilder.h"
  5
  6#include "llvm/BasicBlock.h"
  7#include "llvm/Function.h"
  8#include "llvm/Instructions.h"
  9#include "llvm/Support/ManagedStatic.h"
 10#include "llvm/Support/raw_ostream.h"
 11
 12using llvm::BasicBlock;
 13using llvm::ConstantInt;
 14using llvm::Function;
 15using llvm::Type;
 16using llvm::Value;
 17using llvm::errs;
 18
 19#ifdef Py_WITH_INSTRUMENTATION
 20class CondBranchStats {
 21public:
 22    CondBranchStats()
 23        : total(0), optimized(0), not_enough_data(0), unpredictable(0) {
 24    }
 25
 26
 27    ~CondBranchStats() {
 28        errs() << "\nConditional branch optimization:\n";
 29        errs() << "Total cond branches: " << this->total << "\n";
 30        errs() << "Optimized branches: " << this->optimized << "\n";
 31        errs() << "Insufficient data: " << this->not_enough_data << "\n";
 32        errs() << "Unpredictable branches: " << this->unpredictable << "\n";
 33    }
 34
 35    // Total number of conditional branch opcodes compiled.
 36    unsigned total;
 37    // Number of predictable conditional branches we were able to optimize.
 38    unsigned optimized;
 39    // Number of single-direction branches we don't feel comfortable predicting.
 40    unsigned not_enough_data;
 41    // Number of unpredictable conditional branches (both directions
 42    // taken frequently; unable to be optimized).
 43    unsigned unpredictable;
 44};
 45
 46static llvm::ManagedStatic<CondBranchStats> cond_branch_stats;
 47
 48#define COND_BRANCH_INC_STATS(field) cond_branch_stats->field++
 49#else
 50#define COND_BRANCH_INC_STATS(field)
 51#endif  /* Py_WITH_INSTRUMENTATION */
 52
 53namespace py {
 54
 55OpcodeControl::OpcodeControl(LlvmFunctionBuilder *fbuilder) :
 56    fbuilder_(fbuilder),
 57    state_(fbuilder->state()),
 58    builder_(fbuilder->builder())
 59{
 60}
 61
 62void
 63OpcodeControl::DoRaise(Value *exc_type, Value *exc_inst, Value *exc_tb)
 64{
 65    // Accept code after a raise statement, even though it's never executed.
 66    // Otherwise, CPython's willingness to insert code after block
 67    // terminators causes problems.
 68    BasicBlock *dead_code = this->state_->CreateBasicBlock("dead_code");
 69
 70    // All raises set 'why' to UNWIND_EXCEPTION and the return value to NULL.
 71    // This is redundant with the propagate_exception_block_, but mem2reg will
 72    // remove the redundancy.
 73    this->builder_.CreateStore(
 74        ConstantInt::get(Type::getInt8Ty(this->fbuilder_->context()),
 75                         UNWIND_EXCEPTION),
 76        this->fbuilder_->unwind_reason_addr());
 77    this->builder_.CreateStore(this->state_->GetNull<PyObject*>(),
 78                               this->fbuilder_->retval_addr());
 79
 80#ifdef WITH_TSC
 81    this->state_->LogTscEvent(EXCEPT_RAISE_LLVM);
 82#endif
 83    Function *do_raise = this->state_->GetGlobalFunction<
 84        int(PyObject*, PyObject *, PyObject *)>("_PyEval_DoRaise");
 85    // _PyEval_DoRaise eats references.
 86    Value *is_reraise = this->state_->CreateCall(
 87        do_raise, exc_type, exc_inst, exc_tb, "raise_is_reraise");
 88    // If this is a "re-raise", we jump straight to the unwind block.
 89    // If it's a new raise, we call PyTraceBack_Here from the
 90    // propagate_exception_block_.
 91    this->builder_.CreateCondBr(
 92        this->builder_.CreateICmpEQ(
 93            is_reraise,
 94            ConstantInt::get(is_reraise->getType(), UNWIND_RERAISE)),
 95        this->fbuilder_->unwind_block(), this->fbuilder_->GetExceptionBlock());
 96
 97    this->builder_.SetInsertPoint(dead_code);
 98}
 99
100void
101OpcodeControl::RAISE_VARARGS_ZERO()
102{
103    Value *exc_tb = this->state_->GetNull<PyObject*>();
104    Value *exc_inst = this->state_->GetNull<PyObject*>();
105    Value *exc_type = this->state_->GetNull<PyObject*>();
106    this->DoRaise(exc_type, exc_inst, exc_tb);
107}
108
109void
110OpcodeControl::RAISE_VARARGS_ONE()
111{
112    Value *exc_tb = this->state_->GetNull<PyObject*>();
113    Value *exc_inst = this->state_->GetNull<PyObject*>();
114    Value *exc_type = this->fbuilder_->Pop();
115    this->DoRaise(exc_type, exc_inst, exc_tb);
116}
117
118void
119OpcodeControl::RAISE_VARARGS_TWO()
120{
121    Value *exc_tb = this->state_->GetNull<PyObject*>();
122    Value *exc_inst = this->fbuilder_->Pop();
123    Value *exc_type = this->fbuilder_->Pop();
124    this->DoRaise(exc_type, exc_inst, exc_tb);
125}
126
127void
128OpcodeControl::RAISE_VARARGS_THREE()
129{
130    Value *exc_tb = this->fbuilder_->Pop();
131    Value *exc_inst = this->fbuilder_->Pop();
132    Value *exc_type = this->fbuilder_->Pop();
133    this->DoRaise(exc_type, exc_inst, exc_tb);
134}
135
136void
137OpcodeControl::RETURN_VALUE()
138{
139    // Accept code after a return statement, even though it's never executed.
140    // Otherwise, CPython's willingness to insert code after block
141    // terminators causes problems.
142    BasicBlock *dead_code = this->state_->CreateBasicBlock("dead_code");
143
144    Value *retval = this->fbuilder_->Pop();
145    this->fbuilder_->Return(retval);
146
147    this->builder_.SetInsertPoint(dead_code);
148}
149
150void
151OpcodeControl::YIELD_VALUE()
152{
153    assert(this->fbuilder_->is_generator() && "yield in non-generator!");
154    BasicBlock *yield_resume =
155        this->state_->CreateBasicBlock("yield_resume");
156    // Save the current opcode index into f_lasti when we yield so
157    // that, if tracing gets turned on while we're outside this
158    // function we can jump back to the interpreter at the right
159    // place.
160    ConstantInt *yield_number =
161        ConstantInt::getSigned(
162            PyTypeBuilder<int>::get(this->fbuilder_->context()),
163            this->fbuilder_->GetLasti());
164    this->fbuilder_->AddYieldResumeBB(yield_number, yield_resume);
165
166    Value *retval = this->fbuilder_->Pop();
167
168    // Save everything to the frame object so it'll be there when we
169    // resume from the yield.
170    this->fbuilder_->CopyToFrameObject();
171
172    // Save the right block to jump back to when we resume this generator.
173    this->builder_.CreateStore(yield_number, this->fbuilder_->f_lasti_addr());
174
175    // Yields return from the current function without unwinding the
176    // stack.  They do trace the return and call _PyEval_ResetExcInfo
177    // like everything else, so we jump to the common return block
178    // instead of returning directly.
179    this->builder_.CreateStore(retval, this->fbuilder_->retval_addr());
180    this->builder_.CreateStore(
181        ConstantInt::get(Type::getInt8Ty(this->fbuilder_->context()),
182                         UNWIND_YIELD),
183        this->fbuilder_->unwind_reason_addr());
184    this->builder_.CreateBr(this->fbuilder_->do_return_block());
185
186    // Continue inserting code inside the resume block.
187    this->builder_.SetInsertPoint(yield_resume);
188    // Set frame->f_lasti back to negative so that exceptions are
189    // generated with llvm-provided line numbers.
190    this->builder_.CreateStore(
191        ConstantInt::getSigned(
192            PyTypeBuilder<int>::get(this->fbuilder_->context()), -2),
193        this->fbuilder_->f_lasti_addr());
194}
195
196void
197OpcodeControl::JUMP_ABSOLUTE(llvm::BasicBlock *target,
198                             llvm::BasicBlock *fallthrough)
199{
200    this->builder_.CreateBr(target);
201}
202
203enum BranchInput {
204    BranchInputFalse = -1,
205    BranchInputUnpredictable = 0,
206    BranchInputTrue = 1,
207};
208
209// If the branch was predictable, return the branch direction: return
210// BranchInputTrue if the branch was always True, return BranchInputFalse
211// if the branch was always False. If the branch was unpredictable or if we have
212// no data, return 0.
213static BranchInput
214predict_branch_input(const PyRuntimeFeedback *feedback)
215{
216    if (feedback == NULL) {
217        COND_BRANCH_INC_STATS(not_enough_data);
218        return BranchInputUnpredictable;
219    }
220
221    uintptr_t was_true = feedback->GetCounter(PY_FDO_JUMP_TRUE);
222    uintptr_t was_false = feedback->GetCounter(PY_FDO_JUMP_FALSE);
223
224    // We want to be relatively sure of our prediction. 200 was chosen by
225    // running the benchmarks and increasing this threshold until we stopped
226    // making massively-bad predictions. Example: increasing the threshold from
227    // 100 to 200 reduced bad predictions in 2to3 from 3900+ to 2. We currently
228    // optimize only perfectly-predictable branches as a baseline; later work
229    // should explore the tradeoffs between bail penalties and improved codegen
230    // gained from omiting rarely-taken branches.
231    if (was_true + was_false <= 200) {
232        COND_BRANCH_INC_STATS(not_enough_data);
233        return BranchInputUnpredictable;
234    }
235
236    BranchInput result = (BranchInput)(bool(was_true) - bool(was_false));
237    if (result == BranchInputUnpredictable) {
238        COND_BRANCH_INC_STATS(unpredictable);
239    }
240    return result;
241}
242
243void
244OpcodeControl::GetPyCondBranchBailBlock(unsigned true_idx,
245                                        BasicBlock **true_block,
246                                        unsigned false_idx,
247                                        BasicBlock **false_block,
248                                        unsigned *bail_idx,
249                                        BasicBlock **bail_block)
250{
251    COND_BRANCH_INC_STATS(total);
252    BranchInput branch_dir =
253        predict_branch_input(this->fbuilder_->GetFeedback());
254
255    if (branch_dir == BranchInputFalse) {
256        *bail_idx = false_idx;
257        *false_block = *bail_block = this->state_->CreateBasicBlock("FALSE_bail");
258    }
259    else if (branch_dir == BranchInputTrue) {
260        *bail_idx = true_idx;
261        *true_block = *bail_block = this->state_->CreateBasicBlock("TRUE_bail");
262    }
263    else {
264        *bail_idx = 0;
265        *bail_block = NULL;
266    }
267}
268
269void
270OpcodeControl::FillPyCondBranchBailBlock(BasicBlock *bail_to,
271                                         unsigned bail_idx)
272{
273    COND_BRANCH_INC_STATS(optimized);
274    BasicBlock *current = this->builder_.GetInsertBlock();
275
276    this->builder_.SetInsertPoint(bail_to);
277    this->fbuilder_->CreateGuardBailPoint(bail_idx, _PYGUARD_BRANCH);
278
279    this->builder_.SetInsertPoint(current);
280}
281
282void
283OpcodeControl::POP_JUMP_IF_FALSE(unsigned target_idx,
284                                 unsigned fallthrough_idx,
285                                 BasicBlock *target,
286                                 BasicBlock *fallthrough)
287{
288    unsigned bail_idx = 0;
289    BasicBlock *bail_to = NULL;
290    this->GetPyCondBranchBailBlock(/*on true: */ target_idx, &target,
291                                   /*on false: */ fallthrough_idx, &fallthrough,
292                                   &bail_idx, &bail_to);
293
294    Value *test_value = this->fbuilder_->Pop();
295    Value *is_true = this->fbuilder_->IsPythonTrue(test_value);
296    this->builder_.CreateCondBr(is_true, fallthrough, target);
297
298    if (bail_to)
299        this->FillPyCondBranchBailBlock(bail_to, bail_idx);
300}
301
302void
303OpcodeControl::POP_JUMP_IF_TRUE(unsigned target_idx,
304                                unsigned fallthrough_idx,
305                                BasicBlock *target,
306                                BasicBlock *fallthrough)
307{
308    unsigned bail_idx = 0;
309    BasicBlock *bail_to = NULL;
310    this->GetPyCondBranchBailBlock(/*on true: */ fallthrough_idx, &fallthrough,
311                                   /*on false: */ target_idx, &target,
312                                   &bail_idx, &bail_to);
313
314    Value *test_value = this->fbuilder_->Pop();
315    Value *is_true = this->fbuilder_->IsPythonTrue(test_value);
316    this->builder_.CreateCondBr(is_true, target, fallthrough);
317
318    if (bail_to)
319        this->FillPyCondBranchBailBlock(bail_to, bail_idx);
320}
321
322void
323OpcodeControl::JUMP_IF_FALSE_OR_POP(unsigned target_idx,
324                                    unsigned fallthrough_idx,
325                                    BasicBlock *target,
326                                    BasicBlock *fallthrough)
327{
328    unsigned bail_idx = 0;
329    BasicBlock *bail_to = NULL;
330    this->GetPyCondBranchBailBlock(/*on true: */ target_idx, &target,
331                                   /*on false: */ fallthrough_idx, &fallthrough,
332                                   &bail_idx, &bail_to);
333
334    BasicBlock *true_path =
335        this->state_->CreateBasicBlock("JUMP_IF_FALSE_OR_POP_pop");
336    Value *test_value = this->fbuilder_->Pop();
337    this->fbuilder_->Push(test_value);
338    // IsPythonTrue() will steal the reference to test_value, so make sure
339    // the stack owns one too.
340    this->state_->IncRef(test_value);
341    Value *is_true = this->fbuilder_->IsPythonTrue(test_value);
342    this->builder_.CreateCondBr(is_true, true_path, target);
343    this->builder_.SetInsertPoint(true_path);
344    test_value = this->fbuilder_->Pop();
345    this->state_->DecRef(test_value);
346    this->builder_.CreateBr(fallthrough);
347
348    if (bail_to)
349        this->FillPyCondBranchBailBlock(bail_to, bail_idx);
350}
351
352void
353OpcodeControl::JUMP_IF_TRUE_OR_POP(unsigned target_idx,
354                                   unsigned fallthrough_idx,
355                                   BasicBlock *target,
356                                   BasicBlock *fallthrough)
357{
358    unsigned bail_idx = 0;
359    BasicBlock *bail_to = NULL;
360    this->GetPyCondBranchBailBlock(/*on true: */ fallthrough_idx, &fallthrough,
361                                   /*on false: */ target_idx, &target,
362                                   &bail_idx, &bail_to);
363
364    BasicBlock *false_path =
365        this->state_->CreateBasicBlock("JUMP_IF_TRUE_OR_POP_pop");
366    Value *test_value = this->fbuilder_->Pop();
367    this->fbuilder_->Push(test_value);
368    // IsPythonTrue() will steal the reference to test_value, so make sure
369    // the stack owns one too.
370    this->state_->IncRef(test_value);
371    Value *is_true = this->fbuilder_->IsPythonTrue(test_value);
372    this->builder_.CreateCondBr(is_true, target, false_path);
373    this->builder_.SetInsertPoint(false_path);
374    test_value = this->fbuilder_->Pop();
375    this->state_->DecRef(test_value);
376    this->builder_.CreateBr(fallthrough);
377
378    if (bail_to)
379        this->FillPyCondBranchBailBlock(bail_to, bail_idx);
380}
381
382}