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