Skip to content

Commit

Permalink
[SYCL] Add kernel property parsing to sycl-post-link (#7017)
Browse files Browse the repository at this point in the history
This commit adds parsing of compile-time properties for kernels to
sycl-post-link. This includes the following:
* sycl-post-link is now able to generate metadata for recognized SYCL
LLVM IR attributes on functions. These are "sycl-work-group-size",
"sycl-work-group-size-hint", and "sycl-sub-group-size".
* The previously mentioned new recognized LLVM IR attributes are
translated into existing metadata the SPIR-V Translator translates into
corresponding SPIR-V execution modes. If these metadata nodes already
exist on a function (e.g. added through a SYCL 2020 attribute), the
related property is ignored.

This is split from #6941.

Signed-off-by: Larsen, Steffen <steffen.larsen@intel.com>
  • Loading branch information
steffenlarsen authored Oct 11, 2022
1 parent 404b8e3 commit 332e4ee
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 12 deletions.
46 changes: 46 additions & 0 deletions llvm/test/tools/sycl-post-link/kernel-properties.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
; RUN: sycl-post-link --ir-output-only --device-globals %s -S -o - | FileCheck %s --check-prefix CHECK-IR

; CHECK-IR-DAG: @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE9TheKernel0"() #0 {{.*}}!intel_reqd_sub_group_size ![[SGSizeMD0:[0-9]+]] {{.*}}!reqd_work_group_size ![[WGSizeMD0:[0-9]+]]{{.*}}!work_group_size_hint ![[WGSizeHintMD0:[0-9]+]]
; Function Attrs: convergent norecurse
define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE9TheKernel0"() #0 {
entry:
ret void
}

; CHECK-IR-DAG: @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE9TheKernel1"() #1 {{.*}}!reqd_work_group_size ![[WGSizeMD1:[0-9]+]]{{.*}}!work_group_size_hint ![[WGSizeHintMD1:[0-9]+]]
; Function Attrs: convergent norecurse
define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE9TheKernel1"() #1 {
entry:
ret void
}

; CHECK-IR-DAG: @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE9TheKernel2"() #2 {{.*}}!reqd_work_group_size ![[WGSizeMD2:[0-9]+]]{{.*}}!work_group_size_hint ![[WGSizeHintMD2:[0-9]+]]
; Function Attrs: convergent norecurse
define weak_odr dso_local spir_kernel void @"_ZTSZZ4mainENK3$_0clERN2cl4sycl7handlerEE9TheKernel2"() #2 {
entry:
ret void
}

attributes #0 = { convergent norecurse "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "sycl-module-id"="kernel_properties.cpp" "uniform-work-group-size"="true" "sycl-work-group-size"="1" "sycl-work-group-size-hint"="2" "sycl-sub-group-size"="3" }
attributes #1 = { convergent norecurse "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "sycl-module-id"="kernel_properties.cpp" "uniform-work-group-size"="true" "sycl-work-group-size"="4,5" "sycl-work-group-size-hint"="6,7" }
attributes #2 = { convergent norecurse "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "sycl-module-id"="kernel_properties.cpp" "uniform-work-group-size"="true" "sycl-work-group-size"="8,9,10" "sycl-work-group-size-hint"="11,12,13" }

!opencl.spir.version = !{!0, !0, !0, !0, !0, !0, !0, !0, !0, !0, !0}
!spirv.Source = !{!1, !1, !1, !1, !1, !1, !1, !1, !1, !1, !1}
!llvm.ident = !{!2, !2, !2, !2, !2, !2, !2, !2, !2, !2, !2}
!llvm.module.flags = !{!3, !4}

!0 = !{i32 1, i32 2}
!1 = !{i32 4, i32 100000}
!2 = !{!"clang version 13.0.0 (https://github.com/intel/llvm)"}
!3 = !{i32 1, !"wchar_size", i32 4}
!4 = !{i32 7, !"frame-pointer", i32 2}

