virtualStack.cpp 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172
  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. #include "virtualStack.h"
  24. namespace dsr {
  25. // How many bytes that are allocated directly in thread local memory.
  26. static const size_t DSR_VIRTUAL_STACK_SIZE = 131072;
  27. // TODO: Allow expanding using recycled heap memory when running out of stack space.
  28. // TODO: Align the first allocation in address space from unaligned memory.
  29. // The easiest way would be to allocate memory in reverse order from the end.
  30. // * Subtract the amount of allocated memory from the previous uint8_t pointer.
  31. // * Use the pre-defined alignment mask that has zeroes for the rounded bits in the address.
  32. // * Place the allocation in the aligned location.
  33. // * Store an allocation size integer in front of the allocation to allow freeing.
  34. // The integer stores the total size of the size integer, allocation with padded size and alignment padding.
  35. // Arrays are allowed to access the padded size of all elements to allow optimizations.
  36. // * Store the new stack location pointing at the integer, with a fixed offset from the topmost allocation.
  37. // This would also make it easier to unwind the allocations when freeing memory.
  38. // * Read the integer pointed to and add it to the pointer.
  39. struct StackMemory {
  40. uint8_t data[DSR_VIRTUAL_STACK_SIZE];
  41. uint64_t stackLocation = 0;
  42. // TODO: Try to store stack locations between the allocations to avoid heap allocations.
  43. List<uint64_t> allocationEnds;
  44. };
  45. thread_local StackMemory virtualStack;
  46. uint8_t *virtualStack_push(uint64_t paddedSize, uint64_t alignment) {
  47. uint64_t oldStackLocation = virtualStack.stackLocation;
  48. // Align the start location by rounding up and then add padded elements.
  49. uint64_t startOffset = roundUp(virtualStack.stackLocation, alignment);
  50. virtualStack.stackLocation = startOffset + paddedSize;
  51. if (virtualStack.stackLocation > DSR_VIRTUAL_STACK_SIZE) {
  52. throwError(U"Ran out of stack memory!\n"); // TODO: Expand automatically using more memory blocks instead of crashing.
  53. return nullptr;
  54. } else {
  55. virtualStack.allocationEnds.push(oldStackLocation);
  56. // Clear the allocation for determinism.
  57. std::memset((char*)(virtualStack.data + startOffset), 0, paddedSize);
  58. return virtualStack.data + startOffset;
  59. }
  60. }
  61. void virtualStack_pop() {
  62. virtualStack.stackLocation = virtualStack.allocationEnds.last();
  63. virtualStack.allocationEnds.pop();
  64. }
  65. }