| // RUN: %libomptarget-compilexx-generic -fopenmp-cuda-mode | |
| // RUN: %libomptarget-run-generic | |
| // REQUIRES: libc | |
| // REQUIRES: gpu | |
| #include <assert.h> | |
| #include <omp.h> | |
| #include <stdint.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| // CHECK: PASS | |
| // If the RPC headers are not present, just pass the test. | |
| #if !__has_include(<../../libc/shared/rpc.h>) | |
| int main() { printf("PASS\n"); } | |
| #else | |
| #include <../../libc/shared/rpc.h> | |
| #include <../../libc/shared/rpc_dispatch.h> | |
| [[gnu::weak]] rpc::Client client asm("__llvm_rpc_client"); | |
| #pragma omp declare target to(client) device_type(nohost) | |
| //===------------------------------------------------------------------------=== | |
| // Opcodes. | |
| //===------------------------------------------------------------------------=== | |
| constexpr uint32_t FOO_OPCODE = 1; | |
| constexpr uint32_t VOID_OPCODE = 2; | |
| constexpr uint32_t WRITEBACK_OPCODE = 3; | |
| constexpr uint32_t CONST_PTR_OPCODE = 4; | |
| constexpr uint32_t STRING_OPCODE = 5; | |
| constexpr uint32_t EMPTY_OPCODE = 6; | |
| constexpr uint32_t DIVERGENT_OPCODE = 7; | |
| constexpr uint32_t ARRAY_SUM_OPCODE = 8; | |
| //===------------------------------------------------------------------------=== | |
| // Server-side implementations. | |
| //===------------------------------------------------------------------------=== | |
| struct S { | |
| int arr[4]; | |
| }; | |
| // 1. Non-pointer arguments, non-void return. | |
| int foo(int x, double d, char c) { | |
| assert(x == 42); | |
| assert(d == 0.0); | |
| assert(c == 'c'); | |
| return -1; | |
| } | |
| // 2. Void return type. | |
| void void_fn(int x) { assert(x == 7); } | |
| // 3. Write-back pointer. | |
| void writeback_fn(int *out) { | |
| assert(out != nullptr && *out == 42); | |
| *out = 99; | |
| } | |
| // 4. Const pointer. | |
| int sum_const(const S *p) { | |
| int s = 0; | |
| for (int i = 0; i < 4; ++i) | |
| s += p->arr[i]; | |
| return s; | |
| } | |
| // 5. const char * string. | |
| int c_string(const char *s) { | |
| assert(s != nullptr); | |
| assert(strcmp(s, "hello") == 0); | |
| return strlen(s); | |
| } | |
| // 6. Empty function. | |
| int empty() { return 42; } | |
| // 7. Divergent values. | |
| void divergent(int *p) { | |
| assert(p); | |
| *p = *p; | |
| } | |
| // 8. Array argument via span. | |
| int sum_array(const int *arr, int n) { | |
| int s = 0; | |
| for (int i = 0; i < n; ++i) | |
| s += arr[i]; | |
| return s; | |
| } | |
| //===------------------------------------------------------------------------=== | |
| // RPC client dispatch. | |
| //===------------------------------------------------------------------------=== | |
| #pragma omp begin declare variant match(device = {kind(gpu)}) | |
| int foo(int x, double d, char c) { | |
| return rpc::dispatch<FOO_OPCODE, foo>(client, x, d, c); | |
| } | |
| void void_fn(int x) { rpc::dispatch<VOID_OPCODE, void_fn>(client, x); } | |
| void writeback_fn(int *out) { | |
| rpc::dispatch<WRITEBACK_OPCODE, writeback_fn>(client, out); | |
| } | |
| int sum_const(const S *p) { | |
| return rpc::dispatch<CONST_PTR_OPCODE, sum_const>(client, p); | |
| } | |
| int c_string(const char *s) { | |
| return rpc::dispatch<STRING_OPCODE, c_string>(client, s); | |
| } | |
| int empty() { return rpc::dispatch<EMPTY_OPCODE, empty>(client); } | |
| void divergent(int *p) { | |
| rpc::dispatch<DIVERGENT_OPCODE, divergent>(client, p); | |
| } | |
| int sum_array(const int *arr, int n) { | |
| return rpc::dispatch<ARRAY_SUM_OPCODE, sum_array>( | |
| client, rpc::span<const int>{arr, uint64_t(n)}, n); | |
| } | |
| #pragma omp end declare variant | |
| //===------------------------------------------------------------------------=== | |
| // RPC server dispatch. | |
| //===------------------------------------------------------------------------=== | |
| template <uint32_t NUM_LANES> | |
| rpc::RPCStatus handleOpcodesImpl(rpc::Server::Port &Port) { | |
| switch (Port.get_opcode()) { | |
| case FOO_OPCODE: | |
| rpc::invoke<NUM_LANES>(Port, foo); | |
| break; | |
| case VOID_OPCODE: | |
| rpc::invoke<NUM_LANES>(Port, void_fn); | |
| break; | |
| case WRITEBACK_OPCODE: | |
| rpc::invoke<NUM_LANES>(Port, writeback_fn); | |
| break; | |
| case CONST_PTR_OPCODE: | |
| rpc::invoke<NUM_LANES>(Port, sum_const); | |
| break; | |
| case STRING_OPCODE: | |
| rpc::invoke<NUM_LANES>(Port, c_string); | |
| break; | |
| case EMPTY_OPCODE: | |
| rpc::invoke<NUM_LANES>(Port, empty); | |
| break; | |
| case DIVERGENT_OPCODE: | |
| rpc::invoke<NUM_LANES>(Port, [](int *p) { | |
| assert(p); | |
| *p = *p; | |
| }); | |
| break; | |
| case ARRAY_SUM_OPCODE: | |
| rpc::invoke<NUM_LANES>(Port, sum_array); | |
| break; | |
| default: | |
| return rpc::RPC_UNHANDLED_OPCODE; | |
| } | |
| return rpc::RPC_SUCCESS; | |
| } | |
| static uint32_t handleOpcodes(void *raw, uint32_t numLanes) { | |
| rpc::Server::Port &Port = *reinterpret_cast<rpc::Server::Port *>(raw); | |
| if (numLanes == 1) | |
| return handleOpcodesImpl<1>(Port); | |
| else if (numLanes == 32) | |
| return handleOpcodesImpl<32>(Port); | |
| else if (numLanes == 64) | |
| return handleOpcodesImpl<64>(Port); | |
| else | |
| return rpc::RPC_ERROR; | |
| } | |
| extern "C" void __tgt_register_rpc_callback(unsigned (*callback)(void *, | |
| unsigned)); | |
| [[gnu::constructor]] void register_callback() { | |
| __tgt_register_rpc_callback(&handleOpcodes); | |
| } | |
| int main() { | |
| #pragma omp target | |
| #pragma omp parallel num_threads(32) | |
| { | |
| // 1. Non-pointer return. | |
| assert(foo(42, 0.0, 'c') == -1); | |
| // 2. Void return. | |
| void_fn(7); | |
| // 3. Write-back pointer. | |
| int value = 42; | |
| writeback_fn(&value); | |
| assert(value == 99); | |
| // 4. Const pointer. | |
| S s{1, 2, 3, 4}; | |
| int sum = sum_const(&s); | |
| assert(sum == 10); | |
| // 5. const char * string. | |
| const char *msg = "hello"; | |
| int len = c_string(msg); | |
| assert(len == 5); | |
| // 6. No arguments. | |
| int ret = empty(); | |
| assert(ret == 42); | |
| // 7. Divergent values. | |
| int id = omp_get_thread_num(); | |
| if (id % 2) | |
| divergent(&id); | |
| assert(id == omp_get_thread_num()); | |
| // 8. Array sum via span. | |
| int arr[4] = {1, 2, 3, 4}; | |
| int total = sum_array(arr, 4); | |
| assert(total == 10); | |
| } | |
| printf("PASS\n"); | |
| } | |
| #endif |