Skip to content

[SYCL] Propagate kernel parameter optimization info to SYCL runtime. #2303

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 103 additions & 25 deletions clang/test/Driver/clang-offload-wrapper-exe.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,22 @@
// works, and that the tool generates data properly accessible at runtime.

// --- Prepare test data
// RUN: echo -e -n 'device binary image\n' > %t.bin
// - create the first binary image
// RUN: echo -e -n 'device binary image1\n' > %t.bin
// RUN: echo -e -n '[Category1]\nint_prop1=1|10\n[Category2]\nint_prop2=1|20\n' > %t.props
// RUN: echo -e -n 'kernel1\nkernel2\n' > %t.sym
// RUN: echo -e -n 'Manifest file - arbitrary data generated by the toolchain\n' > %t.mnf

// - create the second binary image with byte array property values
// RUN: echo -e -n 'device binary image2\n' > %t_1.bin
// RUN: echo -e -n '[Category3]\n' > %t_1.props
// RUN: echo -e -n 'kernel1=2|IAAAAAAAAAQA\n' >> %t_1.props
// RUN: echo -e -n 'kernel2=2|oAAAAAAAAAw///3/wB\n' >> %t_1.props

// - create the batch file input for the wrapper
// RUN: echo '[Code|Properties|Symbols|Manifest]' > %t.batch
// RUN: echo %t.bin"|"%t.props"|"%t.sym"|"%t.mnf >> %t.batch
// RUN: echo %t_1.bin"|"%t_1.props"|""|" >> %t.batch
// --- Generate "gold" output
// RUN: cat %t.bin %t.mnf %t.props %t.sym > %t.all
// --- Create the wrapper object
Expand All @@ -22,6 +32,8 @@
// RUN: %t.batch.exe > %t.batch.exe.out
// RUN: diff -b %t.batch.exe.out %t.all

#include <assert.h>
#include <cstring>
#include <iostream>
#include <string>

Expand Down Expand Up @@ -105,31 +117,97 @@ static int getInt(void *Addr) {
return Ptr[0] | (Ptr[1] << 8) | (Ptr[2] << 16) | (Ptr[3] << 24);
}

