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 "src/__support/RPC/rpc_client.h" |
10 | #include "src/string/string_utils.h" |
11 | |
12 | #include <stdio.h> |
13 | |
14 | namespace LIBC_NAMESPACE { |
15 | namespace file { |
16 | |
17 | enum Stream { |
18 | File = 0, |
19 | Stdin = 1, |
20 | Stdout = 2, |
21 | Stderr = 3, |
22 | }; |
23 | |
24 | // When copying between the client and server we need to indicate if this is one |
25 | // of the special streams. We do this by enocding the low order bits of the |
26 | // pointer to indicate if we need to use the host's standard stream. |
27 | LIBC_INLINE uintptr_t from_stream(::FILE *f) { |
28 | if (f == stdin) |
29 | return reinterpret_cast<uintptr_t>(f) | Stdin; |
30 | if (f == stdout) |
31 | return reinterpret_cast<uintptr_t>(f) | Stdout; |
32 | if (f == stderr) |
33 | return reinterpret_cast<uintptr_t>(f) | Stderr; |
34 | return reinterpret_cast<uintptr_t>(f); |
35 | } |
36 | |
37 | // Get the associated stream out of an encoded number. |
38 | LIBC_INLINE ::FILE *to_stream(uintptr_t f) { |
39 | ::FILE *stream = reinterpret_cast<FILE *>(f & ~0x3ull); |
40 | Stream type = static_cast<Stream>(f & 0x3ull); |
41 | if (type == Stdin) |
42 | return stdin; |
43 | if (type == Stdout) |
44 | return stdout; |
45 | if (type == Stderr) |
46 | return stderr; |
47 | return stream; |
48 | } |
49 | |
50 | template <uint16_t opcode> |
51 | LIBC_INLINE uint64_t write_impl(::FILE *file, const void *data, size_t size) { |
52 | uint64_t ret = 0; |
53 | rpc::Client::Port port = rpc::client.open<opcode>(); |
54 | |
55 | if constexpr (opcode == RPC_WRITE_TO_STREAM) { |
56 | port.send([&](rpc::Buffer *buffer) { |
57 | buffer->data[0] = reinterpret_cast<uintptr_t>(file); |
58 | }); |
59 | } |
60 | |
61 | port.send_n(src: data, size); |
62 | port.recv([&](rpc::Buffer *buffer) { |
63 | ret = reinterpret_cast<uint64_t *>(buffer->data)[0]; |
64 | }); |
65 | port.close(); |
66 | return ret; |
67 | } |
68 | |
69 | LIBC_INLINE uint64_t write(::FILE *f, const void *data, size_t size) { |
70 | if (f == stdout) |
71 | return write_impl<RPC_WRITE_TO_STDOUT>(file: f, data, size); |
72 | else if (f == stderr) |
73 | return write_impl<RPC_WRITE_TO_STDERR>(file: f, data, size); |
74 | else |
75 | return write_impl<RPC_WRITE_TO_STREAM>(file: f, data, size); |
76 | } |
77 | |
78 | LIBC_INLINE uint64_t read_from_stream(::FILE *file, void *buf, size_t size) { |
79 | uint64_t ret = 0; |
80 | uint64_t recv_size; |
81 | rpc::Client::Port port = rpc::client.open<RPC_READ_FROM_STREAM>(); |
82 | port.send(fill: [=](rpc::Buffer *buffer) { |
83 | buffer->data[0] = size; |
84 | buffer->data[1] = from_stream(f: file); |
85 | }); |
86 | port.recv_n(dst: &buf, size: &recv_size, alloc: [&](uint64_t) { return buf; }); |
87 | port.recv(use: [&](rpc::Buffer *buffer) { ret = buffer->data[0]; }); |
88 | port.close(); |
89 | return ret; |
90 | } |
91 | |
92 | LIBC_INLINE uint64_t read(::FILE *f, void *data, size_t size) { |
93 | return read_from_stream(file: f, buf: data, size); |
94 | } |
95 | |
96 | } // namespace file |
97 | } // namespace LIBC_NAMESPACE |
98 | |