-
Notifications
You must be signed in to change notification settings - Fork 0
[SYCL] Add SYCL property set registry class #3
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,6 +20,7 @@ | |
#include "llvm/BinaryFormat/Magic.h" | ||
#include "llvm/Bitcode/BitcodeWriter.h" | ||
#include "llvm/CodeGen/CommandFlags.h" | ||
#include "llvm/Frontend/Offloading/Utility.h" | ||
#include "llvm/IR/DiagnosticPrinter.h" | ||
#include "llvm/IR/LLVMContext.h" | ||
#include "llvm/IRReader/IRReader.h" | ||
|
@@ -54,6 +55,7 @@ | |
using namespace llvm; | ||
using namespace llvm::opt; | ||
using namespace llvm::object; | ||
using namespace llvm::offloading::sycl; | ||
|
||
/// Save intermediary results. | ||
static bool SaveTemps = false; | ||
|
@@ -355,18 +357,18 @@ Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) { | |
// result in multiple bitcode codes. | ||
// The following lines are placeholders to represent multiple files and will | ||
// be refactored once SYCL post link support is available. | ||
SmallVector<std::string> SplitModules; | ||
SplitModules.emplace_back(*LinkedFile); | ||
SmallVector<std::pair<std::string, PropertySetRegistry>> SplitModules; | ||
SplitModules.emplace_back(*LinkedFile, PropertySetRegistry{}); | ||
|
||
// SPIR-V code generation step. | ||
for (size_t I = 0, E = SplitModules.size(); I != E; ++I) { | ||
auto Stem = OutputFile.rsplit('.').first; | ||
std::string SPVFile(Stem); | ||
SPVFile.append("_" + utostr(I) + ".spv"); | ||
auto Err = runSPIRVCodeGen(SplitModules[I], Args, SPVFile, C); | ||
auto Err = runSPIRVCodeGen(SplitModules[I].first, Args, SPVFile, C); | ||
if (Err) | ||
return Err; | ||
SplitModules[I] = SPVFile; | ||
SplitModules[I].first = SPVFile; | ||
} | ||
|
||
// Write the final output into file. | ||
|
@@ -376,7 +378,7 @@ Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) { | |
llvm::raw_fd_ostream FS(FD, /*shouldClose=*/true); | ||
|
||
for (size_t I = 0, E = SplitModules.size(); I != E; ++I) { | ||
auto File = SplitModules[I]; | ||
const auto &[File, Properties] = SplitModules[I]; | ||
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileOrErr = | ||
llvm::MemoryBuffer::getFileOrSTDIN(File); | ||
if (std::error_code EC = FileOrErr.getError()) { | ||
|
@@ -385,13 +387,15 @@ Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) { | |
else | ||
return createFileError(File, EC); | ||
} | ||
|
||
OffloadingImage TheImage{}; | ||
TheImage.TheImageKind = IMG_Object; | ||
TheImage.TheOffloadKind = OFK_SYCL; | ||
TheImage.StringData["triple"] = | ||
Args.MakeArgString(Args.getLastArgValue(OPT_triple_EQ)); | ||
TheImage.StringData["arch"] = | ||
Args.MakeArgString(Args.getLastArgValue(OPT_arch_EQ)); | ||
TheImage.StringData["sycl_properties"] = Properties.writeJSON(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would suggest simple "properties". |
||
TheImage.Image = std::move(*FileOrErr); | ||
|
||
llvm::SmallString<0> Buffer = OffloadBinary::write(TheImage); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For now, I've placed the class in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it might be a better idea to add this in a separate file called PropertySet.h (associated impl in PropertySet.cpp). Thanks There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding @sarnex for comment as he has worked with Utility.h before. Thanks |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -11,6 +11,7 @@ | |
|
||
#include <cstdint> | ||
#include <memory> | ||
#include <variant> | ||
|
||
#include "llvm/ADT/StringMap.h" | ||
#include "llvm/ADT/StringRef.h" | ||
|
@@ -159,6 +160,107 @@ namespace intel { | |
/// the Intel runtime offload plugin. | ||
Error containerizeOpenMPSPIRVImage(std::unique_ptr<MemoryBuffer> &Binary); | ||
} // namespace intel | ||
|
||
namespace sycl { | ||
class PropertySetRegistry; | ||
|
||
Comment on lines
+165
to
+166
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need this? Thanks |
||
// A property value. It can be either a 32-bit unsigned integer or a byte array. | ||
class PropertyValue { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that this class was overengineered from the very beginning. Do you have any objections againts the following declaration? using PropertyValue = std::variant<uint64_t, std::string>; |
||
public: | ||
using ByteArrayTy = SmallVector<char, 8>; | ||
|
||
PropertyValue() = default; | ||
PropertyValue(uint32_t Val) : Value(Val) {} | ||
PropertyValue(StringRef Data) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've simplified the byte array code compared to the downstream code, but they won't compatible downstream and would cause ABI-break and runtime changes. The downstream implementation always wrote the number of bits the data uses in the first 8 bytes of a byte-array property value, but after some tests, I found this values way never used, except for one place that could be worked around. |
||
: Value(ByteArrayTy(Data.begin(), Data.end())) {} | ||
|
||
template <typename C, typename T = typename C::value_type> | ||
PropertyValue(const C &Data) | ||
: PropertyValue({reinterpret_cast<const char *>(Data.data()), | ||
Data.size() * sizeof(T)}) {} | ||
|
||
uint32_t asUint32() const { | ||
assert(getType() == PV_UInt32 && "must be UINT32 value"); | ||
return std::get<uint32_t>(Value); | ||
} | ||
|
||
StringRef asByteArray() const { | ||
assert(getType() == PV_ByteArray && "must be BYTE_ARRAY value"); | ||
const auto &ByteArrayRef = std::get<ByteArrayTy>(Value); | ||
return {ByteArrayRef.data(), ByteArrayRef.size()}; | ||
} | ||
|
||
// Note: each enumeration value corresponds one-to-one to the | ||
// index of the std::variant. | ||
enum Type { PV_None = 0, PV_UInt32 = 1, PV_ByteArray = 2 }; | ||
Type getType() const { return static_cast<Type>(Value.index()); } | ||
|
||
bool operator==(const PropertyValue &Rhs) const { return Value == Rhs.Value; } | ||
|
||
private: | ||
std::variant<std::monostate, std::uint32_t, ByteArrayTy> Value = {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've just read what |
||
}; | ||
|
||
/// A property set is a collection of PropertyValues, each identified by a name. | ||
/// A property set registry is a collection of property sets, each identified by | ||
/// a category name. The registry allows for the addition, removal, and | ||
/// retrieval of property sets and their properties. | ||
class PropertySetRegistry { | ||
using PropertyMapTy = StringMap<unsigned>; | ||
/// A property set. Preserves insertion order when iterating elements. | ||
using PropertySet = MapVector<SmallString<16>, PropertyValue, PropertyMapTy>; | ||
using MapTy = MapVector<SmallString<16>, PropertySet, PropertyMapTy>; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would discourage usage of I would rather make it We aren't going to have many keys here and it would make the interface extremely simple. |
||
|
||
public: | ||
/// Function for bulk addition of an entire property set in the given | ||
/// \p Category . | ||
template <typename MapTy> void add(StringRef Category, const MapTy &Props) { | ||
assert(PropSetMap.find(Category) == PropSetMap.end() && | ||
"category already added"); | ||
auto &PropSet = PropSetMap[Category]; | ||
|
||
for (const auto &Prop : Props) | ||
PropSet.insert_or_assign(Prop.first, PropertyValue(Prop.second)); | ||
} | ||
|
||
/// Adds the given \p PropVal with the given \p PropName into the given \p | ||
/// Category . | ||
template <typename T> | ||
void add(StringRef Category, StringRef PropName, const T &PropVal) { | ||
auto &PropSet = PropSetMap[Category]; | ||
PropSet.insert({PropName, PropertyValue(PropVal)}); | ||
} | ||
|
||
void remove(StringRef Category, StringRef PropName) { | ||
auto PropertySetIt = PropSetMap.find(Category); | ||
if (PropertySetIt == PropSetMap.end()) | ||
return; | ||
auto &PropertySet = PropertySetIt->second; | ||
auto PropIt = PropertySet.find(PropName); | ||
if (PropIt == PropertySet.end()) | ||
return; | ||
PropertySet.erase(PropIt); | ||
} | ||
|
||
static Expected<PropertySetRegistry> readJSON(MemoryBufferRef Buf); | ||
|
||
/// Dumps the property set registry to the given \p Out stream. | ||
void writeJSON(raw_ostream &Out) const; | ||
SmallString<0> writeJSON() const; | ||
|
||
MapTy::const_iterator begin() const { return PropSetMap.begin(); } | ||
MapTy::const_iterator end() const { return PropSetMap.end(); } | ||
|
||
/// Retrieves a property set with given \p Name . | ||
PropertySet &operator[](StringRef Name) { return PropSetMap[Name]; } | ||
/// Constant access to the underlying map. | ||
const MapTy &getPropSets() const { return PropSetMap; } | ||
|
||
private: | ||
MapTy PropSetMap; | ||
}; | ||
|
||
} // namespace sycl | ||
} // namespace offloading | ||
} // namespace llvm | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
#include "llvm/ADT/SmallVector.h" | ||
#include "llvm/Frontend/Offloading/Utility.h" | ||
#include "llvm/Support/MemoryBuffer.h" | ||
#include "gtest/gtest.h" | ||
|
||
using namespace llvm::offloading::sycl; | ||
using namespace llvm; | ||
|
||
void checkEquality(const PropertySetRegistry &PSR1, | ||
const PropertySetRegistry &PSR2) { | ||
ASSERT_EQ(PSR1.getPropSets().size(), PSR2.getPropSets().size()); | ||
for (const auto &[Category1, PropSet1] : PSR1.getPropSets()) { | ||
auto It = PSR2.getPropSets().find(Category1); | ||
ASSERT_TRUE(It != PSR2.getPropSets().end()); | ||
const auto &[Category2, PropSet2] = *It; | ||
ASSERT_EQ(PropSet1.size(), PropSet2.size()); | ||
for (auto It1 = PropSet1.begin(), It2 = PropSet2.begin(), | ||
E = PropSet1.end(); | ||
It1 != E; ++It1, ++It2) { | ||
const auto &[PropName1, PropValue1] = *It1; | ||
const auto &[PropName2, PropValue2] = *It2; | ||
ASSERT_EQ(PropName1, PropName2); | ||
ASSERT_EQ(PropValue1, PropValue2); | ||
} | ||
} | ||
} | ||
|
||
TEST(PropertySetRegistryTest, PropertySetRegistry) { | ||
PropertySetRegistry PSR; | ||
PSR.add("Category1", "Prop1", 42); | ||
PSR.add("Category1", "Prop2", "Hello"); | ||
SmallVector<int, 3> arr = {4, 16, 32}; | ||
PSR.add("Category2", "A", arr); | ||
auto Serialized = PSR.writeJSON(); | ||
auto PSR2 = PropertySetRegistry::readJSON({Serialized, ""}); | ||
if (auto Err = PSR2.takeError()) | ||
FAIL(); | ||
checkEquality(PSR, *PSR2); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changes to ClangSYCLLinker.cpp can be done in a separate PR. WDYT?
Thanks