diff --git a/llvm/include/llvm/Support/PropertySetIO.h b/llvm/include/llvm/Support/PropertySetIO.h index 525aacccfbc40..cd7688d10f2db 100644 --- a/llvm/include/llvm/Support/PropertySetIO.h +++ b/llvm/include/llvm/Support/PropertySetIO.h @@ -43,6 +43,7 @@ #include #include #include +#include namespace llvm { namespace util { @@ -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 - void add(StringRef Category, const std::map &Props) { + template void add(StringRef Category, const MapTy &Props) { + using KeyTy = typename MapTy::value_type::first_type; + static_assert(std::is_same::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 - void add(StringRef Category, const MapVector &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. diff --git a/llvm/lib/Support/PropertySetIO.cpp b/llvm/lib/Support/PropertySetIO.cpp index f583c91088762..97016364e0d0a 100644 --- a/llvm/lib/Support/PropertySetIO.cpp +++ b/llvm/lib/Support/PropertySetIO.cpp @@ -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 diff --git a/llvm/test/tools/sycl-post-link/spec-constants/SYCL-2020.ll b/llvm/test/tools/sycl-post-link/spec-constants/SYCL-2020.ll index 6661b7f0b861c..48fe9349170d3 100644 --- a/llvm/test/tools/sycl-post-link/spec-constants/SYCL-2020.ll +++ b/llvm/test/tools/sycl-post-link/spec-constants/SYCL-2020.ll @@ -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} ; @@ -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 }} diff --git a/llvm/tools/sycl-post-link/SpecConstants.cpp b/llvm/tools/sycl-post-link/SpecConstants.cpp index aee09058833e5..2bf6745dea786 100644 --- a/llvm/tools/sycl-post-link/SpecConstants.cpp +++ b/llvm/tools/sycl-post-link/SpecConstants.cpp @@ -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) @@ -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(Default))); +} + /// Recursively iterates over a composite type in order to collect information /// about its scalar elements. void collectCompositeElementsInfoRecursive( @@ -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 - +/// factor out the common code, e.g. using visitor pattern. +void collectCompositeElementsDefaultValuesRecursive( + const Module &M, Constant *C, unsigned &Offset, + std::vector &DefaultValues) { + Type *Ty = C->getType(); + if (auto *ArrTy = dyn_cast(Ty)) { + for (size_t I = 0; I < ArrTy->getNumElements(); ++I) { + Constant *El = cast(C->getOperand(I)); + collectCompositeElementsDefaultValuesRecursive(M, El, Offset, + DefaultValues); + } + } else if (auto *StructTy = dyn_cast(Ty)) { + const StructLayout *SL = M.getDataLayout().getStructLayout(StructTy); + for (size_t I = 0, E = StructTy->getNumElements(); I < E; ++I) { + Constant *El = cast(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(Ty)) { + for (size_t I = 0; I < VecTy->getNumElements(); ++I) { + Constant *El = cast(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(C)) { + auto Val = IntConst->getValue().getZExtValue(); + CharPtr = reinterpret_cast(&Val); + } else if (auto FPConst = dyn_cast(C)) { + auto Val = FPConst->getValue(); + + if (NumBytes == 4) { + float v = Val.convertToFloat(); + CharPtr = reinterpret_cast(&v); + } else if (NumBytes == 8) { + double v = Val.convertToDouble(); + CharPtr = reinterpret_cast(&v); + } + } + std::copy_n(CharPtr, NumBytes, std::back_inserter(DefaultValues)); + Offset += NumBytes; + } +} + MDNode *generateSpecConstantMetadata(const Module &M, StringRef SymbolicID, Type *SCTy, ArrayRef IDs, bool IsNativeSpecConstant) { @@ -476,6 +551,7 @@ PreservedAnalyses SpecConstantsPass::run(Module &M, StringMap> IDMap; StringMap OffsetMap; MapVector SCMetadata; + SmallVector DefaultsMetadata; // Iterate through all declarations of instances of function template // template T __sycl_get*SpecConstantValue(const char *ID) @@ -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( + 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(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); + } + } + 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, @@ -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( - CI->getArgOperand(NameArgNo + 1)->stripPointerCasts()); - if (GV) { - assert(GV->hasInitializer() && "expected initializer"); - auto *Initializer = GV->getInitializer(); - assert((isa(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); @@ -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. @@ -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; @@ -699,3 +788,19 @@ bool SpecConstantsPass::collectSpecConstantMetadata(Module &M, return true; } + +bool SpecConstantsPass::collectSpecConstantDefaultValuesMetadata( + Module &M, std::vector &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(Node->getOperand(0))->getValue(); + collectCompositeElementsDefaultValuesRecursive(M, Constant, Offset, + DefaultValues); + } + + return true; +} diff --git a/llvm/tools/sycl-post-link/SpecConstants.h b/llvm/tools/sycl-post-link/SpecConstants.h index a47af79436bc0..f6d2e645b8694 100644 --- a/llvm/tools/sycl-post-link/SpecConstants.h +++ b/llvm/tools/sycl-post-link/SpecConstants.h @@ -62,6 +62,11 @@ class SpecConstantsPass : public llvm::PassInfoMixin { // 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 &DefaultValues); private: bool SetValAtRT; diff --git a/llvm/tools/sycl-post-link/sycl-post-link.cpp b/llvm/tools/sycl-post-link/sycl-post-link.cpp index acc209e230446..304ce183d44c8 100644 --- a/llvm/tools/sycl-post-link/sycl-post-link.cpp +++ b/llvm/tools/sycl-post-link/sycl-post-link.cpp @@ -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 DefaultValues; + SpecConstantsPass::collectSpecConstantDefaultValuesMetadata( + *ResultModules[I].get(), DefaultValues); + PropSet.add(llvm::util::PropertySetRegistry:: + SYCL_SPEC_CONSTANTS_DEFAULT_VALUES, + "all", DefaultValues); + } } } if (ImgPSInfo.EmitKernelParamInfo) { diff --git a/sycl/test/basic_tests/SYCL-2020-spec-constants.cpp b/sycl/test/basic_tests/SYCL-2020-spec-constants.cpp index 8bfb21a2f11d3..1db6b8f2587da 100644 --- a/sycl/test/basic_tests/SYCL-2020-spec-constants.cpp +++ b/sycl/test/basic_tests/SYCL-2020-spec-constants.cpp @@ -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 // @@ -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|