; Note that work-group sizes are padded with 1's after being reversed.
; CHECK-IR-DAG: ![[SGSizeMD0]] = !{i32 3}
; CHECK-IR-DAG: ![[WGSizeMD0]] = !{i{{[0-9]+}} 1, i{{[0-9]+}} 1, i{{[0-9]+}} 1}
; CHECK-IR-DAG: ![[WGSizeHintMD0]] = !{i{{[0-9]+}} 2, i{{[0-9]+}} 1, i{{[0-9]+}} 1}
; CHECK-IR-DAG: ![[WGSizeMD1]] = !{i{{[0-9]+}} 5, i{{[0-9]+}} 4, i{{[0-9]+}} 1}
; CHECK-IR-DAG: ![[WGSizeHintMD1]] = !{i{{[0-9]+}} 7, i{{[0-9]+}} 6, i{{[0-9]+}} 1}
; CHECK-IR-DAG: ![[WGSizeMD2]] = !{i{{[0-9]+}} 10, i{{[0-9]+}} 9, i{{[0-9]+}} 8}
; CHECK-IR-DAG: ![[WGSizeHintMD2]] = !{i{{[0-9]+}} 13, i{{[0-9]+}} 12, i{{[0-9]+}} 11}
154 changes: 142 additions & 12 deletions llvm/tools/sycl-post-link/CompileTimePropertiesPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,15 @@ MDNode *buildSpirvDecorMetadata(LLVMContext &Ctx, uint32_t OpCode,
return MDNode::get(Ctx, MD);
}

