| 1 | //===--- GPU helper functions for file I/O using RPC ----------------------===// |
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #include "hdr/stdio_macros.h" // For stdin/out/err |
| 10 | #include "hdr/types/FILE.h" |
| 11 | #include "src/__support/RPC/rpc_client.h" |
| 12 | #include "src/__support/common.h" |
| 13 | #include "src/__support/macros/attributes.h" |
| 14 | |
| 15 | namespace LIBC_NAMESPACE_DECL { |
| 16 | namespace file { |
| 17 | |
| 18 | enum Stream { |
| 19 | File = 0, |
| 20 | Stdin = 1, |
| 21 | Stdout = 2, |
| 22 | Stderr = 3, |
| 23 | }; |
| 24 | |
| 25 | // When copying between the client and server we need to indicate if this is one |
| 26 | // of the special streams. We do this by enocding the low order bits of the |
| 27 | // pointer to indicate if we need to use the host's standard stream. |
| 28 | LIBC_INLINE uintptr_t from_stream(::FILE *f) { |
| 29 | if (f == stdin) |
| 30 | return reinterpret_cast<uintptr_t>(f) | Stdin; |
| 31 | if (f == stdout) |
| 32 | return reinterpret_cast<uintptr_t>(f) | Stdout; |
| 33 | if (f == stderr) |
| 34 | return reinterpret_cast<uintptr_t>(f) | Stderr; |
| 35 | return reinterpret_cast<uintptr_t>(f); |
| 36 | } |
| 37 | |
| 38 | // Get the associated stream out of an encoded number. |
| 39 | LIBC_INLINE ::FILE *to_stream(uintptr_t f) { |
| 40 | ::FILE *stream = reinterpret_cast<FILE *>(f & ~0x3ull); |
| 41 | Stream type = static_cast<Stream>(f & 0x3ull); |
| 42 | if (type == Stdin) |
| 43 | return stdin; |
| 44 | if (type == Stdout) |
| 45 | return stdout; |
| 46 | if (type == Stderr) |
| 47 | return stderr; |
| 48 | return stream; |
| 49 | } |
| 50 | |
| 51 | template <uint32_t opcode> |
| 52 | LIBC_INLINE uint64_t write_impl(::FILE *file, const void *data, size_t size) { |
| 53 | uint64_t ret = 0; |
| 54 | rpc::Client::Port port = rpc::client.open<opcode>(); |
| 55 | |
| 56 | if constexpr (opcode == LIBC_WRITE_TO_STREAM) { |
| 57 | port.send([&](rpc::Buffer *buffer, uint32_t) { |
| 58 | buffer->data[0] = reinterpret_cast<uintptr_t>(file); |
| 59 | }); |
| 60 | } |
| 61 | |
| 62 | port.send_n(data, size); |
| 63 | port.recv([&](rpc::Buffer *buffer, uint32_t) { |
| 64 | ret = reinterpret_cast<uint64_t *>(buffer->data)[0]; |
| 65 | }); |
| 66 | port.close(); |
| 67 | return ret; |
| 68 | } |
| 69 | |
| 70 | LIBC_INLINE uint64_t write(::FILE *f, const void *data, size_t size) { |
| 71 | if (f == stdout) |
| 72 | return write_impl<LIBC_WRITE_TO_STDOUT>(f, data, size); |
| 73 | else if (f == stderr) |
| 74 | return write_impl<LIBC_WRITE_TO_STDERR>(f, data, size); |
| 75 | else |
| 76 | return write_impl<LIBC_WRITE_TO_STREAM>(f, data, size); |
| 77 | } |
| 78 | |
| 79 | LIBC_INLINE uint64_t read_from_stream(::FILE *file, void *buf, size_t size) { |
| 80 | uint64_t ret = 0; |
| 81 | uint64_t recv_size; |
| 82 | rpc::Client::Port port = rpc::client.open<LIBC_READ_FROM_STREAM>(); |
| 83 | port.send([=](rpc::Buffer *buffer, uint32_t) { |
| 84 | buffer->data[0] = size; |
| 85 | buffer->data[1] = from_stream(file); |
| 86 | }); |
| 87 | port.recv_n(&buf, &recv_size, [&](uint64_t) { return buf; }); |
| 88 | port.recv([&](rpc::Buffer *buffer, uint32_t) { ret = buffer->data[0]; }); |
| 89 | port.close(); |
| 90 | return ret; |
| 91 | } |
| 92 | |
| 93 | LIBC_INLINE uint64_t read(::FILE *f, void *data, size_t size) { |
| 94 | return read_from_stream(f, data, size); |
| 95 | } |
| 96 | |
| 97 | } // namespace file |
| 98 | } // namespace LIBC_NAMESPACE_DECL |
| 99 | |