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
171171
172172 auto dest_arr = _arr + (this_begin_index * _element_type->size_in_bytes);
173173
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.
175176 if (_element_type->map_in_bytes != 0)
176177 {
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;
178182 auto source_array_element = source._arr;
183+ auto dest_array_element = dest_arr;
179184 for (auto i = word_t{ 0 }; i < source._length; ++i)
180185 {
181− source_array_element += (i * type_desc.size_in_bytes);
182186 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;
183191 }
184192 }
185193
186− // Moving data within the same array is permitted so use memmove().
194+ // Copy data from within the same array is permitted.
187195 std::memmove(dest_arr, source._arr, (source._length * _element_type->size_in_bytes));
188196 }
src/vm/execution_table.cpp+30−24
@@ -79,7 +79,7 @@ namespace · 78 unmodified lines
7979 vt_ref<big_t>(r.dest) = vt_ref<big_t>(r.mid) >> vt_ref<big_t>(r.src);
8080 // Right shifting a negative number is implementation dependent in C++.
8181 // 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));
8383 }
8484
8585 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
206206 assert(static_cast<std::size_t>(len) <= buffer.size());
207207
208208 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));
209− auto str = at_val<vm_string_t>(r.dest);
210− dec_ref_count_and_free(str);
209+ auto prev = at_val<vm_alloc_t>(r.dest);
210+ dec_ref_count_and_free(prev);
211211
212212 pt_ref(r.dest) = new_string->get_allocation();
213213 }
@@ -233,9 +233,10 @@ namespace · 19 unmodified lines
233233
234234 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));
235235
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+
237239 pt_ref(r.dest) = new_string->get_allocation();
238− dec_ref_count_and_free(str);
239240 }
240241
241242 EXEC_DECL(cvtcl)
@@ -259,9 +260,10 @@ namespace · 17 unmodified lines
259260
260261 auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data()));
261262
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+
263266 pt_ref(r.dest) = new_string->get_allocation();
264− dec_ref_count_and_free(str);
265267 }
266268
267269 //
@@ -725,9 +727,9 @@ namespace · 457 unmodified lines
725727 auto index = vt_ref<word_t>(r.mid);
726728 auto str = at_val<vm_string_t>(r.dest);
727729 if (str == nullptr)
728− str = new vm_string_t();
730+ str = new vm_string_t{};
729731 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() };
731733
732734 // Set the rune at the supplied index.
733735 str->set_rune(index, rune);
@@ -740,7 +742,8 @@ namespace · 6 unmodified lines
740742 }
741743 }
742744
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)
744747 {
745748 if (s1 == nullptr)
746749 {
@@ -750,19 +753,23 @@ namespace · 3 unmodified lines
750753 return new vm_string_t{ *s2, 0, s2->get_length() };
751754 }
752755
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.
754757 if (s1->get_ref_count() > 1)
755− append_to_s1 = false;
758+ try_append_to_s1 = false;
756759
757760 if (s2 == nullptr)
758761 {
759− if (append_to_s1)
760− return s1;
762+ if (try_append_to_s1)
763+ return nullptr;
761764
762765 return new vm_string_t(*s1, 0, s1->get_length());
763766 }
764767
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;
766773 }
767774
768775 // [SPEC] 'Concat strings' op code
@@ -775,20 +782,18 @@ namespace · 6 unmodified lines
775782 // dst = src2 + src1
776783 EXEC_DECL(addc)
777784 {
785+ const auto try_append_to_s1 = r.mid == r.dest;
778786 auto s1 = at_val<vm_string_t>(r.mid);
779787 const auto s2 = at_val<vm_string_t>(r.src);
780− auto dest_maybe = at_val<vm_string_t>(r.dest);
781788
782− // If the source is the same as the destination and the
783− // destination is not null, append the string. This can
784− // change during the concat process.
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+ // If the source is the same as the destination, try to append the string during concatenation.
790+ const auto new_str_maybe = concat_string(s1, s2, try_append_to_s1);
791+ if (new_str_maybe != nullptr)
789792 {
790− pt_ref(r.dest) = str->get_allocation();
793+ auto dest_maybe = at_val<vm_alloc_t>(r.dest);
791794 dec_ref_count_and_free(dest_maybe);
795+
796+ pt_ref(r.dest) = new_str_maybe->get_allocation();
792797 }
793798 }
794799
@@ -1659,6 +1664,7 @@ namespace · 864 unmodified lines
16591664 // The remainder of the type is user-defined.
16601665 auto content = e->get_allocation();
16611666 exception_id = at_val<vm_string_t>(content);
1667+ assert(exception_id != nullptr);
16621668
16631669 if (debug::is_component_tracing_enabled<debug::component_trace_t::exception>())
16641670 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
169169 // Length + 1 (null) + space for string expansion
170170 return (current_length + 1 + (current_length / 4));
171171 }
172+
173+ template<typename T, word_t S>
174+ constexpr word_t compute_max_length(const T(&)[S])
175+ {
176+ return S - 1;
177+ }
172178 }
173179
174180 vm_string_t::vm_string_t()
@@ -177,7 +183,7 @@ vm_string_t::vm_string_t() · 2 unmodified lines
177183 , _encoded_str{ nullptr }
178184 , _is_alloc{ false }
179185 , _length{ 0 }
180− , _length_max{ sizeof(_mem.local) }
186+ , _length_max{ compute_max_length(_mem.local) }
181187 , _mem{}
182188 { }
183189
@@ -187,7 +193,7 @@ vm_string_t::vm_string_t(std::size_t encoded_str_len, const uint8_t *encoded_str · 3 unmodified lines
187193 , _encoded_str{ nullptr }
188194 , _is_alloc{ false }
189195 , _length{ 0 }
190− , _length_max{ sizeof(_mem.local) }
196+ , _length_max{ compute_max_length(_mem.local) }
191197 , _mem{}
192198 {
193199 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
203209 _character_size = (encoded_str_len != utf8_length.codepoint_count) ? sizeof(rune_t) : sizeof(char);
204210
205211 _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;
207213
208214 // Allocate memory based on character count and size
209215 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)))
211217 {
212218 // If we are going to allocate, make sure it is larger than needed.
213219 _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
268274 , _length_max{ compute_max_length(end_index - begin_index) }
269275 , _mem{}
270276 {
271− if (end_index <= begin_index)
277+ if (end_index < begin_index)
272278 throw out_of_range_memory{};
273279
274280 if (begin_index < 0)
@@ -346,7 +352,7 @@ vm_string_t &vm_string_t::append(const vm_string_t &other) · 71 unmodified lines
346352
347353 // Define the values for the appended string
348354 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);
350356 const auto new_is_rune = local_is_rune || other_is_rune;
351357 uint8_t *new_source = nullptr;
352358
@@ -442,7 +448,7 @@ void vm_string_t::set_rune(word_t index, rune_t value) · 89 unmodified lines
442448 _character_size = static_cast<uint8_t>(new_character_size);
443449 assert(_character_size == sizeof(rune_t));
444450
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);
446452 auto new_alloc = calloc_memory<rune_t>(_length_max * _character_size);
447453
448454 // 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
460466 }
461467 else if (static_cast<word_t>(_length_max) <= index) // Check if string can accomodate the new character.
462468 {
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);
464470 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);
466472
467473 // Free memory, if allocated
468474 if (_is_alloc)
@@ -503,7 +509,7 @@ const char *vm_string_t::str() const · 34 unmodified lines
503509 if (_encoded_str != nullptr)
504510 return _encoded_str;
505511
506− // If the string is ASCII then just return the memory
512+ // If the string is ASCII then return the memory
507513 if (_character_size == sizeof(char))
508514 {
509515 _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
4444 const auto fmt_len = msg_fmt.get_length();
4545 auto i = word_t{ 0 };
4646
47+ auto format = std::vector<char>{};
48+ format.reserve(32); // Default size in Inferno.
49+
4750 // Loop over the entire format string
4851 while (i < fmt_len)
4952 {
5053 if (b_curr >= b_end)
5154 return -1;
5255
53− auto c = msg_fmt.get_rune(i);
54− ++i;
56+ auto c = msg_fmt.get_rune(i++);
5557 if (c != '%')
5658 {
5759 b_curr = reinterpret_cast<char *>(utf8::encode(c, reinterpret_cast<uint8_t *>(b_curr)));
5860 continue;
5961 }
6062
61− auto format = std::vector<char>{};
63+ format.clear();
6264 format.push_back('%');
6365 auto wb = int{ 0 };
6466 auto big_flag = false;
67+ auto unsigned_flag = false;
6568
6669 // Decode the format specifier
6770 while (i < fmt_len)
6871 {
69− c = msg_fmt.get_rune(i);
70− ++i;
72+ c = msg_fmt.get_rune(i++);
7173 switch (c)
7274 {
7375 // Format flags
@@ -79,6 +81,21 @@ word_t disvm::runtime::sys::printf_to_buffer( · 5 unmodified lines
7981 format.push_back('l');
8082 big_flag = true;
8183 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;
8299
83100 // Format verbs
84101 case '%': // Escaped specifier
@@ -100,10 +117,13 @@ word_t disvm::runtime::sys::printf_to_buffer( · 15 unmodified lines
100117 msg_args += sizeof(r);
101118 break;
102119 }
103− case 'o': // octal
104120 case 'd': // decimal
121+ if (unsigned_flag)
122+ c = 'u';
123+ case 'o': // octal
105124 case 'x': // hexadecimal
106125 case 'X': // Upper-case hexadecimal
126+ case 'c': // Unicode
107127 format.push_back(static_cast<char>(c));
108128 format.push_back('\0');
109129 if (big_flag)
@@ -121,6 +141,8 @@ word_t disvm::runtime::sys::printf_to_buffer( · 11 unmodified lines
121141 msg_args += sizeof(w);
122142 }
123143 break;
144+ case 'q':
145+ assert(false && "'q' format not implemented");
124146 case 's': // string
125147 {
126148 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