Skip to content

Commit f5388e4

Browse files
committed
[SYCL] Add SYCL property set registry class
1 parent dba8acd commit f5388e4

File tree

5 files changed

+253
-5
lines changed

5 files changed

+253
-5
lines changed

clang/tools/clang-sycl-linker/ClangSYCLLinker.cpp

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "llvm/BinaryFormat/Magic.h"
2121
#include "llvm/Bitcode/BitcodeWriter.h"
2222
#include "llvm/CodeGen/CommandFlags.h"
23+
#include "llvm/Frontend/Offloading/Utility.h"
2324
#include "llvm/IR/DiagnosticPrinter.h"
2425
#include "llvm/IR/LLVMContext.h"
2526
#include "llvm/IRReader/IRReader.h"
@@ -54,6 +55,7 @@
5455
using namespace llvm;
5556
using namespace llvm::opt;
5657
using namespace llvm::object;
58+
using namespace llvm::offloading::sycl;
5759

5860
/// Save intermediary results.
5961
static bool SaveTemps = false;
@@ -355,18 +357,18 @@ Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) {
355357
// result in multiple bitcode codes.
356358
// The following lines are placeholders to represent multiple files and will
357359
// be refactored once SYCL post link support is available.
358-
SmallVector<std::string> SplitModules;
359-
SplitModules.emplace_back(*LinkedFile);
360+
SmallVector<std::pair<std::string, PropertySetRegistry>> SplitModules;
361+
SplitModules.emplace_back(*LinkedFile, PropertySetRegistry{});
360362

361363
// SPIR-V code generation step.
362364
for (size_t I = 0, E = SplitModules.size(); I != E; ++I) {
363365
auto Stem = OutputFile.rsplit('.').first;
364366
std::string SPVFile(Stem);
365367
SPVFile.append("_" + utostr(I) + ".spv");
366-
auto Err = runSPIRVCodeGen(SplitModules[I], Args, SPVFile, C);
368+
auto Err = runSPIRVCodeGen(SplitModules[I].first, Args, SPVFile, C);
367369
if (Err)
368370
return Err;
369-
SplitModules[I] = SPVFile;
371+
SplitModules[I].first = SPVFile;
370372
}
371373

372374
// Write the final output into file.
@@ -376,7 +378,7 @@ Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) {
376378
llvm::raw_fd_ostream FS(FD, /*shouldClose=*/true);
377379

378380
for (size_t I = 0, E = SplitModules.size(); I != E; ++I) {
379-
auto File = SplitModules[I];
381+
const auto &[File, Properties] = SplitModules[I];
380382
llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> FileOrErr =
381383
llvm::MemoryBuffer::getFileOrSTDIN(File);
382384
if (std::error_code EC = FileOrErr.getError()) {
@@ -385,13 +387,15 @@ Error runSYCLLink(ArrayRef<std::string> Files, const ArgList &Args) {
385387
else
386388
return createFileError(File, EC);
387389
}
390+
388391
OffloadingImage TheImage{};
389392
TheImage.TheImageKind = IMG_Object;
390393
TheImage.TheOffloadKind = OFK_SYCL;
391394
TheImage.StringData["triple"] =
392395
Args.MakeArgString(Args.getLastArgValue(OPT_triple_EQ));
393396
TheImage.StringData["arch"] =
394397
Args.MakeArgString(Args.getLastArgValue(OPT_arch_EQ));
398+
TheImage.StringData["sycl_properties"] = Properties.writeJSON();
395399
TheImage.Image = std::move(*FileOrErr);
396400

397401
llvm::SmallString<0> Buffer = OffloadBinary::write(TheImage);

llvm/include/llvm/Frontend/Offloading/Utility.h

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include <cstdint>
1313
#include <memory>
14+
#include <variant>
1415

1516
#include "llvm/ADT/StringMap.h"
1617
#include "llvm/ADT/StringRef.h"
@@ -159,6 +160,107 @@ namespace intel {
159160
/// the Intel runtime offload plugin.
160161
Error containerizeOpenMPSPIRVImage(std::unique_ptr<MemoryBuffer> &Binary);
161162
} // namespace intel
163+
164+
namespace sycl {
165+
class PropertySetRegistry;
166+
167+
// A property value. It can be either a 32-bit unsigned integer or a byte array.
168+
class PropertyValue {
169+
public:
170+
using ByteArrayTy = SmallVector<char, 8>;
171+
172+
PropertyValue() = default;
173+
PropertyValue(uint32_t Val) : Value(Val) {}
174+
PropertyValue(StringRef Data)
175+
: Value(ByteArrayTy(Data.begin(), Data.end())) {}
176+
177+
template <typename C, typename T = typename C::value_type>
178+
PropertyValue(const C &Data)
179+
: PropertyValue({reinterpret_cast<const char *>(Data.data()),
180+
Data.size() * sizeof(T)}) {}
181+
182+
uint32_t asUint32() const {
183+
assert(getType() == PV_UInt32 && "must be UINT32 value");
184+
return std::get<uint32_t>(Value);
185+
}
186+
187+
StringRef asByteArray() const {
188+
assert(getType() == PV_ByteArray && "must be BYTE_ARRAY value");
189+
const auto &ByteArrayRef = std::get<ByteArrayTy>(Value);
190+
return {ByteArrayRef.data(), ByteArrayRef.size()};
191+
}
192+
193+
// Note: each enumeration value corresponds one-to-one to the
194+
// index of the std::variant.
195+
enum Type { PV_None = 0, PV_UInt32 = 1, PV_ByteArray = 2 };
196+
Type getType() const { return static_cast<Type>(Value.index()); }
197+
198+
bool operator==(const PropertyValue &Rhs) const { return Value == Rhs.Value; }
199+
200+
private:
201+
std::variant<std::monostate, std::uint32_t, ByteArrayTy> Value = {};
202+
};
203+
204+
/// A property set is a collection of PropertyValues, each identified by a name.
205+
/// A property set registry is a collection of property sets, each identified by
206+
/// a category name. The registry allows for the addition, removal, and
207+
/// retrieval of property sets and their properties.
208+
class PropertySetRegistry {
209+
using PropertyMapTy = StringMap<unsigned>;
210+
/// A property set. Preserves insertion order when iterating elements.
211+
using PropertySet = MapVector<SmallString<16>, PropertyValue, PropertyMapTy>;
212+
using MapTy = MapVector<SmallString<16>, PropertySet, PropertyMapTy>;
213+
214+
public:
215+
/// Function for bulk addition of an entire property set in the given
216+
/// \p Category .
217+
template <typename MapTy> void add(StringRef Category, const MapTy &Props) {
218+
assert(PropSetMap.find(Category) == PropSetMap.end() &&
219+
"category already added");
220+
auto &PropSet = PropSetMap[Category];
221+
222+
for (const auto &Prop : Props)
223+
PropSet.insert_or_assign(Prop.first, PropertyValue(Prop.second));
224+
}
225+
226+
/// Adds the given \p PropVal with the given \p PropName into the given \p
227+
/// Category .
228+
template <typename T>
229+
void add(StringRef Category, StringRef PropName, const T &PropVal) {
230+
auto &PropSet = PropSetMap[Category];
231+
PropSet.insert({PropName, PropertyValue(PropVal)});
232+
}
233+
234+
void remove(StringRef Category, StringRef PropName) {
235+
auto PropertySetIt = PropSetMap.find(Category);
236+
if (PropertySetIt == PropSetMap.end())
237+
return;
238+
auto &PropertySet = PropertySetIt->second;
239+
auto PropIt = PropertySet.find(PropName);
240+
if (PropIt == PropertySet.end())
241+
return;
242+
PropertySet.erase(PropIt);
243+
}
244+
245+
static Expected<PropertySetRegistry> readJSON(MemoryBufferRef Buf);
246+
247+
/// Dumps the property set registry to the given \p Out stream.
248+
void writeJSON(raw_ostream &Out) const;
249+
SmallString<0> writeJSON() const;
250+
251+
MapTy::const_iterator begin() const { return PropSetMap.begin(); }
252+
MapTy::const_iterator end() const { return PropSetMap.end(); }
253+
254+
/// Retrieves a property set with given \p Name .
255+
PropertySet &operator[](StringRef Name) { return PropSetMap[Name]; }
256+
/// Constant access to the underlying map.
257+
const MapTy &getPropSets() const { return PropSetMap; }
258+
259+
private:
260+
MapTy PropSetMap;
261+
};
262+
263+
} // namespace sycl
162264
} // namespace offloading
163265
} // namespace llvm
164266

llvm/lib/Frontend/Offloading/Utility.cpp

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,3 +459,105 @@ Error offloading::intel::containerizeOpenMPSPIRVImage(
459459
Img = MemoryBuffer::getMemBufferCopy(YamlFile);
460460
return Error::success();
461461
}
462+
463+
namespace llvm::offloading::sycl {
464+
465+
void PropertySetRegistry::writeJSON(raw_ostream &Out) const {
466+
json::OStream J(Out);
467+
J.object([&] {
468+
for (const auto &PropSet : PropSetMap) {
469+
// Note: we do not output the property sets as objects,
470+
// but as arrays of [name, value] arrays because we have to
471+
// preserve the order of insertion of the properties
472+
// in the property set (note: this currently only used by the
473+
// spec constant metadata). Even if the key-value pairs
474+
// of an object are serialized in the order they were inserted,
475+
// the order is not guaranteed to be preserved when
476+
// deserializing because json::Object uses a DenseMap to store
477+
// its key-value pairs.
478+
J.attributeArray(PropSet.first, [&] {
479+
for (const auto &Props : PropSet.second) {
480+
J.array([&] {
481+
J.value(Props.first);
482+
switch (Props.second.getType()) {
483+
case PropertyValue::PV_UInt32:
484+
J.value(Props.second.asUint32());
485+
break;
486+
case PropertyValue::PV_ByteArray: {
487+
StringRef ByteArrayRef = Props.second.asByteArray();
488+
J.value(json::Array(ByteArrayRef.bytes()));
489+
break;
490+
}
491+
default:
492+
llvm_unreachable(("unsupported property type: " +
493+
utostr(Props.second.getType()))
494+
.c_str());
495+
}
496+
});
497+
}
498+
});
499+
}
500+
});
501+
}
502+
503+
SmallString<0> PropertySetRegistry::writeJSON() const {
504+
SmallString<0> Str;
505+
raw_svector_ostream OS(Str);
506+
writeJSON(OS);
507+
return Str;
508+
}
509+
510+
Expected<PropertySetRegistry>
511+
PropertySetRegistry::readJSON(MemoryBufferRef Buf) {
512+
PropertySetRegistry Res;
513+
Expected<json::Value> V = json::parse(Buf.getBuffer());
514+
if (!V)
515+
return V.takeError();
516+
const json::Object *O = V->getAsObject();
517+
if (!O)
518+
return createStringError("expected JSON object");
519+
for (const auto &[CategoryName, Value] : *O) {
520+
const json::Array *PropsArray = Value.getAsArray();
521+
if (!PropsArray)
522+
return createStringError("expected JSON array for properties");
523+
PropertySet &PropSet = Res.PropSetMap[StringRef(CategoryName)];
524+
for (const json::Value &PropPair : *PropsArray) {
525+
const json::Array *PropArray = PropPair.getAsArray();
526+
if (!PropArray || PropArray->size() != 2)
527+
return createStringError(
528+
"expected property as [PropertyName, PropertyValue] pair");
529+
530+
const json::Value &PropNameVal = (*PropArray)[0];
531+
const json::Value &PropValueVal = (*PropArray)[1];
532+
533+
std::optional<StringRef> PropName = PropNameVal.getAsString();
534+
if (!PropName)
535+
return createStringError("expected property name as string");
536+
537+
PropertyValue Prop;
538+
if (std::optional<uint64_t> Val = PropValueVal.getAsUINT64()) {
539+
Prop = PropertyValue(static_cast<uint32_t>(*Val));
540+
} else if (const json::Array *Val = PropValueVal.getAsArray()) {
541+
SmallVector<unsigned char, 8> Vec;
542+
for (const json::Value &V : *Val) {
543+
std::optional<uint64_t> Byte = V.getAsUINT64();
544+
if (!Byte)
545+
return createStringError("invalid byte array value");
546+
if (*Byte > std::numeric_limits<unsigned char>::max())
547+
return createStringError("byte array value out of range");
548+
Vec.push_back(static_cast<unsigned char>(*Byte));
549+
}
550+
Prop = PropertyValue(Vec);
551+
} else {
552+
return createStringError("unsupported property type");
553+
}
554+
555+
auto [It, Inserted] = PropSet.insert({*PropName, Prop});
556+
if (!Inserted)
557+
return createStringError("duplicate property name");
558+
}
559+
}
560+
return Res;
561+
}
562+
563+
} // namespace llvm::offloading::sycl

llvm/unittests/Frontend/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ add_llvm_unittest(LLVMFrontendTests
1616
OpenMPParsingTest.cpp
1717
OpenMPCompositionTest.cpp
1818
OpenMPDecompositionTest.cpp
19+
PropertySetRegistryTest.cpp
1920

2021
DEPENDS
2122
acc_gen
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#include "llvm/ADT/SmallVector.h"
2+
#include "llvm/Frontend/Offloading/Utility.h"
3+
#include "llvm/Support/MemoryBuffer.h"
4+
#include "gtest/gtest.h"
5+
6+
using namespace llvm::offloading::sycl;
7+
using namespace llvm;
8+
9+
void checkEquality(const PropertySetRegistry &PSR1,
10+
const PropertySetRegistry &PSR2) {
11+
ASSERT_EQ(PSR1.getPropSets().size(), PSR2.getPropSets().size());
12+
for (const auto &[Category1, PropSet1] : PSR1.getPropSets()) {
13+
auto It = PSR2.getPropSets().find(Category1);
14+
ASSERT_TRUE(It != PSR2.getPropSets().end());
15+
const auto &[Category2, PropSet2] = *It;
16+
ASSERT_EQ(PropSet1.size(), PropSet2.size());
17+
for (auto It1 = PropSet1.begin(), It2 = PropSet2.begin(),
18+
E = PropSet1.end();
19+
It1 != E; ++It1, ++It2) {
20+
const auto &[PropName1, PropValue1] = *It1;
21+
const auto &[PropName2, PropValue2] = *It2;
22+
ASSERT_EQ(PropName1, PropName2);
23+
ASSERT_EQ(PropValue1, PropValue2);
24+
}
25+
}
26+
}
27+
28+
TEST(PropertySetRegistryTest, PropertySetRegistry) {
29+
PropertySetRegistry PSR;
30+
PSR.add("Category1", "Prop1", 42);
31+
PSR.add("Category1", "Prop2", "Hello");
32+
SmallVector<int, 3> arr = {4, 16, 32};
33+
PSR.add("Category2", "A", arr);
34+
auto Serialized = PSR.writeJSON();
35+
auto PSR2 = PropertySetRegistry::readJSON({Serialized, ""});
36+
if (auto Err = PSR2.takeError())
37+
FAIL();
38+
checkEquality(PSR, *PSR2);
39+
}

0 commit comments

Comments
 (0)