Skip to content

Commit 334bbc0

Browse files
committed
[libc] Add support for the 'fread' function on the GPU
This patch adds support for `fread` on the GPU via the RPC mechanism. Here we simply pass the size of the read to the server and then copy it back to the client via the RPC channel. This should allow us to do the basic operations on files now. This will obviously be slow for large sizes due ot the number of RPC calls involved, this could be optimized further by having a special RPC call that can initiate a memcpy between the two pointers. Reviewed By: jdoerfert Differential Revision: https://reviews.llvm.org/D155121
1 parent 91eb99b commit 334bbc0

File tree

6 files changed

+85
-8
lines changed

6 files changed

+85
-8
lines changed

libc/config/gpu/entrypoints.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ set(TARGET_LIBC_ENTRYPOINTS
8383
# stdio.h entrypoints
8484
libc.src.stdio.puts
8585
libc.src.stdio.fputs
86+
libc.src.stdio.fread
8687
libc.src.stdio.fclose
8788
libc.src.stdio.fopen
8889
libc.src.stdio.stdin

libc/include/llvm-libc-types/rpc_opcodes_t.h

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@ typedef enum : unsigned short {
1515
RPC_WRITE_TO_STDOUT = 2,
1616
RPC_WRITE_TO_STDERR = 3,
1717
RPC_WRITE_TO_STREAM = 4,
18-
RPC_OPEN_FILE = 5,
19-
RPC_CLOSE_FILE = 6,
20-
RPC_MALLOC = 7,
21-
RPC_FREE = 8,
22-
RPC_HOST_CALL = 9,
18+
RPC_READ_FROM_STDIN = 5,
19+
RPC_READ_FROM_STREAM = 6,
20+
RPC_OPEN_FILE = 7,
21+
RPC_CLOSE_FILE = 8,
22+
RPC_MALLOC = 9,
23+
RPC_FREE = 10,
24+
RPC_HOST_CALL = 11,
2325
} rpc_opcode_t;
2426

2527
#endif // __LLVM_LIBC_TYPES_RPC_OPCODE_H__

libc/src/__support/File/gpu/file.cpp

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace __llvm_libc {
1919
namespace {
2020

2121
FileIOResult write_func(File *, const void *, size_t);
22+
FileIOResult read_func(File *, void *, size_t);
2223
int close_func(File *);
2324

2425
} // namespace
@@ -28,7 +29,7 @@ class GPUFile : public File {
2829

2930
public:
3031
constexpr GPUFile(uintptr_t file, File::ModeFlags modeflags)
31-
: File(&write_func, nullptr, nullptr, &close_func, nullptr, 0, _IONBF,
32+
: File(&write_func, &read_func, nullptr, &close_func, nullptr, 0, _IONBF,
3233
false, modeflags),
3334
file(file) {}
3435

@@ -87,6 +88,46 @@ FileIOResult write_func(File *f, const void *data, size_t size) {
8788
return ret;
8889
}
8990

91+
int read_from_stdin(void *buf, size_t size) {
92+
int ret = 0;
93+
uint64_t recv_size;
94+
rpc::Client::Port port = rpc::client.open<RPC_READ_FROM_STDIN>();
95+
port.send([=](rpc::Buffer *buffer) { buffer->data[0] = size; });
96+
port.recv_n(&buf, &recv_size, [&](uint64_t) { return buf; });
97+
port.recv([&](rpc::Buffer *buffer) { ret = buffer->data[0]; });
98+
port.close();
99+
return ret;
100+
}
101+
102+
int read_from_stream(uintptr_t file, void *buf, size_t size) {
103+
int ret = 0;
104+
uint64_t recv_size;
105+
// TODO: For large sizes being written to a pointer in global memory, we
106+
// should be able to initiate a H2D memcpy via a separate RPC call at high
107+
// bandwidth.
108+
rpc::Client::Port port = rpc::client.open<RPC_READ_FROM_STREAM>();
109+
port.send([=](rpc::Buffer *buffer) {
110+
buffer->data[0] = size;
111+
buffer->data[1] = file;
112+
});
113+
port.recv_n(&buf, &recv_size, [&](uint64_t) { return buf; });
114+
port.recv([&](rpc::Buffer *buffer) { ret = buffer->data[0]; });
115+
port.close();
116+
return ret;
117+
}
118+
119+
FileIOResult read_func(File *f, void *buf, size_t size) {
120+
auto *gpu_file = reinterpret_cast<GPUFile *>(f);
121+
int ret = 0;
122+
if (gpu_file == stdin)
123+
ret = read_from_stdin(buf, size);
124+
else
125+
ret = read_from_stream(gpu_file->get_file(), buf, size);
126+
if (ret < 0)
127+
return {0, -ret};
128+
return ret;
129+
}
130+
90131
int close_func(File *file) {
91132
int ret = 0;
92133
GPUFile *gpu_file = reinterpret_cast<GPUFile *>(file);

libc/test/src/stdio/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,7 @@ add_libc_test(
227227
SRCS
228228
fopen_test.cpp
229229
DEPENDS
230+
libc.src.stdio.fread
230231
libc.src.stdio.fputs
231232
libc.src.stdio.fclose
232233
libc.src.stdio.fopen

libc/test/src/stdio/fopen_test.cpp

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "src/stdio/fclose.h"
1111
#include "src/stdio/fopen.h"
1212
#include "src/stdio/fputs.h"
13+
#include "src/stdio/fread.h"
1314

1415
#include "test/UnitTest/Test.h"
1516

@@ -19,9 +20,20 @@ TEST(LlvmLibcFOpenTest, PrintToFile) {
1920
FILE *file = __llvm_libc::fopen("./testdata/test_data.txt", "w");
2021
ASSERT_FALSE(file == nullptr);
2122

22-
constexpr char another[] = "A simple string written to a file\n";
23-
result = __llvm_libc::fputs(another, file);
23+
static constexpr char STRING[] = "A simple string written to a file\n";
24+
result = __llvm_libc::fputs(STRING, file);
2425
EXPECT_GE(result, 0);
2526

2627
ASSERT_EQ(0, __llvm_libc::fclose(file));
28+
29+
FILE *new_file = __llvm_libc::fopen("./testdata/test_data.txt", "r");
30+
ASSERT_FALSE(new_file == nullptr);
31+
32+
static char data[64] = {0};
33+
ASSERT_EQ(__llvm_libc::fread(data, 1, sizeof(STRING) - 1, new_file),
34+
sizeof(STRING) - 1);
35+
data[sizeof(STRING) - 1] = '\0';
36+
ASSERT_STREQ(data, STRING);
37+
38+
ASSERT_EQ(0, __llvm_libc::fclose(new_file));
2739
}

libc/utils/gpu/server/rpc_server.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,26 @@ struct Server {
103103
}
104104
break;
105105
}
106+
case RPC_READ_FROM_STREAM:
107+
case RPC_READ_FROM_STDIN: {
108+
uint64_t sizes[rpc::MAX_LANE_SIZE] = {0};
109+
void *data[rpc::MAX_LANE_SIZE] = {nullptr};
110+
uint64_t rets[rpc::MAX_LANE_SIZE] = {0};
111+
port->recv([&](rpc::Buffer *buffer, uint32_t id) {
112+
sizes[id] = buffer->data[0];
113+
data[id] = new char[sizes[id]];
114+
FILE *file = port->get_opcode() == RPC_READ_FROM_STREAM
115+
? reinterpret_cast<FILE *>(buffer->data[1])
116+
: stdin;
117+
rets[id] = fread(data[id], 1, sizes[id], file);
118+
});
119+
port->send_n(data, sizes);
120+
port->send([&](rpc::Buffer *buffer, uint32_t id) {
121+
delete[] reinterpret_cast<uint8_t *>(data[id]);
122+
std::memcpy(buffer->data, &rets[id], sizeof(uint64_t));
123+
});
124+
break;
125+
}
106126
case RPC_OPEN_FILE: {
107127
uint64_t sizes[rpc::MAX_LANE_SIZE] = {0};
108128
void *paths[rpc::MAX_LANE_SIZE] = {nullptr};

0 commit comments

Comments
 (0)