xref: /llvm-project/libc/utils/gpu/server/rpc_server.cpp (revision e85a9f5540f5399b20a32c8d87474e6fc906ad33)
1e0b487bfSJoseph Huber //===-- Shared memory RPC server instantiation ------------------*- C++ -*-===//
2e0b487bfSJoseph Huber //
3e0b487bfSJoseph Huber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e0b487bfSJoseph Huber // See https://llvm.org/LICENSE.txt for license information.
5e0b487bfSJoseph Huber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e0b487bfSJoseph Huber //
7e0b487bfSJoseph Huber //===----------------------------------------------------------------------===//
8e0b487bfSJoseph Huber 
977118536SMarc Auberer // Workaround for missing __has_builtin in < GCC 10.
1077118536SMarc Auberer #ifndef __has_builtin
1177118536SMarc Auberer #define __has_builtin(x) 0
1277118536SMarc Auberer #endif
1377118536SMarc Auberer 
14f126bc98SJoseph Huber // Make sure these are included first so they don't conflict with the system.
15f126bc98SJoseph Huber #include <limits.h>
16f126bc98SJoseph Huber 
1789614cebSJoseph Huber #include "shared/rpc.h"
18d7c20a6fSJoseph Huber #include "shared/rpc_opcodes.h"
1989614cebSJoseph Huber 
207327014bSJoseph Huber #include "src/__support/arg_list.h"
217327014bSJoseph Huber #include "src/stdio/printf_core/converter.h"
227327014bSJoseph Huber #include "src/stdio/printf_core/parser.h"
237327014bSJoseph Huber #include "src/stdio/printf_core/writer.h"
247327014bSJoseph Huber 
257327014bSJoseph Huber #include <algorithm>
26e0b487bfSJoseph Huber #include <atomic>
27e0b487bfSJoseph Huber #include <cstdio>
28e0b487bfSJoseph Huber #include <cstring>
29e0b487bfSJoseph Huber #include <memory>
30e0b487bfSJoseph Huber #include <mutex>
31e0b487bfSJoseph Huber #include <unordered_map>
32e0b487bfSJoseph Huber #include <variant>
33e0b487bfSJoseph Huber #include <vector>
34e0b487bfSJoseph Huber 
35b6bc9d72SGuillaume Chatelet using namespace LIBC_NAMESPACE;
367327014bSJoseph Huber using namespace LIBC_NAMESPACE::printf_core;
37e0b487bfSJoseph Huber 
38bbe79a80SIvan Butygin namespace {
39bbe79a80SIvan Butygin struct TempStorage {
40bbe79a80SIvan Butygin   char *alloc(size_t size) {
41bbe79a80SIvan Butygin     storage.emplace_back(std::make_unique<char[]>(size));
42bbe79a80SIvan Butygin     return storage.back().get();
43bbe79a80SIvan Butygin   }
44bbe79a80SIvan Butygin 
45bbe79a80SIvan Butygin   std::vector<std::unique_ptr<char[]>> storage;
46bbe79a80SIvan Butygin };
47bbe79a80SIvan Butygin } // namespace
48bbe79a80SIvan Butygin 
4989614cebSJoseph Huber enum Stream {
5089614cebSJoseph Huber   File = 0,
5189614cebSJoseph Huber   Stdin = 1,
5289614cebSJoseph Huber   Stdout = 2,
5389614cebSJoseph Huber   Stderr = 3,
5489614cebSJoseph Huber };
5589614cebSJoseph Huber 
5689614cebSJoseph Huber // Get the associated stream out of an encoded number.
5789614cebSJoseph Huber LIBC_INLINE ::FILE *to_stream(uintptr_t f) {
5889614cebSJoseph Huber   ::FILE *stream = reinterpret_cast<FILE *>(f & ~0x3ull);
5989614cebSJoseph Huber   Stream type = static_cast<Stream>(f & 0x3ull);
6089614cebSJoseph Huber   if (type == Stdin)
6189614cebSJoseph Huber     return stdin;
6289614cebSJoseph Huber   if (type == Stdout)
6389614cebSJoseph Huber     return stdout;
6489614cebSJoseph Huber   if (type == Stderr)
6589614cebSJoseph Huber     return stderr;
6689614cebSJoseph Huber   return stream;
6789614cebSJoseph Huber }
6889614cebSJoseph Huber 
69b4d49fb5SJoseph Huber template <bool packed, uint32_t num_lanes>
70bbe79a80SIvan Butygin static void handle_printf(rpc::Server::Port &port, TempStorage &temp_storage) {
71b4d49fb5SJoseph Huber   FILE *files[num_lanes] = {nullptr};
727327014bSJoseph Huber   // Get the appropriate output stream to use.
73a6ef0debSJoseph Huber   if (port.get_opcode() == LIBC_PRINTF_TO_STREAM ||
74a6ef0debSJoseph Huber       port.get_opcode() == LIBC_PRINTF_TO_STREAM_PACKED)
757327014bSJoseph Huber     port.recv([&](rpc::Buffer *buffer, uint32_t id) {
767327014bSJoseph Huber       files[id] = reinterpret_cast<FILE *>(buffer->data[0]);
777327014bSJoseph Huber     });
78a6ef0debSJoseph Huber   else if (port.get_opcode() == LIBC_PRINTF_TO_STDOUT ||
79a6ef0debSJoseph Huber            port.get_opcode() == LIBC_PRINTF_TO_STDOUT_PACKED)
80b4d49fb5SJoseph Huber     std::fill(files, files + num_lanes, stdout);
817327014bSJoseph Huber   else
82b4d49fb5SJoseph Huber     std::fill(files, files + num_lanes, stderr);
837327014bSJoseph Huber 
84b4d49fb5SJoseph Huber   uint64_t format_sizes[num_lanes] = {0};
85b4d49fb5SJoseph Huber   void *format[num_lanes] = {nullptr};
867327014bSJoseph Huber 
87b4d49fb5SJoseph Huber   uint64_t args_sizes[num_lanes] = {0};
88b4d49fb5SJoseph Huber   void *args[num_lanes] = {nullptr};
897327014bSJoseph Huber 
907327014bSJoseph Huber   // Recieve the format string and arguments from the client.
917327014bSJoseph Huber   port.recv_n(format, format_sizes,
92bbe79a80SIvan Butygin               [&](uint64_t size) { return temp_storage.alloc(size); });
9340effc7aSJoseph Huber 
9440effc7aSJoseph Huber   // Parse the format string to get the expected size of the buffer.
95b4d49fb5SJoseph Huber   for (uint32_t lane = 0; lane < num_lanes; ++lane) {
9640effc7aSJoseph Huber     if (!format[lane])
9740effc7aSJoseph Huber       continue;
9840effc7aSJoseph Huber 
9940effc7aSJoseph Huber     WriteBuffer wb(nullptr, 0);
10040effc7aSJoseph Huber     Writer writer(&wb);
10140effc7aSJoseph Huber 
10240effc7aSJoseph Huber     internal::DummyArgList<packed> printf_args;
10340effc7aSJoseph Huber     Parser<internal::DummyArgList<packed> &> parser(
10440effc7aSJoseph Huber         reinterpret_cast<const char *>(format[lane]), printf_args);
10540effc7aSJoseph Huber 
10640effc7aSJoseph Huber     for (FormatSection cur_section = parser.get_next_section();
10740effc7aSJoseph Huber          !cur_section.raw_string.empty();
10840effc7aSJoseph Huber          cur_section = parser.get_next_section())
10940effc7aSJoseph Huber       ;
11040effc7aSJoseph Huber     args_sizes[lane] = printf_args.read_count();
11140effc7aSJoseph Huber   }
11240effc7aSJoseph Huber   port.send([&](rpc::Buffer *buffer, uint32_t id) {
11340effc7aSJoseph Huber     buffer->data[0] = args_sizes[id];
11440effc7aSJoseph Huber   });
115bbe79a80SIvan Butygin   port.recv_n(args, args_sizes,
116bbe79a80SIvan Butygin               [&](uint64_t size) { return temp_storage.alloc(size); });
1177327014bSJoseph Huber 
1187327014bSJoseph Huber   // Identify any arguments that are actually pointers to strings on the client.
1197327014bSJoseph Huber   // Additionally we want to determine how much buffer space we need to print.
120b4d49fb5SJoseph Huber   std::vector<void *> strs_to_copy[num_lanes];
121b4d49fb5SJoseph Huber   int buffer_size[num_lanes] = {0};
122b4d49fb5SJoseph Huber   for (uint32_t lane = 0; lane < num_lanes; ++lane) {
1237327014bSJoseph Huber     if (!format[lane])
1247327014bSJoseph Huber       continue;
1257327014bSJoseph Huber 
1267327014bSJoseph Huber     WriteBuffer wb(nullptr, 0);
1277327014bSJoseph Huber     Writer writer(&wb);
1287327014bSJoseph Huber 
12940effc7aSJoseph Huber     internal::StructArgList<packed> printf_args(args[lane], args_sizes[lane]);
13040effc7aSJoseph Huber     Parser<internal::StructArgList<packed>> parser(
1317327014bSJoseph Huber         reinterpret_cast<const char *>(format[lane]), printf_args);
1327327014bSJoseph Huber 
1337327014bSJoseph Huber     for (FormatSection cur_section = parser.get_next_section();
1347327014bSJoseph Huber          !cur_section.raw_string.empty();
1357327014bSJoseph Huber          cur_section = parser.get_next_section()) {
1367327014bSJoseph Huber       if (cur_section.has_conv && cur_section.conv_name == 's' &&
1377327014bSJoseph Huber           cur_section.conv_val_ptr) {
1387327014bSJoseph Huber         strs_to_copy[lane].emplace_back(cur_section.conv_val_ptr);
139c8e69fa4SJoseph Huber         // Get the minimum size of the string in the case of padding.
140c8e69fa4SJoseph Huber         char c = '\0';
141c8e69fa4SJoseph Huber         cur_section.conv_val_ptr = &c;
142c8e69fa4SJoseph Huber         convert(&writer, cur_section);
1437327014bSJoseph Huber       } else if (cur_section.has_conv) {
1447327014bSJoseph Huber         // Ignore conversion errors for the first pass.
1457327014bSJoseph Huber         convert(&writer, cur_section);
1467327014bSJoseph Huber       } else {
1477327014bSJoseph Huber         writer.write(cur_section.raw_string);
1487327014bSJoseph Huber       }
1497327014bSJoseph Huber     }
1507327014bSJoseph Huber     buffer_size[lane] = writer.get_chars_written();
1517327014bSJoseph Huber   }
1527327014bSJoseph Huber 
1537327014bSJoseph Huber   // Recieve any strings from the client and push them into a buffer.
154b4d49fb5SJoseph Huber   std::vector<void *> copied_strs[num_lanes];
1557327014bSJoseph Huber   while (std::any_of(std::begin(strs_to_copy), std::end(strs_to_copy),
1567327014bSJoseph Huber                      [](const auto &v) { return !v.empty() && v.back(); })) {
1577327014bSJoseph Huber     port.send([&](rpc::Buffer *buffer, uint32_t id) {
1587327014bSJoseph Huber       void *ptr = !strs_to_copy[id].empty() ? strs_to_copy[id].back() : nullptr;
1597327014bSJoseph Huber       buffer->data[1] = reinterpret_cast<uintptr_t>(ptr);
1607327014bSJoseph Huber       if (!strs_to_copy[id].empty())
1617327014bSJoseph Huber         strs_to_copy[id].pop_back();
1627327014bSJoseph Huber     });
163b4d49fb5SJoseph Huber     uint64_t str_sizes[num_lanes] = {0};
164b4d49fb5SJoseph Huber     void *strs[num_lanes] = {nullptr};
165bbe79a80SIvan Butygin     port.recv_n(strs, str_sizes,
166bbe79a80SIvan Butygin                 [&](uint64_t size) { return temp_storage.alloc(size); });
167b4d49fb5SJoseph Huber     for (uint32_t lane = 0; lane < num_lanes; ++lane) {
1687327014bSJoseph Huber       if (!strs[lane])
1697327014bSJoseph Huber         continue;
1707327014bSJoseph Huber 
1717327014bSJoseph Huber       copied_strs[lane].emplace_back(strs[lane]);
1727327014bSJoseph Huber       buffer_size[lane] += str_sizes[lane];
1737327014bSJoseph Huber     }
1747327014bSJoseph Huber   }
1757327014bSJoseph Huber 
1767327014bSJoseph Huber   // Perform the final formatting and printing using the LLVM C library printf.
177b4d49fb5SJoseph Huber   int results[num_lanes] = {0};
178b4d49fb5SJoseph Huber   for (uint32_t lane = 0; lane < num_lanes; ++lane) {
1797327014bSJoseph Huber     if (!format[lane])
1807327014bSJoseph Huber       continue;
1817327014bSJoseph Huber 
182bbe79a80SIvan Butygin     char *buffer = temp_storage.alloc(buffer_size[lane]);
183bbe79a80SIvan Butygin     WriteBuffer wb(buffer, buffer_size[lane]);
1847327014bSJoseph Huber     Writer writer(&wb);
1857327014bSJoseph Huber 
18640effc7aSJoseph Huber     internal::StructArgList<packed> printf_args(args[lane], args_sizes[lane]);
18740effc7aSJoseph Huber     Parser<internal::StructArgList<packed>> parser(
1887327014bSJoseph Huber         reinterpret_cast<const char *>(format[lane]), printf_args);
1897327014bSJoseph Huber 
1907327014bSJoseph Huber     // Parse and print the format string using the arguments we copied from
1917327014bSJoseph Huber     // the client.
1927327014bSJoseph Huber     int ret = 0;
1937327014bSJoseph Huber     for (FormatSection cur_section = parser.get_next_section();
1947327014bSJoseph Huber          !cur_section.raw_string.empty();
1957327014bSJoseph Huber          cur_section = parser.get_next_section()) {
1967327014bSJoseph Huber       // If this argument was a string we use the memory buffer we copied from
1977327014bSJoseph Huber       // the client by replacing the raw pointer with the copied one.
1987327014bSJoseph Huber       if (cur_section.has_conv && cur_section.conv_name == 's') {
1997327014bSJoseph Huber         if (!copied_strs[lane].empty()) {
2007327014bSJoseph Huber           cur_section.conv_val_ptr = copied_strs[lane].back();
2017327014bSJoseph Huber           copied_strs[lane].pop_back();
2027327014bSJoseph Huber         } else {
2037327014bSJoseph Huber           cur_section.conv_val_ptr = nullptr;
2047327014bSJoseph Huber         }
2057327014bSJoseph Huber       }
2067327014bSJoseph Huber       if (cur_section.has_conv) {
2077327014bSJoseph Huber         ret = convert(&writer, cur_section);
2087327014bSJoseph Huber         if (ret == -1)
2097327014bSJoseph Huber           break;
2107327014bSJoseph Huber       } else {
2117327014bSJoseph Huber         writer.write(cur_section.raw_string);
2127327014bSJoseph Huber       }
2137327014bSJoseph Huber     }
2147327014bSJoseph Huber 
215bbe79a80SIvan Butygin     results[lane] = fwrite(buffer, 1, writer.get_chars_written(), files[lane]);
2167327014bSJoseph Huber     if (results[lane] != writer.get_chars_written() || ret == -1)
2177327014bSJoseph Huber       results[lane] = -1;
2187327014bSJoseph Huber   }
2197327014bSJoseph Huber 
2207327014bSJoseph Huber   // Send the final return value and signal completion by setting the string
2217327014bSJoseph Huber   // argument to null.
2227327014bSJoseph Huber   port.send([&](rpc::Buffer *buffer, uint32_t id) {
2237327014bSJoseph Huber     buffer->data[0] = static_cast<uint64_t>(results[id]);
2247327014bSJoseph Huber     buffer->data[1] = reinterpret_cast<uintptr_t>(nullptr);
2257327014bSJoseph Huber   });
2267327014bSJoseph Huber }
2277327014bSJoseph Huber 
228b4d49fb5SJoseph Huber template <uint32_t num_lanes>
229b4d49fb5SJoseph Huber rpc::Status handle_port_impl(rpc::Server::Port &port) {
230ef390b36SIvan Butygin   TempStorage temp_storage;
231ef390b36SIvan Butygin 
232b4d49fb5SJoseph Huber   switch (port.get_opcode()) {
233a6ef0debSJoseph Huber   case LIBC_WRITE_TO_STREAM:
234a6ef0debSJoseph Huber   case LIBC_WRITE_TO_STDERR:
235a6ef0debSJoseph Huber   case LIBC_WRITE_TO_STDOUT:
236a6ef0debSJoseph Huber   case LIBC_WRITE_TO_STDOUT_NEWLINE: {
237b4d49fb5SJoseph Huber     uint64_t sizes[num_lanes] = {0};
238b4d49fb5SJoseph Huber     void *strs[num_lanes] = {nullptr};
239b4d49fb5SJoseph Huber     FILE *files[num_lanes] = {nullptr};
240a6ef0debSJoseph Huber     if (port.get_opcode() == LIBC_WRITE_TO_STREAM) {
241b4d49fb5SJoseph Huber       port.recv([&](rpc::Buffer *buffer, uint32_t id) {
242e0b487bfSJoseph Huber         files[id] = reinterpret_cast<FILE *>(buffer->data[0]);
243e0b487bfSJoseph Huber       });
244a6ef0debSJoseph Huber     } else if (port.get_opcode() == LIBC_WRITE_TO_STDERR) {
245b4d49fb5SJoseph Huber       std::fill(files, files + num_lanes, stderr);
246791b2799SJoseph Huber     } else {
247b4d49fb5SJoseph Huber       std::fill(files, files + num_lanes, stdout);
248791b2799SJoseph Huber     }
249791b2799SJoseph Huber 
250b4d49fb5SJoseph Huber     port.recv_n(strs, sizes,
251ef390b36SIvan Butygin                 [&](uint64_t size) { return temp_storage.alloc(size); });
252b4d49fb5SJoseph Huber     port.send([&](rpc::Buffer *buffer, uint32_t id) {
253bf02c84cSJoseph Huber       flockfile(files[id]);
254bf02c84cSJoseph Huber       buffer->data[0] = fwrite_unlocked(strs[id], 1, sizes[id], files[id]);
255a6ef0debSJoseph Huber       if (port.get_opcode() == LIBC_WRITE_TO_STDOUT_NEWLINE &&
256791b2799SJoseph Huber           buffer->data[0] == sizes[id])
257bf02c84cSJoseph Huber         buffer->data[0] += fwrite_unlocked("\n", 1, 1, files[id]);
258bf02c84cSJoseph Huber       funlockfile(files[id]);
259e0b487bfSJoseph Huber     });
260e0b487bfSJoseph Huber     break;
261e0b487bfSJoseph Huber   }
262a6ef0debSJoseph Huber   case LIBC_READ_FROM_STREAM: {
263b4d49fb5SJoseph Huber     uint64_t sizes[num_lanes] = {0};
264b4d49fb5SJoseph Huber     void *data[num_lanes] = {nullptr};
265b4d49fb5SJoseph Huber     port.recv([&](rpc::Buffer *buffer, uint32_t id) {
266ef390b36SIvan Butygin       data[id] = temp_storage.alloc(buffer->data[0]);
26729762e37SJoseph Huber       sizes[id] =
26889614cebSJoseph Huber           fread(data[id], 1, buffer->data[0], to_stream(buffer->data[1]));
269334bbc0dSJoseph Huber     });
270b4d49fb5SJoseph Huber     port.send_n(data, sizes);
271b4d49fb5SJoseph Huber     port.send([&](rpc::Buffer *buffer, uint32_t id) {
272f548d19fSJoseph Huber       std::memcpy(buffer->data, &sizes[id], sizeof(uint64_t));
273334bbc0dSJoseph Huber     });
274334bbc0dSJoseph Huber     break;
275334bbc0dSJoseph Huber   }
276a6ef0debSJoseph Huber   case LIBC_READ_FGETS: {
277b4d49fb5SJoseph Huber     uint64_t sizes[num_lanes] = {0};
278b4d49fb5SJoseph Huber     void *data[num_lanes] = {nullptr};
279b4d49fb5SJoseph Huber     port.recv([&](rpc::Buffer *buffer, uint32_t id) {
280ef390b36SIvan Butygin       data[id] = temp_storage.alloc(buffer->data[0]);
28189614cebSJoseph Huber       const char *str = fgets(reinterpret_cast<char *>(data[id]),
28289614cebSJoseph Huber                               buffer->data[0], to_stream(buffer->data[1]));
283a3921576SJoseph Huber       sizes[id] = !str ? 0 : std::strlen(str) + 1;
284a3921576SJoseph Huber     });
285b4d49fb5SJoseph Huber     port.send_n(data, sizes);
286a3921576SJoseph Huber     break;
287a3921576SJoseph Huber   }
288a6ef0debSJoseph Huber   case LIBC_OPEN_FILE: {
289b4d49fb5SJoseph Huber     uint64_t sizes[num_lanes] = {0};
290b4d49fb5SJoseph Huber     void *paths[num_lanes] = {nullptr};
291b4d49fb5SJoseph Huber     port.recv_n(paths, sizes,
292ef390b36SIvan Butygin                 [&](uint64_t size) { return temp_storage.alloc(size); });
293b4d49fb5SJoseph Huber     port.recv_and_send([&](rpc::Buffer *buffer, uint32_t id) {
294c850ea14SJoseph Huber       FILE *file = fopen(reinterpret_cast<char *>(paths[id]),
295c850ea14SJoseph Huber                          reinterpret_cast<char *>(buffer->data));
296c850ea14SJoseph Huber       buffer->data[0] = reinterpret_cast<uintptr_t>(file);
297c850ea14SJoseph Huber     });
298c850ea14SJoseph Huber     break;
299c850ea14SJoseph Huber   }
300a6ef0debSJoseph Huber   case LIBC_CLOSE_FILE: {
301b4d49fb5SJoseph Huber     port.recv_and_send([&](rpc::Buffer *buffer, uint32_t id) {
302c850ea14SJoseph Huber       FILE *file = reinterpret_cast<FILE *>(buffer->data[0]);
303c850ea14SJoseph Huber       buffer->data[0] = fclose(file);
304c850ea14SJoseph Huber     });
305c850ea14SJoseph Huber     break;
306c850ea14SJoseph Huber   }
307a6ef0debSJoseph Huber   case LIBC_EXIT: {
308667c1035SJoseph Huber     // Send a response to the client to signal that we are ready to exit.
309b4d49fb5SJoseph Huber     port.recv_and_send([](rpc::Buffer *, uint32_t) {});
310b4d49fb5SJoseph Huber     port.recv([](rpc::Buffer *buffer, uint32_t) {
311e0b487bfSJoseph Huber       int status = 0;
312e0b487bfSJoseph Huber       std::memcpy(&status, buffer->data, sizeof(int));
313e0b487bfSJoseph Huber       exit(status);
314e0b487bfSJoseph Huber     });
315e0b487bfSJoseph Huber     break;
316e0b487bfSJoseph Huber   }
317a6ef0debSJoseph Huber   case LIBC_ABORT: {
31807102a11SJoseph Huber     // Send a response to the client to signal that we are ready to abort.
319b4d49fb5SJoseph Huber     port.recv_and_send([](rpc::Buffer *, uint32_t) {});
320b4d49fb5SJoseph Huber     port.recv([](rpc::Buffer *, uint32_t) {});
32107102a11SJoseph Huber     abort();
32207102a11SJoseph Huber     break;
32307102a11SJoseph Huber   }
324a6ef0debSJoseph Huber   case LIBC_HOST_CALL: {
325b4d49fb5SJoseph Huber     uint64_t sizes[num_lanes] = {0};
326b4d49fb5SJoseph Huber     unsigned long long results[num_lanes] = {0};
327b4d49fb5SJoseph Huber     void *args[num_lanes] = {nullptr};
328b4d49fb5SJoseph Huber     port.recv_n(args, sizes,
329ef390b36SIvan Butygin                 [&](uint64_t size) { return temp_storage.alloc(size); });
330b4d49fb5SJoseph Huber     port.recv([&](rpc::Buffer *buffer, uint32_t id) {
33126ca8ef8SIvan Butygin       using func_ptr_t = unsigned long long (*)(void *);
33226ca8ef8SIvan Butygin       auto func = reinterpret_cast<func_ptr_t>(buffer->data[0]);
33326ca8ef8SIvan Butygin       results[id] = func(args[id]);
334e537c839SJoseph Huber     });
335b4d49fb5SJoseph Huber     port.send([&](rpc::Buffer *buffer, uint32_t id) {
33626ca8ef8SIvan Butygin       buffer->data[0] = static_cast<uint64_t>(results[id]);
33726ca8ef8SIvan Butygin     });
338e537c839SJoseph Huber     break;
339e537c839SJoseph Huber   }
340a6ef0debSJoseph Huber   case LIBC_FEOF: {
341b4d49fb5SJoseph Huber     port.recv_and_send([](rpc::Buffer *buffer, uint32_t) {
34289614cebSJoseph Huber       buffer->data[0] = feof(to_stream(buffer->data[0]));
343a1be5d69SJoseph Huber     });
344a1be5d69SJoseph Huber     break;
345a1be5d69SJoseph Huber   }
346a6ef0debSJoseph Huber   case LIBC_FERROR: {
347b4d49fb5SJoseph Huber     port.recv_and_send([](rpc::Buffer *buffer, uint32_t) {
34889614cebSJoseph Huber       buffer->data[0] = ferror(to_stream(buffer->data[0]));
349a1be5d69SJoseph Huber     });
350a1be5d69SJoseph Huber     break;
351a1be5d69SJoseph Huber   }
352a6ef0debSJoseph Huber   case LIBC_CLEARERR: {
353b4d49fb5SJoseph Huber     port.recv_and_send([](rpc::Buffer *buffer, uint32_t) {
35489614cebSJoseph Huber       clearerr(to_stream(buffer->data[0]));
355a1be5d69SJoseph Huber     });
356a1be5d69SJoseph Huber     break;
357a1be5d69SJoseph Huber   }
358a6ef0debSJoseph Huber   case LIBC_FSEEK: {
359b4d49fb5SJoseph Huber     port.recv_and_send([](rpc::Buffer *buffer, uint32_t) {
36089614cebSJoseph Huber       buffer->data[0] =
36189614cebSJoseph Huber           fseek(to_stream(buffer->data[0]), static_cast<long>(buffer->data[1]),
3627ac8e26fSJoseph Huber                 static_cast<int>(buffer->data[2]));
3637ac8e26fSJoseph Huber     });
3647ac8e26fSJoseph Huber     break;
3657ac8e26fSJoseph Huber   }
366a6ef0debSJoseph Huber   case LIBC_FTELL: {
367b4d49fb5SJoseph Huber     port.recv_and_send([](rpc::Buffer *buffer, uint32_t) {
36889614cebSJoseph Huber       buffer->data[0] = ftell(to_stream(buffer->data[0]));
3697ac8e26fSJoseph Huber     });
3707ac8e26fSJoseph Huber     break;
3717ac8e26fSJoseph Huber   }
372a6ef0debSJoseph Huber   case LIBC_FFLUSH: {
373b4d49fb5SJoseph Huber     port.recv_and_send([](rpc::Buffer *buffer, uint32_t) {
37489614cebSJoseph Huber       buffer->data[0] = fflush(to_stream(buffer->data[0]));
3757ac8e26fSJoseph Huber     });
3767ac8e26fSJoseph Huber     break;
3777ac8e26fSJoseph Huber   }
378a6ef0debSJoseph Huber   case LIBC_UNGETC: {
379b4d49fb5SJoseph Huber     port.recv_and_send([](rpc::Buffer *buffer, uint32_t) {
38089614cebSJoseph Huber       buffer->data[0] =
38189614cebSJoseph Huber           ungetc(static_cast<int>(buffer->data[0]), to_stream(buffer->data[1]));
382ddc30ff8SJoseph Huber     });
383ddc30ff8SJoseph Huber     break;
384ddc30ff8SJoseph Huber   }
385a6ef0debSJoseph Huber   case LIBC_PRINTF_TO_STREAM_PACKED:
386a6ef0debSJoseph Huber   case LIBC_PRINTF_TO_STDOUT_PACKED:
387a6ef0debSJoseph Huber   case LIBC_PRINTF_TO_STDERR_PACKED: {
388b4d49fb5SJoseph Huber     handle_printf<true, num_lanes>(port, temp_storage);
38940effc7aSJoseph Huber     break;
39040effc7aSJoseph Huber   }
391a6ef0debSJoseph Huber   case LIBC_PRINTF_TO_STREAM:
392a6ef0debSJoseph Huber   case LIBC_PRINTF_TO_STDOUT:
393a6ef0debSJoseph Huber   case LIBC_PRINTF_TO_STDERR: {
394b4d49fb5SJoseph Huber     handle_printf<false, num_lanes>(port, temp_storage);
3957327014bSJoseph Huber     break;
3967327014bSJoseph Huber   }
397a6ef0debSJoseph Huber   case LIBC_REMOVE: {
398b4d49fb5SJoseph Huber     uint64_t sizes[num_lanes] = {0};
399b4d49fb5SJoseph Huber     void *args[num_lanes] = {nullptr};
400b4d49fb5SJoseph Huber     port.recv_n(args, sizes,
401ef390b36SIvan Butygin                 [&](uint64_t size) { return temp_storage.alloc(size); });
402b4d49fb5SJoseph Huber     port.send([&](rpc::Buffer *buffer, uint32_t id) {
403ec0e6ef0SJoseph Huber       buffer->data[0] = static_cast<uint64_t>(
404ec0e6ef0SJoseph Huber           remove(reinterpret_cast<const char *>(args[id])));
405ec0e6ef0SJoseph Huber     });
406ec0e6ef0SJoseph Huber     break;
407ec0e6ef0SJoseph Huber   }
408a6ef0debSJoseph Huber   case LIBC_RENAME: {
409b4d49fb5SJoseph Huber     uint64_t oldsizes[num_lanes] = {0};
410b4d49fb5SJoseph Huber     uint64_t newsizes[num_lanes] = {0};
411b4d49fb5SJoseph Huber     void *oldpath[num_lanes] = {nullptr};
412b4d49fb5SJoseph Huber     void *newpath[num_lanes] = {nullptr};
413b4d49fb5SJoseph Huber     port.recv_n(oldpath, oldsizes,
414ef390b36SIvan Butygin                 [&](uint64_t size) { return temp_storage.alloc(size); });
415b4d49fb5SJoseph Huber     port.recv_n(newpath, newsizes,
416ef390b36SIvan Butygin                 [&](uint64_t size) { return temp_storage.alloc(size); });
417b4d49fb5SJoseph Huber     port.send([&](rpc::Buffer *buffer, uint32_t id) {
418fe6a3d46SJoseph Huber       buffer->data[0] = static_cast<uint64_t>(
419fe6a3d46SJoseph Huber           rename(reinterpret_cast<const char *>(oldpath[id]),
420fe6a3d46SJoseph Huber                  reinterpret_cast<const char *>(newpath[id])));
421fe6a3d46SJoseph Huber     });
422fe6a3d46SJoseph Huber     break;
423fe6a3d46SJoseph Huber   }
424a6ef0debSJoseph Huber   case LIBC_SYSTEM: {
425b4d49fb5SJoseph Huber     uint64_t sizes[num_lanes] = {0};
426b4d49fb5SJoseph Huber     void *args[num_lanes] = {nullptr};
427b4d49fb5SJoseph Huber     port.recv_n(args, sizes,
428ef390b36SIvan Butygin                 [&](uint64_t size) { return temp_storage.alloc(size); });
429b4d49fb5SJoseph Huber     port.send([&](rpc::Buffer *buffer, uint32_t id) {
43016d11e26SJoseph Huber       buffer->data[0] = static_cast<uint64_t>(
43116d11e26SJoseph Huber           system(reinterpret_cast<const char *>(args[id])));
43216d11e26SJoseph Huber     });
43316d11e26SJoseph Huber     break;
43416d11e26SJoseph Huber   }
435a6ef0debSJoseph Huber   case LIBC_NOOP: {
436b4d49fb5SJoseph Huber     port.recv([](rpc::Buffer *, uint32_t) {});
437e0b487bfSJoseph Huber     break;
438e0b487bfSJoseph Huber   }
43929762e37SJoseph Huber   default:
440*e85a9f55SJinsong Ji     return rpc::RPC_UNHANDLED_OPCODE;
44129762e37SJoseph Huber   }
44229762e37SJoseph Huber 
443*e85a9f55SJinsong Ji   return rpc::RPC_SUCCESS;
444e0b487bfSJoseph Huber }
445e0b487bfSJoseph Huber 
4461d810eceSJoseph Huber namespace rpc {
4471d810eceSJoseph Huber // The implementation of this function currently lives in the utility directory
4481d810eceSJoseph Huber // at 'utils/gpu/server/rpc_server.cpp'.
4491d810eceSJoseph Huber rpc::Status handle_libc_opcodes(rpc::Server::Port &port, uint32_t num_lanes) {
450b4d49fb5SJoseph Huber   switch (num_lanes) {
451b4d49fb5SJoseph Huber   case 1:
4521d810eceSJoseph Huber     return handle_port_impl<1>(port);
453b4d49fb5SJoseph Huber   case 32:
4541d810eceSJoseph Huber     return handle_port_impl<32>(port);
455b4d49fb5SJoseph Huber   case 64:
4561d810eceSJoseph Huber     return handle_port_impl<64>(port);
457b4d49fb5SJoseph Huber   default:
458*e85a9f55SJinsong Ji     return rpc::RPC_ERROR;
459e0b487bfSJoseph Huber   }
460e0b487bfSJoseph Huber }
4611d810eceSJoseph Huber } // namespace rpc
462