Skip to content

Commit 61acc7c

Browse files
committed
🐛 Separate control from GCC & e-except
GCC needs more memory because it needs to allocate the exception header for every exception.
1 parent e2f9d77 commit 61acc7c

File tree

4 files changed

+205
-127
lines changed

4 files changed

+205
-127
lines changed

CMakeLists.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ libhal_make_library(
3232
LIBRARY_NAME libhal-exceptions
3333

3434
SOURCES
35-
src/control.cpp
3635
${SOURCE_LIST}
3736

3837
PACKAGES
@@ -49,7 +48,6 @@ libhal_unit_test(
4948
LIBRARY_NAME libhal-exceptions
5049

5150
SOURCES
52-
src/control.cpp
5351
tests/main.test.cpp
5452
tests/control.test.cpp
5553
${SOURCE_LIST}

src/arm_cortex/estell/exception.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2139,3 +2139,85 @@ extern "C"
21392139
}
21402140
} // extern "C"
21412141
// NOLINTEND(bugprone-reserved-identifier)
2142+
2143+
namespace __cxxabiv1 { // NOLINT
2144+
std::terminate_handler __terminate_handler = +[]() { // NOLINT
2145+
while (true) {
2146+
continue;
2147+
}
2148+
};
2149+
} // namespace __cxxabiv1
2150+
2151+
namespace hal {
2152+
std::terminate_handler set_terminate(
2153+
std::terminate_handler p_terminate_handler) noexcept
2154+
{
2155+
auto copy = __cxxabiv1::__terminate_handler;
2156+
__cxxabiv1::__terminate_handler = p_terminate_handler;
2157+
return copy;
2158+
}
2159+
2160+
std::terminate_handler get_terminate() noexcept
2161+
{
2162+
return __cxxabiv1::__terminate_handler;
2163+
}
2164+
2165+
/**
2166+
* @brief Simple exception allocator
2167+
*
2168+
* This allocator can only allocates space for a single exception object at a
2169+
* time.
2170+
*/
2171+
class single_exception_allocator : public std::pmr::memory_resource
2172+
{
2173+
public:
2174+
single_exception_allocator() = default;
2175+
~single_exception_allocator() override = default;
2176+
2177+
private:
2178+
void* do_allocate(std::size_t p_size, // NOLINT
2179+
[[maybe_unused]] std::size_t p_alignment) override
2180+
{
2181+
if (m_allocated || p_size > m_buffer.size()) {
2182+
return nullptr;
2183+
}
2184+
m_allocated = true;
2185+
return m_buffer.data();
2186+
}
2187+
2188+
void do_deallocate(void* p_address,
2189+
[[maybe_unused]] std::size_t p_size, // NOLINT
2190+
[[maybe_unused]] std::size_t p_alignment) override
2191+
{
2192+
if (p_address != m_buffer.data()) {
2193+
std::terminate();
2194+
}
2195+
m_allocated = false;
2196+
}
2197+
2198+
[[nodiscard]] bool do_is_equal(
2199+
std::pmr::memory_resource const& other) const noexcept override
2200+
{
2201+
return this == &other;
2202+
}
2203+
2204+
alignas(std::max_align_t) std::array<std::uint8_t, 64> m_buffer{};
2205+
bool m_allocated = false;
2206+
};
2207+
2208+
// TODO(#11): Add macro to IFDEF this out if the user want to save 64 bytes.
2209+
// NOLINTNEXTLINE
2210+
single_exception_allocator _default_allocator{};
2211+
// NOLINTNEXTLINE
2212+
std::pmr::memory_resource* _exception_allocator = &_default_allocator;
2213+
2214+
void set_exception_allocator(std::pmr::memory_resource& p_allocator) noexcept
2215+
{
2216+
_exception_allocator = &p_allocator;
2217+
}
2218+
2219+
std::pmr::memory_resource& get_exception_allocator() noexcept
2220+
{
2221+
return *_exception_allocator;
2222+
}
2223+
} // namespace hal

src/builtin/gcc/impl.cpp

Lines changed: 123 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,44 +16,64 @@
1616
#include <cstdint>
1717

1818
#include <algorithm>
19+
#include <array>
1920
#include <exception>
21+
#include <memory_resource>
2022

2123
#include <libhal-exceptions/control.hpp>
2224

