kernel.h 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. #pragma once
  2. #include <array>
  3. #include <vector>
  4. #include <string>
  5. #include <cstdlib>
  6. #include <cstring>
  7. #include <cassert>
  8. #include <typeindex>
  9. #include <stdexcept>
  10. #include <unordered_map>
  11. #include "pocketpy.h"
  12. #include "type_traits.h"
  13. namespace pkbind {
  14. class handle;
  15. /// hold the object temporarily
  16. template <int N>
  17. struct reg_t {
  18. py_Ref value;
  19. void operator= (py_Ref ref) & { py_setreg(N, ref); }
  20. operator py_Ref () & {
  21. assert(value && "register is not initialized");
  22. return value;
  23. }
  24. void operator= (handle value) &;
  25. operator handle () &;
  26. // pkpy provide user 8 registers.
  27. // 8th register is used for object pool, so N is limited to [0, 7).
  28. static_assert(N >= 0 && N <= 6, "N must be in [0, 7)");
  29. };
  30. struct retv_t {
  31. py_Ref value;
  32. void operator= (py_Ref ref) & { py_assign(value, ref); }
  33. operator py_Ref () & {
  34. assert(value && "return value is not initialized");
  35. return value;
  36. }
  37. void operator= (handle value) &;
  38. operator handle () &;
  39. };
  40. /// hold the object long time.
  41. struct object_pool {
  42. inline static int cache = -1;
  43. inline static py_Ref pool = nullptr;
  44. inline static std::vector<int>* indices_ = nullptr;
  45. struct object_ref {
  46. py_Ref data;
  47. int index;
  48. };
  49. static void initialize(int size) noexcept {
  50. // use 8th register.
  51. pool = py_getreg(7);
  52. py_newtuple(pool, size);
  53. indices_ = new std::vector<int>(size, 0);
  54. }
  55. static void finalize() noexcept {
  56. delete indices_;
  57. indices_ = nullptr;
  58. }
  59. /// alloc an object from pool, note that the object is uninitialized.
  60. static object_ref alloc() {
  61. auto& indices = *indices_;
  62. if(cache != -1) {
  63. auto index = cache;
  64. cache = -1;
  65. indices[index] = 1;
  66. return {py_tuple_getitem(pool, index), index};
  67. }
  68. for(int i = 0; i < indices.size(); ++i) {
  69. if(indices[i] == 0) {
  70. indices[i] = 1;
  71. return {py_tuple_getitem(pool, i), i};
  72. }
  73. }
  74. throw std::runtime_error("object pool is full");
  75. }
  76. /// alloc an object from pool, the object is initialized with ref.
  77. static object_ref realloc(py_Ref ref) {
  78. auto result = alloc();
  79. py_assign(result.data, ref);
  80. return result;
  81. }
  82. static void inc_ref(object_ref ref) {
  83. if(!indices_) { return; }
  84. if(ref.data == py_tuple_getitem(pool, ref.index)) {
  85. auto& indices = *indices_;
  86. indices[ref.index] += 1;
  87. } else {
  88. throw std::runtime_error("object_ref is invalid");
  89. }
  90. }
  91. static void dec_ref(object_ref ref) {
  92. if(!indices_) { return; }
  93. if(ref.data == py_tuple_getitem(pool, ref.index)) {
  94. auto& indices = *indices_;
  95. indices[ref.index] -= 1;
  96. assert(indices[ref.index] >= 0 && "ref count is negative");
  97. if(indices[ref.index] == 0) { cache = ref.index; }
  98. } else {
  99. throw std::runtime_error("object_ref is invalid");
  100. }
  101. }
  102. };
  103. struct action {
  104. using function = void (*)();
  105. inline static std::vector<function> starts;
  106. static void initialize() noexcept {
  107. for(auto func: starts) {
  108. func();
  109. }
  110. }
  111. // register a function to be called at the start of the vm.
  112. static void register_start(function func) { starts.push_back(func); }
  113. };
  114. template <int N>
  115. inline reg_t<N> reg;
  116. inline retv_t retv;
  117. inline std::unordered_map<std::type_index, py_Type>* m_type_map = nullptr;
  118. } // namespace pkbind