Skip to content

Commit e2f9d77

Browse files
authored
✨ (minor) Add handled state to e-except (#85)
Resolves #37 - Add handled state and its usage to the exception algorithm. Resolves #19 - Make exception control block thread_local which reduces RAM usage for exceptions by only allocating the amount of memory needed for the `exception_ptr` instance. Resolves #42 - Use memory resource for allocations over static array of bytes. - Add `ke::exception_allocation<>` header for exceptions. It contains the object's total size and the allocator that allocated it. This allocator is used when the object is freed. - Make the size of the default exception memory resource 64 bytes. - Limit RTTI hierarchy to 8 nodes.
1 parent 1fe9289 commit e2f9d77

4 files changed

Lines changed: 120 additions & 129 deletions

File tree

demos/applications/multiple_inheritance.cpp

Lines changed: 12 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -14,49 +14,35 @@
1414

1515
#include <resource_list.hpp>
1616

17-
struct V
17+
struct virtual_base_v
1818
{
1919
int inner_detail_v = 0;
2020
};
2121

22-
struct M
22+
struct virtual_base_m
2323
{
2424
int inner_detail_m = 0x8888;
2525
};
2626

27-
struct R1 : public virtual V
27+
struct base1 : public virtual virtual_base_v
2828
{
2929
int inner_detail_r1 = 0;
3030
};
3131

32-
struct R2 : public virtual V
32+
struct base2 : public virtual virtual_base_v
3333
{
3434
int inner_detail_r2 = 0;
3535
};
3636

37-
struct R3
38-
: protected virtual M
39-
, public virtual V
40-
{
41-
int inner_detail_r3 = 0;
42-
};
43-
44-
struct R4
45-
{
46-
int inner_detail_r4 = 0;
47-
};
48-
49-
struct R
50-
: public R1
51-
, protected virtual M
52-
, public R2
53-
, public R3
54-
, public R4
37+
struct multi_inheritor
38+
: public base1
39+
, protected virtual virtual_base_m
40+
, public base2
5541
{
5642
int inner_detail_r = 0;
5743
};
5844

59-
struct error : public R
45+
struct error : public multi_inheritor
6046
{
6147
int data = 0;
6248

@@ -78,15 +64,13 @@ std::uint64_t end = 0;
7864
void foo()
7965
{
8066
if (value) {
81-
start = get_uptime();
8267
error obj{};
8368
obj.inner_detail_r = 0x1111;
8469
obj.inner_detail_r1 = 0x2222;
8570
obj.inner_detail_r2 = 0x3333;
86-
obj.inner_detail_r3 = 0x4444;
87-
obj.inner_detail_r4 = 0x5555;
8871
obj.inner_detail_v = 0x6666;
8972
obj.data = 0x9999;
73+
start = get_uptime();
9074
throw obj;
9175
}
9276
}
@@ -97,10 +81,10 @@ void application()
9781
{
9882
try {
9983
foo();
100-
} catch (M const& p_error) {
84+
} catch (virtual_base_m const& p_error) {
10185
end = get_uptime();
10286
global = p_error.inner_detail_m;
103-
} catch (V const& p_error) {
87+
} catch (virtual_base_v const& p_error) {
10488
end = get_uptime();
10589
global = p_error.inner_detail_v;
10690
}

src/arm_cortex/estell/exception.cpp

Lines changed: 72 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,12 @@
1818
#include <cstdint>
1919
#include <cstdlib>
2020
#include <exception>
21+
#include <memory_resource>
2122
#include <span>
2223
#include <typeinfo>
2324

25+
#include <libhal-exceptions/control.hpp>
26+
2427
#include "internal.hpp"
2528

2629
// NOLINTBEGIN(bugprone-reserved-identifier)
@@ -57,13 +60,7 @@ struct instructions_t
5760
};
5861
};
5962

60-
namespace {
61-
// TODO(#19): Make this thread local and figure out how to support is using
62-
// emutls.
63-
exception_ptr active_exception = nullptr;
64-
// TODO(#42): Use the applications's polymorphic allocator, not our own space.
65-
std::array<std::uint8_t, 256> exception_buffer{};
66-
} // namespace
63+
thread_local exception_control_block control_block{};
6764

6865
su16_t* get_su16(void* p_ptr)
6966
{
@@ -74,11 +71,6 @@ lu_t* get_lu(void* p_ptr)
7471
return reinterpret_cast<lu_t*>(p_ptr);
7572
}
7673

77-
exception_ptr current_exception() noexcept
78-
{
79-
return active_exception;
80-
}
81-
8274
/**
8375
* @brief Captures all registers CPU registers
8476
*
@@ -473,13 +465,13 @@ inline lsda_header_info parse_header(std::uint8_t const** p_lsda)
473465
return info;
474466
}
475467

476-
inline auto const* to_lsda(exception_object& p_exception_object)
468+
inline auto const* to_lsda(exception_control_block& p_exception_object)
477469
{
478470
return reinterpret_cast<std::uint8_t const*>(
479471
index_entry_t::lsda_data(p_exception_object.cache.personality));
480472
}
481473

482-
inline auto calculate_relative_pc(exception_object& p_exception_object)
474+
inline auto calculate_relative_pc(exception_control_block& p_exception_object)
483475
{
484476
return p_exception_object.cache.relative_address() & ~1;
485477
}
@@ -626,7 +618,7 @@ class action_decoder
626618
std::int32_t m_filter = 0;
627619
};
628620

629-
inline void enter_function(exception_object& p_exception_object)
621+
inline void enter_function(exception_control_block& p_exception_object)
630622
{
631623
auto const* lsda = to_lsda(p_exception_object);
632624
auto info = parse_header(&lsda);
@@ -1732,7 +1724,7 @@ constexpr instructions_t personality_to_unwind_instructions(
17321724

17331725
[[gnu::always_inline]]
17341726
constexpr instructions_t create_instructions_from_entry(
1735-
exception_object const& p_exception_object)
1727+
exception_control_block const& p_exception_object)
17361728
{
17371729
constexpr auto generic = hal::bit_mask::from<31>();
17381730

@@ -1752,10 +1744,14 @@ constexpr instructions_t create_instructions_from_entry(
17521744
return personality_to_unwind_instructions(handler_data);
17531745
}
17541746

1755-
void raise_exception(exception_object& p_exception_object)
1747+
void raise_exception(exception_control_block& p_exception_object)
17561748
{
17571749
while (true) {
17581750
switch (p_exception_object.cache.state()) {
1751+
[[unlikely]]
1752+
case runtime_state::handled_state: {
1753+
std::terminate();
1754+
}
17591755
case runtime_state::get_next_frame: {
17601756
auto const& index_entry = get_index_entry(p_exception_object.cpu.pc);
17611757
p_exception_object.cache.entry_ptr = &index_entry;
@@ -1831,8 +1827,8 @@ instructions_t cache(F* p_function_to_be_cached)
18311827
}
18321828
}
18331829

1834-
instructions_t cxa_throw_unwind_instructions = cache(&__wrap___cxa_throw);
1835-
instructions_t cxa_rethrow_unwind_instructions = cache(&__wrap___cxa_rethrow);
1830+
auto const cxa_throw_unwind_instructions = cache(&__wrap___cxa_throw);
1831+
auto const cxa_rethrow_unwind_instructions = cache(&__wrap___cxa_rethrow);
18361832
} // namespace ke
18371833

18381834
// NOLINTBEGIN(bugprone-reserved-identifier)
@@ -1960,8 +1956,8 @@ void flatten_rtti(ke::exception_ptr p_thrown_exception,
19601956
auto iter = p_map.begin();
19611957
auto info = get_rtti_type(p_type_info);
19621958

1963-
// If this is a non-class type, then there is in hierarchy and this first
1964-
// element we pushed is the only one.
1959+
// If this is a non-class type, then there is no hierarchy and the first
1960+
// element we pushed onto the list is the only one that exists.
19651961
if (info == rtti_type::anything_else) {
19661962
return;
19671963
}
@@ -1985,6 +1981,7 @@ void flatten_rtti(ke::exception_ptr p_thrown_exception,
19851981
}
19861982
}
19871983
}
1984+
19881985
} // namespace ke
19891986

19901987
namespace {
@@ -1998,23 +1995,37 @@ extern "C"
19981995
{
19991996
std::terminate();
20001997
}
1998+
20011999
// NOLINTNEXTLINE(readability-identifier-naming)
20022000
void* __wrap___cxa_allocate_exception(size_t p_thrown_size)
20032001
{
2004-
// TODO(#42): Use the applications's polymorphic allocator, not our own
2005-
// space.
2006-
if (p_thrown_size >
2007-
ke::exception_buffer.size() + sizeof(ke::exception_object)) {
2002+
if (ke::control_block.cache.state() != ke::runtime_state::handled_state) {
20082003
std::terminate();
20092004
}
20102005

2011-
return ke::exception_buffer.data() + sizeof(ke::exception_object);
2006+
// We set the state to `get_next_frame` to prep the control block state but
2007+
// more importantly, in order to detect if the allocator throws bad alloc
2008+
// and we re-enter this function, we can detect that and terminate.
2009+
ke::control_block.cache.state(ke::runtime_state::get_next_frame);
2010+
auto const allocation_amount =
2011+
sizeof(ke::exception_allocation<>) + p_thrown_size;
2012+
2013+
auto exception_memory_resource = &hal::get_exception_allocator();
2014+
2015+
auto* object = static_cast<ke::exception_allocation<>*>(
2016+
exception_memory_resource->allocate(allocation_amount));
2017+
2018+
object->allocator = exception_memory_resource;
2019+
object->size = allocation_amount;
2020+
2021+
return &object->data;
20122022
}
20132023

20142024
// NOLINTNEXTLINE(readability-identifier-naming)
2015-
void __wrap___cxa_free_exception([[maybe_unused]] void* p_thrown_exception)
2025+
void __wrap___cxa_free_exception(void* p_thrown_exception)
20162026
{
2017-
ke::exception_buffer.fill(0);
2027+
auto object = ke::get_allocation_from_exception(p_thrown_exception);
2028+
object->allocator->deallocate(object, object->size);
20182029
}
20192030

20202031
// NOLINTNEXTLINE(readability-identifier-naming)
@@ -2027,29 +2038,29 @@ extern "C"
20272038
// NOLINTNEXTLINE(readability-identifier-naming)
20282039
void __wrap___cxa_end_catch()
20292040
{
2030-
auto& exception_object = ke::extract_exception_object(ke::active_exception);
2031-
if (exception_object.cache.rethrown()) {
2032-
exception_object.cache.rethrown(false);
2041+
auto& control_block = ke::control_block;
2042+
if (control_block.cache.rethrown()) {
2043+
control_block.cache.rethrown(false);
20332044
} else {
2034-
__wrap___cxa_free_exception(ke::active_exception);
2045+
__wrap___cxa_free_exception(control_block.thrown_object);
20352046
}
20362047
}
20372048

20382049
// NOLINTNEXTLINE(readability-identifier-naming)
2039-
void* __wrap___cxa_begin_catch(void* p_exception_object)
2050+
void* __wrap___cxa_begin_catch(void*)
20402051
{
2041-
auto* eo = reinterpret_cast<ke::exception_object*>(p_exception_object);
2042-
auto* thrown_object = ke::extract_thrown_object(eo);
2043-
return thrown_object;
2052+
auto& control_block = ke::control_block;
2053+
control_block.cache.state(ke::runtime_state::handled_state);
2054+
return control_block.thrown_object;
20442055
}
20452056

20462057
// NOLINTNEXTLINE(readability-identifier-naming)
20472058
void __wrap___cxa_end_cleanup()
20482059
{
2049-
auto& exception_object = ke::extract_exception_object(ke::active_exception);
2050-
exception_object.cache.state(ke::runtime_state::unwind_frame);
2060+
auto& control_block = ke::control_block;
2061+
control_block.cache.state(ke::runtime_state::unwind_frame);
20512062
// Raise exception returns when an error or call to terminate has been found
2052-
ke::raise_exception(exception_object);
2063+
ke::raise_exception(control_block);
20532064
std::terminate();
20542065
}
20552066

@@ -2058,18 +2069,22 @@ extern "C"
20582069
void __wrap__Unwind_Resume(void*)
20592070
// NOLINTEND(readability-identifier-naming)
20602071
{
2061-
__wrap___cxa_end_cleanup();
2072+
auto& control_block = ke::control_block;
2073+
control_block.cache.state(ke::runtime_state::unwind_frame);
2074+
// Raise exception returns when an error or call to terminate has been found
2075+
ke::raise_exception(control_block);
2076+
std::terminate();
20622077
}
20632078

20642079
// NOLINTNEXTLINE(readability-identifier-naming)
20652080
void __wrap___cxa_rethrow() noexcept(false)
20662081
{
2067-
auto& exception_object = ke::extract_exception_object(ke::active_exception);
2082+
auto& control_block = ke::control_block;
20682083

2069-
ke::capture_cpu_core(exception_object.cpu);
2084+
ke::capture_cpu_core(control_block.cpu);
20702085

2071-
exception_object.cache.state(ke::runtime_state::get_next_frame);
2072-
exception_object.cache.rethrown(true);
2086+
control_block.cache.state(ke::runtime_state::get_next_frame);
2087+
control_block.cache.rethrown(true);
20732088

20742089
// This must ALWAYS evaluate to false. But since the variable is volatile,
20752090
// the compiler will not optimize it away and thus, __wrap___cxa_throw will
@@ -2081,10 +2096,9 @@ extern "C"
20812096
// have in the C++ throw RTTI list, might as well
20822097
// reuse it here.
20832098
}
2084-
ke::unwind_frame(ke::cxa_rethrow_unwind_instructions, exception_object.cpu);
2085-
2099+
ke::unwind_frame(ke::cxa_rethrow_unwind_instructions, control_block.cpu);
20862100
// Raise exception returns when an error or call to terminate has been found
2087-
ke::raise_exception(exception_object);
2101+
ke::raise_exception(control_block);
20882102
// TODO(#38): this area is considered a catch block, meaning that the
20892103
// exception is handled at this point. We should mark it as such.
20902104
std::terminate();
@@ -2095,13 +2109,12 @@ extern "C"
20952109
std::type_info* p_type_info,
20962110
ke::destructor_t p_destructor) noexcept(false)
20972111
{
2098-
ke::active_exception = p_thrown_exception;
2099-
auto& exception_object = ke::extract_exception_object(p_thrown_exception);
2100-
exception_object.destructor = p_destructor;
2101-
ke::capture_cpu_core(exception_object.cpu);
2112+
auto& control_block = ke::control_block;
21022113

2103-
ke::flatten_rtti<12>(
2104-
p_thrown_exception, exception_object.type_info, p_type_info);
2114+
control_block.thrown_object = p_thrown_exception;
2115+
control_block.destructor = p_destructor;
2116+
ke::capture_cpu_core(control_block.cpu);
2117+
ke::flatten_rtti(p_thrown_exception, control_block.type_info, p_type_info);
21052118

21062119
// This must ALWAYS evaluate to false. But since the variable is volatile,
21072120
// the compiler will not optimize it away and thus, __wrap___cxa_throw will
@@ -2113,9 +2126,13 @@ extern "C"
21132126
// have in the C++ throw RTTI list, might as well
21142127
// reuse it here.
21152128
}
2116-
ke::unwind_frame(ke::cxa_throw_unwind_instructions, exception_object.cpu);
2129+
// Use the expanded unwind instructions to quickly unwind cxa_throw.
2130+
ke::unwind_frame(ke::cxa_throw_unwind_instructions, control_block.cpu);
2131+
21172132
// Raise exception returns when an error or call to terminate has been found
2118-
ke::raise_exception(exception_object);
2133+
ke::raise_exception(control_block);
2134+
2135+
control_block.cache.state(ke::runtime_state::handled_state);
21192136
// TODO(#38): this area is considered a catch block, meaning that the
21202137
// exception is handled at this point. We should mark it as such.
21212138
std::terminate();

0 commit comments

Comments
 (0)