Unified · Split
| @@ -759,9 +759,11 @@ concheck(n: ref Node, isglobal: int) · 758 unmodified lines | ||
| 759 | 759 | exname(d: ref Decl): string |
| 760 | 760 | { |
| 761 | 761 | s := ""; |
| 762 | − m := impmods.sym; | |
| 762 | + m : ref Sym; | |
| 763 | 763 | if(d.dot != nil) |
| 764 | 764 | m = d.dot.sym; |
| 765 | + else if(impmods != nil) | |
| 766 | + m = impmods.sym; | |
| 765 | 767 | if(m != nil) |
| 766 | 768 | s += m.name+"."; |
| 767 | 769 | if(fndec != nil) |
| @@ -116,6 +116,57 @@ The above copyright notice and this permission notice shall be included in all c · 115 unmodified lines | ||
| 116 | 116 | |
| 117 | 117 | 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. |
| 118 | 118 | |
| 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 | + | |
| 119 | 170 | ### Unless otherwise stated in a `license.txt` or above - [MIT License](https://opensource.org/licenses/MIT) ### |
| 120 | 171 | |
| 121 | 172 | Copyright (c) 2017 arr |
| @@ -124,6 +124,15 @@ namespace disvm · 123 unmodified lines | ||
| 124 | 124 | const word_t invalid_value; |
| 125 | 125 | }; |
| 126 | 126 | |
| 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 | + | |
| 127 | 136 | class unhandled_user_exception final : public vm_user_exception |
| 128 | 137 | { |
| 129 | 138 | public: |
| @@ -76,12 +76,12 @@ namespace disvm · 75 unmodified lines | ||
| 76 | 76 | |
| 77 | 77 | // Adopted from 'winnt.h' in the Windows SDK |
| 78 | 78 | #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)); } \ | |
| 80 | 80 | 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)); } \ | |
| 82 | 82 | 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)); } \ | |
| 84 | 84 | 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)); } | |
| 86 | 86 | |
| 87 | 87 | #endif // _DISVM_SRC_INCLUDE_UTILS_H_ |
| @@ -32,7 +32,10 @@ void dec_src<address_mode_t::offset_double_indirect_fp>(const vm_exec_op_t &inst · 31 unmodified lines | ||
| 32 | 32 | { |
| 33 | 33 | assert(inst.source.mode == address_mode_t::offset_double_indirect_fp); |
| 34 | 34 | 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); | |
| 36 | 39 | } |
| 37 | 40 | |
| 38 | 41 | template<> |
| @@ -40,7 +43,10 @@ void dec_src<address_mode_t::offset_double_indirect_mp>(const vm_exec_op_t &inst · 1 unmodified line | ||
| 40 | 43 | { |
| 41 | 44 | assert(inst.source.mode == address_mode_t::offset_double_indirect_mp); |
| 42 | 45 | 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); | |
| 44 | 50 | } |
| 45 | 51 | |
| 46 | 52 | template<> |
| @@ -85,7 +91,10 @@ void dec_dest<address_mode_t::offset_double_indirect_fp>(const vm_exec_op_t &ins · 38 unmodified lines | ||
| 85 | 91 | { |
| 86 | 92 | assert(inst.destination.mode == address_mode_t::offset_double_indirect_fp); |
| 87 | 93 | 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); | |
| 89 | 98 | } |
| 90 | 99 | |
| 91 | 100 | template<> |
| @@ -93,7 +102,10 @@ void dec_dest<address_mode_t::offset_double_indirect_mp>(const vm_exec_op_t &ins · 1 unmodified line | ||
| 93 | 102 | { |
| 94 | 103 | assert(inst.destination.mode == address_mode_t::offset_double_indirect_mp); |
| 95 | 104 | 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); | |
| 97 | 109 | } |
| 98 | 110 | |
| 99 | 111 | template<> |
| @@ -24,10 +24,12 @@ · 23 unmodified lines | ||
| 24 | 24 | 'vm_memory.cpp', |
| 25 | 25 | 'vm_exception_handler.cpp', |
| 26 | 26 | 'math/Mathmod.cpp', |
| 27 | + 'math/dgemm.c', | |
| 28 | + 'math/lsame.c', | |
| 27 | 29 | 'sys/print.cpp', |
| 28 | 30 | 'sys/Sysmod.cpp', |
| 29 | − 'sys/file_system.cpp', | |
| 30 | 31 | 'sys/fd_manager.cpp', |
| 32 | + 'sys/fd_types.cpp', | |
| 31 | 33 | ], |
| 32 | 34 | 'include_dirs': [ |
| 33 | 35 | '../include' |
| @@ -164,7 +164,7 @@ namespace · 163 unmodified lines | ||
| 164 | 164 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); |
| 165 | 165 | |
| 166 | 166 | 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 }; | |
| 168 | 168 | pt_ref(r.dest) = new_array->get_allocation(); |
| 169 | 169 | } |
| 170 | 170 | |
| @@ -537,7 +537,7 @@ namespace · 366 unmodified lines | ||
| 537 | 537 | auto type_id = vt_ref<word_t>(r.mid); |
| 538 | 538 | const auto &types = r.module_ref->type_section; |
| 539 | 539 | 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]; | |
| 541 | 541 | |
| 542 | 542 | disvm::runtime::inc_ref_count_in_memory(*type, r.src); |
| 543 | 543 | destroy_memory(*type, r.dest); |
| @@ -853,7 +853,7 @@ namespace · 309 unmodified lines | ||
| 853 | 853 | throw out_of_range_memory{}; |
| 854 | 854 | } |
| 855 | 855 | |
| 856 | − auto new_array = new vm_array_t(*arr, begin_index, length); | |
| 856 | + auto new_array = new vm_array_t{ *arr, begin_index, length }; | |
| 857 | 857 | |
| 858 | 858 | if (arr->alloc_type->map_in_bytes > 0) |
| 859 | 859 | vm.get_garbage_collector().track_allocation(new_array); |
| @@ -971,14 +971,15 @@ namespace · 111 unmodified lines | ||
| 971 | 971 | auto tail_maybe = at_val<vm_list_t>(r.dest); |
| 972 | 972 | |
| 973 | 973 | // 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 }; | |
| 975 | 976 | if (tail_maybe != nullptr) |
| 976 | 977 | tail_maybe->release(); |
| 977 | 978 | |
| 978 | 979 | 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); | |
| 980 | 981 | |
| 981 | − if (type->map_in_bytes > 0) | |
| 982 | + if (type_size_in_bytes > 0) | |
| 982 | 983 | vm.get_garbage_collector().track_allocation(new_list); |
| 983 | 984 | |
| 984 | 985 | // Return the list |
| @@ -1329,9 +1330,10 @@ namespace · 344 unmodified lines | ||
| 1329 | 1330 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); |
| 1330 | 1331 | |
| 1331 | 1332 | 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)); | |
| 1333 | 1335 | |
| 1334 | − if (type_desc->map_in_bytes > 0) | |
| 1336 | + if (track_object) | |
| 1335 | 1337 | vm.get_garbage_collector().track_allocation(new_alloc); |
| 1336 | 1338 | |
| 1337 | 1339 | pt_ref(r.dest) = new_alloc->get_allocation(); |
| @@ -1363,9 +1365,10 @@ namespace · 25 unmodified lines | ||
| 1363 | 1365 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); |
| 1364 | 1366 | |
| 1365 | 1367 | 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)); | |
| 1367 | 1370 | |
| 1368 | − if (type_desc->map_in_bytes > 0) | |
| 1371 | + if (track_object) | |
| 1369 | 1372 | vm.get_garbage_collector().track_allocation(new_alloc); |
| 1370 | 1373 | |
| 1371 | 1374 | pt_ref(r.dest) = new_alloc->get_allocation(); |
| @@ -1385,9 +1388,10 @@ namespace · 13 unmodified lines | ||
| 1385 | 1388 | dec_ref_count_and_free(at_val<vm_alloc_t>(r.dest)); |
| 1386 | 1389 | |
| 1387 | 1390 | 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 }; | |
| 1389 | 1393 | |
| 1390 | − if (type_desc->map_in_bytes > 0) | |
| 1394 | + if (track_object) | |
| 1391 | 1395 | vm.get_garbage_collector().track_allocation(new_array); |
| 1392 | 1396 | |
| 1393 | 1397 | pt_ref(r.dest) = new_array->get_allocation(); |
| @@ -1437,7 +1441,7 @@ namespace · 43 unmodified lines | ||
| 1437 | 1441 | |
| 1438 | 1442 | const auto frame_type_id = vt_ref<word_t>(r.src); |
| 1439 | 1443 | 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]; | |
| 1441 | 1445 | |
| 1442 | 1446 | pt_ref(r.dest) = r.stack.alloc_frame(std::move(frame_type))->base(); |
| 1443 | 1447 | } |
| @@ -1456,9 +1460,9 @@ namespace · 12 unmodified lines | ||
| 1456 | 1460 | |
| 1457 | 1461 | const auto frame_type_id = function_ref.frame_type; |
| 1458 | 1462 | 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]; | |
| 1460 | 1464 | |
| 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(); | |
| 1462 | 1466 | } |
| 1463 | 1467 | |
| 1464 | 1468 | EXEC_DECL(ret) |
| @@ -1503,6 +1507,9 @@ namespace · 38 unmodified lines | ||
| 1503 | 1507 | disvm::debug::log_msg(debug::component_trace_t::stack, debug::log_level_t::debug, "enter: function"); |
| 1504 | 1508 | } |
| 1505 | 1509 | |
| 1510 | + // Forward declaration | |
| 1511 | + EXEC_DECL(raise); | |
| 1512 | + | |
| 1506 | 1513 | EXEC_DECL(mcall) |
| 1507 | 1514 | { |
| 1508 | 1515 | auto target_module = at_val<vm_module_ref_t>(r.dest); |
| @@ -1547,22 +1554,52 @@ namespace · 38 unmodified lines | ||
| 1547 | 1554 | r.mp_base = r.module_ref->mp_base; |
| 1548 | 1555 | r.next_pc = function_pc; |
| 1549 | 1556 | |
| 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()) | |
| 1551 | 1559 | { |
| 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. | |
| 1554 | 1570 | |
| 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 | + { | |
| 1556 | 1579 | inst.native(r, vm); |
| 1557 | − ret(r, vm); | |
| 1558 | 1580 | |
| 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); | |
| 1562 | 1583 | } |
| 1563 | − else | |
| 1584 | + catch (const marshallable_user_exception &e) | |
| 1564 | 1585 | { |
| 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); | |
| 1566 | 1603 | } |
| 1567 | 1604 | } |
| 1568 | 1605 | |
| @@ -1706,10 +1743,23 @@ namespace · 137 unmodified lines | ||
| 1706 | 1743 | |
| 1707 | 1744 | if (target_frame != r.stack.peek_frame()) |
| 1708 | 1745 | { |
| 1709 | − while (target_frame != r.stack.pop_frame()) | |
| 1746 | + // Unwind stack to find target frame | |
| 1747 | + do | |
| 1710 | 1748 | { |
| 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 | + } | |
| 1712 | 1761 | } |
| 1762 | + while (target_frame != r.stack.pop_frame()); | |
| 1713 | 1763 | } |
| 1714 | 1764 | |
| 1715 | 1765 | // Re-initialize the current frame |
| @@ -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 | ||
| 45 | 45 | |
| 46 | 46 | vm_list_t::~vm_list_t() |
| 47 | 47 | { |
| 48 | − // Remove tail | |
| 49 | − dec_ref_count_and_free(_tail); | |
| 50 | − debug::assign_debug_pointer(&_tail); | |
| 51 | − | |
| 52 | 48 | // Destroy and free the list element, if default element size was surpassed. |
| 53 | 49 | if (_is_alloc) |
| 54 | 50 | { |
| @@ -61,7 +57,13 @@ vm_list_t::~vm_list_t() · 6 unmodified lines | ||
| 61 | 57 | } |
| 62 | 58 | |
| 63 | 59 | 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"); | |
| 65 | 67 | } |
| 66 | 68 | |
| 67 | 69 | std::shared_ptr<const type_descriptor_t> vm_list_t::get_element_type() const |
| @@ -24,6 +24,7 @@ using disvm::debug::log_level_t; · 23 unmodified lines | ||
| 24 | 24 | |
| 25 | 25 | using disvm::runtime::dereference_nil; |
| 26 | 26 | using disvm::runtime::intrinsic_type_desc; |
| 27 | +using disvm::runtime::out_of_range_memory; | |
| 27 | 28 | using disvm::runtime::real_t; |
| 28 | 29 | using disvm::runtime::vm_alloc_t; |
| 29 | 30 | using disvm::runtime::vm_array_t; |
| @@ -430,7 +431,28 @@ void · 400 unmodified lines | ||
| 430 | 431 | Math_export_int(vm_registers_t &r, vm_t &vm) |
| 431 | 432 | { |
| 432 | 433 | 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 | + } | |
| 434 | 456 | } |
| 435 | 457 | |
| 436 | 458 | void |
| @@ -445,7 +467,7 @@ Math_export_real(vm_registers_t &r, vm_t &vm) · 8 unmodified lines | ||
| 445 | 467 | assert(values->get_element_type()->is_equal(intrinsic_type_desc::type<real_t>().get())); |
| 446 | 468 | const auto values_len = values->get_length(); |
| 447 | 469 | |
| 448 | − if (buffer->get_length() != (8 * values_len)) | |
| 470 | + if (buffer->get_length() != (sizeof(real_t) * values_len)) | |
| 449 | 471 | throw vm_user_exception{ "Invalid buffer size based on conversion" }; |
| 450 | 472 | |
| 451 | 473 | // Insert as big endian |
| @@ -472,7 +494,32 @@ void · 20 unmodified lines | ||
| 472 | 494 | Math_export_real32(vm_registers_t &r, vm_t &vm) |
| 473 | 495 | { |
| 474 | 496 | 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 | + } | |
| 476 | 523 | } |
| 477 | 524 | |
| 478 | 525 | void |
| @@ -524,11 +571,71 @@ Math_fmod(vm_registers_t &r, vm_t &vm) · 45 unmodified lines | ||
| 524 | 571 | *fp.ret = std::fmod(fp.x, fp.y); |
| 525 | 572 | } |
| 526 | 573 | |
| 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 | + | |
| 527 | 585 | void |
| 528 | 586 | Math_gemm(vm_registers_t &r, vm_t &vm) |
| 529 | 587 | { |
| 530 | 588 | 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" }; | |
| 532 | 639 | } |
| 533 | 640 | |
| 534 | 641 | void |
| @@ -556,7 +663,34 @@ void · 21 unmodified lines | ||
| 556 | 663 | Math_iamax(vm_registers_t &r, vm_t &vm) |
| 557 | 664 | { |
| 558 | 665 | 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; | |
| 560 | 694 | } |
| 561 | 695 | |
| 562 | 696 | void |
| @@ -570,7 +704,27 @@ void · 7 unmodified lines | ||
| 570 | 704 | Math_import_int(vm_registers_t &r, vm_t &vm) |
| 571 | 705 | { |
| 572 | 706 | 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 | + } | |
| 574 | 728 | } |
| 575 | 729 | |
| 576 | 730 | void |
| @@ -585,14 +739,14 @@ Math_import_real(vm_registers_t &r, vm_t &vm) · 8 unmodified lines | ||
| 585 | 739 | assert(result->get_element_type()->is_equal(intrinsic_type_desc::type<real_t>().get())); |
| 586 | 740 | const auto result_len = result->get_length(); |
| 587 | 741 | |
| 588 | − if (buffer->get_length() != (8 * result_len)) | |
| 742 | + if (buffer->get_length() != (sizeof(real_t) * result_len)) | |
| 589 | 743 | throw vm_user_exception{ "Invalid buffer size based on conversion" }; |
| 590 | 744 | |
| 591 | 745 | // Process as big endian |
| 592 | 746 | for (int i = 0; i < result_len; ++i) |
| 593 | 747 | { |
| 594 | 748 | 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) | |
| 596 | 750 | t = (t << 8) | buffer->at<byte_t>(j); |
| 597 | 751 | |
| 598 | 752 | real_t x; |
| @@ -607,7 +761,31 @@ void · 8 unmodified lines | ||
| 607 | 761 | Math_import_real32(vm_registers_t &r, vm_t &vm) |
| 608 | 762 | { |
| 609 | 763 | 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 | + } | |
| 611 | 789 | } |
| 612 | 790 | |
| 613 | 791 | void |
| @@ -621,6 +799,8 @@ void · 7 unmodified lines | ||
| 621 | 799 | Math_j0(vm_registers_t &r, vm_t &vm) |
| 622 | 800 | { |
| 623 | 801 | auto &fp = r.stack.peek_frame()->base<F_Math_j0>(); |
| 802 | + | |
| 803 | + // [PAL] Consume platform API | |
| 624 | 804 | throw vm_system_exception{ "Function not implemented" }; |
| 625 | 805 | } |
| 626 | 806 | |
| @@ -628,6 +808,8 @@ void · 1 unmodified line | ||
| 628 | 808 | Math_j1(vm_registers_t &r, vm_t &vm) |
| 629 | 809 | { |
| 630 | 810 | auto &fp = r.stack.peek_frame()->base<F_Math_j1>(); |
| 811 | + | |
| 812 | + // [PAL] Consume platform API | |
| 631 | 813 | throw vm_system_exception{ "Function not implemented" }; |
| 632 | 814 | } |
| 633 | 815 | |
| @@ -635,6 +817,8 @@ void · 1 unmodified line | ||
| 635 | 817 | Math_jn(vm_registers_t &r, vm_t &vm) |
| 636 | 818 | { |
| 637 | 819 | auto &fp = r.stack.peek_frame()->base<F_Math_jn>(); |
| 820 | + | |
| 821 | + // [PAL] Consume platform API | |
| 638 | 822 | throw vm_system_exception{ "Function not implemented" }; |
| 639 | 823 | } |
| 640 | 824 | |
| @@ -670,7 +854,13 @@ void · 29 unmodified lines | ||
| 670 | 854 | Math_modf(vm_registers_t &r, vm_t &vm) |
| 671 | 855 | { |
| 672 | 856 | 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; | |
| 674 | 864 | } |
| 675 | 865 | |
| 676 | 866 | void |
| @@ -705,9 +895,7 @@ Math_norm1(vm_registers_t &r, vm_t &vm) · 28 unmodified lines | ||
| 705 | 895 | void |
| 706 | 896 | Math_norm2(vm_registers_t &r, vm_t &vm) |
| 707 | 897 | { |
| 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)) | |
| 711 | 899 | auto &fp = r.stack.peek_frame()->base<F_Math_norm2>(); |
| 712 | 900 | |
| 713 | 901 | auto xs = vm_alloc_t::from_allocation<vm_array_t>(fp.x); |
| @@ -740,7 +928,7 @@ void · 26 unmodified lines | ||
| 740 | 928 | Math_pow10(vm_registers_t &r, vm_t &vm) |
| 741 | 929 | { |
| 742 | 930 | 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)); | |
| 744 | 932 | } |
| 745 | 933 | |
| 746 | 934 | void |
| @@ -786,7 +974,7 @@ void · 39 unmodified lines | ||
| 786 | 974 | Math_scalbn(vm_registers_t &r, vm_t &vm) |
| 787 | 975 | { |
| 788 | 976 | 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); | |
| 790 | 978 | } |
| 791 | 979 | |
| 792 | 980 | void |
| @@ -863,6 +1051,8 @@ void · 70 unmodified lines | ||
| 863 | 1051 | Math_y0(vm_registers_t &r, vm_t &vm) |
| 864 | 1052 | { |
| 865 | 1053 | auto &fp = r.stack.peek_frame()->base<F_Math_y0>(); |
| 1054 | + | |
| 1055 | + // [PAL] Consume platform API | |
| 866 | 1056 | throw vm_system_exception{ "Function not implemented" }; |
| 867 | 1057 | } |
| 868 | 1058 | |
| @@ -870,6 +1060,8 @@ void · 1 unmodified line | ||
| 870 | 1060 | Math_y1(vm_registers_t &r, vm_t &vm) |
| 871 | 1061 | { |
| 872 | 1062 | auto &fp = r.stack.peek_frame()->base<F_Math_y1>(); |
| 1063 | + | |
| 1064 | + // [PAL] Consume platform API | |
| 873 | 1065 | throw vm_system_exception{ "Function not implemented" }; |
| 874 | 1066 | } |
| 875 | 1067 | |
| @@ -877,5 +1069,7 @@ void · 1 unmodified line | ||
| 877 | 1069 | Math_yn(vm_registers_t &r, vm_t &vm) |
| 878 | 1070 | { |
| 879 | 1071 | auto &fp = r.stack.peek_frame()->base<F_Math_yn>(); |
| 1072 | + | |
| 1073 | + // [PAL] Consume platform API | |
| 880 | 1074 | throw vm_system_exception{ "Function not implemented" }; |
| 881 | 1075 | } |
| @@ -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 | + | |
| @@ -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 | + | |
| @@ -17,15 +17,18 @@ · 16 unmodified lines | ||
| 17 | 17 | #include <vm_memory.h> |
| 18 | 18 | #include "Sysmod.h" |
| 19 | 19 | #include "sys_utils.h" |
| 20 | +#include "fd_types.h" | |
| 20 | 21 | |
| 21 | 22 | using disvm::vm_t; |
| 22 | 23 | |
| 23 | 24 | using disvm::debug::component_trace_t; |
| 24 | 25 | using disvm::debug::log_level_t; |
| 25 | 26 | |
| 27 | +using disvm::runtime::byte_t; | |
| 26 | 28 | using disvm::runtime::dereference_nil; |
| 27 | 29 | using disvm::runtime::intrinsic_type_desc; |
| 28 | 30 | using disvm::runtime::out_of_range_memory; |
| 31 | +using disvm::runtime::pointer_t; | |
| 29 | 32 | using disvm::runtime::type_descriptor_t; |
| 30 | 33 | using disvm::runtime::vm_alloc_t; |
| 31 | 34 | using disvm::runtime::vm_array_t; |
| @@ -34,8 +37,13 @@ using disvm::runtime::vm_string_t; · 2 unmodified lines | ||
| 34 | 37 | using disvm::runtime::vm_syscall_exception; |
| 35 | 38 | using disvm::runtime::vm_system_exception; |
| 36 | 39 | using disvm::runtime::vm_user_exception; |
| 40 | +using disvm::runtime::marshallable_user_exception; | |
| 37 | 41 | using disvm::runtime::word_t; |
| 38 | 42 | |
| 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 | + | |
| 39 | 47 | namespace |
| 40 | 48 | { |
| 41 | 49 | disvm::runtime::builtin::vm_runtab_t Sysmodtab[] = { |
| @@ -93,12 +101,14 @@ namespace · 51 unmodified lines | ||
| 93 | 101 | std::shared_ptr<const type_descriptor_t> T_Connection; |
| 94 | 102 | std::shared_ptr<const type_descriptor_t> T_FileIO; |
| 95 | 103 | |
| 96 | − class vm_fd_t final : public Sys_FD | |
| 104 | + class Sys_FD_Impl final : public Sys_FD | |
| 97 | 105 | { |
| 98 | 106 | public: // static |
| 99 | 107 | static void finalizer(vm_alloc_t *fileHandle) |
| 100 | 108 | { |
| 101 | − auto fd = fileHandle->get_allocation<vm_fd_t>(); | |
| 109 | + auto fd = fileHandle->get_allocation<Sys_FD_Impl>(); | |
| 110 | + | |
| 111 | + fd->~Sys_FD_Impl(); | |
| 102 | 112 | |
| 103 | 113 | if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>()) |
| 104 | 114 | { |
| @@ -108,128 +118,64 @@ namespace · 3 unmodified lines | ||
| 108 | 118 | "sys: fd: finalize: %d", |
| 109 | 119 | fd->fd); |
| 110 | 120 | } |
| 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(); | |
| 138 | 121 | } |
| 139 | 122 | |
| 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) | |
| 160 | 125 | { |
| 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"); | |
| 165 | 128 | |
| 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{}; | |
| 169 | 131 | |
| 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(); | |
| 173 | 135 | |
| 174 | − word_t get_original_mode() const | |
| 175 | − { | |
| 176 | − return _fd_mode; | |
| 136 | + return alloc; | |
| 177 | 137 | } |
| 178 | 138 | |
| 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) | |
| 180 | 141 | { |
| 181 | − return _fd_path; | |
| 182 | − } | |
| 142 | + assert(vm_fd != nullptr); | |
| 143 | + assert(fd >= 0); | |
| 183 | 144 | |
| 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{}; | |
| 188 | 147 | |
| 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; | |
| 192 | 150 | |
| 193 | − return static_cast<word_t>(_istream->gcount()); | |
| 151 | + return alloc; | |
| 194 | 152 | } |
| 195 | 153 | |
| 196 | − void write(disvm::vm_t &vm, const word_t buffer_size_in_bytes, void *buffer) | |
| 154 | + public: | |
| 155 | + ~Sys_FD_Impl() | |
| 197 | 156 | { |
| 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); | |
| 204 | 160 | } |
| 205 | 161 | |
| 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; | |
| 214 | 163 | |
| 215 | 164 | 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; | |
| 221 | 166 | }; |
| 222 | 167 | |
| 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; | |
| 226 | 172 | |
| 227 | 173 | word_t printf_to_fd( |
| 228 | 174 | disvm::vm_t &vm, |
| 229 | 175 | 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) | |
| 233 | 179 | { |
| 234 | 180 | auto static_buffer = std::array<char, 1024>{}; |
| 235 | 181 | 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 | ||
| 247 | 193 | return written; |
| 248 | 194 | } |
| 249 | 195 | |
| 250 | − std::ios::openmode convert_to_openmode(const word_t mode) | |
| 196 | + open_mode_t convert_to_openmode(const word_t mode) | |
| 251 | 197 | { |
| 252 | − auto om = std::ios::openmode{}; | |
| 198 | + auto om = open_mode_t{}; | |
| 253 | 199 | |
| 254 | 200 | // Lowest bit is for read/write. |
| 255 | 201 | if (mode & Sys_OWRITE) |
| 256 | − om |= std::ios::out; | |
| 202 | + om |= open_mode_t::write; | |
| 257 | 203 | else |
| 258 | − om |= std::ios::in; // Sys_OREAD | |
| 204 | + om |= open_mode_t::read; // Sys_OREAD | |
| 259 | 205 | |
| 260 | 206 | if (mode & Sys_ORDWR) |
| 261 | − om |= (std::ios::in | std::ios::out); | |
| 207 | + om |= (open_mode_t::write | open_mode_t::read); | |
| 262 | 208 | |
| 263 | 209 | 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; | |
| 265 | 217 | |
| 266 | 218 | return om; |
| 267 | 219 | } |
| 268 | 220 | |
| 269 | − std::ios::seekdir convert_to_seekdir(const word_t start) | |
| 221 | + seek_start_t convert_to_seekdir(const word_t start) | |
| 270 | 222 | { |
| 271 | 223 | switch (start) |
| 272 | 224 | { |
| 273 | 225 | case Sys_SEEKSTART: |
| 274 | − return std::ios::beg; | |
| 226 | + return seek_start_t::start; | |
| 275 | 227 | case Sys_SEEKRELA: |
| 276 | − return std::ios::cur; | |
| 228 | + return seek_start_t::relative; | |
| 277 | 229 | case Sys_SEEKEND: |
| 278 | − return std::ios::end; | |
| 230 | + return seek_start_t::end; | |
| 279 | 231 | default: |
| 280 | 232 | throw vm_user_exception{ "Invalid seek mode" }; |
| 281 | 233 | } |
| @@ -288,49 +240,55 @@ Sysmodinit(void) · 6 unmodified lines | ||
| 288 | 240 | disvm::runtime::builtin::register_module_exports(Sys_PATH, Sysmodlen, Sysmodtab); |
| 289 | 241 | T_Qid = type_descriptor_t::create(sizeof(Sys_Qid), sizeof(Sys_Qid_map), Sys_Qid_map); |
| 290 | 242 | 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); | |
| 292 | 244 | T_Connection = type_descriptor_t::create(sizeof(Sys_Connection), sizeof(Sys_Connection_map), Sys_Connection_map); |
| 293 | 245 | T_FileIO = type_descriptor_t::create(sizeof(Sys_FileIO), sizeof(Sys_FileIO_map), Sys_FileIO_map); |
| 294 | 246 | |
| 295 | 247 | // Initialize default file descriptors |
| 248 | + auto std_streams = disvm::runtime::sys::get_std_streams(); | |
| 296 | 249 | |
| 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 | |
| 303 | 252 | |
| 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); | |
| 310 | 256 | |
| 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); | |
| 317 | 264 | } |
| 318 | 265 | |
| 319 | 266 | void |
| 320 | 267 | Sys_announce(vm_registers_t &r, vm_t &vm) |
| 321 | 268 | { |
| 322 | 269 | auto &fp = r.stack.peek_frame()->base<F_Sys_announce>(); |
| 270 | + | |
| 271 | + // [PAL] Consume platform API | |
| 323 | 272 | throw vm_system_exception{ "Function not implemented" }; |
| 324 | 273 | } |
| 325 | 274 | |
| 326 | 275 | void |
| 327 | 276 | Sys_aprint(vm_registers_t &r, vm_t &vm) |
| 328 | 277 | { |
| 278 | + auto fp_base = r.stack.peek_frame()->base(); | |
| 329 | 279 | 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" }; | |
| 330 | 283 | |
| 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(); | |
| 334 | 292 | } |
| 335 | 293 | |
| 336 | 294 | void |
| @@ -421,6 +379,8 @@ void · 84 unmodified lines | ||
| 421 | 379 | Sys_chdir(vm_registers_t &r, vm_t &vm) |
| 422 | 380 | { |
| 423 | 381 | auto &fp = r.stack.peek_frame()->base<F_Sys_chdir>(); |
| 382 | + | |
| 383 | + // [PAL] Consume platform API | |
| 424 | 384 | throw vm_system_exception{ "Function not implemented" }; |
| 425 | 385 | } |
| 426 | 386 | |
| @@ -429,46 +389,42 @@ Sys_create(vm_registers_t &r, vm_t &vm) · 2 unmodified lines | ||
| 429 | 389 | { |
| 430 | 390 | auto &fp = r.stack.peek_frame()->base<F_Sys_create>(); |
| 431 | 391 | |
| 392 | + dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret)); | |
| 393 | + *fp.ret = nullptr; | |
| 394 | + | |
| 432 | 395 | auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s); |
| 433 | 396 | const auto open_mode = convert_to_openmode(fp.mode); |
| 434 | 397 | |
| 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); | |
| 441 | 400 | |
| 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) | |
| 446 | 403 | return; |
| 447 | − } | |
| 448 | 404 | |
| 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(); | |
| 452 | 407 | |
| 453 | 408 | if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>()) |
| 454 | 409 | { |
| 410 | + auto fd_impl = fd_alloc->get_allocation<Sys_FD_Impl>(); | |
| 455 | 411 | disvm::debug::log_msg( |
| 456 | 412 | component_trace_t::builtin, |
| 457 | 413 | log_level_t::debug, |
| 458 | 414 | "sys: create: %d >>%s<< %#x %#x", |
| 459 | − fd, | |
| 415 | + fd_impl->fd, | |
| 460 | 416 | path->str(), |
| 461 | 417 | fp.mode, |
| 462 | 418 | fp.perm); |
| 463 | 419 | } |
| 464 | − | |
| 465 | − *fp.ret = fd_alloc->get_allocation(); | |
| 466 | 420 | } |
| 467 | 421 | |
| 468 | 422 | void |
| 469 | 423 | Sys_dial(vm_registers_t &r, vm_t &vm) |
| 470 | 424 | { |
| 471 | 425 | auto &fp = r.stack.peek_frame()->base<F_Sys_dial>(); |
| 426 | + | |
| 427 | + // [PAL] Consume platform API | |
| 472 | 428 | throw vm_system_exception{ "Function not implemented" }; |
| 473 | 429 | } |
| 474 | 430 | |
| @@ -476,14 +432,42 @@ void · 1 unmodified line | ||
| 476 | 432 | Sys_dirread(vm_registers_t &r, vm_t &vm) |
| 477 | 433 | { |
| 478 | 434 | auto &fp = r.stack.peek_frame()->base<F_Sys_dirread>(); |
| 435 | + | |
| 436 | + // [PAL] Consume platform API | |
| 479 | 437 | throw vm_system_exception{ "Function not implemented" }; |
| 480 | 438 | } |
| 481 | 439 | |
| 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 | + | |
| 482 | 447 | void |
| 483 | 448 | Sys_dup(vm_registers_t &r, vm_t &vm) |
| 484 | 449 | { |
| 485 | 450 | 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); | |
| 487 | 471 | } |
| 488 | 472 | |
| 489 | 473 | void |
| @@ -521,11 +505,11 @@ Sys_fildes(vm_registers_t &r, vm_t &vm) · 31 unmodified lines | ||
| 521 | 505 | dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret)); |
| 522 | 506 | *fp.ret = nullptr; |
| 523 | 507 | |
| 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) | |
| 526 | 510 | { |
| 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(); | |
| 529 | 513 | } |
| 530 | 514 | } |
| 531 | 515 | |
| @@ -546,18 +530,20 @@ Sys_fprint(vm_registers_t &r, vm_t &vm) · 14 unmodified lines | ||
| 546 | 530 | auto &fp = r.stack.peek_frame()->base<F_Sys_fprint>(); |
| 547 | 531 | auto fd_alloc = vm_alloc_t::from_allocation(fp.fd); |
| 548 | 532 | 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>(); | |
| 550 | 534 | auto str = vm_alloc_t::from_allocation<vm_string_t>(fp.s); |
| 551 | 535 | if (str == nullptr) |
| 552 | 536 | throw dereference_nil{ "Print string to FD" }; |
| 553 | 537 | |
| 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); | |
| 555 | 539 | } |
| 556 | 540 | |
| 557 | 541 | void |
| 558 | 542 | Sys_fstat(vm_registers_t &r, vm_t &vm) |
| 559 | 543 | { |
| 560 | 544 | auto &fp = r.stack.peek_frame()->base<F_Sys_fstat>(); |
| 545 | + | |
| 546 | + // [PAL] Consume platform API | |
| 561 | 547 | throw vm_system_exception{ "Function not implemented" }; |
| 562 | 548 | } |
| 563 | 549 | |
| @@ -572,6 +558,8 @@ void · 8 unmodified lines | ||
| 572 | 558 | Sys_fwstat(vm_registers_t &r, vm_t &vm) |
| 573 | 559 | { |
| 574 | 560 | auto &fp = r.stack.peek_frame()->base<F_Sys_fwstat>(); |
| 561 | + | |
| 562 | + // [PAL] Consume platform API | |
| 575 | 563 | throw vm_system_exception{ "Function not implemented" }; |
| 576 | 564 | } |
| 577 | 565 | |
| @@ -586,6 +574,8 @@ void · 8 unmodified lines | ||
| 586 | 574 | Sys_listen(vm_registers_t &r, vm_t &vm) |
| 587 | 575 | { |
| 588 | 576 | auto &fp = r.stack.peek_frame()->base<F_Sys_listen>(); |
| 577 | + | |
| 578 | + // [PAL] Consume platform API | |
| 589 | 579 | throw vm_system_exception{ "Function not implemented" }; |
| 590 | 580 | } |
| 591 | 581 | |
| @@ -611,41 +601,41 @@ Sys_open(vm_registers_t &r, vm_t &vm) · 19 unmodified lines | ||
| 611 | 601 | { |
| 612 | 602 | auto &fp = r.stack.peek_frame()->base<F_Sys_open>(); |
| 613 | 603 | |
| 604 | + dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret)); | |
| 605 | + *fp.ret = nullptr; | |
| 606 | + | |
| 614 | 607 | auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s); |
| 615 | 608 | const auto open_mode = convert_to_openmode(fp.mode); |
| 616 | 609 | |
| 617 | 610 | // [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); | |
| 619 | 612 | |
| 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) | |
| 624 | 615 | return; |
| 625 | − } | |
| 626 | 616 | |
| 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(); | |
| 630 | 619 | |
| 631 | 620 | if (disvm::debug::is_component_tracing_enabled<component_trace_t::builtin>()) |
| 632 | 621 | { |
| 622 | + auto fd_impl = fd_alloc->get_allocation<Sys_FD_Impl>(); | |
| 633 | 623 | disvm::debug::log_msg( |
| 634 | 624 | component_trace_t::builtin, |
| 635 | 625 | log_level_t::debug, |
| 636 | 626 | "sys: open: %d >>%s<< %#x", |
| 637 | − fd, | |
| 627 | + fd_impl->fd, | |
| 638 | 628 | path->str(), |
| 639 | 629 | fp.mode); |
| 640 | 630 | } |
| 641 | − | |
| 642 | − *fp.ret = fd_alloc->get_allocation(); | |
| 643 | 631 | } |
| 644 | 632 | |
| 645 | 633 | void |
| 646 | 634 | Sys_pctl(vm_registers_t &r, vm_t &vm) |
| 647 | 635 | { |
| 648 | 636 | auto &fp = r.stack.peek_frame()->base<F_Sys_pctl>(); |
| 637 | + | |
| 638 | + // [PAL] Consume platform API | |
| 649 | 639 | throw vm_system_exception{ "Function not implemented" }; |
| 650 | 640 | } |
| 651 | 641 | |
| @@ -653,6 +643,8 @@ void · 1 unmodified line | ||
| 653 | 643 | Sys_pipe(vm_registers_t &r, vm_t &vm) |
| 654 | 644 | { |
| 655 | 645 | auto &fp = r.stack.peek_frame()->base<F_Sys_pipe>(); |
| 646 | + | |
| 647 | + // [PAL] Consume platform API | |
| 656 | 648 | throw vm_system_exception{ "Function not implemented" }; |
| 657 | 649 | } |
| 658 | 650 | |
| @@ -672,7 +664,12 @@ Sys_print(vm_registers_t &r, vm_t &vm) · 13 unmodified lines | ||
| 672 | 664 | if (str == nullptr) |
| 673 | 665 | throw dereference_nil{ "Print string" }; |
| 674 | 666 | |
| 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); | |
| 676 | 673 | } |
| 677 | 674 | |
| 678 | 675 | void |
| @@ -708,8 +705,8 @@ Sys_read(vm_registers_t &r, vm_t &vm) · 29 unmodified lines | ||
| 708 | 705 | |
| 709 | 706 | assert(fd_alloc->alloc_type == T_FD); |
| 710 | 707 | |
| 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)); | |
| 713 | 710 | } |
| 714 | 711 | |
| 715 | 712 | void |
| @@ -742,10 +739,10 @@ Sys_seek(vm_registers_t &r, vm_t &vm) · 26 unmodified lines | ||
| 742 | 739 | throw dereference_nil{ "Seek in file descriptor" }; |
| 743 | 740 | |
| 744 | 741 | 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>(); | |
| 746 | 743 | |
| 747 | 744 | 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); | |
| 749 | 746 | } |
| 750 | 747 | |
| 751 | 748 | void |
| @@ -785,6 +782,7 @@ Sys_sprint(vm_registers_t &r, vm_t &vm) · 33 unmodified lines | ||
| 785 | 782 | new_string = new vm_string_t{ static_cast<std::size_t>(written), reinterpret_cast<uint8_t *>(dynamic_buffer.data()) }; |
| 786 | 783 | } |
| 787 | 784 | |
| 785 | + disvm::runtime::dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret)); | |
| 788 | 786 | *fp.ret = new_string->get_allocation(); |
| 789 | 787 | } |
| 790 | 788 | |
| @@ -792,6 +790,8 @@ void · 1 unmodified line | ||
| 792 | 790 | Sys_stat(vm_registers_t &r, vm_t &vm) |
| 793 | 791 | { |
| 794 | 792 | auto &fp = r.stack.peek_frame()->base<F_Sys_stat>(); |
| 793 | + | |
| 794 | + // [PAL] Consume platform API | |
| 795 | 795 | throw vm_system_exception{ "Function not implemented" }; |
| 796 | 796 | } |
| 797 | 797 | |
| @@ -799,7 +799,36 @@ void · 1 unmodified line | ||
| 799 | 799 | Sys_stream(vm_registers_t &r, vm_t &vm) |
| 800 | 800 | { |
| 801 | 801 | 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; | |
| 803 | 832 | } |
| 804 | 833 | |
| 805 | 834 | namespace |
| @@ -993,8 +1022,8 @@ Sys_write(vm_registers_t &r, vm_t &vm) · 187 unmodified lines | ||
| 993 | 1022 | |
| 994 | 1023 | assert(fd_alloc->alloc_type == T_FD); |
| 995 | 1024 | |
| 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)); | |
| 998 | 1027 | |
| 999 | 1028 | *fp.ret = n; |
| 1000 | 1029 | } |
| @@ -1003,5 +1032,7 @@ void · 2 unmodified lines | ||
| 1003 | 1032 | Sys_wstat(vm_registers_t &r, vm_t &vm) |
| 1004 | 1033 | { |
| 1005 | 1034 | auto &fp = r.stack.peek_frame()->base<F_Sys_wstat>(); |
| 1035 | + | |
| 1036 | + // [PAL] Consume platform API | |
| 1006 | 1037 | throw vm_system_exception{ "Function not implemented" }; |
| 1007 | 1038 | } |
| @@ -6,7 +6,8 @@ · 5 unmodified lines | ||
| 6 | 6 | |
| 7 | 7 | #include <array> |
| 8 | 8 | #include <mutex> |
| 9 | −#include <unordered_map> | |
| 9 | +#include <vector> | |
| 10 | +#include <queue> | |
| 10 | 11 | #include <exceptions.h> |
| 11 | 12 | #include <vm_memory.h> |
| 12 | 13 | #include "sys_utils.h" |
| @@ -14,72 +15,90 @@ · 1 unmodified line | ||
| 14 | 15 | using disvm::runtime::vm_alloc_t; |
| 15 | 16 | using disvm::runtime::word_t; |
| 16 | 17 | |
| 18 | +using disvm::runtime::sys::vm_fd_t; | |
| 19 | + | |
| 17 | 20 | namespace |
| 18 | 21 | { |
| 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); | |
| 20 | 30 | |
| 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 | |
| 22 | 40 | { |
| 23 | − nullptr, // stdin | |
| 24 | − nullptr, // stdout | |
| 25 | − nullptr, // stderr | |
| 26 | − }; | |
| 41 | + new_fd = fd_available.top(); | |
| 42 | + fd_available.pop(); | |
| 27 | 43 | |
| 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 | + } | |
| 29 | 47 | |
| 30 | − std::mutex fd_map_lock; | |
| 31 | − auto fd_map = fd_map_t{}; | |
| 48 | + return new_fd; | |
| 32 | 49 | } |
| 33 | 50 | |
| 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) | |
| 35 | 52 | { |
| 36 | − assert(fd_alloc != nullptr); | |
| 53 | + assert(fd != sys::vm_invalid_fd); | |
| 54 | + assert(vm_fd != nullptr); | |
| 37 | 55 | |
| 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; | |
| 55 | 67 | } |
| 56 | 68 | |
| 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) | |
| 58 | 70 | { |
| 59 | − assert(fd != sys::vm_invalid_fd && "Invalid FD value"); | |
| 71 | + assert(fd != sys::vm_invalid_fd); | |
| 60 | 72 | |
| 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; | |
| 63 | 76 | |
| 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(); | |
| 69 | 80 | |
| 70 | − return iter->second; | |
| 71 | − } | |
| 81 | + return vm_fd; | |
| 72 | 82 | } |
| 73 | 83 | |
| 74 | 84 | void disvm::runtime::sys::drop_fd_record(word_t fd) |
| 75 | 85 | { |
| 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; | |
| 78 | 91 | |
| 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) | |
| 79 | 99 | { |
| 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); | |
| 84 | 103 | } |
| 85 | −} | |
| 104 | +} | |
| @@ -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 | +} | |
| @@ -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_ | |
| @@ -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 | −} | |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | ||
| 12 | 12 | #include <memory> |
| 13 | 13 | #include <disvm.h> |
| 14 | 14 | #include <utils.h> |
| 15 | +#include "fd_types.h" | |
| 15 | 16 | |
| 16 | 17 | namespace disvm |
| 17 | 18 | { |
| @@ -38,25 +39,22 @@ namespace disvm · 20 unmodified lines | ||
| 38 | 39 | pointer_t base, |
| 39 | 40 | std::vector<char> &buffer); |
| 40 | 41 | |
| 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 | − | |
| 52 | 42 | const word_t vm_invalid_fd = -1; |
| 53 | 43 | |
| 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); | |
| 56 | 50 | |
| 57 | 51 | // 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); | |
| 59 | 55 | |
| 56 | + // The associated vm_fd_t instance reference count is decremented | |
| 57 | + // and will be deleted once all references are released. | |
| 60 | 58 | void drop_fd_record(word_t fd); |
| 61 | 59 | } |
| 62 | 60 | } |