25+
namespace {
26+
27+
// Size of the GCC exception object header is 128 bytes. Will have to update
28+
// this if the size of the EO increases. 😅
29+
// Might need to add some GCC macro flags here to keep track of all of the
30+
// EO sizes over the versions.
31+
constexpr size_t gcc_header_size = 128;
32+
33+
template<typename T = std::byte>
34+
struct exception_allocation
35+
{
36+
std::size_t size;
37+
std::array<std::byte, 256> header;
38+
alignas(std::max_align_t) T data;
39+
};
40+
41+
template<typename T = std::byte>
42+
exception_allocation<T>* get_allocation_from_exception(void* p_exception_ptr)
43+
{
44+
auto const member_offset = offsetof(exception_allocation<T>, data);
45+
auto const exception_bytes = static_cast<std::byte*>(p_exception_ptr);
46+
return reinterpret_cast<exception_allocation<T>*>(exception_bytes -
47+
member_offset);
48+
}
49+
50+
} // namespace
51+
2352
extern "C"
2453
{
2554
void _exit([[maybe_unused]] int rc) // NOLINT
2655
{
2756
std::terminate();
2857
}
2958

30-
// Size of the GCC exception object header is 128 bytes. Will have to update
31-
// this if the size of the EO increases. 😅
32-
// Might need to add some GCC macro flags here to keep track of all of the
33-
// EO sizes over the versions.
34-
constexpr size_t gcc_header_size = 128;
35-
constexpr auto header_size = sizeof(std::size_t) + gcc_header_size;
36-
3759
void* __wrap___cxa_allocate_exception(std::size_t p_size) noexcept // NOLINT
3860
{
39-
auto const total_allocation = header_size + p_size;
40-
auto* exception_memory = reinterpret_cast<std::uint8_t*>(
41-
hal::get_exception_allocator().allocate(total_allocation));
4261

43-
if (exception_memory == nullptr) {
62+
auto const allocation_amount = sizeof(exception_allocation<>) + p_size;
63+
auto exception_memory_resource = &hal::get_exception_allocator();
64+
65+
auto* object = static_cast<exception_allocation<>*>(
66+
exception_memory_resource->allocate(allocation_amount));
67+
68+
if (object == nullptr) {
4469
std::terminate();
4570
}
4671

4772
// Required for GCC's impl to work correctly as it assumes that all bytes
4873
// default to 0.
49-
std::fill_n(exception_memory, total_allocation, 0U);
50-
51-
// Set the first std::size_t bytes to the header size for deallocation
52-
reinterpret_cast<std::size_t*>(exception_memory)[0] = total_allocation;
53-
54-
// Return the pointer to the memory after the header, which is the start of
55-
// the thrown object's allocated memory.
56-
return exception_memory + header_size;
74+
object->header.fill(std::byte{ 0 });
75+
object->size = allocation_amount;
76+
return &object->data;
5777
}
5878

5979
void __wrap___cxa_call_unexpected(void*) // NOLINT
@@ -63,9 +83,89 @@ extern "C"
6383

6484
void __wrap___cxa_free_exception(void* p_exception) noexcept // NOLINT
6585
{
66-
auto* exception = reinterpret_cast<std::uint8_t*>(p_exception);
67-
auto* original_pointer = exception - header_size;
68-
auto exception_size = *reinterpret_cast<std::size_t*>(original_pointer);
69-
hal::get_exception_allocator().deallocate(original_pointer, exception_size);
86+
auto* object = get_allocation_from_exception(p_exception);
87+
hal::get_exception_allocator().deallocate(object, object->size);
7088
}
7189
} // extern "C"
90+
91+
namespace __cxxabiv1 { // NOLINT
92+
std::terminate_handler __terminate_handler = +[]() { // NOLINT
93+
while (true) {
94+
continue;
95+
}
96+
};
97+
} // namespace __cxxabiv1
98+
99+
namespace hal {
100+
std::terminate_handler set_terminate(
101+
std::terminate_handler p_terminate_handler) noexcept
102+
{
103+
auto copy = __cxxabiv1::__terminate_handler;
104+
__cxxabiv1::__terminate_handler = p_terminate_handler;
105+
return copy;
106+
}
107+
108+
std::terminate_handler get_terminate() noexcept
109+
{
110+
return __cxxabiv1::__terminate_handler;
111+
}
112+
113+
/**
114+
* @brief Simple exception allocator
115+
*
116+
* This allocator can only allocates space for a single exception object at a
117+
* time.
118+
*/
119+
class single_exception_allocator : public std::pmr::memory_resource
120+
{
121+
public:
122+
single_exception_allocator() = default;
123+
~single_exception_allocator() override = default;
124+
125+
private:
126+
void* do_allocate(std::size_t p_size, // NOLINT
127+
[[maybe_unused]] std::size_t p_alignment) override
128+
{
129+
if (m_allocated || p_size > m_buffer.size()) {
130+
return nullptr;
131+
}
132+
m_allocated = true;
133+
return m_buffer.data();
134+
}
135+
136+
void do_deallocate(void* p_address,
137+
[[maybe_unused]] std::size_t p_size, // NOLINT
138+
[[maybe_unused]] std::size_t p_alignment) override
139+
{
140+
if (p_address != m_buffer.data()) {
141+
std::terminate();
142+
}
143+
m_allocated = false;
144+
}
145+
146+
[[nodiscard]] bool do_is_equal(
147+
std::pmr::memory_resource const& other) const noexcept override
148+
{
149+
return this == &other;
150+
}
151+
152+
alignas(std::max_align_t) std::array<std::uint8_t, 256> m_buffer{};
153+
bool m_allocated = false;
154+
};
155+
156+
// TODO(#11): Add macro to IFDEF this out if the user want to save 64 bytes.
157+
// NOLINTNEXTLINE
158+
single_exception_allocator _default_allocator{};
159+
// NOLINTNEXTLINE
160+
std::pmr::memory_resource* _exception_allocator = &_default_allocator;
161+
162+
void set_exception_allocator(std::pmr::memory_resource& p_allocator) noexcept
163+
{
164+
_exception_allocator = &p_allocator;
165+
}
166+
167+
std::pmr::memory_resource& get_exception_allocator() noexcept
168+
{
169+
return *_exception_allocator;
170+
}
171+
} // namespace hal

src/control.cpp

Lines changed: 0 additions & 102 deletions
This file was deleted.

0 commit comments

Comments
 (0)