History

Bugs found during limbo compiler work - Ref counting mistakes in vm_array copy_from() - document perf opportunity - Multiple bugs in addc instruction - set_rune() on vm_string was creating non-null terminated strings - Add support for '*', 'c', and 'u' flags in print formatting

arr committed 9 years ago

Commit 57e9d241 parent 3f81a2c4 files · +86 −44Browse files

Unified · Split

4 files
  1. src/vm/array.cpp +12 −4
  2. src/vm/execution_table.cpp +30 −24
  3. src/vm/string.cpp +16 −10
  4. src/vm/sys/print.cpp +28 −6
src/vm/array.cpp+12−4
@@ -171,18 +171,26 @@ void vm_array_t::copy_from(const vm_array_t &source, word_t this_begin_index) · 170 unmodified lines
171 171
172 auto dest_arr = _arr + (this_begin_index * _element_type->size_in_bytes);172 auto dest_arr = _arr + (this_begin_index * _element_type->size_in_bytes);
173 173
174 // Increment all pointers if we have reference types.174 // If we have reference types, increment references in the
175 // source array and decrement them in the destination array.
175 if (_element_type->map_in_bytes != 0)176 if (_element_type->map_in_bytes != 0)
176 {177 {
177 auto &type_desc = *_element_type;178 // [PERF] There is a chance this copy is within the same array, therefore
179 // a check could be made to not ref count memory that is going to be moved.
180 // The Inferno implementation of slicela handles this case (libinterp/xec.c).
181 const auto &type_desc = *_element_type;
178 auto source_array_element = source._arr;182 auto source_array_element = source._arr;
183 auto dest_array_element = dest_arr;
179 for (auto i = word_t{ 0 }; i < source._length; ++i)184 for (auto i = word_t{ 0 }; i < source._length; ++i)
180 {185 {
181 source_array_element += (i * type_desc.size_in_bytes);
182 inc_ref_count_in_memory(type_desc, source_array_element);186 inc_ref_count_in_memory(type_desc, source_array_element);
187 source_array_element += type_desc.size_in_bytes;
188
189 destroy_memory(type_desc, dest_array_element);
190 dest_array_element += type_desc.size_in_bytes;
183 }191 }
184 }192 }
185 193
186 // Moving data within the same array is permitted so use memmove().194 // Copy data from within the same array is permitted.
187 std::memmove(dest_arr, source._arr, (source._length * _element_type->size_in_bytes));195 std::memmove(dest_arr, source._arr, (source._length * _element_type->size_in_bytes));
188}196}
src/vm/execution_table.cpp+30−24
@@ -79,7 +79,7 @@ namespace · 78 unmodified lines
79 vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) >> vt_ref<big_t>(r.src);79 vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) >> vt_ref<big_t>(r.src);
80 // Right shifting a negative number is implementation dependent in C++.80 // Right shifting a negative number is implementation dependent in C++.
81 // Assert the current compiler defaults to arithmetic shifting, not logical.81 // Assert the current compiler defaults to arithmetic shifting, not logical.
82 assert(vt_ref<word_t>(r.mid) >= 0 || (vt_ref<word_t>(r.mid) < 0 && vt_ref<word_t>(r.dest) < 0));82 assert(vt_ref<big_t>(r.mid) >= 0 || (vt_ref<big_t>(r.mid) < 0 && vt_ref<big_t>(r.dest) < 0));
83 }83 }
84 84
85 EXEC_DECL(lsrw) { vt_ref<word_t>(r.dest) = vt_ref<uword_t>(r.mid) >> vt_ref<word_t>(r.src); }85 EXEC_DECL(lsrw) { vt_ref<word_t>(r.dest) = vt_ref<uword_t>(r.mid) >> vt_ref<word_t>(r.src); }
@@ -206,8 +206,8 @@ namespace · 120 unmodified lines
206 assert(static_cast<std::size_t>(len) <= buffer.size());206 assert(static_cast<std::size_t>(len) <= buffer.size());
207 207
208 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));208 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));
209 auto str = at_val<vm_string_t>(r.dest);209 auto prev = at_val<vm_alloc_t>(r.dest);
210 dec_ref_count_and_free(str);210 dec_ref_count_and_free(prev);
211 211
212 pt_ref(r.dest) = new_string->get_allocation();212 pt_ref(r.dest) = new_string->get_allocation();
213 }213 }
@@ -233,9 +233,10 @@ namespace · 19 unmodified lines
233 233
234 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));234 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));
235 235
236 auto str = at_val<vm_string_t>(r.dest);236 auto prev = at_val<vm_alloc_t>(r.dest);
237 dec_ref_count_and_free(prev);
238
237 pt_ref(r.dest) = new_string->get_allocation();239 pt_ref(r.dest) = new_string->get_allocation();
238 dec_ref_count_and_free(str);
239 }240 }
240 241
241 EXEC_DECL(cvtcl)242 EXEC_DECL(cvtcl)
@@ -259,9 +260,10 @@ namespace · 17 unmodified lines
259 260
260 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));261 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));
261 262
262 auto str = at_val<vm_string_t>(r.dest);263 auto prev = at_val<vm_alloc_t>(r.dest);
264 dec_ref_count_and_free(prev);
265
263 pt_ref(r.dest) = new_string->get_allocation();266 pt_ref(r.dest) = new_string->get_allocation();
264 dec_ref_count_and_free(str);
265 }267 }
266 268
267 //269 //
@@ -725,9 +727,9 @@ namespace · 457 unmodified lines
725 auto index = vt_ref<word_t>(r.mid);727 auto index = vt_ref<word_t>(r.mid);
726 auto str = at_val<vm_string_t>(r.dest);728 auto str = at_val<vm_string_t>(r.dest);
727 if (str == nullptr)729 if (str == nullptr)
728 str = new vm_string_t();730 str = new vm_string_t{};
729 else if (str->get_ref_count() > 1)731 else if (str->get_ref_count() > 1)
730 str = new vm_string_t(*str, 0, str->get_length());732 str = new vm_string_t{ *str, 0, str->get_length() };
731 733
732 // Set the rune at the supplied index.734 // Set the rune at the supplied index.
733 str->set_rune(index, rune);735 str->set_rune(index, rune);
@@ -740,7 +742,8 @@ namespace · 6 unmodified lines
740 }742 }
741 }743 }
742 744
743 vm_string_t *_addc(vm_string_t *s1, const vm_string_t *s2, /* in out */ bool &append_to_s1)745 // Returns a string instance only if it is new
746 vm_string_t *concat_string(vm_string_t *s1, const vm_string_t *s2, bool try_append_to_s1)
744 {747 {
745 if (s1 == nullptr)748 if (s1 == nullptr)
746 {749 {
@@ -750,19 +753,23 @@ namespace · 3 unmodified lines
750 return new vm_string_t{ *s2, 0, s2->get_length() };753 return new vm_string_t{ *s2, 0, s2->get_length() };
751 }754 }
752 755
753 // There are other references to this string so we will not alter it.756 // If there are other references to the string, it is immutable.
754 if (s1->get_ref_count() > 1)757 if (s1->get_ref_count() > 1)
755 append_to_s1 = false;758 try_append_to_s1 = false;
756 759
757 if (s2 == nullptr)760 if (s2 == nullptr)
758 {761 {
759 if (append_to_s1)762 if (try_append_to_s1)
760 return s1;763 return nullptr;
761 764
762 return new vm_string_t(*s1, 0, s1->get_length());765 return new vm_string_t(*s1, 0, s1->get_length());
763 }766 }
764 767
765 return (append_to_s1) ? &(s1->append(*s2)) : new vm_string_t(*s1, *s2);768 if (!try_append_to_s1)
769 return new vm_string_t(*s1, *s2);
770
771 s1->append(*s2);
772 return nullptr;
766 }773 }
767 774
768 // [SPEC] 'Concat strings' op code775 // [SPEC] 'Concat strings' op code
@@ -775,20 +782,18 @@ namespace · 6 unmodified lines
775 // dst = src2 + src1782 // dst = src2 + src1
776 EXEC_DECL(addc)783 EXEC_DECL(addc)
777 {784 {
785 const auto try_append_to_s1 = r.mid == r.dest;
778 auto s1 = at_val<vm_string_t>(r.mid);786 auto s1 = at_val<vm_string_t>(r.mid);
779 const auto s2 = at_val<vm_string_t>(r.src);787 const auto s2 = at_val<vm_string_t>(r.src);
780 auto dest_maybe = at_val<vm_string_t>(r.dest);
781 788
782 // If the source is the same as the destination and the789 // If the source is the same as the destination, try to append the string during concatenation.
783 // destination is not null, append the string. This can 790 const auto new_str_maybe = concat_string(s1, s2, try_append_to_s1);
784 // change during the concat process.791 if (new_str_maybe != nullptr)
785 auto append_to_source = dest_maybe != nullptr && s1 == dest_maybe;
786 const auto str = _addc(s1, s2, append_to_source);
787
788 if (!append_to_source)
789 {792 {
790 pt_ref(r.dest) = str->get_allocation();793 auto dest_maybe = at_val<vm_alloc_t>(r.dest);
791 dec_ref_count_and_free(dest_maybe);794 dec_ref_count_and_free(dest_maybe);
795
796 pt_ref(r.dest) = new_str_maybe->get_allocation();
792 }797 }
793 }798 }
794 799
@@ -1659,6 +1664,7 @@ namespace · 864 unmodified lines
1659 // The remainder of the type is user-defined.1664 // The remainder of the type is user-defined.
1660 auto content = e->get_allocation();1665 auto content = e->get_allocation();
1661 exception_id = at_val<vm_string_t>(content);1666 exception_id = at_val<vm_string_t>(content);
1667 assert(exception_id != nullptr);
1662 1668
1663 if (debug::is_component_tracing_enabled<debug::component_trace_t::exception>())1669 if (debug::is_component_tracing_enabled<debug::component_trace_t::exception>())
1664 debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "raise: exception: >>%s<<", exception_id->str());1670 debug::log_msg(debug::component_trace_t::exception, debug::log_level_t::debug, "raise: exception: >>%s<<", exception_id->str());
src/vm/string.cpp+16−10
@@ -169,6 +169,12 @@ namespace · 168 unmodified lines
169 // Length + 1 (null) + space for string expansion169 // Length + 1 (null) + space for string expansion
170 return (current_length + 1 + (current_length / 4));170 return (current_length + 1 + (current_length / 4));
171 }171 }
172
173 template<typename T, word_t S>
174 constexpr word_t compute_max_length(const T(&)[S])
175 {
176 return S - 1;
177 }
172}178}
173 179
174vm_string_t::vm_string_t()180vm_string_t::vm_string_t()
@@ -177,7 +183,7 @@ vm_string_t::vm_string_t() · 2 unmodified lines
177 , _encoded_str{ nullptr }183 , _encoded_str{ nullptr }
178 , _is_alloc{ false }184 , _is_alloc{ false }
179 , _length{ 0 }185 , _length{ 0 }
180 , _length_max{ sizeof(_mem.local) }186 , _length_max{ compute_max_length(_mem.local) }
181 , _mem{}187 , _mem{}
182{ }188{ }
183 189
@@ -187,7 +193,7 @@ vm_string_t::vm_string_t(std::size_t encoded_str_len, const uint8_t *encoded_str · 3 unmodified lines
187 , _encoded_str{ nullptr }193 , _encoded_str{ nullptr }
188 , _is_alloc{ false }194 , _is_alloc{ false }
189 , _length{ 0 }195 , _length{ 0 }
190 , _length_max{ sizeof(_mem.local) }196 , _length_max{ compute_max_length(_mem.local) }
191 , _mem{}197 , _mem{}
192{198{
193 assert((encoded_str_len > 0 && encoded_str != nullptr) || encoded_str_len == 0);199 assert((encoded_str_len > 0 && encoded_str != nullptr) || encoded_str_len == 0);
@@ -203,11 +209,11 @@ vm_string_t::vm_string_t(std::size_t encoded_str_len, const uint8_t *encoded_str · 9 unmodified lines
203 _character_size = (encoded_str_len != utf8_length.codepoint_count) ? sizeof(rune_t) : sizeof(char);209 _character_size = (encoded_str_len != utf8_length.codepoint_count) ? sizeof(rune_t) : sizeof(char);
204 210
205 _length = static_cast<word_t>(utf8_length.codepoint_count);211 _length = static_cast<word_t>(utf8_length.codepoint_count);
206 _length_max = sizeof(_mem.local) / _character_size;212 _length_max = compute_max_length(_mem.local) / _character_size;
207 213
208 // Allocate memory based on character count and size214 // Allocate memory based on character count and size
209 auto dest = _mem.local;215 auto dest = _mem.local;
210 if ((encoded_str_len + 1) > sizeof(_mem.local))216 if (encoded_str_len > static_cast<std::size_t>(compute_max_length(_mem.local)))
211 {217 {
212 // If we are going to allocate, make sure it is larger than needed.218 // If we are going to allocate, make sure it is larger than needed.
213 _length_max = compute_max_length(_length);219 _length_max = compute_max_length(_length);
@@ -268,7 +274,7 @@ vm_string_t::vm_string_t(const vm_string_t &other, word_t begin_index, word_t en · 54 unmodified lines
268 , _length_max{ compute_max_length(end_index - begin_index) }274 , _length_max{ compute_max_length(end_index - begin_index) }
269 , _mem{}275 , _mem{}
270{276{
271 if (end_index <= begin_index)277 if (end_index < begin_index)
272 throw out_of_range_memory{};278 throw out_of_range_memory{};
273 279
274 if (begin_index < 0)280 if (begin_index < 0)
@@ -346,7 +352,7 @@ vm_string_t &vm_string_t::append(const vm_string_t &other) · 71 unmodified lines
346 352
347 // Define the values for the appended string353 // Define the values for the appended string
348 const auto new_length = local_length + other_length;354 const auto new_length = local_length + other_length;
349 const auto new_length_max = (new_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(new_length);355 const auto new_length_max = (new_length == 0) ? static_cast<word_t>(compute_max_length(_mem.local)) : compute_max_length(new_length);
350 const auto new_is_rune = local_is_rune || other_is_rune;356 const auto new_is_rune = local_is_rune || other_is_rune;
351 uint8_t *new_source = nullptr;357 uint8_t *new_source = nullptr;
352 358
@@ -442,7 +448,7 @@ void vm_string_t::set_rune(word_t index, rune_t value) · 89 unmodified lines
442 _character_size = static_cast<uint8_t>(new_character_size);448 _character_size = static_cast<uint8_t>(new_character_size);
443 assert(_character_size == sizeof(rune_t));449 assert(_character_size == sizeof(rune_t));
444 450
445 _length_max = (_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(_length);451 _length_max = (_length == 0) ? static_cast<word_t>(compute_max_length(_mem.local)) : compute_max_length(_length);
446 auto new_alloc = calloc_memory<rune_t>(_length_max * _character_size);452 auto new_alloc = calloc_memory<rune_t>(_length_max * _character_size);
447 453
448 // Copy source into the new alloc.454 // Copy source into the new alloc.
@@ -460,9 +466,9 @@ void vm_string_t::set_rune(word_t index, rune_t value) · 11 unmodified lines
460 }466 }
461 else if (static_cast<word_t>(_length_max) <= index) // Check if string can accomodate the new character.467 else if (static_cast<word_t>(_length_max) <= index) // Check if string can accomodate the new character.
462 {468 {
463 _length_max = (_length == 0) ? static_cast<word_t>(sizeof(_mem.local)) : compute_max_length(_length);469 _length_max = (_length == 0) ? static_cast<word_t>(compute_max_length(_mem.local)) : compute_max_length(_length);
464 auto new_alloc = calloc_memory<uint8_t>(_length_max * _character_size);470 auto new_alloc = calloc_memory<uint8_t>(_length_max * _character_size);
465 ::memcpy(new_alloc, source, _length * _character_size);471 std::memcpy(new_alloc, source, _length * _character_size);
466 472
467 // Free memory, if allocated473 // Free memory, if allocated
468 if (_is_alloc)474 if (_is_alloc)
@@ -503,7 +509,7 @@ const char *vm_string_t::str() const · 34 unmodified lines
503 if (_encoded_str != nullptr)509 if (_encoded_str != nullptr)
504 return _encoded_str;510 return _encoded_str;
505 511
506 // If the string is ASCII then just return the memory512 // If the string is ASCII then return the memory
507 if (_character_size == sizeof(char))513 if (_character_size == sizeof(char))
508 {514 {
509 _encoded_str = (_is_alloc) ? reinterpret_cast<char *>(_mem.alloc) : reinterpret_cast<char *>(_mem.local);515 _encoded_str = (_is_alloc) ? reinterpret_cast<char *>(_mem.alloc) : reinterpret_cast<char *>(_mem.local);
src/vm/sys/print.cpp+28−6
@@ -44,30 +44,32 @@ word_t disvm::runtime::sys::printf_to_buffer( · 43 unmodified lines
44 const auto fmt_len = msg_fmt.get_length();44 const auto fmt_len = msg_fmt.get_length();
45 auto i = word_t{ 0 };45 auto i = word_t{ 0 };
46 46
47 auto format = std::vector<char>{};
48 format.reserve(32); // Default size in Inferno.
49
47 // Loop over the entire format string50 // Loop over the entire format string
48 while (i < fmt_len)51 while (i < fmt_len)
49 {52 {
50 if (b_curr >= b_end)53 if (b_curr >= b_end)
51 return -1;54 return -1;
52 55
53 auto c = msg_fmt.get_rune(i);56 auto c = msg_fmt.get_rune(i++);
54 ++i;
55 if (c != '%')57 if (c != '%')
56 {58 {
57 b_curr = reinterpret_cast<char *>(utf8::encode(c, reinterpret_cast<uint8_t *>(b_curr)));59 b_curr = reinterpret_cast<char *>(utf8::encode(c, reinterpret_cast<uint8_t *>(b_curr)));
58 continue;60 continue;
59 }61 }
60 62
61 auto format = std::vector<char>{};63 format.clear();
62 format.push_back('%');64 format.push_back('%');
63 auto wb = int{ 0 };65 auto wb = int{ 0 };
64 auto big_flag = false;66 auto big_flag = false;
67 auto unsigned_flag = false;
65 68
66 // Decode the format specifier69 // Decode the format specifier
67 while (i < fmt_len)70 while (i < fmt_len)
68 {71 {
69 c = msg_fmt.get_rune(i);72 c = msg_fmt.get_rune(i++);
70 ++i;
71 switch (c)73 switch (c)
72 {74 {
73 // Format flags75 // Format flags
@@ -79,6 +81,21 @@ word_t disvm::runtime::sys::printf_to_buffer( · 5 unmodified lines
79 format.push_back('l');81 format.push_back('l');
80 big_flag = true;82 big_flag = true;
81 continue;83 continue;
84 case '*': // Placeholder for word argument
85 {
86 const auto w = *reinterpret_cast<word_t *>(msg_args);
87 std::array<char, sizeof("-2147483648")> buffer;
88 const auto len = std::sprintf(buffer.data(), "%d", w);
89 if (len < 0)
90 return -1;
91
92 format.insert(std::end(format), buffer.data(), buffer.data() + len);
93 msg_args += sizeof(w);
94 continue;
95 }
96 case 'u':
97 unsigned_flag = true;
98 continue;
82 99
83 // Format verbs100 // Format verbs
84 case '%': // Escaped specifier101 case '%': // Escaped specifier
@@ -100,10 +117,13 @@ word_t disvm::runtime::sys::printf_to_buffer( · 15 unmodified lines
100 msg_args += sizeof(r);117 msg_args += sizeof(r);
101 break;118 break;
102 }119 }
103 case 'o': // octal
104 case 'd': // decimal120 case 'd': // decimal
121 if (unsigned_flag)
122 c = 'u';
123 case 'o': // octal
105 case 'x': // hexadecimal124 case 'x': // hexadecimal
106 case 'X': // Upper-case hexadecimal125 case 'X': // Upper-case hexadecimal
126 case 'c': // Unicode
107 format.push_back(static_cast<char>(c));127 format.push_back(static_cast<char>(c));
108 format.push_back('\0');128 format.push_back('\0');
109 if (big_flag)129 if (big_flag)
@@ -121,6 +141,8 @@ word_t disvm::runtime::sys::printf_to_buffer( · 11 unmodified lines
121 msg_args += sizeof(w);141 msg_args += sizeof(w);
122 }142 }
123 break;143 break;
144 case 'q':
145 assert(false && "'q' format not implemented");
124 case 's': // string146 case 's': // string
125 {147 {
126 auto p = *reinterpret_cast<pointer_t *>(msg_args);148 auto p = *reinterpret_cast<pointer_t *>(msg_args);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t