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