/// Gets the string value in a global variable. If the parameter is not a global
/// variable or it does not contain string data, then \c None is returned.
///
/// @param StringV [in] the LLVM value of supposed \c GlobalVariable type with
/// a string value.
///
/// @returns a \c StringRef with the string contained in \c StringV and \c None
/// if \c StringV is not a \c GlobalVariable or does not contain string
/// data.
Optional<StringRef> getGlobalVariableString(const Value *StringV) {
if (const auto *StringGV = dyn_cast<GlobalVariable>(StringV))
if (const auto *StringData =
Expand All @@ -102,6 +111,106 @@ Optional<StringRef> getGlobalVariableString(const Value *StringV) {
return {};
}

/// Tries to generate a SPIR-V decorate metadata node from an attribute. If
/// the attribute is unknown \c nullptr will be returned.
///
/// @param Ctx [in] the LLVM context.
/// @param Attr [in] the LLVM attribute to generate metadata for.
///
/// @returns a pointer to a new metadata node if \c Attr is an attribute with a
/// known corresponding SPIR-V decorate and the arguments are valid.
/// Otherwise \c nullptr is returned.
MDNode *attributeToDecorateMetadata(LLVMContext &Ctx, const Attribute &Attr) {
// Currently, only string attributes are supported
if (!Attr.isStringAttribute())
return nullptr;
auto DecorIt = SpirvDecorMap.find(Attr.getKindAsString());
if (DecorIt == SpirvDecorMap.end())
return nullptr;
Decor DecorFound = DecorIt->second;
uint32_t DecorCode = DecorFound.Code;
switch (DecorFound.Type) {
case DecorValueTy::uint32:
return buildSpirvDecorMetadata(Ctx, DecorCode,
getAttributeAsInteger<uint32_t>(Attr));
case DecorValueTy::boolean:
return buildSpirvDecorMetadata(Ctx, DecorCode, hasProperty(Attr));
default:
llvm_unreachable("Unhandled decorator type.");
}
}

/// Tries to generate a SPIR-V execution mode metadata node from an attribute.
/// If the attribute is unknown \c None will be returned.
///
/// @param M [in] the LLVM module.
/// @param Attr [in] the LLVM attribute to generate metadata for.
///
/// @returns a pair with the name of the resulting metadata and a pointer to
/// the metadata node with its values if the attribute has a
/// corresponding SPIR-V execution mode. Otherwise \c None is returned.
Optional<std::pair<std::string, MDNode *>>
attributeToExecModeMetadata(Module &M, const Attribute &Attr) {
LLVMContext &Ctx = M.getContext();
const DataLayout &DLayout = M.getDataLayout();

// Currently, only string attributes are supported
if (!Attr.isStringAttribute())
return None;
StringRef AttrKindStr = Attr.getKindAsString();
// Early exit if it is not a sycl-* attribute.
if (!AttrKindStr.startswith("sycl-"))
return None;

if (AttrKindStr == "sycl-work-group-size" ||
AttrKindStr == "sycl-work-group-size-hint") {
// Split values in the comma-separated list integers.
SmallVector<StringRef, 3> ValStrs;
Attr.getValueAsString().split(ValStrs, ',');

assert(ValStrs.size() <= 3 &&
"sycl-work-group-size and sycl-work-group-size-hint currently only "
"support up to three values");

// SYCL work-group sizes must be reversed for SPIR-V.
std::reverse(ValStrs.begin(), ValStrs.end());

// Use integer pointer size as closest analogue to size_t.
IntegerType *IntPtrTy = DLayout.getIntPtrType(Ctx);
IntegerType *SizeTTy = Type::getIntNTy(Ctx, IntPtrTy->getBitWidth());
unsigned SizeTBitSize = SizeTTy->getBitWidth();

// Get the integers from the strings.
SmallVector<Metadata *, 3> MDVals;
for (StringRef ValStr : ValStrs)
MDVals.push_back(ConstantAsMetadata::get(Constant::getIntegerValue(
SizeTTy, APInt(SizeTBitSize, ValStr, 10))));

// The SPIR-V translator expects 3 values, so we pad the remaining
// dimensions with 1.
for (size_t I = MDVals.size(); I < 3; ++I)
MDVals.push_back(ConstantAsMetadata::get(
Constant::getIntegerValue(SizeTTy, APInt(SizeTBitSize, 1))));

const char *MDName = (AttrKindStr == "sycl-work-group-size")
? "reqd_work_group_size"
: "work_group_size_hint";
return std::pair<std::string, MDNode *>(MDName, MDNode::get(Ctx, MDVals));
}

if (AttrKindStr == "sycl-sub-group-size") {
uint32_t SubGroupSize = getAttributeAsInteger<uint32_t>(Attr);
IntegerType *Ty = Type::getInt32Ty(Ctx);
Metadata *MDVal = ConstantAsMetadata::get(
Constant::getIntegerValue(Ty, APInt(32, SubGroupSize)));
SmallVector<Metadata *, 1> MD{MDVal};
return std::pair<std::string, MDNode *>("intel_reqd_sub_group_size",
MDNode::get(Ctx, MD));
}

return None;
}

} // anonymous namespace

PreservedAnalyses CompileTimePropertiesPass::run(Module &M,
Expand All @@ -117,18 +226,9 @@ PreservedAnalyses CompileTimePropertiesPass::run(Module &M,
// decorations in the SPV_INTEL_* extensions.
SmallVector<Metadata *, 8> MDOps;
for (auto &Attribute : GV.getAttributes()) {
// Currently, only string attributes are supported
if (!Attribute.isStringAttribute())
continue;
auto DecorIt = SpirvDecorMap.find(Attribute.getKindAsString());
if (DecorIt == SpirvDecorMap.end())
continue;
auto Decor = DecorIt->second;
auto DecorCode = Decor.Code;
auto DecorValue = Decor.Type == DecorValueTy::uint32
? getAttributeAsInteger<uint32_t>(Attribute)
: hasProperty(Attribute);
MDOps.push_back(buildSpirvDecorMetadata(Ctx, DecorCode, DecorValue));
MDNode *SPIRVMetadata = attributeToDecorateMetadata(Ctx, Attribute);
if (SPIRVMetadata)
MDOps.push_back(SPIRVMetadata);
}

// Some properties should be handled specially.
Expand All @@ -154,6 +254,36 @@ PreservedAnalyses CompileTimePropertiesPass::run(Module &M,
}
}

// Process all properties on kernels.
for (Function &F : M) {
// Only consider kernels.
if (F.getCallingConv() != CallingConv::SPIR_KERNEL)
continue;

SmallVector<Metadata *, 8> MDOps;
SmallVector<std::pair<std::string, MDNode *>, 8> NamedMDOps;
for (const Attribute &Attribute : F.getAttributes().getFnAttrs()) {
if (MDNode *SPIRVMetadata = attributeToDecorateMetadata(Ctx, Attribute))
MDOps.push_back(SPIRVMetadata);
else if (auto NamedMetadata = attributeToExecModeMetadata(M, Attribute))
NamedMDOps.push_back(*NamedMetadata);
}

// Add the generated metadata to the kernel function.
if (!MDOps.empty()) {
F.addMetadata(MDKindID, *MDNode::get(Ctx, MDOps));
CompileTimePropertiesMet = true;
}

// Add the new named metadata to the kernel function.
for (std::pair<std::string, MDNode *> NamedMD : NamedMDOps) {
// If multiple sources defined this metadata, prioritize the existing one.
if (F.hasMetadata(NamedMD.first))
continue;
F.addMetadata(NamedMD.first, *NamedMD.second);
}
}

// Check pointer annotations.
SmallVector<IntrinsicInst *, 4> RemovableAnnots;
for (Function &F : M)
Expand Down

0 comments on commit 332e4ee

Please sign in to comment.