Primitive aligned allocation from the OS. This function guarantees the allocated memory is aligned.
| 746 | // Primitive aligned allocation from the OS. |
| 747 | // This function guarantees the allocated memory is aligned. |
| 748 | static void* mi_os_mem_alloc_aligned(size_t size, size_t alignment, bool commit, bool allow_large, bool* is_large, mi_stats_t* stats) { |
| 749 | mi_assert_internal(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0)); |
| 750 | mi_assert_internal(size > 0 && (size % _mi_os_page_size()) == 0); |
| 751 | mi_assert_internal(is_large != NULL); |
| 752 | if (!commit) allow_large = false; |
| 753 | if (!(alignment >= _mi_os_page_size() && ((alignment & (alignment - 1)) == 0))) return NULL; |
| 754 | size = _mi_align_up(size, _mi_os_page_size()); |
| 755 | |
| 756 | // try first with a hint (this will be aligned directly on Win 10+ or BSD) |
| 757 | void* p = mi_os_mem_alloc(size, alignment, commit, allow_large, is_large, stats); |
| 758 | if (p == NULL) return NULL; |
| 759 | |
| 760 | // if not aligned, free it, overallocate, and unmap around it |
| 761 | if (((uintptr_t)p % alignment != 0)) { |
| 762 | mi_os_mem_free(p, size, commit, stats); |
| 763 | _mi_warning_message("unable to allocate aligned OS memory directly, fall back to over-allocation (%zu bytes, address: %p, alignment: %zu, commit: %d)\n", size, p, alignment, commit); |
| 764 | if (size >= (SIZE_MAX - alignment)) return NULL; // overflow |
| 765 | const size_t over_size = size + alignment; |
| 766 | |
| 767 | #if _WIN32 |
| 768 | // over-allocate uncommitted (virtual) memory |
| 769 | p = mi_os_mem_alloc(over_size, 0 /*alignment*/, false /* commit? */, false /* allow_large */, is_large, stats); |
| 770 | if (p == NULL) return NULL; |
| 771 | |
| 772 | // set p to the aligned part in the full region |
| 773 | // note: this is dangerous on Windows as VirtualFree needs the actual region pointer |
| 774 | // but in mi_os_mem_free we handle this (hopefully exceptional) situation. |
| 775 | p = mi_align_up_ptr(p, alignment); |
| 776 | |
| 777 | // explicitly commit only the aligned part |
| 778 | if (commit) { |
| 779 | _mi_os_commit(p, size, NULL, stats); |
| 780 | } |
| 781 | #else |
| 782 | // overallocate... |
| 783 | p = mi_os_mem_alloc(over_size, 1, commit, false, is_large, stats); |
| 784 | if (p == NULL) return NULL; |
| 785 | // and selectively unmap parts around the over-allocated area. (noop on sbrk) |
| 786 | void* aligned_p = mi_align_up_ptr(p, alignment); |
| 787 | size_t pre_size = (uint8_t*)aligned_p - (uint8_t*)p; |
| 788 | size_t mid_size = _mi_align_up(size, _mi_os_page_size()); |
| 789 | size_t post_size = over_size - pre_size - mid_size; |
| 790 | mi_assert_internal(pre_size < over_size && post_size < over_size && mid_size >= size); |
| 791 | if (pre_size > 0) mi_os_mem_free(p, pre_size, commit, stats); |
| 792 | if (post_size > 0) mi_os_mem_free((uint8_t*)aligned_p + mid_size, post_size, commit, stats); |
| 793 | // we can return the aligned pointer on `mmap` (and sbrk) systems |
| 794 | p = aligned_p; |
| 795 | #endif |
| 796 | } |
| 797 | |
| 798 | mi_assert_internal(p == NULL || (p != NULL && ((uintptr_t)p % alignment) == 0)); |
| 799 | return p; |
| 800 | } |
| 801 | |
| 802 | |
| 803 | /* ----------------------------------------------------------- |
no test coverage detected