int main(int argc, char **argv) {
ASSERT(BinDesc->NumDeviceBinaries == 1, "BinDesc->NumDeviceBinaries");
ASSERT(BinDesc->Version == 1, "BinDesc->Version");
using byte = unsigned char;

static void printProp(const pi_device_binary_property &Prop) {
std::cerr << "Property " << Prop->Name << " {\n";
std::cerr << " Type: " << Prop->Type << "\n";
if (Prop->Type != 1)
std::cerr << " Size = " << Prop->ValSize << "\n";

std::cerr << " Value = ";
if (Prop->Type == 1)
std::cerr << getInt(&Prop->ValSize);
else {
std::cerr << " {\n ";

for (int I = 0; I < BinDesc->NumDeviceBinaries; ++I) {
pi_device_binary Bin = &BinDesc->DeviceBinaries[I];
ASSERT(Bin->Kind == 4, "Bin->Kind");
ASSERT(Bin->Format == 1, "Bin->Format");

// dump code
std::cout << getString(Bin->BinaryStart, Bin->BinaryEnd);
// dump manifest
std::cout << std::string(Bin->ManifestStart, Bin->ManifestEnd - Bin->ManifestStart);
// dump properties
for (pi_device_binary_property_set PropSet = Bin->PropertySetsBegin; PropSet != Bin->PropertySetsEnd; ++PropSet) {
std::cout << "[" << PropSet->Name << "]"
<< "\n";

for (pi_device_binary_property Prop = PropSet->PropertiesBegin; Prop != PropSet->PropertiesEnd; ++Prop)
// values fitting into 64 bits are written into the 'ValAddr' field:
std::cout << Prop->Name << "=" << Prop->Type << "|" << getInt(&Prop->ValSize) << "\n";
byte *Ptr = (byte *)Prop->ValAddr;

for (auto I = 0; I < Prop->ValSize && I < 100; ++I) {
std::cerr << " 0x" << std::hex << (unsigned int)Ptr[I];
std::cerr << std::dec;
}
std::cerr << "\n }";
}
std::cerr << "\n";
std::cerr << "}\n";
}

static int dumpBinary0() {
pi_device_binary Bin = &BinDesc->DeviceBinaries[0];
ASSERT(Bin->Kind == 4, "Bin->Kind");
ASSERT(Bin->Format == 1, "Bin->Format");

// dump code
std::cout << getString(Bin->BinaryStart, Bin->BinaryEnd);
// dump manifest
std::cout << std::string(Bin->ManifestStart, Bin->ManifestEnd - Bin->ManifestStart);
// dump properties
for (pi_device_binary_property_set PropSet = Bin->PropertySetsBegin; PropSet != Bin->PropertySetsEnd; ++PropSet) {
std::cout << "[" << PropSet->Name << "]"
<< "\n";

for (pi_device_binary_property Prop = PropSet->PropertiesBegin; Prop != PropSet->PropertiesEnd; ++Prop) {
ASSERT(Prop->Type == 1, "Prop->Type");
std::cout << Prop->Name << "=" << Prop->Type << "|" << getInt(&Prop->ValSize) << "\n";
}
// dump symbols
for (_pi_offload_entry Entry = Bin->EntriesBegin; Entry != Bin->EntriesEnd; ++Entry)
std::cout << Entry->name << "\n";
}
// dump symbols
for (_pi_offload_entry Entry = Bin->EntriesBegin; Entry != Bin->EntriesEnd; ++Entry)
std::cout << Entry->name << "\n";
return 0;
}
}

// Clang offload wrapper does Base64 decoding on byte array property values, so
// they can't be dumped as is and compared to the original. Instead, this
// testcase checks that the byte array in the property value is equal to the
// pre-decoded byte array.
static int checkBinary1() {
// Decoded from "IAAAAAAAAAQA":
const byte Arr0[] = {8, 0, 0, 0, 0, 0, 0, 0, 0x1};
// Decoded from "oAAAAAAAAAw///3/wB":
const byte Arr1[] = {40, 0, 0, 0, 0, 0, 0, 0, 0xFF, 0xFF, 0x7F, 0xFF, 0x70};

struct {
const byte *Ptr;
const size_t Size;
} GoldArrays[] = {
{Arr0, sizeof(Arr0)},
{Arr1, sizeof(Arr1)}};
pi_device_binary Bin = &BinDesc->DeviceBinaries[1];
ASSERT(Bin->Kind == 4, "Bin->Kind");
ASSERT(Bin->Format == 1, "Bin->Format");

for (pi_device_binary_property_set PropSet = Bin->PropertySetsBegin; PropSet != Bin->PropertySetsEnd; ++PropSet) {
int Cnt = 0;

for (pi_device_binary_property Prop = PropSet->PropertiesBegin; Prop != PropSet->PropertiesEnd; ++Prop, ++Cnt) {
ASSERT(Prop->Type == 2, "Prop->Type"); // must be a byte array
char *Ptr = reinterpret_cast<char *>(Prop->ValAddr);
int Cmp = std::memcmp(Prop->ValAddr, GoldArrays[Cnt].Ptr, GoldArrays[Cnt].Size);
ASSERT(Cmp == 0, "byte array property");
}
}
return 0;
}

int main(int argc, char **argv) {
ASSERT(BinDesc->NumDeviceBinaries == 2, "BinDesc->NumDeviceBinaries");
ASSERT(BinDesc->Version == 1, "BinDesc->Version");

if (dumpBinary0() != 0)
return 1;
if (checkBinary1() != 0)
return 1;
return 0;
}
17 changes: 17 additions & 0 deletions clang/tools/clang-offload-wrapper/ClangOffloadWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,15 @@ class BinaryWrapper {
return std::make_pair(ImageB, ImageE);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We probably need code owners for clang-offload-wrapper/bundler tools.

// Adds given data buffer as constant byte array and returns a constant
// pointer to it. The pointer type does not carry size information.
Constant *addRawDataToModule(ArrayRef<char> Data, const Twine &Name) {
auto *Var = addGlobalArrayVariable(Name, Data);
auto *DataPtr = ConstantExpr::getGetElementPtr(Var->getValueType(), Var,
getSizetConstPair(0u, 0u));
return DataPtr;
}

// Creates all necessary data objects for the given image and returns a pair
// of pointers that point to the beginning and end of the global variable that
// contains the image data.
Expand Down Expand Up @@ -718,6 +727,14 @@ class BinaryWrapper {
ConstantInt::get(Type::getInt64Ty(C), Prop.second.asUint32());
break;
}
case llvm::util::PropertyValue::BYTE_ARRAY: {
const char *Ptr =
reinterpret_cast<const char *>(Prop.second.asRawByteArray());
uint64_t Size = Prop.second.getRawByteArraySize();
PropValSize = ConstantInt::get(Type::getInt64Ty(C), Size);
PropValAddr = addRawDataToModule(ArrayRef<char>(Ptr, Size), "prop_val");
break;
}
default:
llvm_unreachable_internal("unsupported property");
}
Expand Down
33 changes: 32 additions & 1 deletion llvm/include/llvm/Support/Base64.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@
#ifndef LLVM_SUPPORT_BASE64_H
#define LLVM_SUPPORT_BASE64_H

#include "llvm/Support/Error.h"
#include "llvm/Support/raw_ostream.h"

#include <memory>
#include <string>

namespace llvm {
Expand Down Expand Up @@ -51,6 +55,33 @@ template <class InputBytes> std::string encodeBase64(InputBytes const &Bytes) {
return Buffer;
}

// General-purpose Base64 encoder/decoder.
// TODO update WinCOFFObjectWriter.cpp to use this library.
class Base64 {
public:
using byte = uint8_t;

// Get the size of the encoded byte sequence of given size.
static size_t getEncodedSize(size_t SrcSize);

// Encode a byte sequence of given size into an output stream.
// Returns the number of bytes in the encoded result.
static size_t encode(const byte *Src, raw_ostream &Out, size_t SrcSize);

// Get the size of the encoded byte sequence of given size.
static size_t getDecodedSize(size_t SrcSize);

// Decode a sequence of given size into a pre-allocated memory.
// Returns the number of bytes in the decoded result or 0 in case of error.
static Expected<size_t> decode(const char *Src, byte *Dst, size_t SrcSize);

// Allocate minimum required amount of memory and decode a sequence of given
// size into it.
// Returns the decoded result. The size can be obtained via getDecodedSize.
static Expected<std::unique_ptr<byte>> decode(const char *Src,
size_t SrcSize);
};

} // end namespace llvm

#endif
#endif // LLVM_SUPPORT_BASE64_H
77 changes: 67 additions & 10 deletions llvm/include/llvm/Support/PropertySetIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,14 @@ namespace util {
// container.
class PropertyValue {
public:
// Type of the size of the value. Value size gets serialized along with the
// value data in some cases for later reading at runtime, so size_t is not
// suitable as its size varies.
using SizeTy = uint64_t;
using byte = uint8_t;

// Defines supported property types
enum Type { first = 0, NONE = first, UINT32, last = UINT32 };
enum Type { first = 0, NONE = first, UINT32, BYTE_ARRAY, last = BYTE_ARRAY };

// Translates C++ type to the corresponding type tag.
template <typename T> static Type getTypeTag();
Expand All @@ -64,53 +70,103 @@ class PropertyValue {
return static_cast<Type>(T);
}

~PropertyValue() {
if ((getType() == BYTE_ARRAY) && Val.ByteArrayVal)
delete[] Val.ByteArrayVal;
}

PropertyValue() = default;
PropertyValue(Type T) : Ty(T) {}

PropertyValue(uint32_t Val) : Ty(UINT32), Val({Val}) {}
PropertyValue(const PropertyValue &P) = default;
PropertyValue(PropertyValue &&P) = default;
PropertyValue(const byte *Data, SizeTy DataBitSize);
PropertyValue(const PropertyValue &P);
PropertyValue(PropertyValue &&P);

PropertyValue &operator=(PropertyValue &&P) = default;
PropertyValue &operator=(PropertyValue &&P);

PropertyValue &operator=(const PropertyValue &P) = default;
PropertyValue &operator=(const PropertyValue &P);

// get property value as unsigned 32-bit integer
uint32_t asUint32() const {
assert(Ty == UINT32);
if (Ty != UINT32)
llvm_unreachable("must be UINT32 value");
return Val.UInt32Val;
}

// Get raw data size in bits.
SizeTy getByteArraySizeInBits() const {
if (Ty != BYTE_ARRAY)
llvm_unreachable("must be BYTE_ARRAY value");
SizeTy Res = 0;

for (auto I = 0; I < sizeof(SizeTy); ++I)
Res |= (SizeTy)Val.ByteArrayVal[I] << (8 * I);
return Res;
}

// Get byte array data size in bytes.
SizeTy getByteArraySize() const {
SizeTy SizeInBits = getByteArraySizeInBits();
constexpr unsigned int MASK = 0x7;
return ((SizeInBits + MASK) & ~MASK) / 8;
}

// Get byte array data size in bytes, including the leading bytes encoding the
// size.
SizeTy getRawByteArraySize() const {
return getByteArraySize() + sizeof(SizeTy);
}

// Get byte array data including the leading bytes encoding the size.
const byte *asRawByteArray() const {
if (Ty != BYTE_ARRAY)
llvm_unreachable("must be BYTE_ARRAY value");
return Val.ByteArrayVal;
}

// Get byte array data excluding the leading bytes encoding the size.
const byte *asByteArray() const {
if (Ty != BYTE_ARRAY)
llvm_unreachable("must be BYTE_ARRAY value");
return Val.ByteArrayVal + sizeof(SizeTy);
}

bool isValid() const { return getType() != NONE; }

// set property value; the 'T' type must be convertible to a property type tag
template <typename T> void set(T V) {
assert(getTypeTag<T>() == Ty);
if (getTypeTag<T>() != Ty)
llvm_unreachable("invalid type tag for this operation");
getValueRef<T>() = V;
}

Type getType() const { return Ty; }

size_t size() const {
SizeTy size() const {
switch (Ty) {
case UINT32:
return sizeof(Val.UInt32Val);
case BYTE_ARRAY:
return getRawByteArraySize();
default:
llvm_unreachable_internal("unsupported property type");
}
}

private:
template <typename T> T &getValueRef();
void copy(const PropertyValue &P);

Type Ty = NONE;
// TODO: replace this union with std::variant when uplifting to C++17
union {
uint32_t UInt32Val;
// Holds first sizeof(size_t) bytes of size followed by actual raw data.
byte *ByteArrayVal;
} Val;
};

std::ostream &operator<<(std::ostream &Out, const PropertyValue &V);

// A property set. Preserves insertion order when iterating elements.
using PropertySet = MapVector<StringRef, PropertyValue>;

Expand All @@ -124,6 +180,7 @@ class PropertySetRegistry {
static constexpr char SYCL_SPECIALIZATION_CONSTANTS[] =
"SYCL/specialization constants";
static constexpr char SYCL_DEVICELIB_REQ_MASK[] = "SYCL/devicelib req mask";
static constexpr char SYCL_KERNEL_PARAM_OPT_INFO[] = "SYCL/kernel param opt";

// Function for bulk addition of an entire property set under given category
// (property set name).
Expand Down
Loading