|
| 1 | +// REQUIRES: system-linux || system-windows |
| 2 | + |
| 3 | +// End-to-end clang-offload-wrapper executable test: check that -batch options |
| 4 | +// works, and that the tool generates data properly accessible at runtime. |
| 5 | + |
| 6 | +// --- Prepare test data |
| 7 | +// - create the first binary image |
| 8 | +// RUN: echo -e -n 'device binary image1\n' > %t.bin |
| 9 | +// RUN: echo -e -n '[Category1]\nint_prop1=1|10\n[Category2]\nint_prop2=1|20\n' > %t.props |
| 10 | +// RUN: echo -e -n 'kernel1\nkernel2\n' > %t.sym |
| 11 | + |
| 12 | +// - create the second binary image with byte array property values |
| 13 | +// RUN: echo -e -n 'device binary image2\n' > %t_1.bin |
| 14 | +// RUN: echo -e -n '[Category3]\n' > %t_1.props |
| 15 | +// RUN: echo -e -n 'kernel1=2|IAAAAAAAAAQA\n' >> %t_1.props |
| 16 | +// RUN: echo -e -n 'kernel2=2|oAAAAAAAAAw///3/wB\n' >> %t_1.props |
| 17 | + |
| 18 | +// - create the batch file input for the wrapper |
| 19 | +// RUN: echo '[Code|Properties|Symbols]' > %t.batch |
| 20 | +// RUN: echo %t.bin"|"%t.props"|"%t.sym >> %t.batch |
| 21 | +// RUN: echo %t_1.bin"|"%t_1.props"|" >> %t.batch |
| 22 | +// --- Generate "gold" output |
| 23 | +// RUN: cat %t.bin %t.props %t.sym > %t.all |
| 24 | +// --- Create the wrapper object |
| 25 | +// -host omitted - generate object for the host triple: |
| 26 | +// RUN: clang-offload-wrapper -kind=sycl -target=TARGET -format=native -batch %t.batch -o %t.wrapped.bc -fpreview-breaking-changes |
| 27 | +// RUN: llc --filetype=obj %t.wrapped.bc -o %t.wrapped.o |
| 28 | +// --- Compile & link the test with the wrapper |
| 29 | +// RUN: %clangxx %t.wrapped.o %s -o %t.batch.exe -v -fpreview-breaking-changes |
| 30 | +// --- Run and check ignoring white spaces |
| 31 | +// RUN: %t.batch.exe > %t.batch.exe.out |
| 32 | +// RUN: diff -b %t.batch.exe.out %t.all |
| 33 | + |
| 34 | +#include <cassert> |
| 35 | +#include <cstdint> |
| 36 | +#include <cstring> |
| 37 | +#include <iostream> |
| 38 | +#include <string> |
| 39 | + |
| 40 | +// Data types created by the offload wrapper and inserted in the wrapper object. |
| 41 | +// Match those defined in SYCL runtime Plugin Interface. |
| 42 | +struct _pi_offload_entry_struct { |
| 43 | + void *addr; |
| 44 | + char *name; |
| 45 | + size_t size; |
| 46 | + int32_t flags; |
| 47 | + int32_t reserved; |
| 48 | +}; |
| 49 | + |
| 50 | +typedef _pi_offload_entry_struct *_pi_offload_entry; |
| 51 | + |
| 52 | +struct _pi_device_binary_property_struct { |
| 53 | + char *Name; // null-terminated property name |
| 54 | + void *ValAddr; // address of property value |
| 55 | + uint32_t Type; // pi_property_type |
| 56 | + uint64_t ValSize; // size of property value in bytes |
| 57 | +}; |
| 58 | + |
| 59 | +typedef _pi_device_binary_property_struct *pi_device_binary_property; |
| 60 | + |
| 61 | +struct _pi_device_binary_property_set_struct { |
| 62 | + char *Name; // the name |
| 63 | + pi_device_binary_property PropertiesBegin; // array start |
| 64 | + pi_device_binary_property PropertiesEnd; // array end |
| 65 | +}; |
| 66 | + |
| 67 | +typedef _pi_device_binary_property_set_struct *pi_device_binary_property_set; |
| 68 | + |
| 69 | +struct pi_device_binary_struct { |
| 70 | + uint16_t Version; |
| 71 | + uint8_t Kind; // 4 for SYCL |
| 72 | + uint8_t Format; // 1 for native |
| 73 | + const char *DeviceTargetSpec; |
| 74 | + const char *CompileOptions; |
| 75 | + const char *LinkOptions; |
| 76 | + const unsigned char *BinaryStart; |
| 77 | + const unsigned char *BinaryEnd; |
| 78 | + _pi_offload_entry EntriesBegin; |
| 79 | + _pi_offload_entry EntriesEnd; |
| 80 | + pi_device_binary_property_set PropertySetsBegin; |
| 81 | + pi_device_binary_property_set PropertySetsEnd; |
| 82 | +}; |
| 83 | +typedef pi_device_binary_struct *pi_device_binary; |
| 84 | + |
| 85 | +struct pi_device_binaries_struct { |
| 86 | + uint16_t Version; |
| 87 | + uint16_t NumDeviceBinaries; |
| 88 | + pi_device_binary DeviceBinaries; |
| 89 | + _pi_offload_entry *HostEntriesBegin; |
| 90 | + _pi_offload_entry *HostEntriesEnd; |
| 91 | +}; |
| 92 | +typedef pi_device_binaries_struct *pi_device_binaries; |
| 93 | + |
| 94 | +static pi_device_binaries BinDesc = nullptr; |
| 95 | + |
| 96 | +// Wrapper object has code which calls these 2 functions below |
| 97 | +extern "C" void __sycl_register_lib(pi_device_binaries desc) { |
| 98 | + BinDesc = desc; |
| 99 | +} |
| 100 | + |
| 101 | +extern "C" void __sycl_unregister_lib() {} |
| 102 | + |
| 103 | +#define ASSERT(Cond, Msg) \ |
| 104 | + if (!(Cond)) { \ |
| 105 | + std::cerr << "*** ERROR: wrong " << Msg << "\n"; \ |
| 106 | + return 1; \ |
| 107 | + } |
| 108 | + |
| 109 | +static std::string getString(const unsigned char *B, const unsigned char *E) { |
| 110 | + return std::string(reinterpret_cast<const char *>(B), E - B); |
| 111 | +} |
| 112 | + |
| 113 | +static int getInt(void *Addr) { |
| 114 | + const char *Ptr = reinterpret_cast<const char *>(Addr); |
| 115 | + return Ptr[0] | (Ptr[1] << 8) | (Ptr[2] << 16) | (Ptr[3] << 24); |
| 116 | +} |
| 117 | + |
| 118 | +using byte = unsigned char; |
| 119 | + |
| 120 | +static void printProp(const pi_device_binary_property &Prop) { |
| 121 | + std::cerr << "Property " << Prop->Name << " {\n"; |
| 122 | + std::cerr << " Type: " << Prop->Type << "\n"; |
| 123 | + if (Prop->Type != 1) |
| 124 | + std::cerr << " Size = " << Prop->ValSize << "\n"; |
| 125 | + |
| 126 | + std::cerr << " Value = "; |
| 127 | + if (Prop->Type == 1) |
| 128 | + std::cerr << getInt(&Prop->ValSize); |
| 129 | + else { |
| 130 | + std::cerr << " {\n "; |
| 131 | + |
| 132 | + byte *Ptr = (byte *)Prop->ValAddr; |
| 133 | + |
| 134 | + for (auto I = 0; I < Prop->ValSize && I < 100; ++I) { |
| 135 | + std::cerr << " 0x" << std::hex << (unsigned int)Ptr[I]; |
| 136 | + std::cerr << std::dec; |
| 137 | + } |
| 138 | + std::cerr << "\n }"; |
| 139 | + } |
| 140 | + std::cerr << "\n"; |
| 141 | + std::cerr << "}\n"; |
| 142 | +} |
| 143 | + |
| 144 | +static int dumpBinary0() { |
| 145 | + pi_device_binary Bin = &BinDesc->DeviceBinaries[0]; |
| 146 | + ASSERT(Bin->Kind == 4, "Bin->Kind"); |
| 147 | + ASSERT(Bin->Format == 1, "Bin->Format"); |
| 148 | + |
| 149 | + // dump code |
| 150 | + std::cout << getString(Bin->BinaryStart, Bin->BinaryEnd); |
| 151 | + // dump properties |
| 152 | + for (pi_device_binary_property_set PropSet = Bin->PropertySetsBegin; PropSet != Bin->PropertySetsEnd; ++PropSet) { |
| 153 | + std::cout << "[" << PropSet->Name << "]" |
| 154 | + << "\n"; |
| 155 | + |
| 156 | + for (pi_device_binary_property Prop = PropSet->PropertiesBegin; Prop != PropSet->PropertiesEnd; ++Prop) { |
| 157 | + ASSERT(Prop->Type == 1, "Prop->Type"); |
| 158 | + std::cout << Prop->Name << "=" << Prop->Type << "|" << getInt(&Prop->ValSize) << "\n"; |
| 159 | + } |
| 160 | + } |
| 161 | + // dump symbols |
| 162 | + for (_pi_offload_entry Entry = Bin->EntriesBegin; Entry != Bin->EntriesEnd; ++Entry) |
| 163 | + std::cout << Entry->name << "\n"; |
| 164 | + return 0; |
| 165 | +} |
| 166 | + |
| 167 | +// Clang offload wrapper does Base64 decoding on byte array property values, so |
| 168 | +// they can't be dumped as is and compared to the original. Instead, this |
| 169 | +// testcase checks that the byte array in the property value is equal to the |
| 170 | +// pre-decoded byte array. |
| 171 | +static int checkBinary1() { |
| 172 | + // Decoded from "IAAAAAAAAAQA": |
| 173 | + const byte Arr0[] = {8, 0, 0, 0, 0, 0, 0, 0, 0x1}; |
| 174 | + // Decoded from "oAAAAAAAAAw///3/wB": |
| 175 | + const byte Arr1[] = {40, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0x7F, 0xFF, 0x70}; |
| 176 | + |
| 177 | + struct { |
| 178 | + const byte *Ptr; |
| 179 | + const size_t Size; |
| 180 | + } GoldArrays[] = { |
| 181 | + {Arr0, sizeof(Arr0)}, |
| 182 | + {Arr1, sizeof(Arr1)}}; |
| 183 | + pi_device_binary Bin = &BinDesc->DeviceBinaries[1]; |
| 184 | + ASSERT(Bin->Kind == 4, "Bin->Kind"); |
| 185 | + ASSERT(Bin->Format == 1, "Bin->Format"); |
| 186 | + |
| 187 | + for (pi_device_binary_property_set PropSet = Bin->PropertySetsBegin; PropSet != Bin->PropertySetsEnd; ++PropSet) { |
| 188 | + int Cnt = 0; |
| 189 | + |
| 190 | + for (pi_device_binary_property Prop = PropSet->PropertiesBegin; Prop != PropSet->PropertiesEnd; ++Prop, ++Cnt) { |
| 191 | + ASSERT(Prop->Type == 2, "Prop->Type"); // must be a byte array |
| 192 | + char *Ptr = reinterpret_cast<char *>(Prop->ValAddr); |
| 193 | + int Cmp = std::memcmp(Prop->ValAddr, GoldArrays[Cnt].Ptr, GoldArrays[Cnt].Size); |
| 194 | + ASSERT(Cmp == 0, "byte array property"); |
| 195 | + } |
| 196 | + } |
| 197 | + return 0; |
| 198 | +} |
| 199 | + |
| 200 | +int main(int argc, char **argv) { |
| 201 | + ASSERT(BinDesc->NumDeviceBinaries == 2, "BinDesc->NumDeviceBinaries"); |
| 202 | + ASSERT(BinDesc->Version == 3, "BinDesc->Version"); |
| 203 | + |
| 204 | + if (dumpBinary0() != 0) |
| 205 | + return 1; |
| 206 | + if (checkBinary1() != 0) |
| 207 | + return 1; |
| 208 | + return 0; |
| 209 | +} |
0 commit comments