-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtrix.cpp
More file actions
136 lines (117 loc) · 4.99 KB
/
Copy pathtrix.cpp
File metadata and controls
136 lines (117 loc) · 4.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include "trix.h"
#include <chrono>
#include <thread>
#include <vector>
// -----------------------------------------------------------------------
// Example user operators -- demonstrates the public operator API.
// -----------------------------------------------------------------------
// Background interrupt workers spawned by my-raise-interrupt. Host-owned and
// joined in main() before the Trix object is destroyed, so a worker can never
// lock the destroyed m_mutex or touch the freed VM heap after the script ends.
static std::vector<std::thread> g_interrupt_workers;
#ifdef TRX_DEBUG
// Self-introspection test anchor (debug builds only -- absent from the shipped
// optimized binary, which carries no debug info anyway). A global with a known
// fixed value that the Trix DWARF reader can find in THIS running binary, so the
// dwarf-peek-live regression can decode it and assert exact field values.
// [[gnu::used, gnu::retain]] keeps the storage through -ffunction-sections +
// -Wl,--gc-sections even though nothing in the program references it.
struct TrixDwarfSelfProbe {
unsigned int magic;
double scale;
int count;
unsigned char tag;
};
[[gnu::used, gnu::retain]] TrixDwarfSelfProbe g_trix_dwarf_self_probe = {0xC0FFEEu, 1.5, -42, 7};
#endif
// Integer :- Integer
// Computes the square of the top integer; raises NumericalOverflow on overflow.
static void my_square_op(Trix *trx) {
trx->verify_operands(Trix::VerifyInteger);
auto val = trx->m_op_ptr->integer_value();
Trix::integer_t result;
if (__builtin_smul_overflow(val, val, &result)) {
trx->error(Trix::Error::NumericalOverflow, "'my-square': {0} * {0} overflows Integer", val);
} else {
*trx->m_op_ptr = Trix::Object::make_integer(result);
}
}
// Integer Integer Integer :- Integer
// Stack order: value min max my-clamp :- clamped-value
// Clamps value to [min, max]; raises RangeCheck if min > max.
static void my_clamp_op(Trix *trx) {
trx->verify_operands(Trix::VerifyInteger, Trix::VerifyInteger, Trix::VerifyInteger);
auto max_val = trx->m_op_ptr->integer_value();
--trx->m_op_ptr;
auto min_val = trx->m_op_ptr->integer_value();
--trx->m_op_ptr;
auto val = trx->m_op_ptr->integer_value();
if (min_val > max_val) {
trx->error(Trix::Error::RangeCheck, "'my-clamp': min {} > max {}", min_val, max_val);
} else {
auto result = (val < min_val) ? min_val : (val > max_val) ? max_val : val;
*trx->m_op_ptr = Trix::Object::make_integer(result);
}
}
// Integer :- --
// Raises an interrupt at the given level (0, 1, or 2) from a background thread
// after a brief delay. The interrupt is delivered asynchronously, exercising
// the full raise_interrupt -> process_interrupt -> exec stack dispatch path.
// Level 3 raises ExitIRQ -- the host-side stop signal a parked --resident
// instance wakes on (see Stream::init's resident floor handling).
static void my_raise_interrupt_op(Trix *trx) {
trx->verify_operands(Trix::VerifyInteger);
auto level = trx->m_op_ptr->integer_value();
--trx->m_op_ptr;
Trix::interrupt_t irq;
switch (level) {
case 0:
irq = Trix::Level0IRQ;
break;
case 1:
irq = Trix::Level1IRQ;
break;
case 2:
irq = Trix::Level2IRQ;
break;
case 3:
irq = Trix::ExitIRQ;
break;
default:
trx->error(Trix::Error::RangeCheck, "'my-raise-interrupt': level must be 0, 1, 2, or 3 (3 = exit)");
}
g_interrupt_workers.emplace_back([trx, irq]() {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
static_cast<void>(trx->raise_interrupt(irq));
});
}
// Null-terminated user operator table.
static constexpr Trix::Operator user_ops[] = {
{ my_square_op, "my-square"},
{ my_clamp_op, "my-clamp"},
{my_raise_interrupt_op, "my-raise-interrupt"},
{ nullptr, {}},
};
int main(int argc, char *argv[]) {
auto result = Trix::parse_args(argc, argv);
if (result.should_exit) {
return result.exit_code;
} else {
result.config.m_useroperators = user_ops;
// Only override when the user left the library default, so an explicit
// --stream-count=N is honored (parse_args has already applied it).
if (result.config.m_stream_count == Trix::DefaultStreamCount) {
result.config.m_stream_count = 16;
}
// NOT const: a my-raise-interrupt worker thread holds a Trix* captured
// inside the constructor and calls the mutating raise_interrupt() on it.
// NOLINTNEXTLINE(misc-const-correctness) -- cross-thread mutation clang-tidy can't see.
Trix trx(result.vm_size, result.config);
// Drain background interrupt workers while `trx` is still alive, so no
// worker can lock the destroyed m_mutex or touch the freed VM heap.
for (auto &worker : g_interrupt_workers) {
worker.join();
}
return trx.exit_code();
}
}