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 | |