Skip to content

[sycl-post-link] Add property with the default values of specialization constants #3666

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 17 commits into from
May 27, 2021
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
24 changes: 13 additions & 11 deletions llvm/include/llvm/Support/PropertySetIO.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
#include <map>
#include <memory>
#include <string>
#include <type_traits>

namespace llvm {
namespace util {
Expand Down Expand Up @@ -183,32 +184,33 @@ class PropertySetRegistry {
// Specific property category names used by tools.
static constexpr char SYCL_SPECIALIZATION_CONSTANTS[] =
"SYCL/specialization constants";
static constexpr char SYCL_COMPOSITE_SPECIALIZATION_CONSTANTS[] =
"SYCL/composite specialization constants";
static constexpr char SYCL_SPEC_CONSTANTS_DEFAULT_VALUES[] =
"SYCL/specialization constants default values";
static constexpr char SYCL_DEVICELIB_REQ_MASK[] = "SYCL/devicelib req mask";
static constexpr char SYCL_KERNEL_PARAM_OPT_INFO[] = "SYCL/kernel param opt";
static constexpr char SYCL_MISC_PROP[] = "SYCL/misc properties";

// Function for bulk addition of an entire property set under given category
// (property set name).
template <typename T>
void add(StringRef Category, const std::map<StringRef, T> &Props) {
template <typename MapTy> void add(StringRef Category, const MapTy &Props) {
using KeyTy = typename MapTy::value_type::first_type;
static_assert(std::is_same<typename std::remove_const<KeyTy>::type,
llvm::StringRef>::value,
"wrong key type");

assert(PropSetMap.find(Category) == PropSetMap.end() &&
"category already added");
auto &PropSet = PropSetMap[Category];

for (const auto &Prop : Props)
PropSet.insert(std::make_pair(Prop.first, PropertyValue(Prop.second)));
PropSet.insert({Prop.first, PropertyValue(Prop.second)});
}

// Function to add a property to a given category (property set name).
template <typename T>
void add(StringRef Category, const MapVector<StringRef, T> &Props) {
assert(PropSetMap.find(Category) == PropSetMap.end() &&
"category already added");
void add(StringRef Category, StringRef PropName, const T &PropVal) {
auto &PropSet = PropSetMap[Category];

for (const auto &Prop : Props)
PropSet.insert({Prop.first, PropertyValue(Prop.second)});
PropSet.insert({PropName, PropertyValue(PropVal)});
}

// Parses and creates a property set registry.
Expand Down
2 changes: 1 addition & 1 deletion llvm/lib/Support/PropertySetIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,8 @@ void PropertyValue::copy(const PropertyValue &P) {

constexpr char PropertySetRegistry::SYCL_SPECIALIZATION_CONSTANTS[];
constexpr char PropertySetRegistry::SYCL_DEVICELIB_REQ_MASK[];
constexpr char PropertySetRegistry::SYCL_SPEC_CONSTANTS_DEFAULT_VALUES[];
constexpr char PropertySetRegistry::SYCL_KERNEL_PARAM_OPT_INFO[];
constexpr char PropertySetRegistry::SYCL_COMPOSITE_SPECIALIZATION_CONSTANTS[];
constexpr char PropertySetRegistry::SYCL_MISC_PROP[];

} // namespace util
Expand Down
8 changes: 8 additions & 0 deletions llvm/test/tools/sycl-post-link/spec-constants/SYCL-2020.ll
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,9 @@ attributes #3 = { nounwind }

; CHECK: !sycl.specialization-constants = !{![[#ID0:]], ![[#ID1:]], ![[#ID2:]], ![[#ID3:]]}
;
; CHECK-DEF: !sycl.specialization-constants-default-values = !{![[#ID4:]], ![[#ID5:]], ![[#ID6:]], ![[#ID7:]]}
; CHECK-RT-NOT: !sycl.specialization-constants-default-values
;
; CHECK: ![[#ID0]] = !{!"_ZTS14name_generatorIL_Z9id_doubleEE", i32 0, i32 0, i32 8}
; CHECK: ![[#ID1]] = !{!"_ZTS14name_generatorIL_Z6id_intEE", i32 1, i32 0, i32 4}
;
Expand All @@ -148,3 +151,8 @@ attributes #3 = { nounwind }
; CHECK-RT-SAME: i32 [[#SCID7]], i32 4, i32 4,
; CHECK-RT-SAME: i32 [[#SCID8]], i32 8, i32 4,
; CHECK-RT-SAME: i32 [[#SCID9]], i32 16, i32 8}
;
; CHECK-DEF: ![[#ID4]] = !{double 3.140000e+00}
; CHECK-DEF: ![[#ID5]] = !{i32 42}
; CHECK-DEF: ![[#ID6]] = !{%struct.ComposConst { i32 1, double 2.000000e+00, %struct.myConst { i32 13, float 0x4020666660000000 } }}
; CHECK-DEF: ![[#ID7]] = !{%struct.ComposConst2 { i8 1, %struct.myConst { i32 52, float 0x40479999A0000000 }, double 2.000000e+00 }}
145 changes: 125 additions & 20 deletions llvm/tools/sycl-post-link/SpecConstants.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ constexpr char SPIRV_GET_SPEC_CONST_COMPOSITE[] =
// Name of the metadata which holds a list of all specialization constants (with
// associated information) encountered in the module
constexpr char SPEC_CONST_MD_STRING[] = "sycl.specialization-constants";
// Name of the metadata which holds a default value list of all specialization
// constants encountered in the module
constexpr char SPEC_CONST_DEFAULT_VAL_MD_STRING[] =
"sycl.specialization-constants-default-values";

void AssertRelease(bool Cond, const char *Msg) {
if (!Cond)
Expand Down Expand Up @@ -214,6 +218,11 @@ std::string mangleFuncItanium(StringRef BaseName, const FunctionType *FT) {
return Res;
}

MDNode *generateSpecConstDefaultValueMetadata(StringRef SymID, Value *Default) {
LLVMContext &Ctx = Default->getContext();
return MDNode::get(Ctx, ConstantAsMetadata::get(cast<Constant>(Default)));
}

/// Recursively iterates over a composite type in order to collect information
/// about its scalar elements.
void collectCompositeElementsInfoRecursive(
Expand Down Expand Up @@ -264,6 +273,72 @@ void collectCompositeElementsInfoRecursive(
}
}

/// Recursively iterates over a composite type in order to collect information
/// about default values of its scalar elements.
/// TODO: processing of composite spec constants here is similar to
/// collectCompositeElementsInfoRecursive. Possible place for improvement -
Copy link
Contributor

Choose a reason for hiding this comment

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

Is it also similar to emitSpecConstantRecursiveImpl and generateSpecConstDefaultValueMetadata?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not really, it doesn't look similar. generateSpecConstDefaultValueMetadata is not related to recursive methods, and emitSpecConstantRecursiveImpl is too different from this function.

/// factor out the common code, e.g. using visitor pattern.
void collectCompositeElementsDefaultValuesRecursive(
const Module &M, Constant *C, unsigned &Offset,
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: processing of composite spec constants is similar in this function, emitSpecConstantRecursiveImpl and generateSpecConstDefaultValueMetadata. Could be factored out using e.g. the visitor pattern. At least a TODO for now would be good.

std::vector<char> &DefaultValues) {
Type *Ty = C->getType();
if (auto *ArrTy = dyn_cast<ArrayType>(Ty)) {
for (size_t I = 0; I < ArrTy->getNumElements(); ++I) {
Constant *El = cast<Constant>(C->getOperand(I));
collectCompositeElementsDefaultValuesRecursive(M, El, Offset,
DefaultValues);
}
} else if (auto *StructTy = dyn_cast<StructType>(Ty)) {
const StructLayout *SL = M.getDataLayout().getStructLayout(StructTy);
for (size_t I = 0, E = StructTy->getNumElements(); I < E; ++I) {
Constant *El = cast<Constant>(C->getOperand(I));
// When handling elements of a structure, we do not use manually
// calculated offsets (which are sum of sizes of all previously
// encountered elements), but instead rely on data provided for us by
// DataLayout, because the structure can be unpacked, i.e. padded in
// order to ensure particular alignment of its elements.
unsigned LocalOffset = Offset + SL->getElementOffset(I);

// If there was some alignment, fill the data between values with zeros.
while (LocalOffset != DefaultValues.size())
DefaultValues.push_back(0);

collectCompositeElementsDefaultValuesRecursive(M, El, LocalOffset,
DefaultValues);
}
// Update "global" offset according to the total size of a handled struct
// type.
Offset += SL->getSizeInBytes();
} else if (auto *VecTy = dyn_cast<FixedVectorType>(Ty)) {
for (size_t I = 0; I < VecTy->getNumElements(); ++I) {
Constant *El = cast<Constant>(C->getOperand(I));
collectCompositeElementsDefaultValuesRecursive(M, El, Offset,
DefaultValues);
}
} else { // Assume that we encountered some scalar element
int NumBytes = Ty->getScalarSizeInBits() / CHAR_BIT +
(Ty->getScalarSizeInBits() % 8 != 0);
char *CharPtr;

if (auto IntConst = dyn_cast<ConstantInt>(C)) {
auto Val = IntConst->getValue().getZExtValue();
CharPtr = reinterpret_cast<char *>(&Val);
} else if (auto FPConst = dyn_cast<ConstantFP>(C)) {
auto Val = FPConst->getValue();

if (NumBytes == 4) {
float v = Val.convertToFloat();
CharPtr = reinterpret_cast<char *>(&v);
} else if (NumBytes == 8) {
double v = Val.convertToDouble();
CharPtr = reinterpret_cast<char *>(&v);
}
}
std::copy_n(CharPtr, NumBytes, std::back_inserter(DefaultValues));
Offset += NumBytes;
}
}

MDNode *generateSpecConstantMetadata(const Module &M, StringRef SymbolicID,
Type *SCTy, ArrayRef<unsigned> IDs,
bool IsNativeSpecConstant) {
Expand Down Expand Up @@ -476,6 +551,7 @@ PreservedAnalyses SpecConstantsPass::run(Module &M,
StringMap<SmallVector<unsigned, 1>> IDMap;
StringMap<unsigned> OffsetMap;
MapVector<StringRef, MDNode *> SCMetadata;
SmallVector<MDNode *, 4> DefaultsMetadata;

// Iterate through all declarations of instances of function template
// template <typename T> T __sycl_get*SpecConstantValue(const char *ID)
Expand Down Expand Up @@ -531,6 +607,26 @@ PreservedAnalyses SpecConstantsPass::run(Module &M,
StringRef SymID = getStringLiteralArg(CI, NameArgNo, DelInsts);
Value *Replacement = nullptr;

Constant *DefaultValue = nullptr;
if (Is2020Intrinsic) {
// For SYCL 2020, there is a mechanism to specify the default value.
// It is stored as an initializer of a global variable referenced by
// the second argument of the intrinsic.
auto *GV = dyn_cast<GlobalVariable>(
CI->getArgOperand(NameArgNo + 1)->stripPointerCasts());
// Go through global variable if the second argument was not null.
if (GV) {
assert(GV->hasInitializer() && "expected initializer");
auto *Initializer = GV->getInitializer();
assert((isa<ConstantAggregate>(Initializer) ||
Initializer->isZeroValue()) &&
"expected specialization_id instance");
// specialization_id structure contains a single field which is the
// default value of corresponding specialization constant.
DefaultValue = Initializer->getAggregateElement(0u);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

what GV == 0 means? please add a comment or handle

Copy link
Contributor

Choose a reason for hiding this comment

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

Probably we should just use cast instead of dyn_cast and always expect to see a value there.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can meet nullptr instead of value here - this check is still needed. I left a comment above the if-.


if (SetValAtRT) {
// 2. Spec constant value will be set at run time - then add the literal
// to a "spec const string literal ID" -> "vector of integer IDs" map,
Expand All @@ -545,25 +641,6 @@ PreservedAnalyses SpecConstantsPass::run(Module &M,
IDs.push_back(NextID);
}

Constant *DefaultValue = nullptr;
if (Is2020Intrinsic) {
// For SYCL 2020, there is a mechanism to specify the default value.
// It is stored as an initializer of a global variable referenced by
// the second argument of the intrinsic.
auto *GV = dyn_cast<GlobalVariable>(
CI->getArgOperand(NameArgNo + 1)->stripPointerCasts());
if (GV) {
assert(GV->hasInitializer() && "expected initializer");
auto *Initializer = GV->getInitializer();
assert((isa<ConstantAggregate>(Initializer) ||
Initializer->isZeroValue()) &&
"expected specialization_id instance");
// specialization_id structure contains a single field which is the
// default value of corresponding specialization constant.
DefaultValue = Initializer->getAggregateElement(0u);
}
}

// 3. Transform to spirv intrinsic _Z*__spirv_SpecConstant* or
// _Z*__spirv_SpecConstantComposite
Replacement = emitSpecConstantRecursive(SCTy, CI, IDs, DefaultValue);
Expand Down Expand Up @@ -630,6 +707,10 @@ PreservedAnalyses SpecConstantsPass::run(Module &M,
GEP, PointerType::get(SCTy, GEP->getAddressSpace()), "bc", CI);

Replacement = new LoadInst(SCTy, BitCast, "load", CI);

if (IsNewSpecConstant && DefaultValue)
DefaultsMetadata.push_back(
generateSpecConstDefaultValueMetadata(SymID, DefaultValue));
} else {
// Replace the intrinsic with default C++ value for the spec constant
// type.
Expand Down Expand Up @@ -667,12 +748,20 @@ PreservedAnalyses SpecConstantsPass::run(Module &M,
for (const auto &P : SCMetadata)
MD->addOperand(P.second);

// Emit default values metadata only in native (default) spec constants mode.
if (!SetValAtRT) {
NamedMDNode *MDDefaults =
M.getOrInsertNamedMetadata(SPEC_CONST_DEFAULT_VAL_MD_STRING);
for (const auto &P : DefaultsMetadata)
MDDefaults->addOperand(P);
}

return IRModified ? PreservedAnalyses::none() : PreservedAnalyses::all();
}

bool SpecConstantsPass::collectSpecConstantMetadata(Module &M,
SpecIDMapTy &IDMap) {
NamedMDNode *MD = M.getOrInsertNamedMetadata(SPEC_CONST_MD_STRING);
NamedMDNode *MD = M.getNamedMetadata(SPEC_CONST_MD_STRING);
if (!MD)
return false;

Expand All @@ -699,3 +788,19 @@ bool SpecConstantsPass::collectSpecConstantMetadata(Module &M,

return true;
}

bool SpecConstantsPass::collectSpecConstantDefaultValuesMetadata(
Module &M, std::vector<char> &DefaultValues) {
NamedMDNode *N = M.getNamedMetadata(SPEC_CONST_DEFAULT_VAL_MD_STRING);
if (!N)
return false;

unsigned Offset = 0;
for (const auto *Node : N->operands()) {
auto *Constant = cast<ConstantAsMetadata>(Node->getOperand(0))->getValue();
collectCompositeElementsDefaultValuesRecursive(M, Constant, Offset,
DefaultValues);
}

return true;
}
5 changes: 5 additions & 0 deletions llvm/tools/sycl-post-link/SpecConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,11 @@ class SpecConstantsPass : public llvm::PassInfoMixin<SpecConstantsPass> {
// metadata and builds "spec constant name" -> vector<"spec constant int ID">
// map
static bool collectSpecConstantMetadata(llvm::Module &M, SpecIDMapTy &IDMap);
// Searches given module for occurrences of specialization constant-specific
// metadata and builds vector of default values for every spec constant.
static bool
collectSpecConstantDefaultValuesMetadata(llvm::Module &M,
std::vector<char> &DefaultValues);

private:
bool SetValAtRT;
Expand Down
11 changes: 11 additions & 0 deletions llvm/tools/sycl-post-link/sycl-post-link.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,6 +420,17 @@ static string_vector saveDeviceImageProperty(
PropSet.add(
llvm::util::PropertySetRegistry::SYCL_SPECIALIZATION_CONSTANTS,
TmpSpecIDMap);

// Add property with the default values of spec constants only in native
// (default) mode.
if (!ImgPSInfo.SetSpecConstAtRT) {
std::vector<char> DefaultValues;
SpecConstantsPass::collectSpecConstantDefaultValuesMetadata(
*ResultModules[I].get(), DefaultValues);
PropSet.add(llvm::util::PropertySetRegistry::
SYCL_SPEC_CONSTANTS_DEFAULT_VALUES,
"all", DefaultValues);
}
}
}
if (ImgPSInfo.EmitKernelParamInfo) {
Expand Down
8 changes: 6 additions & 2 deletions sycl/test/basic_tests/SYCL-2020-spec-constants.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// RUN: %clangxx -fsycl -fsycl-device-only -c -o %t.bc %s
// RUN: sycl-post-link %t.bc -spec-const=rt -o %t-split1.txt
// RUN: cat %t-split1_0.prop | FileCheck %s
// RUN: cat %t-split1_0.prop | FileCheck %s -check-prefixes=CHECK,CHECK-RT
// RUN: sycl-post-link %t.bc -spec-const=default -o %t-split2.txt
// RUN: cat %t-split2_0.prop | FileCheck %s
// RUN: cat %t-split2_0.prop | FileCheck %s -check-prefixes=CHECK,CHECK-DEF
// RUN: llvm-spirv -o %t-split1_0.spv -spirv-max-version=1.1 -spirv-ext=+all %t-split1_0.bc
// RUN: llvm-spirv -o %t-split2_0.spv -spirv-max-version=1.1 -spirv-ext=+all %t-split2_0.bc
//
Expand Down Expand Up @@ -100,3 +100,7 @@ int main() {
// CHECK-DAG: _ZTSN2cl4sycl6detail32specialization_id_name_generatorIL_ZL9uint32_idEEE=2|
// CHECK-DAG: _ZTSN2cl4sycl6detail32specialization_id_name_generatorIL_ZL9uint64_idEEE=2|
// FIXME: check line for half constant

// CHECK-RT-NOT: [SYCL/specialization constants default values]
// CHECK-DEF: [SYCL/specialization constants default values]
// CHECK-DEF: all=2|