virtualStack.h 4.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. // zlib open source license
  2. //
  3. // Copyright (c) 2024 David Forsgren Piuva
  4. //
  5. // This software is provided 'as-is', without any express or implied
  6. // warranty. In no event will the authors be held liable for any damages
  7. // arising from the use of this software.
  8. //
  9. // Permission is granted to anyone to use this software for any purpose,
  10. // including commercial applications, and to alter it and redistribute it
  11. // freely, subject to the following restrictions:
  12. //
  13. // 1. The origin of this software must not be misrepresented; you must not
  14. // claim that you wrote the original software. If you use this software
  15. // in a product, an acknowledgment in the product documentation would be
  16. // appreciated but is not required.
  17. //
  18. // 2. Altered source versions must be plainly marked as such, and must not be
  19. // misrepresented as being the original software.
  20. //
  21. // 3. This notice may not be removed or altered from any source
  22. // distribution.
  23. #ifndef DFPSR_VIRTUAL_STACK
  24. #define DFPSR_VIRTUAL_STACK
  25. #include "SafePointer.h"
  26. namespace dsr {
  27. template <typename T>
  28. constexpr uint64_t memory_getPaddedSize() {
  29. uint64_t size = (uint64_t)sizeof(T);
  30. uint64_t alignment = (uint64_t)alignof(T);
  31. // Round up with unsigned integers.
  32. return size + (alignment - 1) - ((size - 1) % alignment);
  33. }
  34. // Pre-condition:
  35. // alignment is a power of two (1, 2, 4, 8, 16, 32, 64...)
  36. // Post-condition:
  37. // Returns a bit mask for rounding an integer down to the closest multiple of alignment.
  38. constexpr uintptr_t memory_createAlignmentAndMask(uintptr_t alignment) {
  39. // alignment = ...00001000...
  40. // Subtracting one from a power of two gives a mask with ones for the remainder bits.
  41. // remainder = ...00000111...
  42. // Then we simply negate the mask to get the alignment mask for rounding down.
  43. // mask = ...11111000...
  44. return ~(alignment - 1);
  45. }
  46. struct UnsafeAllocation {
  47. uint8_t *data;
  48. AllocationHeader *header;
  49. UnsafeAllocation(uint8_t *data, AllocationHeader *header)
  50. : data(data), header(header) {}
  51. };
  52. // Allocate memory in the virtual stack owned by the current thread.
  53. // paddedSize is the number of bytes to allocate including all elements and internal padding.
  54. // alignmentMask should only contain zeroes at the bits to round away for alignment.
  55. UnsafeAllocation virtualStack_push(uint64_t paddedSize, uintptr_t alignmentAndMask);
  56. // A simpler way to get the correct alignment is to allocate a number of elements with a specific type.
  57. // TODO: Create another function for manual alignment exceeding the type's alignment using another template argument.
  58. // TODO: Let the address offset be negated and start with the allocation size going down to zero,
  59. // so that rounding up addresses can be done by simply masking the least significant bits.
  60. template <typename T>
  61. SafePointer<T> virtualStack_push(uint64_t elementCount, const char *name) {
  62. // Calculate element size and multiply by element count to get the total size.
  63. uint64_t paddedSize = memory_getPaddedSize<T>() * elementCount;
  64. // Allocate the data with the amount of alignment requested by the element type T.
  65. UnsafeAllocation result = virtualStack_push(paddedSize, memory_createAlignmentAndMask((uintptr_t)alignof(T)));
  66. // Return a safe pointer to the allocated data.
  67. return SafePointer<T>(name, (T*)(result.data), (intptr_t)paddedSize, result.header);
  68. }
  69. // Free the last allocation from the virtual stack.
  70. // Must be called from the same thread that pushed, because virtual stacks are local to their threads.
  71. void virtualStack_pop();
  72. // Allocate this array on the stack to automatically free the memory when the scope ends.
  73. // Replaces VLA or alloca.
  74. template <typename T>
  75. class VirtualStackAllocation : public SafePointer<T> {
  76. public:
  77. VirtualStackAllocation(uint64_t elementCount)
  78. : SafePointer<T>(virtualStack_push<T>(elementCount, "virtual stack allocation")) {}
  79. ~VirtualStackAllocation() {
  80. virtualStack_pop();
  81. }
  82. };
  83. }
  84. #endif