[libc-commits] [libc] [llvm] [libc] Make rpc_server.h independent from libc internals (PR #190423)

Joseph Huber via libc-commits libc-commits at lists.llvm.org
Mon Apr 6 09:35:49 PDT 2026


================
@@ -9,15 +9,776 @@
 #ifndef LLVM_LIBC_SHARED_RPC_SERVER_H
 #define LLVM_LIBC_SHARED_RPC_SERVER_H
 
-#include "libc_common.h"
-#include "src/__support/RPC/rpc_server.h"
+#include "rpc.h"
+#include "rpc_opcodes.h"
 
-namespace LIBC_NAMESPACE_DECL {
-namespace shared {
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
-using LIBC_NAMESPACE::rpc::handle_libc_opcodes;
+#ifdef _WIN32
+#define flockfile _lock_file
+#define funlockfile _unlock_file
+#define fwrite_unlocked _fwrite_nolock
+#endif
 
-} // namespace shared
-} // namespace LIBC_NAMESPACE_DECL
+namespace rpc {
+
+// Minimal replacement for 'std::vector' that works for trivial types.
+template <typename T> class TempVector {
+  T *data_ = nullptr;
+  size_t current = 0;
+  size_t capacity = 0;
+
+public:
+  ~TempVector() { free(data_); }
+
+  void push_back(const T &value) {
+    if (current == capacity)
+      grow();
+    data_[current++] = value;
+  }
+
+  void pop_back() { --current; }
+
+  bool empty() const { return current == 0; }
+
+  size_t size() const { return current; }
+
+  T &operator[](size_t index) { return data_[index]; }
+
+  T &back() { return data_[current - 1]; }
+
+private:
+  void grow() {
+    size_t new_capacity = capacity ? capacity * 2 : 1;
+    void *new_data = realloc(data_, new_capacity * sizeof(T));
+    data_ = static_cast<T *>(new_data);
+    capacity = new_capacity;
+  }
+};
+
+struct TempStorage {
+  char *alloc(size_t size) {
+    storage.push_back(reinterpret_cast<char *>(malloc(size)));
+    return storage.back();
+  }
+
+  ~TempStorage() {
+    for (size_t i = 0; i < storage.size(); ++i)
+      free(storage[i]);
+  }
+
+  TempVector<char *> storage;
+};
+
+// Counts the bytes consumed from a variadic argument list without reading data.
+template <bool packed> struct DummyArgList {
+  size_t count = 0;
+
+  template <class T> inline T next_var() {
+    count =
+        packed ? count + sizeof(T) : align_up(count, alignof(T)) + sizeof(T);
+    return T(count);
+  }
+
+  size_t read_count() const { return count; }
+};
+
+// Reads variadic arguments from a pre-built byte buffer.
+template <bool packed> struct StructArgList {
+  void *ptr;
+  void *end;
+
+  StructArgList() = default;
+  inline StructArgList(void *ptr, size_t size)
+      : ptr(ptr), end(static_cast<unsigned char *>(ptr) + size) {}
+
+  template <class T> inline T next_var() {
+    if (!packed)
+      ptr = reinterpret_cast<void *>(
+          align_up(reinterpret_cast<uintptr_t>(ptr), alignof(T)));
+    if (ptr >= end)
+      return T(-1);
+    T val;
+    memcpy(&val, ptr, sizeof(T));
+    ptr =
+        reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(ptr) + sizeof(T));
+    return val;
+  }
+};
+
+// Get the associated stream out of an encoded number.
+inline FILE *to_stream(uintptr_t f) {
+  enum Stream { File = 0, Stdin = 1, Stdout = 2, Stderr = 3 };
+  FILE *stream = reinterpret_cast<FILE *>(f & ~0x3ull);
+  Stream type = static_cast<Stream>(f & 0x3ull);
+  if (type == Stdin)
+    return stdin;
+  if (type == Stdout)
+    return stdout;
+  if (type == Stderr)
+    return stderr;
+  return stream;
+}
+
+inline constexpr bool is_format_flag(char c) {
+  return c == ' ' || c == '-' || c == '+' || c == '#' || c == '0';
+}
+
+inline constexpr bool is_digit(char c) { return c >= '0' && c <= '9'; }
+
+enum class LengthModifier { none, l };
+enum class SizeArgument { finished, width, precision };
+
+struct Specifier {
+  uintptr_t raw_value = 0;
+  char conv_name = '\0';
+  bool is_string = false;
+  bool is_finished = false;
+  bool is_star = false;
+  bool is_long = false;
+};
+
+// Minimal printf format string parser. Walks the format and extracts the type
+// and size of each variadic argument consumed by a conversion specifier.
+template <typename ArgProvider> struct MicroParser {
+  inline MicroParser(const char *format, ArgProvider args)
+      : format(format), args(args) {}
+
+  inline uint32_t pos() const { return cur_pos; }
+  inline uint32_t spec_start() const { return spec_begin; }
+
+  inline Specifier get_next_specifier() {
+    Specifier specifier{};
+
+    while (format[cur_pos] != '\0' && format[cur_pos] != '%' &&
+           size_pos == SizeArgument::finished)
+      ++cur_pos;
+
+    if (format[cur_pos] == '\0') {
+      specifier.is_finished = true;
+      return specifier;
+    }
+
+    if (size_pos == SizeArgument::finished)
+      spec_begin = cur_pos;
+
+    cur_pos++;
+
+    if (size_pos == SizeArgument::finished) {
+      while (format[cur_pos] != '\0' && is_format_flag(format[cur_pos]))
+        ++cur_pos;
+
+      if (format[cur_pos] == '*') {
+        specifier.raw_value =
+            static_cast<uintptr_t>(args.template next_var<uint32_t>());
+        specifier.is_star = true;
+        size_pos = SizeArgument::width;
+        return specifier;
+      }
+
+      while (format[cur_pos] != '\0' && is_digit(format[cur_pos]))
+        ++cur_pos;
+    }
+
+    if (format[cur_pos] == '.' && size_pos != SizeArgument::precision) {
+      ++cur_pos;
+      if (format[cur_pos] == '*') {
+        specifier.raw_value =
+            static_cast<uintptr_t>(args.template next_var<uint32_t>());
+        specifier.is_star = true;
+        size_pos = SizeArgument::precision;
+        return specifier;
+      }
+      while (format[cur_pos] != '\0' && is_digit(format[cur_pos]))
+        ++cur_pos;
+    }
+
+    LengthModifier lm = parse_length_modifier();
+    specifier.is_long = lm == LengthModifier::l;
+    specifier.conv_name = format[cur_pos];
+
+    switch (format[cur_pos]) {
+    case 'c':
+      specifier.raw_value =
+          static_cast<uintptr_t>(args.template next_var<uint32_t>());
+      break;
+    case 'd':
+    case 'b':
+    case 'B':
+    case 'i':
+    case 'o':
+    case 'x':
+    case 'X':
+    case 'u':
+      if (lm == LengthModifier::none)
+        specifier.raw_value =
+            static_cast<uintptr_t>(args.template next_var<uint32_t>());
+      else
+        specifier.raw_value =
+            static_cast<uintptr_t>(args.template next_var<uint64_t>());
+      break;
+    case 'f':
+    case 'F':
+    case 'e':
+    case 'E':
+    case 'a':
+    case 'A':
+    case 'g':
+    case 'G': {
+      double d = args.template next_var<double>();
+      memcpy(&specifier.raw_value, &d, sizeof(double));
+      break;
+    }
+    case 'p':
+      specifier.raw_value =
+          reinterpret_cast<uintptr_t>(args.template next_var<void *>());
+      break;
+    case 's':
+      specifier.raw_value =
+          reinterpret_cast<uintptr_t>(args.template next_var<void *>());
+      specifier.is_string = true;
+      break;
+    case 'n':
+      specifier.raw_value =
+          reinterpret_cast<uintptr_t>(args.template next_var<void *>());
+      break;
+    case '%':
+      break;
+    default:
+      if (format[cur_pos] == '\0') {
+        specifier.is_finished = true;
+        return specifier;
+      }
+      break;
+    }
+
+    cur_pos++;
+    size_pos = SizeArgument::finished;
+    return specifier;
+  }
+
+private:
+  inline LengthModifier parse_length_modifier() {
+    switch (format[cur_pos]) {
+    case 'l':
+      if (format[cur_pos + 1] == 'l')
+        ++cur_pos;
+      [[fallthrough]];
+    case 't':
+    case 'j':
+    case 'z':
+      ++cur_pos;
+      return LengthModifier::l;
+    case 'h':
+      if (format[cur_pos + 1] == 'h')
+        ++cur_pos;
+      ++cur_pos;
+      return LengthModifier::none;
+    case 'q':
+    case 'L':
+      ++cur_pos;
+      return LengthModifier::l;
+    default:
+      return LengthModifier::none;
+    }
+  }
+
+  const char *const format;
+  ArgProvider args;
+  uint32_t cur_pos = 0;
+  uint32_t spec_begin = 0;
+  SizeArgument size_pos = SizeArgument::finished;
+};
+
+// Dispatch helper that passes dynamic '*' width/precision values to fprintf.
+template <typename T>
+inline int fprintf_with_stars(FILE *file, const char *fmt, int num_stars,
+                              int *star_vals, T val) {
+  if (num_stars == 2)
+    return fprintf(file, fmt, star_vals[0], star_vals[1], val);
+  if (num_stars == 1)
+    return fprintf(file, fmt, star_vals[0], val);
+  return fprintf(file, fmt, val);
+}
+
+// Walks a printf format string using the MicroParser and emits output in
+// chunks via fprintf. Literal text is written directly with fwrite_unlocked.
+// The caller must hold the stream lock via flockfile.
+template <bool packed>
+inline int print_format(FILE *file, const char *fmt, StructArgList<packed> args,
+                        TempVector<void *> &copied_strs) {
+  MicroParser<StructArgList<packed>> parser(fmt, args);
+  int ret = 0;
+  size_t prev = 0;
+  int star_vals[2];
+  int num_stars = 0;
+
+  for (Specifier spec = parser.get_next_specifier(); !spec.is_finished;
+       spec = parser.get_next_specifier()) {
+    if (spec.is_star) {
+      if (num_stars < 2)
+        star_vals[num_stars++] = static_cast<int>(spec.raw_value);
+      continue;
+    }
+
+    size_t start = parser.spec_start();
+    size_t end = parser.pos();
+
+    if (start > prev)
+      ret += fwrite_unlocked(fmt + prev, 1, start - prev, file);
+
+    // Null-terminated copy of the specifier substring for fprintf. Use a
+    // stack buffer for the common case; heap-allocate only for overlong specs.
+    size_t len = end - start;
+    char local_buf[32];
+    char *buf = len < sizeof(local_buf) ? local_buf : new char[len + 1];
+    memcpy(buf, fmt + start, len);
+    buf[len] = '\0';
+
+    switch (spec.conv_name) {
+    case 's': {
+      const char *str = reinterpret_cast<const char *>(spec.raw_value);
+      if (str) {
+        str = reinterpret_cast<const char *>(copied_strs.back());
+        copied_strs.pop_back();
+      }
+      ret += fprintf_with_stars(file, buf, num_stars, star_vals, str);
+      break;
+    }
+    case 'n':
+      break;
+    case 'p':
+      ret += fprintf_with_stars(file, buf, num_stars, star_vals,
+                                reinterpret_cast<void *>(spec.raw_value));
+      break;
+    case 'f':
+    case 'F':
+    case 'e':
+    case 'E':
+    case 'a':
+    case 'A':
+    case 'g':
+    case 'G': {
+      double d;
+      memcpy(&d, &spec.raw_value, sizeof(double));
+      if (spec.is_long)
+        ret += fprintf_with_stars(file, buf, num_stars, star_vals,
+                                  static_cast<long double>(d));
+      else
+        ret += fprintf_with_stars(file, buf, num_stars, star_vals, d);
+      break;
+    }
+    default:
+      if (spec.is_long)
+        ret +=
+            fprintf_with_stars(file, buf, num_stars, star_vals, spec.raw_value);
+      else
+        ret += fprintf_with_stars(file, buf, num_stars, star_vals,
+                                  static_cast<uint32_t>(spec.raw_value));
+      break;
+    }
+    if (buf != local_buf)
+      delete[] buf;
+    num_stars = 0;
+    prev = end;
+  }
+
+  if (parser.pos() > prev)
+    ret += fwrite_unlocked(fmt + prev, 1, parser.pos() - prev, file);
+
+  return ret;
+}
+
+template <bool packed, uint32_t num_lanes>
+inline void handle_printf(Server::Port &port, TempStorage &temp_storage) {
+  FILE *files[num_lanes] = {nullptr};
+  // Get the appropriate output stream to use.
+  if (port.get_opcode() == LIBC_PRINTF_TO_STREAM ||
+      port.get_opcode() == LIBC_PRINTF_TO_STREAM_PACKED) {
+    port.recv([&](Buffer *buffer, uint32_t id) {
+      files[id] = reinterpret_cast<FILE *>(buffer->data[0]);
+    });
+  } else if (port.get_opcode() == LIBC_PRINTF_TO_STDOUT ||
+             port.get_opcode() == LIBC_PRINTF_TO_STDOUT_PACKED) {
+    for (uint32_t i = 0; i < num_lanes; ++i)
+      files[i] = stdout;
+  } else {
+    for (uint32_t i = 0; i < num_lanes; ++i)
+      files[i] = stderr;
+  }
+
+  uint64_t format_sizes[num_lanes] = {0};
+  void *format[num_lanes] = {nullptr};
+
+  uint64_t args_sizes[num_lanes] = {0};
+  void *args[num_lanes] = {nullptr};
+
+  // Receive the format string from the client.
+  port.recv_n(format, format_sizes,
+              [&](uint64_t size) { return temp_storage.alloc(size); });
+
+  // Parse the format string to determine the expected argument buffer size.
+  for (uint32_t lane = 0; lane < num_lanes; ++lane) {
+    if (!format[lane])
+      continue;
+
+    DummyArgList<packed> dummy_args;
+    MicroParser<DummyArgList<packed> &> parser(
+        reinterpret_cast<const char *>(format[lane]), dummy_args);
+    for (Specifier spec = parser.get_next_specifier(); !spec.is_finished;
+         spec = parser.get_next_specifier())
+      ;
+    args_sizes[lane] = dummy_args.read_count();
+  }
+  port.send(
+      [&](Buffer *buffer, uint32_t id) { buffer->data[0] = args_sizes[id]; });
+  port.recv_n(args, args_sizes,
+              [&](uint64_t size) { return temp_storage.alloc(size); });
+
+  // Identify any arguments that are actually pointers to strings on the client.
+  TempVector<void *> strs_to_copy[num_lanes];
+  for (uint32_t lane = 0; lane < num_lanes; ++lane) {
+    if (!format[lane])
+      continue;
+
+    StructArgList<packed> struct_args(args[lane], args_sizes[lane]);
+    MicroParser<StructArgList<packed>> parser(
+        reinterpret_cast<const char *>(format[lane]), struct_args);
+    for (Specifier spec = parser.get_next_specifier(); !spec.is_finished;
+         spec = parser.get_next_specifier()) {
+      if (spec.is_string && spec.raw_value)
+        strs_to_copy[lane].push_back(reinterpret_cast<void *>(spec.raw_value));
+    }
+  }
+
+  // Receive any strings from the client and push them into a buffer.
+  TempVector<void *> copied_strs[num_lanes];
+  auto has_pending = [](TempVector<void *> v[num_lanes]) {
+    for (uint32_t i = 0; i < num_lanes; ++i)
+      if (!v[i].empty() && v[i].back())
+        return true;
+    return false;
+  };
+  while (has_pending(strs_to_copy)) {
+    port.send([&](Buffer *buffer, uint32_t id) {
+      void *ptr = !strs_to_copy[id].empty() ? strs_to_copy[id].back() : nullptr;
+      buffer->data[1] = reinterpret_cast<uintptr_t>(ptr);
+      if (!strs_to_copy[id].empty())
+        strs_to_copy[id].pop_back();
+    });
+    uint64_t str_sizes[num_lanes] = {0};
+    void *strs[num_lanes] = {nullptr};
+    port.recv_n(strs, str_sizes,
+                [&](uint64_t size) { return temp_storage.alloc(size); });
+    for (uint32_t lane = 0; lane < num_lanes; ++lane) {
+      if (!strs[lane])
+        continue;
+      copied_strs[lane].push_back(strs[lane]);
+    }
+  }
+
+  // Print using a locked stream, emitting each format chunk via fprintf.
+  int results[num_lanes] = {0};
+  for (uint32_t lane = 0; lane < num_lanes; ++lane) {
+    if (!format[lane])
+      continue;
+
+    StructArgList<packed> printf_args(args[lane], args_sizes[lane]);
+    flockfile(files[lane]);
+    results[lane] = print_format<packed>(
+        files[lane], reinterpret_cast<const char *>(format[lane]), printf_args,
+        copied_strs[lane]);
+    funlockfile(files[lane]);
+  }
+
+  // Send the final return value and signal completion by setting the string
+  // argument to null.
+  port.send([&](Buffer *buffer, uint32_t id) {
+    buffer->data[0] = static_cast<uint64_t>(results[id]);
+    buffer->data[1] = reinterpret_cast<uintptr_t>(nullptr);
+  });
+}
+
+template <uint32_t num_lanes>
+inline RPCStatus handle_port_impl(Server::Port &port) {
+  TempStorage temp_storage;
+
+  switch (port.get_opcode()) {
+  case LIBC_WRITE_TO_STREAM:
+  case LIBC_WRITE_TO_STDERR:
+  case LIBC_WRITE_TO_STDOUT:
+  case LIBC_WRITE_TO_STDOUT_NEWLINE: {
+    uint64_t sizes[num_lanes] = {0};
+    void *strs[num_lanes] = {nullptr};
+    FILE *files[num_lanes] = {nullptr};
+    if (port.get_opcode() == LIBC_WRITE_TO_STREAM) {
+      port.recv([&](Buffer *buffer, uint32_t id) {
+        files[id] = reinterpret_cast<FILE *>(buffer->data[0]);
+      });
+    } else {
+      for (uint32_t i = 0; i < num_lanes; ++i)
+        files[i] = port.get_opcode() == LIBC_WRITE_TO_STDERR ? stderr : stdout;
+    }
+
+    port.recv_n(strs, sizes,
+                [&](uint64_t size) { return temp_storage.alloc(size); });
+    port.send([&](Buffer *buffer, uint32_t id) {
+      flockfile(files[id]);
+      buffer->data[0] = fwrite_unlocked(strs[id], 1, sizes[id], files[id]);
+      if (port.get_opcode() == LIBC_WRITE_TO_STDOUT_NEWLINE &&
+          buffer->data[0] == sizes[id])
+        buffer->data[0] += fwrite_unlocked("\n", 1, 1, files[id]);
+      funlockfile(files[id]);
+    });
+    break;
+  }
+  case LIBC_READ_FROM_STREAM: {
+    uint64_t sizes[num_lanes] = {0};
+    void *data[num_lanes] = {nullptr};
+    port.recv([&](Buffer *buffer, uint32_t id) {
+      data[id] = temp_storage.alloc(buffer->data[0]);
+      sizes[id] =
+          fread(data[id], 1, buffer->data[0], to_stream(buffer->data[1]));
+    });
+    port.send_n(data, sizes);
+    port.send([&](Buffer *buffer, uint32_t id) {
+      memcpy(buffer->data, &sizes[id], sizeof(uint64_t));
+    });
+    break;
+  }
+  case LIBC_READ_FGETS: {
+    uint64_t sizes[num_lanes] = {0};
+    void *data[num_lanes] = {nullptr};
+    port.recv([&](Buffer *buffer, uint32_t id) {
+      data[id] = temp_storage.alloc(buffer->data[0]);
+      const char *str = ::fgets(reinterpret_cast<char *>(data[id]),
+                                static_cast<int>(buffer->data[0]),
+                                to_stream(buffer->data[1]));
+      sizes[id] = !str ? 0 : __builtin_strlen(str) + 1;
+    });
+    port.send_n(data, sizes);
+    break;
+  }
+  case LIBC_OPEN_FILE: {
+    uint64_t sizes[num_lanes] = {0};
+    void *paths[num_lanes] = {nullptr};
+    port.recv_n(paths, sizes,
+                [&](uint64_t size) { return temp_storage.alloc(size); });
+    port.recv_and_send([&](Buffer *buffer, uint32_t id) {
+      FILE *file = fopen(reinterpret_cast<char *>(paths[id]),
+                         reinterpret_cast<char *>(buffer->data));
+      buffer->data[0] = reinterpret_cast<uintptr_t>(file);
+    });
+    break;
+  }
+  case LIBC_CLOSE_FILE: {
+    port.recv_and_send([&](Buffer *buffer, uint32_t) {
+      FILE *file = reinterpret_cast<FILE *>(buffer->data[0]);
+      buffer->data[0] = ::fclose(file);
+    });
+    break;
+  }
+  case LIBC_EXIT: {
+    port.recv_and_send([](Buffer *, uint32_t) {});
+    port.recv([](Buffer *buffer, uint32_t) {
+      int status = 0;
+      memcpy(&status, buffer->data, sizeof(int));
+      quick_exit(status);
+    });
+    break;
+  }
+  case LIBC_ABORT: {
+    port.recv_and_send([](Buffer *, uint32_t) {});
+    port.recv([](Buffer *, uint32_t) {});
+    abort();
+    break;
+  }
+  case LIBC_HOST_CALL: {
+    uint64_t sizes[num_lanes] = {0};
+    unsigned long long results[num_lanes] = {0};
+    void *args[num_lanes] = {nullptr};
+    port.recv_n(args, sizes,
+                [&](uint64_t size) { return temp_storage.alloc(size); });
+    port.recv([&](Buffer *buffer, uint32_t id) {
+      using func_ptr_t = unsigned long long (*)(void *);
+      auto func = reinterpret_cast<func_ptr_t>(buffer->data[0]);
+      results[id] = func(args[id]);
+    });
+    port.send([&](Buffer *buffer, uint32_t id) {
+      buffer->data[0] = static_cast<uint64_t>(results[id]);
+    });
+    break;
+  }
+  case LIBC_FEOF: {
+    port.recv_and_send([](Buffer *buffer, uint32_t) {
+      buffer->data[0] = feof(to_stream(buffer->data[0]));
+    });
+    break;
+  }
+  case LIBC_FERROR: {
+    port.recv_and_send([](Buffer *buffer, uint32_t) {
+      buffer->data[0] = ferror(to_stream(buffer->data[0]));
+    });
+    break;
+  }
+  case LIBC_CLEARERR: {
+    port.recv_and_send(
+        [](Buffer *buffer, uint32_t) { clearerr(to_stream(buffer->data[0])); });
+    break;
+  }
+  case LIBC_FSEEK: {
+    port.recv_and_send([](Buffer *buffer, uint32_t) {
+      buffer->data[0] =
+          fseek(to_stream(buffer->data[0]), static_cast<long>(buffer->data[1]),
+                static_cast<int>(buffer->data[2]));
+    });
+    break;
+  }
+  case LIBC_FTELL: {
+    port.recv_and_send([](Buffer *buffer, uint32_t) {
+      buffer->data[0] = ftell(to_stream(buffer->data[0]));
+    });
+    break;
+  }
+  case LIBC_FFLUSH: {
+    port.recv_and_send([](Buffer *buffer, uint32_t) {
+      buffer->data[0] = fflush(to_stream(buffer->data[0]));
+    });
+    break;
+  }
+  case LIBC_UNGETC: {
+    port.recv_and_send([](Buffer *buffer, uint32_t) {
+      buffer->data[0] =
+          ungetc(static_cast<int>(buffer->data[0]), to_stream(buffer->data[1]));
+    });
+    break;
+  }
+  case LIBC_PRINTF_TO_STREAM_PACKED:
+  case LIBC_PRINTF_TO_STDOUT_PACKED:
+  case LIBC_PRINTF_TO_STDERR_PACKED: {
+    handle_printf<true, num_lanes>(port, temp_storage);
+    break;
+  }
+  case LIBC_PRINTF_TO_STREAM:
+  case LIBC_PRINTF_TO_STDOUT:
+  case LIBC_PRINTF_TO_STDERR: {
+    handle_printf<false, num_lanes>(port, temp_storage);
+    break;
+  }
+  case LIBC_REMOVE: {
+    uint64_t sizes[num_lanes] = {0};
+    void *args[num_lanes] = {nullptr};
+    port.recv_n(args, sizes,
+                [&](uint64_t size) { return temp_storage.alloc(size); });
+    port.send([&](Buffer *buffer, uint32_t id) {
+      buffer->data[0] = static_cast<uint64_t>(
+          remove(reinterpret_cast<const char *>(args[id])));
+    });
+    break;
+  }
+  case LIBC_RENAME: {
+    uint64_t oldsizes[num_lanes] = {0};
+    uint64_t newsizes[num_lanes] = {0};
+    void *oldpath[num_lanes] = {nullptr};
+    void *newpath[num_lanes] = {nullptr};
+    port.recv_n(oldpath, oldsizes,
+                [&](uint64_t size) { return temp_storage.alloc(size); });
+    port.recv_n(newpath, newsizes,
+                [&](uint64_t size) { return temp_storage.alloc(size); });
+    port.send([&](Buffer *buffer, uint32_t id) {
+      buffer->data[0] = static_cast<uint64_t>(
+          rename(reinterpret_cast<const char *>(oldpath[id]),
+                 reinterpret_cast<const char *>(newpath[id])));
+    });
+    break;
+  }
+  case LIBC_SYSTEM: {
+    uint64_t sizes[num_lanes] = {0};
+    void *args[num_lanes] = {nullptr};
+    port.recv_n(args, sizes,
+                [&](uint64_t size) { return temp_storage.alloc(size); });
+    port.send([&](Buffer *buffer, uint32_t id) {
+      buffer->data[0] = static_cast<uint64_t>(
+          system(reinterpret_cast<const char *>(args[id])));
+    });
+    break;
+  }
+  case LIBC_TEST_INCREMENT: {
+    port.recv_and_send([](Buffer *buffer, uint32_t) {
+      reinterpret_cast<uint64_t *>(buffer->data)[0] += 1;
+    });
+    break;
+  }
+  case LIBC_TEST_INTERFACE: {
+    bool end_with_recv;
+    uint64_t cnt;
+    port.recv(
+        [&](Buffer *buffer, uint32_t) { end_with_recv = buffer->data[0]; });
+    port.recv([&](Buffer *buffer, uint32_t) { cnt = buffer->data[0]; });
+    port.send(
+        [&](Buffer *buffer, uint32_t) { buffer->data[0] = cnt = cnt + 1; });
+    port.recv([&](Buffer *buffer, uint32_t) { cnt = buffer->data[0]; });
+    port.send(
+        [&](Buffer *buffer, uint32_t) { buffer->data[0] = cnt = cnt + 1; });
+    port.recv([&](Buffer *buffer, uint32_t) { cnt = buffer->data[0]; });
+    port.recv([&](Buffer *buffer, uint32_t) { cnt = buffer->data[0]; });
+    port.send(
+        [&](Buffer *buffer, uint32_t) { buffer->data[0] = cnt = cnt + 1; });
+    port.send(
+        [&](Buffer *buffer, uint32_t) { buffer->data[0] = cnt = cnt + 1; });
+    if (end_with_recv)
+      port.recv([&](Buffer *buffer, uint32_t) { cnt = buffer->data[0]; });
+    else
+      port.send(
+          [&](Buffer *buffer, uint32_t) { buffer->data[0] = cnt = cnt + 1; });
+
+    break;
+  }
+  case LIBC_TEST_STREAM: {
+    uint64_t sizes[num_lanes] = {0};
+    void *dst[num_lanes] = {nullptr};
+    port.recv_n(dst, sizes,
+                [](uint64_t size) -> void * { return new char[size]; });
+    port.send_n(dst, sizes);
+    for (uint64_t i = 0; i < num_lanes; ++i) {
+      if (dst[i])
+        delete[] reinterpret_cast<uint8_t *>(dst[i]);
+    }
+    break;
+  }
+  case LIBC_NOOP: {
+    port.recv([](Buffer *, uint32_t) {});
+    break;
+  }
+  default:
+    return RPC_UNHANDLED_OPCODE;
+  }
+
+  return RPC_SUCCESS;
+}
+
+// Handles any opcode generated from the 'libc' client code.
+inline RPCStatus handle_libc_opcodes(Server::Port &port, uint32_t num_lanes) {
----------------
jhuber6 wrote:

rpc_utils is included by both the CPu and GPU while this is not, I'd like to keep their dependency chains separate. However, I should put everything else in an `internal` namespace.

https://github.com/llvm/llvm-project/pull/190423


More information about the libc-commits mailing list