[clang] [libc] RPCLaneSize (PR #84557)
via cfe-commits
cfe-commits at lists.llvm.org
Fri Mar 8 12:19:09 PST 2024
llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang-driver
Author: Joseph Huber (jhuber6)
<details>
<summary>Changes</summary>
- [HIP] Make the new driver bundle outputs for device-only
- [libc][NFCI] Remove lane size template argument on RPC server
---
Patch is 25.25 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/84557.diff
4 Files Affected:
- (modified) clang/lib/Driver/Driver.cpp (+9-1)
- (modified) clang/test/Driver/hip-binding.hip (+7-3)
- (modified) libc/src/__support/RPC/rpc.h (+11-12)
- (modified) libc/utils/gpu/server/rpc_server.cpp (+224-261)
``````````diff
diff --git a/clang/lib/Driver/Driver.cpp b/clang/lib/Driver/Driver.cpp
index fce43430a91374..eba43d97431364 100644
--- a/clang/lib/Driver/Driver.cpp
+++ b/clang/lib/Driver/Driver.cpp
@@ -4638,7 +4638,10 @@ Action *Driver::BuildOffloadingActions(Compilation &C,
}
}
- if (offloadDeviceOnly())
+ // All kinds exit now in device-only mode except for non-RDC mode HIP.
+ if (offloadDeviceOnly() &&
+ (!C.isOffloadingHostKind(Action::OFK_HIP) ||
+ Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, false)))
return C.MakeAction<OffloadAction>(DDeps, types::TY_Nothing);
if (OffloadActions.empty())
@@ -4671,6 +4674,11 @@ Action *Driver::BuildOffloadingActions(Compilation &C,
nullptr, C.getActiveOffloadKinds());
}
+ // HIP wants '--offload-device-only' to create a fatbinary by default.
+ if (offloadDeviceOnly() && C.isOffloadingHostKind(Action::OFK_HIP) &&
+ !Args.hasFlag(options::OPT_fgpu_rdc, options::OPT_fno_gpu_rdc, false))
+ return C.MakeAction<OffloadAction>(DDep, types::TY_Nothing);
+
// If we are unable to embed a single device output into the host, we need to
// add each device output as a host dependency to ensure they are still built.
bool SingleDeviceOutput = !llvm::any_of(OffloadActions, [](Action *A) {
diff --git a/clang/test/Driver/hip-binding.hip b/clang/test/Driver/hip-binding.hip
index 79ec2039edb74c..cb17112c28d90a 100644
--- a/clang/test/Driver/hip-binding.hip
+++ b/clang/test/Driver/hip-binding.hip
@@ -64,10 +64,14 @@
// MULTI-D-ONLY-NEXT: # "amdgcn-amd-amdhsa" - "clang", inputs: ["[[INPUT]]"], output: "[[GFX90a:.+]]"
// MULTI-D-ONLY-NEXT: # "amdgcn-amd-amdhsa" - "AMDGCN::Linker", inputs: ["[[GFX90a]]"], output: "[[GFX90a_OUT:.+]]"
//
-// RUN: not %clang -### --target=x86_64-linux-gnu --offload-new-driver -ccc-print-bindings -nogpulib -nogpuinc \
-// RUN: --offload-arch=gfx90a --offload-arch=gfx908 --offload-device-only -c -o %t %s 2>&1 \
+// RUN: %clang -### --target=x86_64-linux-gnu --offload-new-driver -ccc-print-bindings -nogpulib -nogpuinc \
+// RUN: --offload-arch=gfx90a --offload-arch=gfx908 --offload-device-only -c -o a.out %s 2>&1 \
// RUN: | FileCheck -check-prefix=MULTI-D-ONLY-O %s
-// MULTI-D-ONLY-O: error: cannot specify -o when generating multiple output files
+// MULTI-D-ONLY-O: "amdgcn-amd-amdhsa" - "clang", inputs: ["[[INPUT:.+]]"], output: "[[GFX908_OBJ:.+]]"
+// MULTI-D-ONLY-O-NEXT: "amdgcn-amd-amdhsa" - "AMDGCN::Linker", inputs: ["[[GFX908_OBJ]]"], output: "[[GFX908:.+]]"
+// MULTI-D-ONLY-O-NEXT: "amdgcn-amd-amdhsa" - "clang", inputs: ["[[INPUT]]"], output: "[[GFX90A_OBJ:.+]]"
+// MULTI-D-ONLY-O-NEXT: "amdgcn-amd-amdhsa" - "AMDGCN::Linker", inputs: ["[[GFX90A_OBJ]]"], output: "[[GFX90A:.+]]"
+// MULTI-D-ONLY-O-NEXT: "amdgcn-amd-amdhsa" - "AMDGCN::Linker", inputs: ["[[GFX908]]", "[[GFX90A]]"], output: "a.out"
//
// Check to ensure that we can use '-fsyntax-only' for HIP output with the new
diff --git a/libc/src/__support/RPC/rpc.h b/libc/src/__support/RPC/rpc.h
index 5ed39ae0d7f7a9..5dcae518bb6f8f 100644
--- a/libc/src/__support/RPC/rpc.h
+++ b/libc/src/__support/RPC/rpc.h
@@ -310,7 +310,7 @@ template <bool T> struct Port {
LIBC_INLINE Port &operator=(Port &&) = default;
friend struct Client;
- template <uint32_t U> friend struct Server;
+ friend struct Server;
friend class cpp::optional<Port<T>>;
public:
@@ -369,7 +369,7 @@ static_assert(cpp::is_trivially_copyable<Client>::value &&
"The client is not trivially copyable from the server");
/// The RPC server used to respond to the client.
-template <uint32_t lane_size> struct Server {
+struct Server {
LIBC_INLINE Server() = default;
LIBC_INLINE Server(const Server &) = delete;
LIBC_INLINE Server &operator=(const Server &) = delete;
@@ -379,10 +379,12 @@ template <uint32_t lane_size> struct Server {
: process(port_count, buffer) {}
using Port = rpc::Port<true>;
- LIBC_INLINE cpp::optional<Port> try_open(uint32_t start = 0);
- LIBC_INLINE Port open();
+ LIBC_INLINE cpp::optional<Port> try_open(uint32_t lane_size,
+ uint32_t start = 0);
+ LIBC_INLINE Port open(uint32_t lane_size);
- LIBC_INLINE static uint64_t allocation_size(uint32_t port_count) {
+ LIBC_INLINE static uint64_t allocation_size(uint32_t lane_size,
+ uint32_t port_count) {
return Process<true>::allocation_size(port_count, lane_size);
}
@@ -556,10 +558,8 @@ template <uint16_t opcode>
/// Attempts to open a port to use as the server. The server can only open a
/// port if it has a pending receive operation
-template <uint32_t lane_size>
-[[clang::convergent]] LIBC_INLINE
- cpp::optional<typename Server<lane_size>::Port>
- Server<lane_size>::try_open(uint32_t start) {
+[[clang::convergent]] LIBC_INLINE cpp::optional<typename Server::Port>
+Server::try_open(uint32_t lane_size, uint32_t start) {
// Perform a naive linear scan for a port that has a pending request.
for (uint32_t index = start; index < process.port_count; ++index) {
uint64_t lane_mask = gpu::get_lane_mask();
@@ -588,10 +588,9 @@ template <uint32_t lane_size>
return cpp::nullopt;
}
-template <uint32_t lane_size>
-LIBC_INLINE typename Server<lane_size>::Port Server<lane_size>::open() {
+LIBC_INLINE Server::Port Server::open(uint32_t lane_size) {
for (;;) {
- if (cpp::optional<Server::Port> p = try_open())
+ if (cpp::optional<Server::Port> p = try_open(lane_size))
return cpp::move(p.value());
sleep_briefly();
}
diff --git a/libc/utils/gpu/server/rpc_server.cpp b/libc/utils/gpu/server/rpc_server.cpp
index 707807a5cbaf7d..e21a9c05eaa68f 100644
--- a/libc/utils/gpu/server/rpc_server.cpp
+++ b/libc/utils/gpu/server/rpc_server.cpp
@@ -27,228 +27,218 @@ static_assert(sizeof(rpc_buffer_t) == sizeof(rpc::Buffer),
static_assert(RPC_MAXIMUM_PORT_COUNT == rpc::MAX_PORT_COUNT,
"Incorrect maximum port count");
-// The client needs to support different lane sizes for the SIMT model. Because
-// of this we need to select between the possible sizes that the client can use.
-struct Server {
- template <uint32_t lane_size>
- Server(std::unique_ptr<rpc::Server<lane_size>> &&server)
- : server(std::move(server)) {}
-
- rpc_status_t handle_server(
- const std::unordered_map<rpc_opcode_t, rpc_opcode_callback_ty> &callbacks,
- const std::unordered_map<rpc_opcode_t, void *> &callback_data,
- uint32_t &index) {
- rpc_status_t ret = RPC_STATUS_SUCCESS;
- std::visit(
- [&](auto &server) {
- ret = handle_server(*server, callbacks, callback_data, index);
- },
- server);
- return ret;
- }
-
-private:
- template <uint32_t lane_size>
- rpc_status_t handle_server(
- rpc::Server<lane_size> &server,
- const std::unordered_map<rpc_opcode_t, rpc_opcode_callback_ty> &callbacks,
- const std::unordered_map<rpc_opcode_t, void *> &callback_data,
- uint32_t &index) {
- auto port = server.try_open(index);
- if (!port)
- return RPC_STATUS_SUCCESS;
-
- switch (port->get_opcode()) {
- case RPC_WRITE_TO_STREAM:
- case RPC_WRITE_TO_STDERR:
- case RPC_WRITE_TO_STDOUT:
- case RPC_WRITE_TO_STDOUT_NEWLINE: {
- uint64_t sizes[lane_size] = {0};
- void *strs[lane_size] = {nullptr};
- FILE *files[lane_size] = {nullptr};
- if (port->get_opcode() == RPC_WRITE_TO_STREAM) {
- port->recv([&](rpc::Buffer *buffer, uint32_t id) {
- files[id] = reinterpret_cast<FILE *>(buffer->data[0]);
- });
- } else if (port->get_opcode() == RPC_WRITE_TO_STDERR) {
- std::fill(files, files + lane_size, stderr);
- } else {
- std::fill(files, files + lane_size, stdout);
- }
-
- port->recv_n(strs, sizes, [&](uint64_t size) { return new char[size]; });
- port->send([&](rpc::Buffer *buffer, uint32_t id) {
- flockfile(files[id]);
- buffer->data[0] = fwrite_unlocked(strs[id], 1, sizes[id], files[id]);
- if (port->get_opcode() == RPC_WRITE_TO_STDOUT_NEWLINE &&
- buffer->data[0] == sizes[id])
- buffer->data[0] += fwrite_unlocked("\n", 1, 1, files[id]);
- funlockfile(files[id]);
- delete[] reinterpret_cast<uint8_t *>(strs[id]);
- });
- break;
- }
- case RPC_READ_FROM_STREAM: {
- uint64_t sizes[lane_size] = {0};
- void *data[lane_size] = {nullptr};
- port->recv([&](rpc::Buffer *buffer, uint32_t id) {
- data[id] = new char[buffer->data[0]];
- sizes[id] = fread(data[id], 1, buffer->data[0],
- file::to_stream(buffer->data[1]));
- });
- port->send_n(data, sizes);
- port->send([&](rpc::Buffer *buffer, uint32_t id) {
- delete[] reinterpret_cast<uint8_t *>(data[id]);
- std::memcpy(buffer->data, &sizes[id], sizeof(uint64_t));
- });
- break;
- }
- case RPC_READ_FGETS: {
- uint64_t sizes[lane_size] = {0};
- void *data[lane_size] = {nullptr};
- port->recv([&](rpc::Buffer *buffer, uint32_t id) {
- data[id] = new char[buffer->data[0]];
- const char *str =
- fgets(reinterpret_cast<char *>(data[id]), buffer->data[0],
- file::to_stream(buffer->data[1]));
- sizes[id] = !str ? 0 : std::strlen(str) + 1;
- });
- port->send_n(data, sizes);
- for (uint32_t id = 0; id < lane_size; ++id)
- if (data[id])
- delete[] reinterpret_cast<uint8_t *>(data[id]);
- break;
- }
- case RPC_OPEN_FILE: {
- uint64_t sizes[lane_size] = {0};
- void *paths[lane_size] = {nullptr};
- port->recv_n(paths, sizes, [&](uint64_t size) { return new char[size]; });
- port->recv_and_send([&](rpc::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 RPC_CLOSE_FILE: {
- port->recv_and_send([&](rpc::Buffer *buffer, uint32_t id) {
- FILE *file = reinterpret_cast<FILE *>(buffer->data[0]);
- buffer->data[0] = fclose(file);
- });
- break;
- }
- case RPC_EXIT: {
- // Send a response to the client to signal that we are ready to exit.
- port->recv_and_send([](rpc::Buffer *) {});
- port->recv([](rpc::Buffer *buffer) {
- int status = 0;
- std::memcpy(&status, buffer->data, sizeof(int));
- exit(status);
- });
- break;
- }
- case RPC_ABORT: {
- // Send a response to the client to signal that we are ready to abort.
- port->recv_and_send([](rpc::Buffer *) {});
- port->recv([](rpc::Buffer *) {});
- abort();
- break;
- }
- case RPC_HOST_CALL: {
- uint64_t sizes[lane_size] = {0};
- void *args[lane_size] = {nullptr};
- port->recv_n(args, sizes, [&](uint64_t size) { return new char[size]; });
+template <uint32_t lane_size>
+rpc_status_t handle_server_impl(
+ rpc::Server &server,
+ const std::unordered_map<rpc_opcode_t, rpc_opcode_callback_ty> &callbacks,
+ const std::unordered_map<rpc_opcode_t, void *> &callback_data,
+ uint32_t &index) {
+ auto port = server.try_open(lane_size, index);
+ if (!port)
+ return RPC_STATUS_SUCCESS;
+
+ switch (port->get_opcode()) {
+ case RPC_WRITE_TO_STREAM:
+ case RPC_WRITE_TO_STDERR:
+ case RPC_WRITE_TO_STDOUT:
+ case RPC_WRITE_TO_STDOUT_NEWLINE: {
+ uint64_t sizes[lane_size] = {0};
+ void *strs[lane_size] = {nullptr};
+ FILE *files[lane_size] = {nullptr};
+ if (port->get_opcode() == RPC_WRITE_TO_STREAM) {
port->recv([&](rpc::Buffer *buffer, uint32_t id) {
- reinterpret_cast<void (*)(void *)>(buffer->data[0])(args[id]);
- });
- port->send([&](rpc::Buffer *, uint32_t id) {
- delete[] reinterpret_cast<uint8_t *>(args[id]);
- });
- break;
- }
- case RPC_FEOF: {
- port->recv_and_send([](rpc::Buffer *buffer) {
- buffer->data[0] = feof(file::to_stream(buffer->data[0]));
- });
- break;
- }
- case RPC_FERROR: {
- port->recv_and_send([](rpc::Buffer *buffer) {
- buffer->data[0] = ferror(file::to_stream(buffer->data[0]));
- });
- break;
- }
- case RPC_CLEARERR: {
- port->recv_and_send([](rpc::Buffer *buffer) {
- clearerr(file::to_stream(buffer->data[0]));
- });
- break;
- }
- case RPC_FSEEK: {
- port->recv_and_send([](rpc::Buffer *buffer) {
- buffer->data[0] = fseek(file::to_stream(buffer->data[0]),
- static_cast<long>(buffer->data[1]),
- static_cast<int>(buffer->data[2]));
- });
- break;
- }
- case RPC_FTELL: {
- port->recv_and_send([](rpc::Buffer *buffer) {
- buffer->data[0] = ftell(file::to_stream(buffer->data[0]));
- });
- break;
- }
- case RPC_FFLUSH: {
- port->recv_and_send([](rpc::Buffer *buffer) {
- buffer->data[0] = fflush(file::to_stream(buffer->data[0]));
+ files[id] = reinterpret_cast<FILE *>(buffer->data[0]);
});
- break;
- }
- case RPC_UNGETC: {
- port->recv_and_send([](rpc::Buffer *buffer) {
- buffer->data[0] = ungetc(static_cast<int>(buffer->data[0]),
- file::to_stream(buffer->data[1]));
- });
- break;
- }
- case RPC_NOOP: {
- port->recv([](rpc::Buffer *) {});
- break;
- }
- default: {
- auto handler =
- callbacks.find(static_cast<rpc_opcode_t>(port->get_opcode()));
-
- // We error out on an unhandled opcode.
- if (handler == callbacks.end())
- return RPC_STATUS_UNHANDLED_OPCODE;
-
- // Invoke the registered callback with a reference to the port.
- void *data =
- callback_data.at(static_cast<rpc_opcode_t>(port->get_opcode()));
- rpc_port_t port_ref{reinterpret_cast<uint64_t>(&*port), lane_size};
- (handler->second)(port_ref, data);
- }
+ } else if (port->get_opcode() == RPC_WRITE_TO_STDERR) {
+ std::fill(files, files + lane_size, stderr);
+ } else {
+ std::fill(files, files + lane_size, stdout);
}
- // Increment the index so we start the scan after this port.
- index = port->get_index() + 1;
- port->close();
- return RPC_STATUS_CONTINUE;
+ port->recv_n(strs, sizes, [&](uint64_t size) { return new char[size]; });
+ port->send([&](rpc::Buffer *buffer, uint32_t id) {
+ flockfile(files[id]);
+ buffer->data[0] = fwrite_unlocked(strs[id], 1, sizes[id], files[id]);
+ if (port->get_opcode() == RPC_WRITE_TO_STDOUT_NEWLINE &&
+ buffer->data[0] == sizes[id])
+ buffer->data[0] += fwrite_unlocked("\n", 1, 1, files[id]);
+ funlockfile(files[id]);
+ delete[] reinterpret_cast<uint8_t *>(strs[id]);
+ });
+ break;
+ }
+ case RPC_READ_FROM_STREAM: {
+ uint64_t sizes[lane_size] = {0};
+ void *data[lane_size] = {nullptr};
+ port->recv([&](rpc::Buffer *buffer, uint32_t id) {
+ data[id] = new char[buffer->data[0]];
+ sizes[id] =
+ fread(data[id], 1, buffer->data[0], file::to_stream(buffer->data[1]));
+ });
+ port->send_n(data, sizes);
+ port->send([&](rpc::Buffer *buffer, uint32_t id) {
+ delete[] reinterpret_cast<uint8_t *>(data[id]);
+ std::memcpy(buffer->data, &sizes[id], sizeof(uint64_t));
+ });
+ break;
+ }
+ case RPC_READ_FGETS: {
+ uint64_t sizes[lane_size] = {0};
+ void *data[lane_size] = {nullptr};
+ port->recv([&](rpc::Buffer *buffer, uint32_t id) {
+ data[id] = new char[buffer->data[0]];
+ const char *str =
+ fgets(reinterpret_cast<char *>(data[id]), buffer->data[0],
+ file::to_stream(buffer->data[1]));
+ sizes[id] = !str ? 0 : std::strlen(str) + 1;
+ });
+ port->send_n(data, sizes);
+ for (uint32_t id = 0; id < lane_size; ++id)
+ if (data[id])
+ delete[] reinterpret_cast<uint8_t *>(data[id]);
+ break;
+ }
+ case RPC_OPEN_FILE: {
+ uint64_t sizes[lane_size] = {0};
+ void *paths[lane_size] = {nullptr};
+ port->recv_n(paths, sizes, [&](uint64_t size) { return new char[size]; });
+ port->recv_and_send([&](rpc::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 RPC_CLOSE_FILE: {
+ port->recv_and_send([&](rpc::Buffer *buffer, uint32_t id) {
+ FILE *file = reinterpret_cast<FILE *>(buffer->data[0]);
+ buffer->data[0] = fclose(file);
+ });
+ break;
+ }
+ case RPC_EXIT: {
+ // Send a response to the client to signal that we are ready to exit.
+ port->recv_and_send([](rpc::Buffer *) {});
+ port->recv([](rpc::Buffer *buffer) {
+ int status = 0;
+ std::memcpy(&status, buffer->data, sizeof(int));
+ exit(status);
+ });
+ break;
+ }
+ case RPC_ABORT: {
+ // Send a response to the client to signal that we are ready to abort.
+ port->recv_and_send([](rpc::Buffer *) {});
+ port->recv([](rpc::Buffer *) {});
+ abort();
+ break;
+ }
+ case RPC_HOST_CALL: {
+ uint64_t sizes[lane_size] = {0};
+ void *args[lane_size] = {nullptr};
+ port->recv_n(args, sizes, [&](uint64_t size) { return new char[size]; });
+ port->recv([&](rpc::Buffer *buffer, uint32_t id) {
+ reinterpret_cast<void (*)(void *)>(buffer->data[0])(args[id]);
+ });
+ port->send([&](rpc::Buffer *, uint32_t id) {
+ delete[] reinterpret_cast<uint8_t *>(args[id]);
+ });
+ break;
+ }
+ case RPC_FEOF: {
+ port->recv_and_send([](rpc::Buffer *buffer) {
+ buffer->data[0] = feof(file::to_stream(buffer->data[0]));
+ });
+ break;
+ }
+ case RPC_FERROR: {
+ port->recv_and_send([](rpc::Buffer *buffer) {
+ buffer->data[0] = ferror(file::to_stream(buffer->data[0]));
+ });
+ break;
+ }
+ case RPC_CLEARERR: {
+ port->recv_and_send([](rpc::Buffer *buffer) {
+ clearerr(file::to_stream(buffer->data[0]));
+ });
+ break;
+ }
+ case RPC_FSEEK: {
+ port->recv_and_send([](rpc::Buffer *buffer) {
+ buffer->data[0] = fseek(file::to_stream(buffer->data[0]),
+ static_cast<long>(buffer->data[1]),
+ static_cast<int>(buffer->data[2]));
+ });
+ break;
+ }
+ case RPC_FTELL: {
+ port->recv_and_send([](rpc::Buffer *buffer) {
+ buffer->data[0] = ftell(file::to_stream(buffer->data[0]));
+ });
+ break;
+ }
+ case RPC_FFLUSH: {
+ port->recv_and_send([](rpc::Buffer *buffer) {
+ buffer->data[0] = fflush(file::to_stream(buffer->data[0]));
+ });
+ break;
+ }
+ case RPC_UNGETC: {
+ port->recv_and_send([](rpc::Buffer *buffer) {
+ buffer->data[0] = ungetc(static_cast<int>(buffer->data[0]),
+ file::to_stream(buffer->data[1]));
+ });
+ break;
+ }
+ case RPC_NOOP: {
+ port->recv([](rpc::Buffer *) {});
+ break;
+ }
+ default: {
+ auto handler =
+ callbacks.find(static_cast<rpc_opcode_t>(port->get_opcode()));
+
+ // We error out on an unhandled opcode.
+ if (handler == callbacks.end())
+ return RPC_STATUS_UNHANDLED_OPCODE;
+
+ // Invoke the registered callback with a reference to the port.
+ void *data =
+ callback_data.at(static_cast<rpc_opcode_t>(port->get_opcode()));
+ rpc_port_t port_ref{reinterpret_cast<uint64_t>(&*port), lane_size};
+ (handler->second)(port_ref, data);
+ }
}
- std::variant<std::unique_ptr<rpc::Server<1>>,
- std::unique_ptr<rpc::Server<32>>,
- std::unique_ptr<rpc::Server<64>>>
- server;
-};
+ // Increment the index so we start the scan after this port.
+ index = port->get_index() + 1;
+ port->close();
+
+ return RPC_STATUS_CONTINUE;
+}
struct Device {
- template <typename T>
- Device(uint32_t num_ports, void *buffer, std::unique_ptr<T> &&server)
- : buffer(buffer), server(std::move(server)), client(num_ports, buffer) {}
+ Device(uint32_t lane_size, uint32_t num_ports, void *buffer)
+ : lane_size(lane_size), buffer(buffer), server(num_ports, buffer),
+ client(num_ports, buffer) {}
+...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/84557
More information about the cfe-commits
mailing list