History

Create specific exception when index is out of range. Update thread class to print out exception details. Throw exception when an invalid op-code is detected. Enforce mspawn instruction cannot start from a built-in module.

arr committed 10 years ago

Commit 266fad61 parent 9af409310 files · +63 −40Browse files

Unified · Split

10 files
  1. src/exec/main.cpp +9 −5
  2. src/include/builtin_module.h +12 −3
  3. src/include/exceptions.h +9 −12
  4. src/include/runtime.h +1 −1
  5. src/vm/array.cpp +1 −1
  6. src/vm/execution_table.cpp +6 −2
  7. src/vm/scheduler.h +1 −1
  8. src/vm/stack.cpp +2 −2
  9. src/vm/string.cpp +4 −4
  10. src/vm/thread.cpp +18 −9
src/exec/main.cpp+9−5
@@ -93,13 +93,17 @@ namespace · 92 unmodified lines
93 if (args != nullptr)93 if (args != nullptr)
94 mp_base[2] = args->get_allocation();94 mp_base[2] = args->get_allocation();
95 95
96 // Define the frame offsets for the two arguments
97 const auto first_arg_offset = vm_frame_constants::limbo_first_arg_register_offset;
98 const auto second_arg_offset = first_arg_offset + vm_frame_constants::register_size_in_bytes;
99
96 e.code_section =100 e.code_section =
97 {101 {
98 { vm_exec_op_t102 { vm_exec_op_t
99 {103 {
100 opcode_t::load,104 opcode_t::load,
101 { address_mode_t::offset_indirect_mp, 0, 0 }, // command module path105 { address_mode_t::offset_indirect_mp, 0, 0 }, // command module path
102 { address_mode_middle_t::small_immediate, 0 },106 { address_mode_middle_t::small_immediate, 0 }, // import table index
103 { address_mode_t::offset_indirect_fp, 20, 0 } // module reference107 { address_mode_t::offset_indirect_fp, 20, 0 } // module reference
104 }108 }
105 },109 },
@@ -107,7 +111,7 @@ namespace · 1 unmodified line
107 {111 {
108 opcode_t::mframe,112 opcode_t::mframe,
109 { address_mode_t::offset_indirect_fp, 20, 0 }, // module reference113 { address_mode_t::offset_indirect_fp, 20, 0 }, // module reference
110 { address_mode_middle_t::small_immediate, 0 },114 { address_mode_middle_t::small_immediate, 0 }, // function index into module
111 { address_mode_t::offset_indirect_fp, 24, 0 } // module call frame115 { address_mode_t::offset_indirect_fp, 24, 0 } // module call frame
112 }116 }
113 },117 },
@@ -116,7 +120,7 @@ namespace · 2 unmodified lines
116 opcode_t::movp,120 opcode_t::movp,
117 { address_mode_t::offset_indirect_mp, 4, 0 }, // UI context121 { address_mode_t::offset_indirect_mp, 4, 0 }, // UI context
118 { address_mode_middle_t::none },122 { address_mode_middle_t::none },
119 { address_mode_t::offset_double_indirect_fp, 24, 32 } // module call frame -> UI context offset123 { address_mode_t::offset_double_indirect_fp, 24, first_arg_offset } // module call frame -> UI context offset
120 }124 }
121 },125 },
122 { vm_exec_op_t126 { vm_exec_op_t
@@ -124,14 +128,14 @@ namespace · 1 unmodified line
124 opcode_t::movp,128 opcode_t::movp,
125 { address_mode_t::offset_indirect_mp, 8, 0 }, // Argument list129 { address_mode_t::offset_indirect_mp, 8, 0 }, // Argument list
126 { address_mode_middle_t::none },130 { address_mode_middle_t::none },
127 { address_mode_t::offset_double_indirect_fp, 24, 36 } // module call frame -> argument list offset131 { address_mode_t::offset_double_indirect_fp, 24, second_arg_offset } // module call frame -> argument list offset
128 }132 }
129 },133 },
130 { vm_exec_op_t134 { vm_exec_op_t
131 {135 {
132 opcode_t::mcall,136 opcode_t::mcall,
133 { address_mode_t::offset_indirect_fp, 24, 0 }, // module call frame137 { address_mode_t::offset_indirect_fp, 24, 0 }, // module call frame
134 { address_mode_middle_t::small_immediate, 0 },138 { address_mode_middle_t::small_immediate, 0 }, // function index into module
135 { address_mode_t::offset_indirect_fp, 20, 0 } // module reference139 { address_mode_t::offset_indirect_fp, 20, 0 } // module reference
136 }140 }
137 },141 },
src/include/builtin_module.h+12−3
@@ -66,9 +66,18 @@ namespace disvm · 65 unmodified lines
66 // Includes registers on base frame plus the implied return register66 // Includes registers on base frame plus the implied return register
67 static const auto default_register_count = (sizeof(vm_frame_base_alloc_t) / register_size_in_bytes) + 1;67 static const auto default_register_count = (sizeof(vm_frame_base_alloc_t) / register_size_in_bytes) + 1;
68 68
69 // See VM frame definition69 // [SPEC] The fixed point registers are defined by their usage in the
70 static const auto fixed_point_register_1_frame_offset = default_register_count * register_size_in_bytes;70 // Inferno implementation. They rely on the 'temp' registers defined below.
71 static const auto fixed_point_register_2_frame_offset = fixed_point_register_1_frame_offset + (2 * register_size_in_bytes);71 // See fixed point op-codes in Inferno (libinterp/xec.c).
72 static const auto fixed_point_register_1_offset = default_register_count * register_size_in_bytes;
73 static const auto fixed_point_register_2_offset = fixed_point_register_1_offset + (2 * register_size_in_bytes);
74
75 // [SPEC] The official Limbo compiler allocates additional 'temp' registers on all call frames.
76 // See 'STemp', 'RTemp', and 'DTemp' in Inferno (limbo/limbo.h)
77 static const auto limbo_temp_register_count = 3;
78
79 // Define the first argument offset in a Limbo compiler generated call frame.
80 static const auto limbo_first_arg_register_offset = (default_register_count + limbo_temp_register_count) * register_size_in_bytes;
72 };81 };
73 82
74 //83 //
src/include/exceptions.h+9−12
@@ -60,7 +60,7 @@ namespace disvm · 59 unmodified lines
60 { }60 { }
61 };61 };
62 62
63 class invalid_utf8 : public vm_user_exception63 class invalid_utf8 final : public vm_user_exception
64 {64 {
65 public:65 public:
66 explicit invalid_utf8()66 explicit invalid_utf8()
@@ -84,7 +84,7 @@ namespace disvm · 17 unmodified lines
84 const char *operation;84 const char *operation;
85 };85 };
86 86
87 class type_violation : public vm_user_exception87 class type_violation final : public vm_user_exception
88 {88 {
89 public:89 public:
90 explicit type_violation()90 explicit type_violation()
@@ -92,26 +92,23 @@ namespace disvm · 1 unmodified line
92 { }92 { }
93 };93 };
94 94
95 class out_of_range_memory final : public vm_user_exception95 class out_of_range_memory : public vm_user_exception
96 {96 {
97 public:97 public:
98 explicit out_of_range_memory()98 explicit out_of_range_memory()
99 : vm_user_exception("Out of range access")99 : vm_user_exception("Out of range access")
100 , invalid_value{ 0 }
101 , range_defined{ false }
102 , valid_max{ 0 }
103 , valid_min{ 0 }
104 { }100 { }
101 };
105 102
106 explicit out_of_range_memory(word_t valid_min, word_t valid_max, word_t invalid_valid)103 class index_out_of_range_memory final : public out_of_range_memory
107 : vm_user_exception("Out of range access")104 {
108 , invalid_value{ invalid_valid }105 public:
109 , range_defined{ true }106 explicit index_out_of_range_memory(word_t valid_min, word_t valid_max, word_t invalid_value)
107 : invalid_value{ invalid_value }
110 , valid_max{ valid_max }108 , valid_max{ valid_max }
111 , valid_min{ valid_min }109 , valid_min{ valid_min }
112 { }110 { }
113 111
114 const bool range_defined;
115 const word_t valid_min;112 const word_t valid_min;
116 const word_t valid_max;113 const word_t valid_max;
117 const word_t invalid_value;114 const word_t invalid_value;
src/include/runtime.h+1−1
@@ -759,7 +759,7 @@ namespace disvm · 758 unmodified lines
759 759
760 ~vm_thread_t();760 ~vm_thread_t();
761 761
762 vm_thread_state_t execute(vm_t &virtual_machine, uint32_t quanta);762 vm_thread_state_t execute(vm_t &virtual_machine, const uint32_t quanta);
763 763
764 vm_thread_state_t get_state() const;764 vm_thread_state_t get_state() const;
765 765
src/vm/array.cpp+1−1
@@ -156,7 +156,7 @@ word_t vm_array_t::get_length() const · 155 unmodified lines
156pointer_t vm_array_t::at(word_t index) const156pointer_t vm_array_t::at(word_t index) const
157{157{
158 if (index < 0 || _length <= index)158 if (index < 0 || _length <= index)
159 throw out_of_range_memory{ 0, _length - 1, index };159 throw index_out_of_range_memory{ 0, _length - 1, index };
160 160
161 return reinterpret_cast<pointer_t>(_arr + (index * _element_type->size_in_bytes));161 return reinterpret_cast<pointer_t>(_arr + (index * _element_type->size_in_bytes));
162}162}
src/vm/execution_table.cpp+6−2
@@ -846,7 +846,7 @@ namespace · 845 unmodified lines
846 846
847 auto index = vt_ref<word_t>(r.dest);847 auto index = vt_ref<word_t>(r.dest);
848 if (index < 0 || arr->get_length() <= index)848 if (index < 0 || arr->get_length() <= index)
849 throw out_of_range_memory{ 0, arr->get_length(), index };849 throw index_out_of_range_memory{ 0, arr->get_length(), index };
850 850
851 // Return the address, not the value851 // Return the address, not the value
852 pt_ref(r.mid) = arr->at(index);852 pt_ref(r.mid) = arr->at(index);
@@ -1577,6 +1577,10 @@ namespace · 724 unmodified lines
1577 1577
1578 const auto &function_ref = target_module_ref->get_function_ref(import_function_id);1578 const auto &function_ref = target_module_ref->get_function_ref(import_function_id);
1579 1579
1580 // Spawning a built-in module is not permitted
1581 if (target_module_ref->is_builtin_module())
1582 throw vm_user_exception{ "Spawning a built-in module is not permitted" };
1583
1580 vm.fork(r.thread, *target_module_ref, *spawned_frame, function_ref.entry_pc);1584 vm.fork(r.thread, *target_module_ref, *spawned_frame, function_ref.entry_pc);
1581 1585
1582 // After the new thread is forked pop off the argument passing frame.1586 // After the new thread is forked pop off the argument passing frame.
@@ -1685,7 +1689,7 @@ namespace · 102 unmodified lines
1685 auto imported_module = std::shared_ptr<const vm_module_t>{};1689 auto imported_module = std::shared_ptr<const vm_module_t>{};
1686 1690
1687 // Handle self loading module optimization.1691 // Handle self loading module optimization.
1688 if (::strcmp(str->str(), "$self") == 0)1692 if (std::strcmp(str->str(), "$self") == 0)
1689 {1693 {
1690 imported_module = r.module_ref->module;1694 imported_module = r.module_ref->module;
1691 }1695 }
src/vm/scheduler.h+1−1
@@ -46,7 +46,7 @@ namespace disvm · 45 unmodified lines
46 std::vector<std::shared_ptr<const vm_thread_t>> get_all_threads() const override final;46 std::vector<std::shared_ptr<const vm_thread_t>> get_all_threads() const override final;
47 47
48 private:48 private:
49 struct thread_instance_t49 struct thread_instance_t final
50 {50 {
51 thread_instance_t(std::unique_ptr<vm_thread_t> t)51 thread_instance_t(std::unique_ptr<vm_thread_t> t)
52 : vm_thread{ std::move(t) }52 : vm_thread{ std::move(t) }
src/vm/stack.cpp+2−2
@@ -161,7 +161,7 @@ vm_module_ref_t *&vm_frame_t::prev_module_ref() const · 160 unmodified lines
161 161
162pointer_t vm_frame_t::fixed_point_register_1() const162pointer_t vm_frame_t::fixed_point_register_1() const
163{163{
164 const auto offset = vm_frame_constants::fixed_point_register_1_frame_offset;164 const auto offset = vm_frame_constants::fixed_point_register_1_offset;
165 assert(offset < frame_type->size_in_bytes);165 assert(offset < frame_type->size_in_bytes);
166 166
167 auto p = reinterpret_cast<uint8_t *>(base()) + offset;167 auto p = reinterpret_cast<uint8_t *>(base()) + offset;
@@ -170,7 +170,7 @@ pointer_t vm_frame_t::fixed_point_register_1() const · 2 unmodified lines
170 170
171pointer_t vm_frame_t::fixed_point_register_2() const171pointer_t vm_frame_t::fixed_point_register_2() const
172{172{
173 const auto offset = vm_frame_constants::fixed_point_register_2_frame_offset;173 const auto offset = vm_frame_constants::fixed_point_register_2_offset;
174 assert(offset < frame_type->size_in_bytes);174 assert(offset < frame_type->size_in_bytes);
175 175
176 auto p = reinterpret_cast<uint8_t *>(base()) + offset;176 auto p = reinterpret_cast<uint8_t *>(base()) + offset;
src/vm/string.cpp+4−4
@@ -264,10 +264,10 @@ vm_string_t::vm_string_t(const vm_string_t &other, word_t begin_index, word_t en · 263 unmodified lines
264 throw out_of_range_memory{};264 throw out_of_range_memory{};
265 265
266 if (begin_index < 0)266 if (begin_index < 0)
267 throw out_of_range_memory{ 0, other._length, begin_index };267 throw index_out_of_range_memory{ 0, other._length, begin_index };
268 268
269 if (other._length < end_index)269 if (other._length < end_index)
270 throw out_of_range_memory{ 0, other._length, end_index };270 throw index_out_of_range_memory{ 0, other._length, end_index };
271 271
272 // Allocate memory based on character count and size272 // Allocate memory based on character count and size
273 auto src = other._mem.local;273 auto src = other._mem.local;
@@ -384,7 +384,7 @@ word_t vm_string_t::get_length() const · 110 unmodified lines
384rune_t vm_string_t::get_rune(word_t index) const384rune_t vm_string_t::get_rune(word_t index) const
385{385{
386 if (index < 0 && _length <= index)386 if (index < 0 && _length <= index)
387 throw out_of_range_memory{ 0, _length - 1, index };387 throw index_out_of_range_memory{ 0, _length - 1, index };
388 388
389 // Determine if local or allocated memory389 // Determine if local or allocated memory
390 auto source = reinterpret_cast<const char *>(_mem.local);390 auto source = reinterpret_cast<const char *>(_mem.local);
@@ -410,7 +410,7 @@ void vm_string_t::set_rune(word_t index, rune_t value) · 19 unmodified lines
410 410
411 // Index equal to length is allowed to grow the string411 // Index equal to length is allowed to grow the string
412 if (index < 0 && _length <= index)412 if (index < 0 && _length <= index)
413 throw out_of_range_memory{ 0, _length, index };413 throw index_out_of_range_memory{ 0, _length, index };
414 414
415 // Determine if local or allocated memory415 // Determine if local or allocated memory
416 auto source = static_cast<uint8_t *>(_mem.local);416 auto source = static_cast<uint8_t *>(_mem.local);
src/vm/thread.cpp+18−9
@@ -117,10 +117,9 @@ namespace · 116 unmodified lines
117#endif117#endif
118 }118 }
119 119
120 std::atomic<uint32_t> thread_id_counter(1);
121
122 uint32_t get_unique_thread_id()120 uint32_t get_unique_thread_id()
123 {121 {
122 static std::atomic<uint32_t> thread_id_counter{ 1 };
124 return thread_id_counter.fetch_add(1);123 return thread_id_counter.fetch_add(1);
125 }124 }
126}125}
@@ -206,15 +205,17 @@ vm_thread_t::vm_thread_t( · 79 unmodified lines
206 // Copy over the frame into this thread - pass arguments to the thread205 // Copy over the frame into this thread - pass arguments to the thread
207 current_frame->copy_frame_contents(initial_frame);206 current_frame->copy_frame_contents(initial_frame);
208 207
209 debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "init: vm thread: %d %d\n", _thread_id, _parent_thread_id);208 if (debug::is_component_tracing_enabled<debug::component_trace_t::thread>())
209 debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "init: vm thread: %d %d\n", _thread_id, _parent_thread_id);
210}210}
211 211
212vm_thread_t::~vm_thread_t()212vm_thread_t::~vm_thread_t()
213{213{
214 debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "destroy: vm thread: %d %d\n", _thread_id, _parent_thread_id);214 if (debug::is_component_tracing_enabled<debug::component_trace_t::thread>())
215 debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "destroy: vm thread: %d %d\n", _thread_id, _parent_thread_id);
215}216}
216 217
217vm_thread_state_t vm_thread_t::execute(vm_t &virtual_machine, uint32_t quanta)218vm_thread_state_t vm_thread_t::execute(vm_t &virtual_machine, const uint32_t quanta)
218{219{
219 _registers.current_thread_state = vm_thread_state_t::running;220 _registers.current_thread_state = vm_thread_state_t::running;
220 assert(_registers.pc != runtime_constants::invalid_program_counter);221 assert(_registers.pc != runtime_constants::invalid_program_counter);
@@ -236,9 +237,13 @@ vm_thread_state_t vm_thread_t::execute(vm_t &virtual_machine, uint32_t quanta) · 15 unmodified lines
236 assert(static_cast<std::size_t>(_registers.pc) < code_section.size());237 assert(static_cast<std::size_t>(_registers.pc) < code_section.size());
237 238
238 const auto &inst = code_section[_registers.pc].op;239 const auto &inst = code_section[_registers.pc].op;
240 const auto op_code = static_cast<std::size_t>(inst.opcode);
241 if (static_cast<std::size_t>(opcode_t::last_opcode) < op_code)
242 throw vm_system_exception{ "Unknown op-code" };
243
239 decode_address(inst, _registers);244 decode_address(inst, _registers);
240 _registers.pc++;245 _registers.pc++;
241 vm_exec_table[static_cast<std::size_t>(inst.opcode)](_registers, virtual_machine);246 vm_exec_table[op_code](_registers, virtual_machine);
242 247
243 if (_registers.current_thread_state != vm_thread_state_t::running)248 if (_registers.current_thread_state != vm_thread_state_t::running)
244 break;249 break;
@@ -247,13 +252,17 @@ vm_thread_state_t vm_thread_t::execute(vm_t &virtual_machine, uint32_t quanta) · 2 unmodified lines
247 catch (const unhandled_user_exception &uue)252 catch (const unhandled_user_exception &uue)
248 {253 {
249 // [TODO] Store the exception254 // [TODO] Store the exception
250 debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "broken: vm thread: unhandled: >>%s<< %d >>%s<<\n", uue.exception_id, uue.program_counter, uue.module_name);255 std::printf("%s - %s in %s at %d\n", uue.what(), uue.exception_id, uue.module_name, uue.program_counter);
256 _registers.current_thread_state = vm_thread_state_t::broken;
257 }
258 catch (const index_out_of_range_memory &ioor)
259 {
260 std::printf("%s - %d [%d,%d]\n", ioor.what(), ioor.invalid_value, ioor.valid_min, ioor.valid_max);
251 _registers.current_thread_state = vm_thread_state_t::broken;261 _registers.current_thread_state = vm_thread_state_t::broken;
252 }262 }
253 catch (const vm_user_exception &ue)263 catch (const vm_user_exception &ue)
254 {264 {
255 // [TODO] Store the exception265 std::printf("%s\n", ue.what());
256 debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "broken: vm thread: >>%s<<\n", ue.what());
257 _registers.current_thread_state = vm_thread_state_t::broken;266 _registers.current_thread_state = vm_thread_state_t::broken;
258 }267 }
259 268