History

Merge remote-tracking branch 'origin/master' into issue_11_fp

arr committed 9 years ago

Commit a50301e2 parents b3354b2 157f07717 files · +1482 −339Browse files

Unified · Split

17 files
  1. limbo/compiler/typecheck.b +3 −1
  2. readme.md +51 −0
  3. src/include/exceptions.h +9 −0
  4. src/include/utils.h +4 −4
  5. src/vm/address_decoding.inc +16 −4
  6. src/vm/disvm.gyp +3 −1
  7. src/vm/execution_table.cpp +77 −27
  8. src/vm/list.cpp +7 −5
  9. src/vm/math/Mathmod.cpp +209 −15
  10. src/vm/math/dgemm.c +411 −0
  11. src/vm/math/lsame.c +113 −0
  12. src/vm/sys/Sysmod.cpp +206 −175
  13. src/vm/sys/fd_manager.cpp +66 −47
  14. src/vm/sys/fd_types.cpp +217 −0
  15. src/vm/sys/fd_types.h +78 −0
  16. src/vm/sys/file_system.cpp +0 −46
  17. src/vm/sys/sys_utils.h +12 −14
limbo/compiler/typecheck.b+3−1
@@ -759,9 +759,11 @@ concheck(n: ref Node, isglobal: int) · 758 unmodified lines
759759 exname(d: ref Decl): string
760760 {
761761 s := "";
762− m := impmods.sym;
762+ m : ref Sym;
763763 if(d.dot != nil)
764764 m = d.dot.sym;
765+ else if(impmods != nil)
766+ m = impmods.sym;
765767 if(m != nil)
766768 s += m.name+".";
767769 if(fndec != nil)
readme.md+51−0
@@ -116,6 +116,57 @@ The above copyright notice and this permission notice shall be included in all c · 115 unmodified lines
116116
117117 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
118118
119+### [LAPACK](http://www.netlib.org/lapack/#_licensing) ###
120+
121+Copyright (c) 1992-2013 The University of Tennessee and The University
122+ of Tennessee Research Foundation. All rights
123+ reserved.
124+Copyright (c) 2000-2013 The University of California Berkeley. All
125+ rights reserved.
126+Copyright (c) 2006-2013 The University of Colorado Denver. All rights
127+ reserved.
128+
129+$COPYRIGHT$
130+
131+Additional copyrights may follow
132+
133+$HEADER$
134+
135+Redistribution and use in source and binary forms, with or without
136+modification, are permitted provided that the following conditions are
137+met:
138+
139+- Redistributions of source code must retain the above copyright
140+ notice, this list of conditions and the following disclaimer.
141+
142+- Redistributions in binary form must reproduce the above copyright
143+ notice, this list of conditions and the following disclaimer listed
144+ in this license in the documentation and/or other materials
145+ provided with the distribution.
146+
147+- Neither the name of the copyright holders nor the names of its
148+ contributors may be used to endorse or promote products derived from
149+ this software without specific prior written permission.
150+
151+The copyright holders provide no reassurances that the source code
152+provided does not infringe any patent, copyright, or any other
153+intellectual property rights of third parties. The copyright holders
154+disclaim any liability to any recipient for claims brought against
155+recipient by any third party for infringement of that parties
156+intellectual property rights.
157+
158+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
159+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
160+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
161+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
162+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
163+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
164+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
165+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
166+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
167+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
168+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
169+
119170 ### Unless otherwise stated in a `license.txt` or above - [MIT License](https://opensource.org/licenses/MIT) ###
120171
121172 Copyright (c) 2017 arr
src/include/exceptions.h+9−0
@@ -124,6 +124,15 @@ namespace disvm · 123 unmodified lines
124124 const word_t invalid_value;
125125 };
126126
127+ // [TODO] Allow non-string exceptions to be marshalled.
128+ class marshallable_user_exception final : public vm_user_exception
129+ {
130+ public:
131+ explicit marshallable_user_exception(const char *message)
132+ : vm_user_exception(message)
133+ { }
134+ };
135+
127136 class unhandled_user_exception final : public vm_user_exception
128137 {
129138 public:
src/include/utils.h+4−4
@@ -76,12 +76,12 @@ namespace disvm · 75 unmodified lines
7676
7777 // Adopted from 'winnt.h' in the Windows SDK
7878 #define DEFINE_ENUM_FLAG_OPERATORS(TYPE) \
79−inline TYPE operator | (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) | static_cast<std::underlying_type_t<TYPE>>(b)); } \
79+constexpr TYPE operator | (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) | static_cast<std::underlying_type_t<TYPE>>(b)); } \
8080 inline TYPE &operator |= (TYPE &a, TYPE b) { a = a | b; return a; } \
81−inline TYPE operator & (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) & static_cast<std::underlying_type_t<TYPE>>(b)); } \
81+constexpr TYPE operator & (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) & static_cast<std::underlying_type_t<TYPE>>(b)); } \
8282 inline TYPE &operator &= (TYPE &a, TYPE b) { a = a & b; return a; } \
83−inline TYPE operator ^ (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) ^ static_cast<std::underlying_type_t<TYPE>>(b)); } \
83+constexpr TYPE operator ^ (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<std::underlying_type_t<TYPE>>(a) ^ static_cast<std::underlying_type_t<TYPE>>(b)); } \
8484 inline TYPE &operator ^= (TYPE &a, TYPE b) { a = a ^ b; return a; } \
85−inline TYPE operator ~ (TYPE a) { return static_cast<TYPE>(~static_cast<std::underlying_type_t<TYPE>>(a)); }
85+constexpr TYPE operator ~ (TYPE a) { return static_cast<TYPE>(~static_cast<std::underlying_type_t<TYPE>>(a)); }
8686
8787 #endif // _DISVM_SRC_INCLUDE_UTILS_H_
src/vm/address_decoding.inc+16−4
@@ -32,7 +32,10 @@ void dec_src<address_mode_t::offset_double_indirect_fp>(const vm_exec_op_t &inst · 31 unmodified lines
3232 {
3333 assert(inst.source.mode == address_mode_t::offset_double_indirect_fp);
3434 const auto frame_offset = *reinterpret_cast<std::size_t *>(reinterpret_cast<uint8_t *>(reg.stack.peek_frame()->base()) + inst.source.register1);
35− reg.src = reinterpret_cast<pointer_t>(frame_offset + inst.source.register2);
35+ if (frame_offset != disvm::runtime::runtime_constants::nil)
36+ reg.src = reinterpret_cast<pointer_t>(frame_offset + inst.source.register2);
37+ else
38+ reg.src = reinterpret_cast<pointer_t>(disvm::runtime::runtime_constants::nil);
3639 }
3740
3841 template<>
@@ -40,7 +43,10 @@ void dec_src<address_mode_t::offset_double_indirect_mp>(const vm_exec_op_t &inst · 1 unmodified line
4043 {
4144 assert(inst.source.mode == address_mode_t::offset_double_indirect_mp);
4245 const auto mp_offset = *reinterpret_cast<std::size_t *>(reinterpret_cast<uint8_t *>(reg.mp_base->get_allocation()) + inst.source.register1);
43− reg.src = reinterpret_cast<pointer_t>(mp_offset + inst.source.register2);
46+ if (mp_offset != disvm::runtime::runtime_constants::nil)
47+ reg.src = reinterpret_cast<pointer_t>(mp_offset + inst.source.register2);
48+ else
49+ reg.src = reinterpret_cast<pointer_t>(disvm::runtime::runtime_constants::nil);
4450 }
4551
4652 template<>
@@ -85,7 +91,10 @@ void dec_dest<address_mode_t::offset_double_indirect_fp>(const vm_exec_op_t &ins · 38 unmodified lines
8591 {
8692 assert(inst.destination.mode == address_mode_t::offset_double_indirect_fp);
8793 const auto frame_offset = *reinterpret_cast<std::size_t *>(reinterpret_cast<uint8_t *>(reg.stack.peek_frame()->base()) + inst.destination.register1);
88− reg.dest = reinterpret_cast<pointer_t>(frame_offset + inst.destination.register2);
94+ if (frame_offset != disvm::runtime::runtime_constants::nil)
95+ reg.dest = reinterpret_cast<pointer_t>(frame_offset + inst.destination.register2);
96+ else
97+ reg.dest = reinterpret_cast<pointer_t>(disvm::runtime::runtime_constants::nil);
8998 }
9099
91100 template<>
@@ -93,7 +102,10 @@ void dec_dest<address_mode_t::offset_double_indirect_mp>(const vm_exec_op_t &ins · 1 unmodified line
93102 {
94103 assert(inst.destination.mode == address_mode_t::offset_double_indirect_mp);
95104 const auto mp_offset = *reinterpret_cast<std::size_t *>(reinterpret_cast<uint8_t *>(reg.mp_base->get_allocation()) + inst.destination.register1);
96− reg.dest = reinterpret_cast<pointer_t>(mp_offset + inst.destination.register2);
105+ if (mp_offset != disvm::runtime::runtime_constants::nil)
106+ reg.dest = reinterpret_cast<pointer_t>(mp_offset + inst.destination.register2);
107+ else
108+ reg.dest = reinterpret_cast<pointer_t>(disvm::runtime::runtime_constants::nil);
97109 }
98110
99111 template<>
src/vm/disvm.gyp+3−1
@@ -24,10 +24,12 @@ · 23 unmodified lines
2424 'vm_memory.cpp',
2525 'vm_exception_handler.cpp',
2626 'math/Mathmod.cpp',
27+ 'math/dgemm.c',
28+ 'math/lsame.c',
2729 'sys/print.cpp',
2830 'sys/Sysmod.cpp',
29− 'sys/file_system.cpp',
3031 'sys/fd_manager.cpp',
32+ 'sys/fd_types.cpp',
3133 ],
3234 'include_dirs': [
3335 '../include'
src/vm/execution_table.cpp+77−27
@@ -164,7 +164,7 @@ namespace · 163 unmodified lines
164164 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
165165
166166 auto str = at_val<vm_string_t>(r.src);
167− auto new_array = new vm_array_t(str);
167+ auto new_array = new vm_array_t{ str };
168168 pt_ref(r.dest) = new_array->get_allocation();
169169 }
170170
@@ -537,7 +537,7 @@ namespace · 366 unmodified lines
537537 auto type_id = vt_ref<word_t>(r.mid);
538538 const auto &types = r.module_ref->type_section;
539539 assert(0 <= type_id && type_id < static_cast<word_t>(types.size()));
540− auto type = types[type_id];
540+ const auto &type = types[type_id];
541541
542542 disvm::runtime::inc_ref_count_in_memory(*type, r.src);
543543 destroy_memory(*type, r.dest);
@@ -853,7 +853,7 @@ namespace · 309 unmodified lines
853853 throw out_of_range_memory{};
854854 }
855855
856− auto new_array = new vm_array_t(*arr, begin_index, length);
856+ auto new_array = new vm_array_t{ *arr, begin_index, length };
857857
858858 if (arr->alloc_type->map_in_bytes > 0)
859859 vm.get_garbage_collector().track_allocation(new_array);
@@ -971,14 +971,15 @@ namespace · 111 unmodified lines
971971 auto tail_maybe = at_val<vm_list_t>(r.dest);
972972
973973 // Create a new list
974− auto new_list = new vm_list_t{ type, tail_maybe };
974+ const auto type_size_in_bytes = type->size_in_bytes;
975+ auto new_list = new vm_list_t{ std::move(type), tail_maybe };
975976 if (tail_maybe != nullptr)
976977 tail_maybe->release();
977978
978979 auto destination = new_list->value();
979− std::memmove(destination, r.src, type->size_in_bytes);
980+ std::memmove(destination, r.src, type_size_in_bytes);
980981
981− if (type->map_in_bytes > 0)
982+ if (type_size_in_bytes > 0)
982983 vm.get_garbage_collector().track_allocation(new_list);
983984
984985 // Return the list
@@ -1329,9 +1330,10 @@ namespace · 344 unmodified lines
13291330 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
13301331
13311332 auto type_desc = types[type_id];
1332− auto new_alloc = vm_alloc_t::allocate(type_desc);
1333+ const auto track_object = type_desc->map_in_bytes > 0;
1334+ auto new_alloc = vm_alloc_t::allocate(std::move(type_desc));
13331335
1334− if (type_desc->map_in_bytes > 0)
1336+ if (track_object)
13351337 vm.get_garbage_collector().track_allocation(new_alloc);
13361338
13371339 pt_ref(r.dest) = new_alloc->get_allocation();
@@ -1363,9 +1365,10 @@ namespace · 25 unmodified lines
13631365 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
13641366
13651367 auto type_desc = types[type_id];
1366− auto new_alloc = vm_alloc_t::allocate(type_desc);
1368+ const auto track_object = type_desc->map_in_bytes > 0;
1369+ auto new_alloc = vm_alloc_t::allocate(std::move(type_desc));
13671370
1368− if (type_desc->map_in_bytes > 0)
1371+ if (track_object)
13691372 vm.get_garbage_collector().track_allocation(new_alloc);
13701373
13711374 pt_ref(r.dest) = new_alloc->get_allocation();
@@ -1385,9 +1388,10 @@ namespace · 13 unmodified lines
13851388 dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest));
13861389
13871390 const auto array_size = vt_ref<word_t>(r.src);
1388− auto new_array = new vm_array_t(type_desc, array_size);
1391+ const auto track_object = type_desc->map_in_bytes > 0;
1392+ auto new_array = new vm_array_t{ std::move(type_desc), array_size };
13891393
1390− if (type_desc->map_in_bytes > 0)
1394+ if (track_object)
13911395 vm.get_garbage_collector().track_allocation(new_array);
13921396
13931397 pt_ref(r.dest) = new_array->get_allocation();
@@ -1437,7 +1441,7 @@ namespace · 43 unmodified lines
14371441
14381442 const auto frame_type_id = vt_ref<word_t>(r.src);
14391443 assert(0 <= frame_type_id && frame_type_id < static_cast<word_t>(types.size()));
1440− const auto frame_type = types[frame_type_id];
1444+ auto frame_type = types[frame_type_id];
14411445
14421446 pt_ref(r.dest) = r.stack.alloc_frame(std::move(frame_type))->base();
14431447 }
@@ -1456,9 +1460,9 @@ namespace · 12 unmodified lines
14561460
14571461 const auto frame_type_id = function_ref.frame_type;
14581462 assert(0 <= frame_type_id && frame_type_id < static_cast<word_t>(types.size()));
1459− const auto frame_type = types[frame_type_id];
1463+ auto frame_type = types[frame_type_id];
14601464
1461− pt_ref(r.dest) = r.stack.alloc_frame(frame_type)->base();
1465+ pt_ref(r.dest) = r.stack.alloc_frame(std::move(frame_type))->base();
14621466 }
14631467
14641468 EXEC_DECL(ret)
@@ -1503,6 +1507,9 @@ namespace · 38 unmodified lines
15031507 disvm::debug::log_msg(debug::component_trace_t::stack, debug::log_level_t::debug, "enter: function");
15041508 }
15051509
1510+ // Forward declaration
1511+ EXEC_DECL(raise);
1512+
15061513 EXEC_DECL(mcall)
15071514 {
15081515 auto target_module = at_val<vm_module_ref_t>(r.dest);
@@ -1547,22 +1554,52 @@ namespace · 38 unmodified lines
15471554 r.mp_base = r.module_ref->mp_base;
15481555 r.next_pc = function_pc;
15491556
1550− if (r.module_ref->is_builtin_module())
1557+ // Non-built-in modules ref count and return
1558+ if (!r.module_ref->is_builtin_module())
15511559 {
1552− const auto &inst = r.module_ref->code_section[function_pc];
1553− assert(r.mp_base == nullptr && "Built-in modules shouldn't have module data (MP register)");
1560+ r.mp_base->add_ref();
1561+ return;
1562+ }
1563+
1564+ // Calling into a built-in module is a bit complicated since control leaves the VM and thus
1565+ // the callee clean-up semantics of DisVM are difficult to enforced. The solution here is
1566+ // to release VM control of the thread and clean-up the stack on behalf of the callee when
1567+ // control returns to the VM.
1568+ // Built-in modules are also permitted to throw marshallable exceptions which requires
1569+ // raising the exception manually.
15541570
1555− r.current_thread_state = vm_thread_state_t::release;
1571+ const auto &inst = r.module_ref->code_section[function_pc];
1572+ assert(r.mp_base == nullptr && "Built-in modules shouldn't have module data (MP register)");
1573+
1574+ vm_alloc_t *marshallable_err{};
1575+ r.current_thread_state = vm_thread_state_t::release;
1576+
1577+ try
1578+ {
15561579 inst.native(r, vm);
1557− ret(r, vm);
15581580
1559− // Only reset the thread if it is in the state set prior to native call.
1560− if (r.current_thread_state == vm_thread_state_t::release)
1561− r.current_thread_state = vm_thread_state_t::running;
1581+ // Handle callee clean-up semantics
1582+ ret(r, vm);
15621583 }
1563− else
1584+ catch (const marshallable_user_exception &e)
15641585 {
1565− r.mp_base->add_ref();
1586+ auto msg = e.what();
1587+ marshallable_err = new vm_string_t{ std::strlen(msg), reinterpret_cast<const uint8_t*>(msg) };
1588+ }
1589+
1590+ // Only reset the thread if it is in the state set prior to native call.
1591+ if (r.current_thread_state == vm_thread_state_t::release)
1592+ r.current_thread_state = vm_thread_state_t::running;
1593+
1594+ // Raise marshallable exception
1595+ if (marshallable_err != nullptr)
1596+ {
1597+ assert(r.current_thread_state == vm_thread_state_t::running && "Throwing a marshallable error should not result in an altered VM thread state");
1598+ auto err = marshallable_err->get_allocation();
1599+ r.src = reinterpret_cast<pointer_t>(&err);
1600+ raise(r, vm);
1601+
1602+ disvm::runtime::dec_ref_count_and_free(marshallable_err);
15661603 }
15671604 }
15681605
@@ -1706,10 +1743,23 @@ namespace · 137 unmodified lines
17061743
17071744 if (target_frame != r.stack.peek_frame())
17081745 {
1709− while (target_frame != r.stack.pop_frame())
1746+ // Unwind stack to find target frame
1747+ do
17101748 {
1711− // Remove frames from the stack until the target frame is found.
1749+ auto curr_frame = r.stack.peek_frame();
1750+
1751+ // Update the module reference register during the stack unwind
1752+ if (curr_frame->prev_module_ref() != nullptr)
1753+ {
1754+ dec_ref_count_and_free(r.module_ref);
1755+ r.module_ref = curr_frame->prev_module_ref();
1756+ curr_frame->prev_module_ref() = nullptr;
1757+
1758+ dec_ref_count_and_free(r.mp_base);
1759+ r.mp_base = r.module_ref->mp_base;
1760+ }
17121761 }
1762+ while (target_frame != r.stack.pop_frame());
17131763 }
17141764
17151765 // Re-initialize the current frame
src/vm/list.cpp+7−5
@@ -45,10 +45,6 @@ vm_list_t::vm_list_t(std::shared_ptr<const type_descriptor_t> td, vm_list_t *tai · 44 unmodified lines
4545
4646 vm_list_t::~vm_list_t()
4747 {
48− // Remove tail
49− dec_ref_count_and_free(_tail);
50− debug::assign_debug_pointer(&_tail);
51−
5248 // Destroy and free the list element, if default element size was surpassed.
5349 if (_is_alloc)
5450 {
@@ -61,7 +57,13 @@ vm_list_t::~vm_list_t() · 6 unmodified lines
6157 }
6258
6359 debug::assign_debug_pointer(&_mem.alloc);
64− disvm::debug::log_msg(component_trace_t::memory, log_level_t::debug, "destroy: vm list");
60+
61+ // Remove tail
62+ dec_ref_count_and_free(_tail);
63+ debug::assign_debug_pointer(&_tail);
64+
65+ if (disvm::debug::is_component_tracing_enabled<component_trace_t::memory>())
66+ disvm::debug::log_msg(component_trace_t::memory, log_level_t::debug, "destroy: vm list");
6567 }
6668
6769 std::shared_ptr<const type_descriptor_t> vm_list_t::get_element_type() const
src/vm/math/Mathmod.cpp+209−15
@@ -24,6 +24,7 @@ using disvm::debug::log_level_t; · 23 unmodified lines
2424
2525 using disvm::runtime::dereference_nil;
2626 using disvm::runtime::intrinsic_type_desc;
27+using disvm::runtime::out_of_range_memory;
2728 using disvm::runtime::real_t;
2829 using disvm::runtime::vm_alloc_t;
2930 using disvm::runtime::vm_array_t;
@@ -430,7 +431,28 @@ void · 400 unmodified lines
430431 Math_export_int(vm_registers_t &r, vm_t &vm)
431432 {
432433 auto &fp = r.stack.peek_frame()->base<F_Math_export_int>();
433− throw vm_system_exception{ "Function not implemented" };
434+
435+ auto buffer = vm_alloc_t::from_allocation<vm_array_t>(fp.b);
436+ assert(buffer->get_element_type()->is_equal(intrinsic_type_desc::type<byte_t>().get()));
437+
438+ auto values = vm_alloc_t::from_allocation<vm_array_t>(fp.x);
439+ assert(values->get_element_type()->is_equal(intrinsic_type_desc::type<word_t>().get()));
440+ const auto values_len = values->get_length();
441+
442+ if (buffer->get_length() != (sizeof(word_t) * values_len))
443+ throw vm_user_exception{ "Invalid buffer size based on conversion" };
444+
445+ // Insert as big endian
446+ for (int i = 0; i < values_len; ++i)
447+ {
448+ auto x = values->at<word_t>(i);
449+
450+ auto base = reinterpret_cast<byte_t *>(buffer->at(i * sizeof(x)));
451+ base[0] = static_cast<byte_t>(x >> 24);
452+ base[1] = static_cast<byte_t>(x >> 16);
453+ base[2] = static_cast<byte_t>(x >> 8);
454+ base[3] = static_cast<byte_t>(x);
455+ }
434456 }
435457
436458 void
@@ -445,7 +467,7 @@ Math_export_real(vm_registers_t &r, vm_t &vm) · 8 unmodified lines
445467 assert(values->get_element_type()->is_equal(intrinsic_type_desc::type<real_t>().get()));
446468 const auto values_len = values->get_length();
447469
448− if (buffer->get_length() != (8 * values_len))
470+ if (buffer->get_length() != (sizeof(real_t) * values_len))
449471 throw vm_user_exception{ "Invalid buffer size based on conversion" };
450472
451473 // Insert as big endian
@@ -472,7 +494,32 @@ void · 20 unmodified lines
472494 Math_export_real32(vm_registers_t &r, vm_t &vm)
473495 {
474496 auto &fp = r.stack.peek_frame()->base<F_Math_export_real32>();
475− throw vm_system_exception{ "Function not implemented" };
497+
498+ auto buffer = vm_alloc_t::from_allocation<vm_array_t>(fp.b);
499+ assert(buffer->get_element_type()->is_equal(intrinsic_type_desc::type<byte_t>().get()));
500+
501+ auto values = vm_alloc_t::from_allocation<vm_array_t>(fp.x);
502+ assert(values->get_element_type()->is_equal(intrinsic_type_desc::type<real_t>().get()));
503+ const auto values_len = values->get_length();
504+
505+ if (buffer->get_length() != (sizeof(float) * values_len))
506+ throw vm_user_exception{ "Invalid buffer size based on conversion" };
507+
508+ // Insert as big endian
509+ for (int i = 0; i < values_len; ++i)
510+ {
511+ auto x_r = values->at<real_t>(i);
512+ auto x = static_cast<float>(x_r);
513+ uint32_t t;
514+ std::memcpy(&t, &x, sizeof(x));
515+ static_assert(sizeof(t) == sizeof(x), "Short-real should be 32-bits");
516+
517+ auto base = reinterpret_cast<byte_t *>(buffer->at(i * sizeof(x)));
518+ base[0] = static_cast<byte_t>(t >> 24);
519+ base[1] = static_cast<byte_t>(t >> 16);
520+ base[2] = static_cast<byte_t>(t >> 8);
521+ base[3] = static_cast<byte_t>(t);
522+ }
476523 }
477524
478525 void
@@ -524,11 +571,71 @@ Math_fmod(vm_registers_t &r, vm_t &vm) · 45 unmodified lines
524571 *fp.ret = std::fmod(fp.x, fp.y);
525572 }
526573
574+// Forward declaration
575+extern "C" int dgemm_(
576+ char *transa, char *transb,
577+ int *m, int *n, int *k,
578+ double *alpha,
579+ double *a, int *lda,
580+ double *b, int *ldb,
581+ double *beta,
582+ double *c, int *ldc,
583+ int a_len, int b_len, int c_len);
584+
527585 void
528586 Math_gemm(vm_registers_t &r, vm_t &vm)
529587 {
530588 auto &fp = r.stack.peek_frame()->base<F_Math_gemm>();
531− throw vm_system_exception{ "Function not implemented" };
589+
590+ char t_a[2] = {};
591+ t_a[0] = static_cast<char>(fp.transa);
592+
593+ char t_b[2] = {};
594+ t_b[0] = static_cast<char>(fp.transb);
595+
596+ auto m = fp.m;
597+ auto n = fp.n;
598+ auto k = fp.k;
599+
600+ auto alpha = fp.alpha;
601+ auto beta = fp.beta;
602+
603+ auto lda = fp.lda;
604+ auto a_v = vm_alloc_t::from_allocation<vm_array_t>(fp.a);
605+ if (a_v == nullptr)
606+ throw dereference_nil{ "gemm - a" };
607+
608+ auto ldb = fp.ldb;
609+ auto b_v = vm_alloc_t::from_allocation<vm_array_t>(fp.b);
610+ if (b_v == nullptr)
611+ throw dereference_nil{ "gemm - b" };
612+
613+ auto ldc = fp.ldc;
614+ auto c_v = vm_alloc_t::from_allocation<vm_array_t>(fp.c);
615+ if (c_v == nullptr)
616+ throw dereference_nil{ "gemm - c" };
617+
618+ assert(a_v->get_element_type()->is_equal(intrinsic_type_desc::type<real_t>().get()));
619+ assert(b_v->get_element_type()->is_equal(intrinsic_type_desc::type<real_t>().get()));
620+ assert(c_v->get_element_type()->is_equal(intrinsic_type_desc::type<real_t>().get()));
621+
622+ const auto a_len = a_v->get_length();
623+ const auto b_len = b_v->get_length();
624+ const auto c_len = c_v->get_length();
625+
626+ // Call BLAS version of function
627+ const auto result = dgemm_(
628+ t_a, t_b,
629+ &m, &n, &k,
630+ &alpha,
631+ reinterpret_cast<double *>(a_v->at(0)), &lda,
632+ reinterpret_cast<double *>(b_v->at(0)), &ldb,
633+ &beta,
634+ reinterpret_cast<double *>(c_v->at(0)), &ldc,
635+ a_len, b_len, c_len);
636+
637+ if (result != 0)
638+ throw vm_user_exception{ "gemm invalid argument" };
532639 }
533640
534641 void
@@ -556,7 +663,34 @@ void · 21 unmodified lines
556663 Math_iamax(vm_registers_t &r, vm_t &vm)
557664 {
558665 auto &fp = r.stack.peek_frame()->base<F_Math_iamax>();
559− throw vm_system_exception{ "Function not implemented" };
666+
667+ // [SPEC] According to the Inferno implementation (libmath/blas.c), 0 is returned
668+ // if the array is empty. This means there is no indication the supplied array was
669+ // empty or the first index was max. The spec does not describe this condition,
670+ // therefore this implementation will indicate with -1.
671+ *fp.ret = -1;
672+
673+ auto result = vm_alloc_t::from_allocation<vm_array_t>(fp.x);
674+ assert(result->get_element_type()->is_equal(intrinsic_type_desc::type<real_t>().get()));
675+ if (result->get_length() <= 0)
676+ return;
677+
678+ auto r_raw = reinterpret_cast<real_t *>(result->at(0));
679+
680+ auto max_idx = 0;
681+ auto r_len = result->get_length();
682+ auto curr_max = std::fabs(r_raw[max_idx]);
683+ for (auto i = word_t{ 1 }; i < r_len; ++i)
684+ {
685+ auto t = std::fabs(r_raw[i]);
686+ if (curr_max < t)
687+ {
688+ max_idx = i;
689+ curr_max = t;
690+ }
691+ }
692+
693+ *fp.ret = max_idx;
560694 }
561695
562696 void
@@ -570,7 +704,27 @@ void · 7 unmodified lines
570704 Math_import_int(vm_registers_t &r, vm_t &vm)
571705 {
572706 auto &fp = r.stack.peek_frame()->base<F_Math_import_int>();
573− throw vm_system_exception{ "Function not implemented" };
707+
708+ auto buffer = vm_alloc_t::from_allocation<vm_array_t>(fp.b);
709+ assert(buffer->get_element_type()->is_equal(intrinsic_type_desc::type<byte_t>().get()));
710+
711+ auto result = vm_alloc_t::from_allocation<vm_array_t>(fp.x);
712+ assert(result->get_element_type()->is_equal(intrinsic_type_desc::type<word_t>().get()));
713+ const auto result_len = result->get_length();
714+
715+ if (buffer->get_length() != (sizeof(word_t) * result_len))
716+ throw vm_user_exception{ "Invalid buffer size based on conversion" };
717+
718+ // Process as big endian
719+ for (int i = 0; i < result_len; ++i)
720+ {
721+ uint32_t t = buffer->at<byte_t>(0);
722+ for (int j = 1; j < sizeof(word_t); ++j)
723+ t = (t << 8) | buffer->at<byte_t>(j);
724+
725+ auto x = static_cast<word_t>(t);
726+ result->at<word_t>(i) = x;
727+ }
574728 }
575729
576730 void
@@ -585,14 +739,14 @@ Math_import_real(vm_registers_t &r, vm_t &vm) · 8 unmodified lines
585739 assert(result->get_element_type()->is_equal(intrinsic_type_desc::type<real_t>().get()));
586740 const auto result_len = result->get_length();
587741
588− if (buffer->get_length() != (8 * result_len))
742+ if (buffer->get_length() != (sizeof(real_t) * result_len))
589743 throw vm_user_exception{ "Invalid buffer size based on conversion" };
590744
591745 // Process as big endian
592746 for (int i = 0; i < result_len; ++i)
593747 {
594748 uint64_t t = buffer->at<byte_t>(0);
595− for (int j = 1; j < 8; ++j)
749+ for (int j = 1; j < sizeof(real_t); ++j)
596750 t = (t << 8) | buffer->at<byte_t>(j);
597751
598752 real_t x;
@@ -607,7 +761,31 @@ void · 8 unmodified lines
607761 Math_import_real32(vm_registers_t &r, vm_t &vm)
608762 {
609763 auto &fp = r.stack.peek_frame()->base<F_Math_import_real32>();
610− throw vm_system_exception{ "Function not implemented" };
764+
765+ auto buffer = vm_alloc_t::from_allocation<vm_array_t>(fp.b);
766+ assert(buffer->get_element_type()->is_equal(intrinsic_type_desc::type<byte_t>().get()));
767+
768+ auto result = vm_alloc_t::from_allocation<vm_array_t>(fp.x);
769+ assert(result->get_element_type()->is_equal(intrinsic_type_desc::type<real_t>().get()));
770+ const auto result_len = result->get_length();
771+
772+ if (buffer->get_length() != (sizeof(float) * result_len))
773+ throw vm_user_exception{ "Invalid buffer size based on conversion" };
774+
775+ // Process as big endian
776+ for (int i = 0; i < result_len; ++i)
777+ {
778+ uint32_t t = buffer->at<byte_t>(0);
779+ for (int j = 1; j < sizeof(float); ++j)
780+ t = (t << 8) | buffer->at<byte_t>(j);
781+
782+ float x_f;
783+ std::memcpy(&x_f, &t, sizeof(t));
784+ static_assert(sizeof(t) == sizeof(x_f), "Short-real should be 32-bits");
785+
786+ auto x = real_t{ x_f };
787+ result->at<real_t>(i) = x;
788+ }
611789 }
612790
613791 void
@@ -621,6 +799,8 @@ void · 7 unmodified lines
621799 Math_j0(vm_registers_t &r, vm_t &vm)
622800 {
623801 auto &fp = r.stack.peek_frame()->base<F_Math_j0>();
802+
803+ // [PAL] Consume platform API
624804 throw vm_system_exception{ "Function not implemented" };
625805 }
626806
@@ -628,6 +808,8 @@ void · 1 unmodified line
628808 Math_j1(vm_registers_t &r, vm_t &vm)
629809 {
630810 auto &fp = r.stack.peek_frame()->base<F_Math_j1>();
811+
812+ // [PAL] Consume platform API
631813 throw vm_system_exception{ "Function not implemented" };
632814 }
633815
@@ -635,6 +817,8 @@ void · 1 unmodified line
635817 Math_jn(vm_registers_t &r, vm_t &vm)
636818 {
637819 auto &fp = r.stack.peek_frame()->base<F_Math_jn>();
820+
821+ // [PAL] Consume platform API
638822 throw vm_system_exception{ "Function not implemented" };
639823 }
640824
@@ -670,7 +854,13 @@ void · 29 unmodified lines
670854 Math_modf(vm_registers_t &r, vm_t &vm)
671855 {
672856 auto &fp = r.stack.peek_frame()->base<F_Math_modf>();
673− throw vm_system_exception{ "Function not implemented" };
857+
858+ real_t integral_out;
859+ const auto fractional_part = std::modf(fp.x, &integral_out);
860+ const auto integral_part = static_cast<word_t>(integral_out);
861+
862+ fp.ret->t0 = integral_part;
863+ fp.ret->t1 = fractional_part;
674864 }
675865
676866 void
@@ -705,9 +895,7 @@ Math_norm1(vm_registers_t &r, vm_t &vm) · 28 unmodified lines
705895 void
706896 Math_norm2(vm_registers_t &r, vm_t &vm)
707897 {
708− // [SPEC] This function is defined as: norm2(x) = sqrt(dot(x, x))
709− // The Inferno implementation (libmath/blas.c) doesn't appear to take
710− // the square root of the resulting dot product.
898+ // Defined as: norm2(x) = sqrt(dot(x, x))
711899 auto &fp = r.stack.peek_frame()->base<F_Math_norm2>();
712900
713901 auto xs = vm_alloc_t::from_allocation<vm_array_t>(fp.x);
@@ -740,7 +928,7 @@ void · 26 unmodified lines
740928 Math_pow10(vm_registers_t &r, vm_t &vm)
741929 {
742930 auto &fp = r.stack.peek_frame()->base<F_Math_pow10>();
743− throw vm_system_exception{ "Function not implemented" };
931+ *fp.ret = std::pow(10., static_cast<real_t>(fp.p));
744932 }
745933
746934 void
@@ -786,7 +974,7 @@ void · 39 unmodified lines
786974 Math_scalbn(vm_registers_t &r, vm_t &vm)
787975 {
788976 auto &fp = r.stack.peek_frame()->base<F_Math_scalbn>();
789− throw vm_system_exception{ "Function not implemented" };
977+ *fp.ret = std::scalbln(fp.x, fp.n);
790978 }
791979
792980 void
@@ -863,6 +1051,8 @@ void · 70 unmodified lines
8631051 Math_y0(vm_registers_t &r, vm_t &vm)
8641052 {
8651053 auto &fp = r.stack.peek_frame()->base<F_Math_y0>();
1054+
1055+ // [PAL] Consume platform API
8661056 throw vm_system_exception{ "Function not implemented" };
8671057 }
8681058
@@ -870,6 +1060,8 @@ void · 1 unmodified line
8701060 Math_y1(vm_registers_t &r, vm_t &vm)
8711061 {
8721062 auto &fp = r.stack.peek_frame()->base<F_Math_y1>();
1063+
1064+ // [PAL] Consume platform API
8731065 throw vm_system_exception{ "Function not implemented" };
8741066 }
8751067
@@ -877,5 +1069,7 @@ void · 1 unmodified line
8771069 Math_yn(vm_registers_t &r, vm_t &vm)
8781070 {
8791071 auto &fp = r.stack.peek_frame()->base<F_Math_yn>();
1072+
1073+ // [PAL] Consume platform API
8801074 throw vm_system_exception{ "Function not implemented" };
8811075 }
src/vm/math/dgemm.c+411−0
@@ -0,0 +1,411 @@
1+
2+/* -- translated by f2c (version 19940927).
3+ You must link the resulting object file with the libraries:
4+ -lf2c -lm (in that order)
5+*/
6+
7+//#include "f2c.h"
8+
9+typedef int integer;
10+typedef int logical;
11+typedef double doublereal;
12+
13+#define max(a,b) ((a) > (b) ? (a) : (b))
14+
15+// Returns '1' based index of invalid argument, otherwise '0'
16+/* Subroutine */ int dgemm_(char *transa, char *transb, integer *m, integer *
17+ n, integer *k, doublereal *alpha, doublereal *a, integer *lda,
18+ doublereal *b, integer *ldb, doublereal *beta, doublereal *c, integer
19+ *ldc, int a_len, int b_len, int c_len)
20+{
21+
22+
23+ /* System generated locals */
24+ integer /*a_dim1, a_offset, b_dim1, b_offset, c_dim1, c_offset, */ i__1, i__2,
25+ i__3;
26+
27+ /* Local variables */
28+ integer info;
29+ logical nota, notb;
30+ doublereal temp;
31+ integer i, j, l, ncola, ncolb;
32+ extern logical lsame_(char *, char *);
33+ integer nrowa, nrowb;
34+ //extern /* Subroutine */ int xerbla_(char *, integer *);
35+
36+
37+/* Purpose
38+ =======
39+
40+ DGEMM performs one of the matrix-matrix operations
41+
42+ C := alpha*op( A )*op( B ) + beta*C,
43+
44+ where op( X ) is one of
45+
46+ op( X ) = X or op( X ) = X',
47+
48+ alpha and beta are scalars, and A, B and C are matrices, with op( A )
49+
50+ an m by k matrix, op( B ) a k by n matrix and C an m by n matrix.
51+
52+
53+ Parameters
54+ ==========
55+
56+ TRANSA - CHARACTER*1.
57+ On entry, TRANSA specifies the form of op( A ) to be used in
58+
59+ the matrix multiplication as follows:
60+
61+ TRANSA = 'N' or 'n', op( A ) = A.
62+
63+ TRANSA = 'T' or 't', op( A ) = A'.
64+
65+ TRANSA = 'C' or 'c', op( A ) = A'.
66+
67+ Unchanged on exit.
68+
69+ TRANSB - CHARACTER*1.
70+ On entry, TRANSB specifies the form of op( B ) to be used in
71+
72+ the matrix multiplication as follows:
73+
74+ TRANSB = 'N' or 'n', op( B ) = B.
75+
76+ TRANSB = 'T' or 't', op( B ) = B'.
77+
78+ TRANSB = 'C' or 'c', op( B ) = B'.
79+
80+ Unchanged on exit.
81+
82+ M - INTEGER.
83+ On entry, M specifies the number of rows of the matrix
84+
85+ op( A ) and of the matrix C. M must be at least zero.
86+
87+ Unchanged on exit.
88+
89+ N - INTEGER.
90+ On entry, N specifies the number of columns of the matrix
91+
92+ op( B ) and the number of columns of the matrix C. N must be
93+
94+ at least zero.
95+ Unchanged on exit.
96+
97+ K - INTEGER.
98+ On entry, K specifies the number of columns of the matrix
99+
100+ op( A ) and the number of rows of the matrix op( B ). K must
101+
102+ be at least zero.
103+ Unchanged on exit.
104+
105+ ALPHA - DOUBLE PRECISION.
106+ On entry, ALPHA specifies the scalar alpha.
107+ Unchanged on exit.
108+
109+ A - DOUBLE PRECISION array of DIMENSION ( LDA, ka ), where ka is
110+
111+ k when TRANSA = 'N' or 'n', and is m otherwise.
112+ Before entry with TRANSA = 'N' or 'n', the leading m by k
113+
114+ part of the array A must contain the matrix A, otherwise
115+
116+ the leading k by m part of the array A must contain the
117+
118+ matrix A.
119+ Unchanged on exit.
120+
121+ LDA - INTEGER.
122+ On entry, LDA specifies the first dimension of A as declared
123+
124+ in the calling (sub) program. When TRANSA = 'N' or 'n' then
125+
126+ LDA must be at least max( 1, m ), otherwise LDA must be at
127+
128+ least max( 1, k ).
129+ Unchanged on exit.
130+
131+ B - DOUBLE PRECISION array of DIMENSION ( LDB, kb ), where kb is
132+
133+ n when TRANSB = 'N' or 'n', and is k otherwise.
134+ Before entry with TRANSB = 'N' or 'n', the leading k by n
135+
136+ part of the array B must contain the matrix B, otherwise
137+
138+ the leading n by k part of the array B must contain the
139+
140+ matrix B.
141+ Unchanged on exit.
142+
143+ LDB - INTEGER.
144+ On entry, LDB specifies the first dimension of B as declared
145+
146+ in the calling (sub) program. When TRANSB = 'N' or 'n' then
147+
148+ LDB must be at least max( 1, k ), otherwise LDB must be at
149+
150+ least max( 1, n ).
151+ Unchanged on exit.
152+
153+ BETA - DOUBLE PRECISION.
154+ On entry, BETA specifies the scalar beta. When BETA is
155+
156+ supplied as zero then C need not be set on input.
157+ Unchanged on exit.
158+
159+ C - DOUBLE PRECISION array of DIMENSION ( LDC, n ).
160+ Before entry, the leading m by n part of the array C must
161+
162+ contain the matrix C, except when beta is zero, in which
163+
164+ case C need not be set on entry.
165+ On exit, the array C is overwritten by the m by n matrix
166+
167+ ( alpha*op( A )*op( B ) + beta*C ).
168+
169+ LDC - INTEGER.
170+ On entry, LDC specifies the first dimension of C as declared
171+
172+ in the calling (sub) program. LDC must be at least
173+
174+ max( 1, m ).
175+ Unchanged on exit.
176+
177+
178+ Level 3 Blas routine.
179+
180+ -- Written on 8-February-1989.
181+ Jack Dongarra, Argonne National Laboratory.
182+ Iain Duff, AERE Harwell.
183+ Jeremy Du Croz, Numerical Algorithms Group Ltd.
184+ Sven Hammarling, Numerical Algorithms Group Ltd.
185+
186+
187+
188+ Set NOTA and NOTB as true if A and B respectively are not
189+
190+ transposed and set NROWA, NCOLA and NROWB as the number of rows
191+
192+ and columns of A and the number of rows of B respectively.
193+
194+
195+
196+ Parameter adjustments
197+ Function Body */
198+
199+#define A(I,J) a[(I)-1 + ((J)-1)* ( *lda)]
200+#define B(I,J) b[(I)-1 + ((J)-1)* ( *ldb)]
201+#define C(I,J) c[(I)-1 + ((J)-1)* ( *ldc)]
202+
203+ nota = lsame_(transa, "N");
204+ notb = lsame_(transb, "N");
205+ if (nota) {
206+ nrowa = *m;
207+ ncola = *k;
208+ } else {
209+ nrowa = *k;
210+ ncola = *m;
211+ }
212+ if (notb) {
213+ nrowb = *k;
214+ ncolb = *n;
215+ } else {
216+ nrowb = *n;
217+ ncolb = *k;
218+ }
219+
220+/* Test the input parameters. */
221+
222+ info = 0;
223+ if (! nota && ! lsame_(transa, "C") && ! lsame_(transa, "T")) {
224+ info = 1;
225+ } else if (! notb && ! lsame_(transb, "C") && ! lsame_(transb,
226+ "T")) {
227+ info = 2;
228+ } else if (*m < 0) {
229+ info = 3;
230+ } else if (*n < 0) {
231+ info = 4;
232+ } else if (*k < 0) {
233+ info = 5;
234+ } else if (((*lda) * (ncola - 1)) > a_len) {
235+ info = 7; // A matrix
236+ } else if (*lda < max(1,nrowa)) {
237+ info = 8;
238+ } else if (((*ldb) * (ncolb - 1)) > b_len) {
239+ info = 9; // B matrix
240+ } else if (*ldb < max(1,nrowb)) {
241+ info = 10;
242+ } else if (((*ldc) * (*n - 1)) > c_len) {
243+ info = 12; // C matrix
244+ } else if (*ldc < max(1,*m)) {
245+ info = 13;
246+ }
247+
248+ if (info != 0) {
249+ //xerbla_("DGEMM ", &info);
250+ return info;
251+ }
252+
253+/* Quick return if possible. */
254+
255+ if (*m == 0 || *n == 0 || (*alpha == 0. || *k == 0) && *beta == 1.) {
256+ return 0;
257+ }
258+
259+/* And if alpha.eq.zero. */
260+
261+ if (*alpha == 0.) {
262+ if (*beta == 0.) {
263+ i__1 = *n;
264+ for (j = 1; j <= *n; ++j) {
265+ i__2 = *m;
266+ for (i = 1; i <= *m; ++i) {
267+ C(i,j) = 0.;
268+/* L10: */
269+ }
270+/* L20: */
271+ }
272+ } else {
273+ i__1 = *n;
274+ for (j = 1; j <= *n; ++j) {
275+ i__2 = *m;
276+ for (i = 1; i <= *m; ++i) {
277+ C(i,j) = *beta * C(i,j);
278+/* L30: */
279+ }
280+/* L40: */
281+ }
282+ }
283+ return 0;
284+ }
285+
286+/* Start the operations. */
287+
288+ if (notb) {
289+ if (nota) {
290+
291+/* Form C := alpha*A*B + beta*C. */
292+
293+ i__1 = *n;
294+ for (j = 1; j <= *n; ++j) {
295+ if (*beta == 0.) {
296+ i__2 = *m;
297+ for (i = 1; i <= *m; ++i) {
298+ C(i,j) = 0.;
299+/* L50: */
300+ }
301+ } else if (*beta != 1.) {
302+ i__2 = *m;
303+ for (i = 1; i <= *m; ++i) {
304+ C(i,j) = *beta * C(i,j);
305+/* L60: */
306+ }
307+ }
308+ i__2 = *k;
309+ for (l = 1; l <= *k; ++l) {
310+ if (B(l,j) != 0.) {
311+ temp = *alpha * B(l,j);
312+ i__3 = *m;
313+ for (i = 1; i <= *m; ++i) {
314+ C(i,j) += temp * A(i,l);
315+/* L70: */
316+ }
317+ }
318+/* L80: */
319+ }
320+/* L90: */
321+ }
322+ } else {
323+
324+/* Form C := alpha*A'*B + beta*C */
325+
326+ i__1 = *n;
327+ for (j = 1; j <= *n; ++j) {
328+ i__2 = *m;
329+ for (i = 1; i <= *m; ++i) {
330+ temp = 0.;
331+ i__3 = *k;
332+ for (l = 1; l <= *k; ++l) {
333+ temp += A(l,i) * B(l,j);
334+/* L100: */
335+ }
336+ if (*beta == 0.) {
337+ C(i,j) = *alpha * temp;
338+ } else {
339+ C(i,j) = *alpha * temp + *beta * C(i,j);
340+ }
341+/* L110: */
342+ }
343+/* L120: */
344+ }
345+ }
346+ } else {
347+ if (nota) {
348+
349+/* Form C := alpha*A*B' + beta*C */
350+
351+ i__1 = *n;
352+ for (j = 1; j <= *n; ++j) {
353+ if (*beta == 0.) {
354+ i__2 = *m;
355+ for (i = 1; i <= *m; ++i) {
356+ C(i,j) = 0.;
357+/* L130: */
358+ }
359+ } else if (*beta != 1.) {
360+ i__2 = *m;
361+ for (i = 1; i <= *m; ++i) {
362+ C(i,j) = *beta * C(i,j);
363+/* L140: */
364+ }
365+ }
366+ i__2 = *k;
367+ for (l = 1; l <= *k; ++l) {
368+ if (B(j,l) != 0.) {
369+ temp = *alpha * B(j,l);
370+ i__3 = *m;
371+ for (i = 1; i <= *m; ++i) {
372+ C(i,j) += temp * A(i,l);
373+/* L150: */
374+ }
375+ }
376+/* L160: */
377+ }
378+/* L170: */
379+ }
380+ } else {
381+
382+/* Form C := alpha*A'*B' + beta*C */
383+
384+ i__1 = *n;
385+ for (j = 1; j <= *n; ++j) {
386+ i__2 = *m;
387+ for (i = 1; i <= *m; ++i) {
388+ temp = 0.;
389+ i__3 = *k;
390+ for (l = 1; l <= *k; ++l) {
391+ temp += A(l,i) * B(j,l);
392+/* L180: */
393+ }
394+ if (*beta == 0.) {
395+ C(i,j) = *alpha * temp;
396+ } else {
397+ C(i,j) = *alpha * temp + *beta * C(i,j);
398+ }
399+/* L190: */
400+ }
401+/* L200: */
402+ }
403+ }
404+ }
405+
406+ return 0;
407+
408+/* End of DGEMM . */
409+
410+} /* dgemm_ */
411+
src/vm/math/lsame.c+113−0
@@ -0,0 +1,113 @@
1+/* -- translated by f2c (version 19940927).
2+ You must link the resulting object file with the libraries:
3+ -lf2c -lm (in that order)
4+*/
5+
6+//#include "f2c.h"
7+
8+typedef int integer;
9+typedef int logical;
10+
11+logical lsame_(char *ca, char *cb)
12+{
13+
14+
15+ /* System generated locals */
16+ logical ret_val;
17+
18+ /* Local variables */
19+ integer inta, intb, zcode;
20+
21+
22+/* -- LAPACK auxiliary routine (version 2.0) --
23+ Univ. of Tennessee, Univ. of California Berkeley, NAG Ltd.,
24+ Courant Institute, Argonne National Lab, and Rice University
25+ January 31, 1994
26+
27+
28+ Purpose
29+ =======
30+
31+ LSAME returns .TRUE. if CA is the same letter as CB regardless of
32+ case.
33+
34+ Arguments
35+ =========
36+
37+ CA (input) CHARACTER*1
38+ CB (input) CHARACTER*1
39+ CA and CB specify the single characters to be compared.
40+
41+ =====================================================================
42+
43+
44+
45+ Test if the characters are equal */
46+
47+ ret_val = *(unsigned char *)ca == *(unsigned char *)cb;
48+ if (ret_val) {
49+ return ret_val;
50+ }
51+
52+/* Now test for equivalence if both characters are alphabetic. */
53+
54+ zcode = 'Z';
55+
56+/* Use 'Z' rather than 'A' so that ASCII can be detected on Prime
57+ machines, on which ICHAR returns a value with bit 8 set.
58+ ICHAR('A') on Prime machines returns 193 which is the same as
59+ ICHAR('A') on an EBCDIC machine. */
60+
61+ inta = *(unsigned char *)ca;
62+ intb = *(unsigned char *)cb;
63+
64+ if (zcode == 90 || zcode == 122) {
65+
66+/* ASCII is assumed - ZCODE is the ASCII code of either lower o
67+r
68+ upper case 'Z'. */
69+
70+ if (inta >= 97 && inta <= 122) {
71+ inta += -32;
72+ }
73+ if (intb >= 97 && intb <= 122) {
74+ intb += -32;
75+ }
76+
77+ } else if (zcode == 233 || zcode == 169) {
78+
79+/* EBCDIC is assumed - ZCODE is the EBCDIC code of either lower
80+ or
81+ upper case 'Z'. */
82+
83+ if (inta >= 129 && inta <= 137 || inta >= 145 && inta <= 153 || inta
84+ >= 162 && inta <= 169) {
85+ inta += 64;
86+ }
87+ if (intb >= 129 && intb <= 137 || intb >= 145 && intb <= 153 || intb
88+ >= 162 && intb <= 169) {
89+ intb += 64;
90+ }
91+
92+ } else if (zcode == 218 || zcode == 250) {
93+
94+/* ASCII is assumed, on Prime machines - ZCODE is the ASCII cod
95+e
96+ plus 128 of either lower or upper case 'Z'. */
97+
98+ if (inta >= 225 && inta <= 250) {
99+ inta += -32;
100+ }
101+ if (intb >= 225 && intb <= 250) {
102+ intb += -32;
103+ }
104+ }
105+ ret_val = inta == intb;
106+
107+/* RETURN
108+
109+ End of LSAME */
110+
111+ return ret_val;
112+} /* lsame_ */
113+
src/vm/sys/Sysmod.cpp+206−175
@@ -17,15 +17,18 @@ · 16 unmodified lines
1717 #include <vm_memory.h>
1818 #include "Sysmod.h"
1919 #include "sys_utils.h"
20+#include "fd_types.h"
2021
2122 using disvm::vm_t;
2223
2324 using disvm::debug::component_trace_t;
2425 using disvm::debug::log_level_t;
2526
27+using disvm::runtime::byte_t;
2628 using disvm::runtime::dereference_nil;
2729 using disvm::runtime::intrinsic_type_desc;
2830 using disvm::runtime::out_of_range_memory;
31+using disvm::runtime::pointer_t;
2932 using disvm::runtime::type_descriptor_t;
3033 using disvm::runtime::vm_alloc_t;
3134 using disvm::runtime::vm_array_t;
@@ -34,8 +37,13 @@ using disvm::runtime::vm_string_t; · 2 unmodified lines
3437 using disvm::runtime::vm_syscall_exception;
3538 using disvm::runtime::vm_system_exception;
3639 using disvm::runtime::vm_user_exception;
40+using disvm::runtime::marshallable_user_exception;
3741 using disvm::runtime::word_t;
3842
43+using disvm::runtime::sys::open_mode_t;
44+using disvm::runtime::sys::seek_start_t;
45+using disvm::runtime::sys::vm_fd_t;
46+
3947 namespace
4048 {
4149 disvm::runtime::builtin::vm_runtab_t Sysmodtab[] = {
@@ -93,12 +101,14 @@ namespace · 51 unmodified lines
93101 std::shared_ptr<const type_descriptor_t> T_Connection;
94102 std::shared_ptr<const type_descriptor_t> T_FileIO;
95103
96− class vm_fd_t final : public Sys_FD
104+ class Sys_FD_Impl final : public Sys_FD
97105 {
98106 public: // static
99107 static void finalizer(vm_alloc_t *fileHandle)
100108 {
101− auto fd = fileHandle->get_allocation<vm_fd_t>();
109+ auto fd = fileHandle->get_allocation<Sys_FD_Impl>();
110+
111+ fd->~Sys_FD_Impl();
102112
103113 if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>())
104114 {
@@ -108,128 +118,64 @@ namespace · 3 unmodified lines
108118 "sys: fd: finalize: %d",
109119 fd->fd);
110120 }
111−
112− disvm::runtime::sys::drop_fd_record(fd->fd);
113− fd->~vm_fd_t();
114− }
115−
116− static std::tuple<word_t, vm_alloc_t *> create(std::unique_ptr<std::iostream> s, word_t fd_mode, vm_string_t *fd_path)
117− {
118− assert(s != nullptr);
119− auto alloc = vm_alloc_t::allocate(T_FD);
120− const auto fd = disvm::runtime::sys::create_fd_record(alloc);
121− auto vm_fd = ::new(alloc->get_allocation())vm_fd_t{ fd, std::move(s), fd_mode, fd_path };
122−
123− return std::make_tuple(vm_fd->fd, alloc);
124− }
125−
126− private:
127− vm_fd_t(const word_t fd, std::unique_ptr<std::iostream> s, const word_t fd_mode, vm_string_t *fd_path)
128− : Sys_FD{ fd }
129− , _fd_mode{ fd_mode }
130− , _fd_path{ fd_path }
131− , _istream{ s.get() }
132− , _ostream{ s.get() }
133− {
134− _stream = std::move(s);
135−
136− if (_fd_path != nullptr)
137− _fd_path->add_ref();
138121 }
139122
140− public:
141− vm_fd_t(const word_t fd, std::istream &is)
142− : Sys_FD{ fd }
143− , _fd_mode{}
144− , _fd_path{ nullptr }
145− , _istream{ &is }
146− , _ostream{ nullptr }
147− {
148− }
149−
150− vm_fd_t(const word_t fd, std::ostream &os)
151− : Sys_FD{ fd }
152− , _fd_mode{}
153− , _fd_path{ nullptr }
154− , _istream{ nullptr }
155− , _ostream{ &os }
156− {
157− }
158−
159− ~vm_fd_t()
123+ // The ref count of the supplied vm_fd_t instance is updated.
124+ static vm_alloc_t * create_new(vm_fd_t *vm_fd)
160125 {
161− disvm::debug::assign_debug_pointer(&_istream);
162− disvm::debug::assign_debug_pointer(&_ostream);
163− if (_stream != nullptr)
164− _stream.reset();
126+ assert(vm_fd != nullptr);
127+ assert(vm_fd->get_ref_count() == 1 && "Should be a new FD");
165128
166− // Honor the close flag
167− if ((_fd_mode & Sys_ORCLOSE) && _fd_path != nullptr)
168− std::remove(_fd_path->str());
129+ auto alloc = vm_alloc_t::allocate(T_FD);
130+ auto fd_impl = ::new(alloc->get_allocation())Sys_FD_Impl{};
169131
170− dec_ref_count_and_free(_fd_path);
171− disvm::debug::assign_debug_pointer(&_fd_path);
172− }
132+ fd_impl->fd = disvm::runtime::sys::create_fd_record(vm_fd);
133+ fd_impl->impl = vm_fd;
134+ vm_fd->add_ref();
173135
174− word_t get_original_mode() const
175− {
176− return _fd_mode;
136+ return alloc;
177137 }
178138
179− vm_string_t *get_original_path() const
139+ // The ref count of the supplied vm_fd_t instance is _not_ altered.
140+ static vm_alloc_t * create_existing(word_t fd, vm_fd_t *vm_fd)
180141 {
181− return _fd_path;
182− }
142+ assert(vm_fd != nullptr);
143+ assert(fd >= 0);
183144
184− word_t read(disvm::vm_t &vm, const word_t buffer_size_in_bytes, void *buffer)
185− {
186− if (_istream == nullptr)
187− throw vm_system_exception{ "File descriptor not open for read operation" };
145+ auto alloc = vm_alloc_t::allocate(T_FD);
146+ auto fd_impl = ::new(alloc->get_allocation())Sys_FD_Impl{};
188147
189− _istream->read(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));
190− if (_istream->bad())
191− throw vm_syscall_exception{ vm, "Read operation error" };
148+ fd_impl->fd = fd;
149+ fd_impl->impl = vm_fd;
192150
193− return static_cast<word_t>(_istream->gcount());
151+ return alloc;
194152 }
195153
196− void write(disvm::vm_t &vm, const word_t buffer_size_in_bytes, void *buffer)
154+ public:
155+ ~Sys_FD_Impl()
197156 {
198− if (_ostream == nullptr)
199− throw vm_system_exception{ "File descriptor not open for write operation" };
200−
201− _ostream->write(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));
202− if (_ostream->bad())
203− throw vm_syscall_exception{ vm, "Write operation error" };
157+ disvm::runtime::sys::drop_fd_record(fd);
158+ disvm::runtime::dec_ref_count_and_free(impl);
159+ disvm::debug::assign_debug_pointer(&impl);
204160 }
205161
206− big_t seek(const std::ios::seekdir start, const big_t offset)
207− {
208− if (_stream == nullptr)
209− throw vm_user_exception{ "Unable to seek on a fifo file descriptor (e.g. stdin, stdout, stderr)" };
210−
211− auto new_offset = _stream->rdbuf()->pubseekoff(static_cast<std::streamoff>(offset), start, _fd_mode);
212− return static_cast<big_t>(new_offset);
213− }
162+ vm_fd_t *impl;
214163
215164 private:
216− const word_t _fd_mode;
217− vm_string_t *_fd_path;
218− std::istream *_istream;
219− std::ostream *_ostream;
220− std::unique_ptr<std::ios> _stream;
165+ Sys_FD_Impl() = default;
221166 };
222167
223− vm_fd_t *fd_stdin = nullptr;
224− vm_fd_t *fd_stdout = nullptr;
225− vm_fd_t *fd_stderr = nullptr;
168+ // File descriptors for std streams.
169+ word_t fd_stdin = disvm::runtime::sys::vm_invalid_fd;
170+ word_t fd_stdout = disvm::runtime::sys::vm_invalid_fd;
171+ word_t fd_stderr = disvm::runtime::sys::vm_invalid_fd;
226172
227173 word_t printf_to_fd(
228174 disvm::vm_t &vm,
229175 vm_fd_t &fd,
230− const disvm::runtime::vm_string_t &msg_fmt,
231− disvm::runtime::byte_t *msg_args,
232− disvm::runtime::pointer_t base)
176+ const vm_string_t &msg_fmt,
177+ byte_t *msg_args,
178+ pointer_t base)
233179 {
234180 auto static_buffer = std::array<char, 1024>{};
235181 auto written = disvm::runtime::sys::printf_to_buffer(vm, msg_fmt, msg_args, base, static_buffer.size(), static_buffer.data());
@@ -247,35 +193,41 @@ namespace · 11 unmodified lines
247193 return written;
248194 }
249195
250− std::ios::openmode convert_to_openmode(const word_t mode)
196+ open_mode_t convert_to_openmode(const word_t mode)
251197 {
252− auto om = std::ios::openmode{};
198+ auto om = open_mode_t{};
253199
254200 // Lowest bit is for read/write.
255201 if (mode & Sys_OWRITE)
256− om |= std::ios::out;
202+ om |= open_mode_t::write;
257203 else
258− om |= std::ios::in; // Sys_OREAD
204+ om |= open_mode_t::read; // Sys_OREAD
259205
260206 if (mode & Sys_ORDWR)
261− om |= (std::ios::in | std::ios::out);
207+ om |= (open_mode_t::write | open_mode_t::read);
262208
263209 if (mode & Sys_OTRUNC)
264− om |= std::ios::trunc;
210+ om |= open_mode_t::truncate;
211+
212+ if (mode & Sys_OEXCL)
213+ om |= open_mode_t::atomic;
214+
215+ if (mode & Sys_ORCLOSE)
216+ om |= open_mode_t::delete_on_close;
265217
266218 return om;
267219 }
268220
269− std::ios::seekdir convert_to_seekdir(const word_t start)
221+ seek_start_t convert_to_seekdir(const word_t start)
270222 {
271223 switch (start)
272224 {
273225 case Sys_SEEKSTART:
274− return std::ios::beg;
226+ return seek_start_t::start;
275227 case Sys_SEEKRELA:
276− return std::ios::cur;
228+ return seek_start_t::relative;
277229 case Sys_SEEKEND:
278− return std::ios::end;
230+ return seek_start_t::end;
279231 default:
280232 throw vm_user_exception{ "Invalid seek mode" };
281233 }
@@ -288,49 +240,55 @@ Sysmodinit(void) · 6 unmodified lines
288240 disvm::runtime::builtin::register_module_exports(Sys_PATH, Sysmodlen, Sysmodtab);
289241 T_Qid = type_descriptor_t::create(sizeof(Sys_Qid), sizeof(Sys_Qid_map), Sys_Qid_map);
290242 T_Dir = type_descriptor_t::create(sizeof(Sys_Dir), sizeof(Sys_Dir_map), Sys_Dir_map);
291− T_FD = type_descriptor_t::create(sizeof(vm_fd_t), sizeof(Sys_FD_map), Sys_FD_map, vm_fd_t::finalizer);
243+ T_FD = type_descriptor_t::create(sizeof(Sys_FD_Impl), sizeof(Sys_FD_map), Sys_FD_map, Sys_FD_Impl::finalizer);
292244 T_Connection = type_descriptor_t::create(sizeof(Sys_Connection), sizeof(Sys_Connection_map), Sys_Connection_map);
293245 T_FileIO = type_descriptor_t::create(sizeof(Sys_FileIO), sizeof(Sys_FileIO_map), Sys_FileIO_map);
294246
295247 // Initialize default file descriptors
248+ auto std_streams = disvm::runtime::sys::get_std_streams();
296249
297− {
298− auto vm_stdin = vm_alloc_t::allocate(T_FD);
299− const auto fd = disvm::runtime::sys::create_fd_record(vm_stdin);
300− fd_stdin = ::new(vm_stdin->get_allocation())vm_fd_t{ fd, std::cin };
301− disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stdin: %d", fd);
302− }
250+ // Initialize in this order to assign POSIX defined values for the 'standard' streams.
251+ // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html
303252
304− {
305− auto vm_stdout = vm_alloc_t::allocate(T_FD);
306− const auto fd = disvm::runtime::sys::create_fd_record(vm_stdout);
307− fd_stdout = ::new(vm_stdout->get_allocation())vm_fd_t{ fd, std::cout };
308− disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stdout: %d", fd);
309− }
253+ fd_stdin = disvm::runtime::sys::create_fd_record(std_streams.input);
254+ assert(fd_stdin == 0);
255+ disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stdin: %d", fd_stdin);
310256
311− {
312− auto vm_stderr = vm_alloc_t::allocate(T_FD);
313− const auto fd = disvm::runtime::sys::create_fd_record(vm_stderr);
314− fd_stderr = ::new(vm_stderr->get_allocation())vm_fd_t{ fd, std::cerr };
315− disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stderr: %d", fd);
316− }
257+ fd_stdout = disvm::runtime::sys::create_fd_record(std_streams.output);
258+ assert(fd_stdout == 1);
259+ disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stdout: %d", fd_stdout);
260+
261+ fd_stderr = disvm::runtime::sys::create_fd_record(std_streams.error);
262+ assert(fd_stderr == 2);
263+ disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: stderr: %d", fd_stderr);
317264 }
318265
319266 void
320267 Sys_announce(vm_registers_t &r, vm_t &vm)
321268 {
322269 auto &fp = r.stack.peek_frame()->base<F_Sys_announce>();
270+
271+ // [PAL] Consume platform API
323272 throw vm_system_exception{ "Function not implemented" };
324273 }
325274
326275 void
327276 Sys_aprint(vm_registers_t &r, vm_t &vm)
328277 {
278+ auto fp_base = r.stack.peek_frame()->base();
329279 auto &fp = r.stack.peek_frame()->base<F_Sys_aprint>();
280+ auto str = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
281+ if (str == nullptr)
282+ throw dereference_nil{ "Print string to array" };
330283
331− //disvm::runtime::dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret));
332− //*fp.ret = nullptr;
333− throw vm_system_exception{ "Function not implemented" };
284+ auto buffer = std::vector<char>(128);
285+ auto msg_args = &fp.vargs;
286+ auto written = disvm::runtime::sys::printf_to_dynamic_buffer(vm, *str, msg_args, fp_base, buffer);
287+ auto new_array = new vm_array_t{ intrinsic_type_desc::type<byte_t>(), written };
288+ std::memcpy(new_array->at(0), buffer.data(), written);
289+
290+ disvm::runtime::dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret));
291+ *fp.ret = new_array->get_allocation();
334292 }
335293
336294 void
@@ -421,6 +379,8 @@ void · 84 unmodified lines
421379 Sys_chdir(vm_registers_t &r, vm_t &vm)
422380 {
423381 auto &fp = r.stack.peek_frame()->base<F_Sys_chdir>();
382+
383+ // [PAL] Consume platform API
424384 throw vm_system_exception{ "Function not implemented" };
425385 }
426386
@@ -429,46 +389,42 @@ Sys_create(vm_registers_t &r, vm_t &vm) · 2 unmodified lines
429389 {
430390 auto &fp = r.stack.peek_frame()->base<F_Sys_create>();
431391
392+ dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret));
393+ *fp.ret = nullptr;
394+
432395 auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
433396 const auto open_mode = convert_to_openmode(fp.mode);
434397
435− auto cfs_flag = disvm::runtime::sys::cfs_flags_t::none;
436− if (fp.mode & Sys_OEXCL)
437− cfs_flag = (disvm::runtime::sys::cfs_flags_t::atomic | disvm::runtime::sys::cfs_flags_t::ensure_create);
438−
439− // [TODO] Apply the requested permissions to the file.
440− auto file_stream = disvm::runtime::sys::create_file_stream(path->str(), (open_mode | std::ios::binary), cfs_flag);
398+ // [TODO] Apply the requested permissions to the file - fp.perm
399+ auto vm_fd = disvm::runtime::sys::create_from_file_path(path, open_mode);
441400
442− // Return null if file stream is not open.
443− if (file_stream == nullptr)
444− {
445− *fp.ret = nullptr;
401+ // Return null if file is not open.
402+ if (vm_fd == nullptr)
446403 return;
447− }
448404
449− vm_alloc_t *fd_alloc;
450− auto fd = word_t{};
451− std::tie(fd, fd_alloc) = vm_fd_t::create(std::move(file_stream), fp.mode, path);
405+ auto fd_alloc = Sys_FD_Impl::create_new(vm_fd);
406+ *fp.ret = fd_alloc->get_allocation();
452407
453408 if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>())
454409 {
410+ auto fd_impl = fd_alloc->get_allocation<Sys_FD_Impl>();
455411 disvm::debug::log_msg(
456412 component_trace_t::builtin,
457413 log_level_t::debug,
458414 "sys: create: %d >>%s<< %#x %#x",
459− fd,
415+ fd_impl->fd,
460416 path->str(),
461417 fp.mode,
462418 fp.perm);
463419 }
464−
465− *fp.ret = fd_alloc->get_allocation();
466420 }
467421
468422 void
469423 Sys_dial(vm_registers_t &r, vm_t &vm)
470424 {
471425 auto &fp = r.stack.peek_frame()->base<F_Sys_dial>();
426+
427+ // [PAL] Consume platform API
472428 throw vm_system_exception{ "Function not implemented" };
473429 }
474430
@@ -476,14 +432,42 @@ void · 1 unmodified line
476432 Sys_dirread(vm_registers_t &r, vm_t &vm)
477433 {
478434 auto &fp = r.stack.peek_frame()->base<F_Sys_dirread>();
435+
436+ // [PAL] Consume platform API
479437 throw vm_system_exception{ "Function not implemented" };
480438 }
481439
440+namespace
441+{
442+ // [SPEC] In the Inferno OS, this error is represented by
443+ // Ebadfd - 'fd out of range or not open' (emu/port/error.c).
444+ auto invalid_fd_error = "Invalid FD value";
445+}
446+
482447 void
483448 Sys_dup(vm_registers_t &r, vm_t &vm)
484449 {
485450 auto &fp = r.stack.peek_frame()->base<F_Sys_dup>();
486− throw vm_system_exception{ "Function not implemented" };
451+
452+ const auto old_fd = fp.old;
453+ auto fd_maybe = disvm::runtime::sys::fetch_fd_record(old_fd);
454+ if (fd_maybe == nullptr)
455+ throw marshallable_user_exception{ invalid_fd_error };
456+
457+ auto new_fd_maybe = fp.new_;
458+ if (new_fd_maybe == disvm::runtime::sys::vm_invalid_fd)
459+ {
460+ new_fd_maybe = disvm::runtime::sys::create_fd_record(fd_maybe);
461+ }
462+ else if (!disvm::runtime::sys::try_update_fd_record(new_fd_maybe, fd_maybe))
463+ {
464+ throw marshallable_user_exception{ invalid_fd_error };
465+ }
466+
467+ *fp.ret = new_fd_maybe;
468+
469+ if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>())
470+ disvm::debug::log_msg(component_trace_t::builtin, log_level_t::debug, "sys: fd: dup %d %d", old_fd, new_fd_maybe);
487471 }
488472
489473 void
@@ -521,11 +505,11 @@ Sys_fildes(vm_registers_t &r, vm_t &vm) · 31 unmodified lines
521505 dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret));
522506 *fp.ret = nullptr;
523507
524− auto fd = disvm::runtime::sys::fetch_fd_record(fp.fd);
525− if (fd != nullptr)
508+ auto vm_fd = disvm::runtime::sys::fetch_fd_record(fp.fd);
509+ if (vm_fd != nullptr)
526510 {
527− fd->add_ref();
528− *fp.ret = fd->get_allocation();
511+ auto fd_alloc = Sys_FD_Impl::create_existing(fp.fd, vm_fd);
512+ *fp.ret = fd_alloc->get_allocation();
529513 }
530514 }
531515
@@ -546,18 +530,20 @@ Sys_fprint(vm_registers_t &r, vm_t &vm) · 14 unmodified lines
546530 auto &fp = r.stack.peek_frame()->base<F_Sys_fprint>();
547531 auto fd_alloc = vm_alloc_t::from_allocation(fp.fd);
548532 assert(fd_alloc->alloc_type == T_FD);
549− auto fd = fd_alloc->get_allocation<vm_fd_t>();
533+ auto fd = fd_alloc->get_allocation<Sys_FD_Impl>();
550534 auto str = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
551535 if (str == nullptr)
552536 throw dereference_nil{ "Print string to FD" };
553537
554− *fp.ret = printf_to_fd(vm, *fd, *str, &fp.vargs, fp_base);
538+ *fp.ret = printf_to_fd(vm, *fd->impl, *str, &fp.vargs, fp_base);
555539 }
556540
557541 void
558542 Sys_fstat(vm_registers_t &r, vm_t &vm)
559543 {
560544 auto &fp = r.stack.peek_frame()->base<F_Sys_fstat>();
545+
546+ // [PAL] Consume platform API
561547 throw vm_system_exception{ "Function not implemented" };
562548 }
563549
@@ -572,6 +558,8 @@ void · 8 unmodified lines
572558 Sys_fwstat(vm_registers_t &r, vm_t &vm)
573559 {
574560 auto &fp = r.stack.peek_frame()->base<F_Sys_fwstat>();
561+
562+ // [PAL] Consume platform API
575563 throw vm_system_exception{ "Function not implemented" };
576564 }
577565
@@ -586,6 +574,8 @@ void · 8 unmodified lines
586574 Sys_listen(vm_registers_t &r, vm_t &vm)
587575 {
588576 auto &fp = r.stack.peek_frame()->base<F_Sys_listen>();
577+
578+ // [PAL] Consume platform API
589579 throw vm_system_exception{ "Function not implemented" };
590580 }
591581
@@ -611,41 +601,41 @@ Sys_open(vm_registers_t &r, vm_t &vm) · 19 unmodified lines
611601 {
612602 auto &fp = r.stack.peek_frame()->base<F_Sys_open>();
613603
604+ dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret));
605+ *fp.ret = nullptr;
606+
614607 auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
615608 const auto open_mode = convert_to_openmode(fp.mode);
616609
617610 // [TODO] Handle opening devices - not just files (i.e. /dev/random, /dev/source, /proc/1234)
618− auto file_stream = disvm::runtime::sys::create_file_stream(path->str(), (open_mode | std::ios::binary));
611+ auto vm_fd = disvm::runtime::sys::create_from_file_path(path, open_mode | open_mode_t::ensure_exists);
619612
620− // Return null if file stream is not open.
621− if (file_stream == nullptr)
622− {
623− *fp.ret = nullptr;
613+ // Return null if file is not open.
614+ if (vm_fd == nullptr)
624615 return;
625− }
626616
627− vm_alloc_t *fd_alloc;
628− auto fd = word_t{};
629− std::tie(fd, fd_alloc) = vm_fd_t::create(std::move(file_stream), fp.mode, path);
617+ auto fd_alloc = Sys_FD_Impl::create_new(vm_fd);
618+ *fp.ret = fd_alloc->get_allocation();
630619
631620 if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>())
632621 {
622+ auto fd_impl = fd_alloc->get_allocation<Sys_FD_Impl>();
633623 disvm::debug::log_msg(
634624 component_trace_t::builtin,
635625 log_level_t::debug,
636626 "sys: open: %d >>%s<< %#x",
637− fd,
627+ fd_impl->fd,
638628 path->str(),
639629 fp.mode);
640630 }
641−
642− *fp.ret = fd_alloc->get_allocation();
643631 }
644632
645633 void
646634 Sys_pctl(vm_registers_t &r, vm_t &vm)
647635 {
648636 auto &fp = r.stack.peek_frame()->base<F_Sys_pctl>();
637+
638+ // [PAL] Consume platform API
649639 throw vm_system_exception{ "Function not implemented" };
650640 }
651641
@@ -653,6 +643,8 @@ void · 1 unmodified line
653643 Sys_pipe(vm_registers_t &r, vm_t &vm)
654644 {
655645 auto &fp = r.stack.peek_frame()->base<F_Sys_pipe>();
646+
647+ // [PAL] Consume platform API
656648 throw vm_system_exception{ "Function not implemented" };
657649 }
658650
@@ -672,7 +664,12 @@ Sys_print(vm_registers_t &r, vm_t &vm) · 13 unmodified lines
672664 if (str == nullptr)
673665 throw dereference_nil{ "Print string" };
674666
675− *fp.ret = printf_to_fd(vm, *fd_stdout, *str, &fp.vargs, fp_base);
667+ auto std_output = disvm::runtime::sys::fetch_fd_record(fd_stdout);
668+ if (std_output == nullptr)
669+ throw vm_system_exception{ "stdout not available" };
670+
671+ *fp.ret = printf_to_fd(vm, *std_output, *str, &fp.vargs, fp_base);
672+ disvm::runtime::dec_ref_count_and_free(std_output);
676673 }
677674
678675 void
@@ -708,8 +705,8 @@ Sys_read(vm_registers_t &r, vm_t &vm) · 29 unmodified lines
708705
709706 assert(fd_alloc->alloc_type == T_FD);
710707
711− auto fd = fd_alloc->get_allocation<vm_fd_t>();
712− *fp.ret = fd->read(vm, n, buffer->at(0));
708+ auto fd = fd_alloc->get_allocation<Sys_FD_Impl>();
709+ *fp.ret = fd->impl->read(vm, n, buffer->at(0));
713710 }
714711
715712 void
@@ -742,10 +739,10 @@ Sys_seek(vm_registers_t &r, vm_t &vm) · 26 unmodified lines
742739 throw dereference_nil{ "Seek in file descriptor" };
743740
744741 assert(fd_alloc->alloc_type == T_FD);
745− auto fd = fd_alloc->get_allocation<vm_fd_t>();
742+ auto fd = fd_alloc->get_allocation<Sys_FD_Impl>();
746743
747744 const auto start = convert_to_seekdir(fp.start);
748− *fp.ret = fd->seek(start, fp.off);
745+ *fp.ret = fd->impl->seek(vm, start, fp.off);
749746 }
750747
751748 void
@@ -785,6 +782,7 @@ Sys_sprint(vm_registers_t &r, vm_t &vm) · 33 unmodified lines
785782 new_string = new vm_string_t{ static_cast<std::size_t>(written), reinterpret_cast<uint8_t *>(dynamic_buffer.data()) };
786783 }
787784
785+ disvm::runtime::dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret));
788786 *fp.ret = new_string->get_allocation();
789787 }
790788
@@ -792,6 +790,8 @@ void · 1 unmodified line
792790 Sys_stat(vm_registers_t &r, vm_t &vm)
793791 {
794792 auto &fp = r.stack.peek_frame()->base<F_Sys_stat>();
793+
794+ // [PAL] Consume platform API
795795 throw vm_system_exception{ "Function not implemented" };
796796 }
797797
@@ -799,7 +799,36 @@ void · 1 unmodified line
799799 Sys_stream(vm_registers_t &r, vm_t &vm)
800800 {
801801 auto &fp = r.stack.peek_frame()->base<F_Sys_stream>();
802− throw vm_system_exception{ "Function not implemented" };
802+
803+ auto alloc_s = vm_alloc_t::from_allocation(fp.src);
804+ if (alloc_s == nullptr)
805+ throw dereference_nil{ "Source in stream" };
806+
807+ auto alloc_d = vm_alloc_t::from_allocation(fp.dst);
808+ if (alloc_d == nullptr)
809+ throw dereference_nil{ "Destination in stream" };
810+
811+ assert(alloc_s->alloc_type->is_equal(T_FD.get()) && alloc_d->alloc_type->is_equal(T_FD.get()));
812+ auto src = alloc_s->get_allocation<Sys_FD_Impl>()->impl;
813+ auto dst = alloc_d->get_allocation<Sys_FD_Impl>()->impl;
814+
815+ const auto buffer_size = fp.bufsiz;
816+ if (buffer_size <= 0)
817+ throw out_of_range_memory{};
818+
819+ std::unique_ptr<void, decltype(&disvm::runtime::free_memory)> buffer{ disvm::runtime::alloc_memory(buffer_size), disvm::runtime::free_memory };
820+ auto written = word_t{};
821+ for (;;)
822+ {
823+ auto bytes_read = src->read(vm, buffer_size, buffer.get());
824+ if (bytes_read == 0)
825+ break;
826+
827+ dst->write(vm, bytes_read, buffer.get());
828+ written += bytes_read;
829+ }
830+
831+ *fp.ret = written;
803832 }
804833
805834 namespace
@@ -993,8 +1022,8 @@ Sys_write(vm_registers_t &r, vm_t &vm) · 187 unmodified lines
9931022
9941023 assert(fd_alloc->alloc_type == T_FD);
9951024
996− auto fd = fd_alloc->get_allocation<vm_fd_t>();
997− fd->write(vm, n, buffer->at(0));
1025+ auto fd = fd_alloc->get_allocation<Sys_FD_Impl>();
1026+ fd->impl->write(vm, n, buffer->at(0));
9981027
9991028 *fp.ret = n;
10001029 }
@@ -1003,5 +1032,7 @@ void · 2 unmodified lines
10031032 Sys_wstat(vm_registers_t &r, vm_t &vm)
10041033 {
10051034 auto &fp = r.stack.peek_frame()->base<F_Sys_wstat>();
1035+
1036+ // [PAL] Consume platform API
10061037 throw vm_system_exception{ "Function not implemented" };
10071038 }
src/vm/sys/fd_manager.cpp+66−47
@@ -6,7 +6,8 @@ · 5 unmodified lines
66
77 #include <array>
88 #include <mutex>
9−#include <unordered_map>
9+#include <vector>
10+#include <queue>
1011 #include <exceptions.h>
1112 #include <vm_memory.h>
1213 #include "sys_utils.h"
@@ -14,72 +15,90 @@ · 1 unmodified line
1415 using disvm::runtime::vm_alloc_t;
1516 using disvm::runtime::word_t;
1617
18+using disvm::runtime::sys::vm_fd_t;
19+
1720 namespace
1821 {
19− using fd_map_t = std::unordered_map<word_t, vm_alloc_t *>;
22+ std::mutex fd_map_lock;
23+ auto fd_map = std::vector<vm_fd_t *>{};
24+ auto fd_available = std::priority_queue<word_t, std::vector<word_t>, std::greater<word_t>>{};
25+}
26+
27+word_t disvm::runtime::sys::create_fd_record(vm_fd_t * vm_fd)
28+{
29+ assert(vm_fd != nullptr);
2030
21− auto std_fd = std::array<vm_alloc_t * , 3>
31+ std::lock_guard<std::mutex> lock{ fd_map_lock };
32+
33+ word_t new_fd;
34+ if (fd_available.empty())
35+ {
36+ new_fd = fd_map.size();
37+ fd_map.push_back(vm_fd);
38+ }
39+ else
2240 {
23− nullptr, // stdin
24− nullptr, // stdout
25− nullptr, // stderr
26− };
41+ new_fd = fd_available.top();
42+ fd_available.pop();
2743
28− auto next_fd = word_t{ 0 };
44+ assert(fd_map[new_fd] == nullptr && "Reused FD should be null");
45+ fd_map[new_fd] = vm_fd;
46+ }
2947
30− std::mutex fd_map_lock;
31− auto fd_map = fd_map_t{};
48+ return new_fd;
3249 }
3350
34−word_t disvm::runtime::sys::create_fd_record(vm_alloc_t * fd_alloc)
51+bool disvm::runtime::sys::try_update_fd_record(word_t fd, vm_fd_t *vm_fd)
3552 {
36− assert(fd_alloc != nullptr);
53+ assert(fd != sys::vm_invalid_fd);
54+ assert(vm_fd != nullptr);
3755
38− {
39− std::lock_guard<std::mutex> lock{ fd_map_lock };
40−
41− auto new_fd = next_fd;
42− next_fd++;
43− if (new_fd < static_cast<word_t>(std_fd.size()))
44− {
45− std_fd[new_fd] = fd_alloc;
46− }
47− else
48− {
49− assert(fd_map.cend() == fd_map.find(new_fd));
50− fd_map[new_fd] = fd_alloc;
51− }
52−
53− return new_fd;
54− }
56+ std::lock_guard<std::mutex> lock{ fd_map_lock };
57+ if (fd < 0 || static_cast<word_t>(fd_map.size()) <= fd)
58+ return false;
59+
60+ auto vm_fd_curr = fd_map[fd];
61+ auto current_ref = vm_fd_curr->release();
62+ if (current_ref == 0)
63+ delete vm_fd_curr;
64+
65+ fd_map[fd] = vm_fd;
66+ return true;
5567 }
5668
57−vm_alloc_t * disvm::runtime::sys::fetch_fd_record(word_t fd)
69+vm_fd_t * disvm::runtime::sys::fetch_fd_record(word_t fd)
5870 {
59− assert(fd != sys::vm_invalid_fd && "Invalid FD value");
71+ assert(fd != sys::vm_invalid_fd);
6072
61− if (0 <= fd && fd < static_cast<word_t>(std_fd.size()))
62− return std_fd[fd];
73+ std::lock_guard<std::mutex> lock{ fd_map_lock };
74+ if (fd < 0 || static_cast<word_t>(fd_map.size()) <= fd)
75+ return nullptr;
6376
64− {
65− std::lock_guard<std::mutex> lock{ fd_map_lock };
66− auto iter = fd_map.find(fd);
67− if (iter == fd_map.cend())
68− return nullptr;
77+ auto vm_fd = fd_map[fd];
78+ if (vm_fd != nullptr)
79+ vm_fd->add_ref();
6980
70− return iter->second;
71− }
81+ return vm_fd;
7282 }
7383
7484 void disvm::runtime::sys::drop_fd_record(word_t fd)
7585 {
76− assert(fd != sys::vm_invalid_fd && "Invalid FD value");
77− assert(fd >= static_cast<word_t>(std_fd.size()) && "Request to drop std file?");
86+ assert(fd != sys::vm_invalid_fd);
87+
88+ std::lock_guard<std::mutex> lock{ fd_map_lock };
89+ if (fd < 0 || static_cast<word_t>(fd_map.size()) <= fd)
90+ return;
7891
92+ assert(fd_map[fd] != nullptr);
93+
94+ // Manual management of file descriptors, since there is an inherent life time
95+ // race amoung the enclosing ADT instances that can have a reference.
96+ auto vm_fd = fd_map[fd];
97+ auto current_ref = vm_fd->release();
98+ if (current_ref == 0)
7999 {
80− std::lock_guard<std::mutex> lock{ fd_map_lock };
81− auto iter = fd_map.find(fd);
82− if (iter != fd_map.cend())
83− fd_map.erase(iter);
100+ delete vm_fd;
101+ fd_map[fd] = nullptr;
102+ fd_available.push(fd);
84103 }
85−}
104+}
src/vm/sys/fd_types.cpp+217−0
@@ -0,0 +1,217 @@
1+//
2+// Dis VM
3+// File: fd_types.cpp
4+// Author: arr
5+//
6+
7+#include <cstdio>
8+#include <algorithm>
9+#include <streambuf>
10+
11+#include <debug.h>
12+#include <exceptions.h>
13+#include <vm_memory.h>
14+#include "fd_types.h"
15+
16+using disvm::vm_t;
17+
18+using disvm::debug::component_trace_t;
19+using disvm::debug::log_level_t;
20+
21+using disvm::runtime::big_t;
22+using disvm::runtime::byte_t;
23+using disvm::runtime::type_descriptor_t;
24+using disvm::runtime::vm_string_t;
25+using disvm::runtime::word_t;
26+using disvm::runtime::vm_system_exception;
27+using disvm::runtime::vm_syscall_exception;
28+using disvm::runtime::vm_user_exception;
29+
30+using disvm::runtime::sys::open_mode_t;
31+using disvm::runtime::sys::seek_start_t;
32+using disvm::runtime::sys::std_streams;
33+using disvm::runtime::sys::vm_fd_t;
34+
35+vm_fd_t::vm_fd_t(std::shared_ptr<const type_descriptor_t> td)
36+ : vm_alloc_t(std::move(td))
37+{ }
38+
39+namespace
40+{
41+ namespace hidden_type_desc
42+ {
43+ const type_descriptor_t vm_fd_t{ 0, 0, nullptr, type_descriptor_t::no_finalizer, "vm_fd_t" };
44+
45+ struct
46+ {
47+ void operator()(const type_descriptor_t *)
48+ {
49+ // Not allocated on heap
50+ }
51+ } deleter;
52+ }
53+}
54+
55+std::shared_ptr<const type_descriptor_t> vm_fd_t::type_desc()
56+{
57+ static std::shared_ptr<const type_descriptor_t> t = { &hidden_type_desc::vm_fd_t, hidden_type_desc::deleter };
58+ return t;
59+}
60+
61+namespace
62+{
63+ int seek_to_int(const seek_start_t m)
64+ {
65+ switch (m)
66+ {
67+ default:
68+ assert(false && "Unknown seek start type");
69+ case seek_start_t::start:
70+ return SEEK_SET;
71+ case seek_start_t::relative:
72+ return SEEK_CUR;
73+ case seek_start_t::end:
74+ return SEEK_END;
75+ }
76+ }
77+
78+ class sys_fd_t final : public vm_fd_t
79+ {
80+ public:
81+ sys_fd_t(std::FILE *sys_fd, vm_string_t *fd_path, const open_mode_t fd_mode)
82+ : vm_fd_t(type_desc())
83+ , _fd_mode{ fd_mode }
84+ , _fd_path{ fd_path }
85+ , _sys_fd{ sys_fd }
86+ {
87+ assert(sys_fd != nullptr);
88+
89+ if (_fd_path != nullptr)
90+ _fd_path->add_ref();
91+ }
92+
93+ ~sys_fd_t()
94+ {
95+ std::fclose(_sys_fd);
96+ disvm::debug::assign_debug_pointer(&_sys_fd);
97+
98+ // Honor the close flag
99+ if (disvm::util::has_flag(_fd_mode, open_mode_t::delete_on_close) && _fd_path != nullptr)
100+ std::remove(_fd_path->str());
101+
102+ disvm::runtime::dec_ref_count_and_free(_fd_path);
103+ disvm::debug::assign_debug_pointer(&_fd_path);
104+ }
105+
106+ word_t read(vm_t &vm, const word_t buffer_size_in_bytes, void *buffer) override
107+ {
108+ if (!disvm::util::has_flag(_fd_mode, open_mode_t::read))
109+ throw vm_system_exception{ "File descriptor not open for read operation" };
110+
111+ auto read = std::fread(buffer, sizeof(byte_t), buffer_size_in_bytes, _sys_fd);
112+ if (std::ferror(_sys_fd) != 0)
113+ throw vm_syscall_exception{ vm, "Read operation error" };
114+
115+ return static_cast<word_t>(read);
116+ }
117+
118+ void write(vm_t &vm, const word_t buffer_size_in_bytes, void *buffer) override
119+ {
120+ if (!disvm::util::has_flag(_fd_mode, open_mode_t::write))
121+ throw vm_system_exception{ "File descriptor not open for write operation" };
122+
123+ std::fwrite(buffer, sizeof(byte_t), buffer_size_in_bytes, _sys_fd);
124+ if (std::ferror(_sys_fd) != 0)
125+ throw vm_syscall_exception{ vm, "Write operation error" };
126+ }
127+
128+ big_t seek(vm_t &vm, const seek_start_t seek_start, const big_t offset) override
129+ {
130+ if (disvm::util::has_flag(_fd_mode, open_mode_t::disable_seek))
131+ throw vm_user_exception{ "Unable to seek on a fifo file descriptor (e.g. stdin, stdout, stderr)" };
132+
133+ auto result = std::fseek(_sys_fd, static_cast<long>(offset), seek_to_int(seek_start));
134+ if (result != 0)
135+ throw vm_syscall_exception{ vm, "Seek operation error" };
136+
137+ return static_cast<big_t>(std::ftell(_sys_fd));
138+ }
139+
140+ private:
141+ const open_mode_t _fd_mode;
142+ vm_string_t *_fd_path;
143+ std::FILE *_sys_fd;
144+ };
145+}
146+
147+namespace
148+{
149+ std::mutex _create_file_path_mutex;
150+
151+ const std::array<char *, 8> fopen_mode_strings =
152+ {
153+ // R W T
154+ nullptr, // 0 0 0
155+ "rb", // 1 0 0
156+ "wb", // 0 1 0
157+ "rwb", // 1 1 0
158+ nullptr, // 0 0 1
159+ "rb+", // 1 0 1
160+ "wb+", // 0 1 1
161+ "wb+", // 1 1 1
162+ };
163+
164+ const auto fopen_modes = (open_mode_t::read | open_mode_t::write | open_mode_t::truncate);
165+
166+ constexpr const char *mode_for_fopen(const open_mode_t o)
167+ {
168+ return fopen_mode_strings[static_cast<std::size_t>(fopen_modes & o)];
169+ }
170+}
171+
172+vm_fd_t *disvm::runtime::sys::create_from_file_path(vm_string_t *path, open_mode_t mode)
173+{
174+ std::unique_lock<std::mutex> lock{ _create_file_path_mutex, std::defer_lock };
175+
176+ if ((mode & ~fopen_modes) != open_mode_t::none)
177+ {
178+ if (util::has_flag(mode, open_mode_t::atomic))
179+ lock.lock();
180+
181+ if (util::has_flag(mode, (open_mode_t::ensure_create | open_mode_t::ensure_exists)))
182+ {
183+ assert(util::has_flag(mode, open_mode_t::ensure_create) != util::has_flag(mode, open_mode_t::ensure_exists) && "Create/Exists flags are mutually exclusive");
184+ // Check if the file already exists/permission to read
185+ auto tmp = std::fopen(path->str(), mode_for_fopen(open_mode_t::read));
186+ const auto exists = (tmp != nullptr);
187+
188+ if (exists)
189+ std::fclose(tmp);
190+
191+ if (exists && util::has_flag(mode, open_mode_t::ensure_create)
192+ || !exists && util::has_flag(mode, open_mode_t::ensure_exists))
193+ {
194+ return{};
195+ }
196+ }
197+ }
198+
199+ auto fopen_mode = mode_for_fopen(mode);
200+ assert(fopen_mode != nullptr);
201+
202+ std::FILE *file = std::fopen(path->str(), fopen_mode);
203+ if (file == nullptr)
204+ return{}; // [TODO] Report system error?
205+
206+ return new sys_fd_t{ file, path, mode };
207+}
208+
209+std_streams disvm::runtime::sys::get_std_streams()
210+{
211+ return
212+ {
213+ new sys_fd_t{ stdin, nullptr, (open_mode_t::read | open_mode_t::disable_seek) },
214+ new sys_fd_t{ stdout, nullptr, (open_mode_t::write | open_mode_t::disable_seek) },
215+ new sys_fd_t{ stderr, nullptr, (open_mode_t::write | open_mode_t::disable_seek) },
216+ };
217+}
src/vm/sys/fd_types.h+78−0
@@ -0,0 +1,78 @@
1+//
2+// Dis VM
3+// File: fd_types.h
4+// Author: arr
5+//
6+
7+#ifndef _DISVM_SRC_VM_SYS_FD_TYPES_H_
8+#define _DISVM_SRC_VM_SYS_FD_TYPES_H_
9+
10+#include <memory>
11+
12+#include <runtime.h>
13+#include <utils.h>
14+
15+namespace disvm
16+{
17+ namespace runtime
18+ {
19+ namespace sys
20+ {
21+ enum class seek_start_t
22+ {
23+ start,
24+ relative,
25+ end,
26+ };
27+
28+ enum class open_mode_t
29+ {
30+ none = 0,
31+ read = (1 << 0),
32+ write = (1 << 1),
33+ truncate = (1 << 2),
34+ atomic = (1 << 3),
35+ ensure_create = (1 << 4), // Mutually exclusive with 'ensure_exists'
36+ ensure_exists = (1 << 5), // Mutually exclusive with 'ensure_create'
37+ delete_on_close = (1 << 6),
38+ disable_seek = (1 << 7),
39+ };
40+
41+ DEFINE_ENUM_FLAG_OPERATORS(open_mode_t);
42+
43+ // VM file descriptor
44+ class vm_fd_t : public vm_alloc_t
45+ {
46+ public: // static
47+ static std::shared_ptr<const type_descriptor_t> type_desc();
48+
49+ public:
50+ // Read from the file descriptor into the supplied buffer
51+ virtual word_t read(vm_t &vm, const word_t buffer_size_in_bytes, void *buffer) = 0;
52+
53+ // Write to the file descriptor from the supplied buffer
54+ virtual void write(vm_t &vm, const word_t buffer_size_in_bytes, void *buffer) = 0;
55+
56+ // Seek to the supplied offset in the file descriptor
57+ virtual big_t seek(vm_t &vm, const seek_start_t seek_start, const big_t offset) = 0;
58+
59+ protected:
60+ vm_fd_t(std::shared_ptr<const type_descriptor_t> td);
61+ };
62+
63+ // Create a file descriptor based on a file system path
64+ vm_fd_t *create_from_file_path(vm_string_t *path, open_mode_t mode);
65+
66+ struct std_streams final
67+ {
68+ vm_fd_t *input;
69+ vm_fd_t *output;
70+ vm_fd_t *error;
71+ };
72+
73+ std_streams get_std_streams();
74+ }
75+ }
76+}
77+
78+#endif // _DISVM_SRC_VM_SYS_FD_TYPES_H_
src/vm/sys/file_system.cpp+0−46
@@ -1,46 +0,0 @@
1−//
2−// Dis VM
3−// File: file_system.cpp
4−// Author: arr
5−//
6−
7−#include <iostream>
8−#include <fstream>
9−#include <mutex>
10−#include <utils.h>
11−#include "sys_utils.h"
12−
13−namespace
14−{
15− std::mutex _create_file_stream_mutex;
16−}
17−
18−std::unique_ptr<std::fstream> disvm::runtime::sys::create_file_stream(const char *path, std::ios::openmode mode, cfs_flags_t flags)
19−{
20− auto file = std::unique_ptr<std::fstream>{};
21− if (flags == cfs_flags_t::none)
22− {
23− file = std::make_unique<std::fstream>(path, mode);
24− }
25− else
26− {
27− std::unique_lock<std::mutex> lock{ _create_file_stream_mutex, std::defer_lock };
28− if (util::has_flag(flags, cfs_flags_t::atomic))
29− lock.lock();
30−
31− if (util::has_flag(flags, cfs_flags_t::ensure_create))
32− {
33− std::fstream file_exists{ path };
34− if (file_exists)
35− return{};
36− }
37−
38− file = std::make_unique<std::fstream>(path, mode);
39− }
40−
41− // Return null if file stream is not open.
42− if (!file->is_open())
43− return{};
44−
45− return file;
46−}
src/vm/sys/sys_utils.h+12−14
@@ -12,6 +12,7 @@ · 11 unmodified lines
1212 #include <memory>
1313 #include <disvm.h>
1414 #include <utils.h>
15+#include "fd_types.h"
1516
1617 namespace disvm
1718 {
@@ -38,25 +39,22 @@ namespace disvm · 20 unmodified lines
3839 pointer_t base,
3940 std::vector<char> &buffer);
4041
41− enum class cfs_flags_t
42− {
43− none = 0,
44− atomic = 1 << 0,
45− ensure_create = 1 << 1,
46− };
47−
48− DEFINE_ENUM_FLAG_OPERATORS(cfs_flags_t);
49−
50− std::unique_ptr<std::fstream> create_file_stream(const char *path, std::ios::openmode mode, cfs_flags_t flags = sys::cfs_flags_t::none);
51−
5242 const word_t vm_invalid_fd = -1;
5343
54− // The first three FD records are optimized for lock-less access.
55− word_t create_fd_record(vm_alloc_t *fd_alloc);
44+ // No reference count updates are performed on supplied vm_fd_t instance.
45+ word_t create_fd_record(vm_fd_t *vm_fd);
46+
47+ // Returns 'false' if the supplied file descriptor is invalid, otherwise 'true'.
48+ // No reference count updates are performed on supplied vm_fd_t instance.
49+ bool try_update_fd_record(word_t fd, vm_fd_t *vm_fd);
5650
5751 // Returns null if the record does not exist.
58− vm_alloc_t * fetch_fd_record(word_t fd);
52+ // If the record does exist, the reference count is incremented prior
53+ // to the instance being returned.
54+ vm_fd_t * fetch_fd_record(word_t fd);
5955
56+ // The associated vm_fd_t instance reference count is decremented
57+ // and will be deleted once all references are released.
6058 void drop_fd_record(word_t fd);
6159 }
6260 }