Files
TheRock/compiler/spirv-llvm-translator/lib/SPIRV/libSPIRV/SPIRVFnVar.h
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

446 lines
15 KiB
C++

//===- SPIRVFnVar.cpp -===//
//
// The LLVM/SPIRV Translator
//
// Copyright (c) 2025 The Khronos Group Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file defines entries from the SPV_INTEL_function_variants extension. It
/// also provides a function for specializing a multi-target module into a
/// targeted one.
///
//===----------------------------------------------------------------------===//
#ifndef SPIRV_LIBSPIRV_SPIRVFNVAR_H
#define SPIRV_LIBSPIRV_SPIRVFNVAR_H
#include "SPIRVEntry.h"
#include "SPIRVInstruction.h"
#include "SPIRVValue.h"
#if _SPIRVDBG
#include <iomanip>
#endif
namespace SPIRV {
// Specialize multitarget module into a targeted one according to
// SPV_INTEL_function_variants.
//
// This is a SPIR-V to SPIR-V transform. After the transform, the module should
// not contain any notion of SPV_INTEL_function_variants and can be processed
// even by consumers that do not support this extension.
bool specializeFnVariants(SPIRVModule *BM, std::string &ErrMsg);
// Below are entries defined by the extension:
class SPIRVConditionalEntryPointINTEL : public SPIRVAnnotation {
public:
static const SPIRVWord FixedWC = 5;
SPIRVConditionalEntryPointINTEL(SPIRVModule *TheModule, SPIRVId Condition,
SPIRVExecutionModelKind TheExecModel,
SPIRVId TheId, const std::string &TheName,
std::vector<SPIRVId> Variables)
: SPIRVAnnotation(OpConditionalEntryPointINTEL,
TheModule->get<SPIRVFunction>(TheId),
getSizeInWords(TheName) + Variables.size() + 4),
Condition(Condition), ExecModel(TheExecModel), Name(TheName),
Variables(std::move(Variables)) {}
SPIRVConditionalEntryPointINTEL()
: SPIRVAnnotation(OpConditionalEntryPointINTEL), Condition() {}
SPIRVId getCondition() const { return Condition; }
SPIRVExecutionModelKind getExecModel() const { return ExecModel; }
std::string getName() const { return Name; }
std::vector<SPIRVId> getVariables() const { return Variables; }
protected:
void encode(spv_ostream &O) const override {
getEncoder(O) << Condition << ExecModel << Target << Name << Variables;
}
void decode(std::istream &I) override {
getDecoder(I) >> Condition >> ExecModel >> Target >> Name;
SPIRVWord NameWC = getSizeInWords(Name);
SPIRVCK(WordCount >= FixedWC + NameWC - 1, InvalidWordCount, "");
Variables.resize(WordCount - FixedWC - NameWC + 1);
getDecoder(I) >> Variables;
Module->setName(getOrCreateTarget(), Name);
Module->addConditionalEntryPoint(Condition, ExecModel, Target, Name,
Variables);
}
private:
SPIRVId Condition;
SPIRVExecutionModelKind ExecModel = ExecutionModelMax;
std::string Name;
std::vector<SPIRVId> Variables;
};
class SPIRVConditionalExtensionINTEL
: public SPIRVEntryNoId<OpConditionalExtensionINTEL> {
public:
SPIRVConditionalExtensionINTEL(SPIRVModule *M, SPIRVId C,
const std::string &SS)
: SPIRVEntryNoId(M, 2 + getSizeInWords(SS)), Condition(C), S(SS) {}
SPIRVConditionalExtensionINTEL() : Condition() {}
std::string getExtensionName() const { return S; }
SPIRVId getCondition() const { return Condition; }
protected:
void encode(spv_ostream &O) const override {
getEncoder(O) << Condition << S;
}
void decode(std::istream &I) override {
getDecoder(I) >> Condition >> S;
Module->getConditionalExtensions().insert(std::make_pair(Condition, S));
}
private:
SPIRVId Condition;
std::string S;
};
class SPIRVConditionalCapabilityINTEL
: public SPIRVEntryNoId<OpConditionalCapabilityINTEL> {
public:
static const SPIRVWord FixedWC = 3;
SPIRVConditionalCapabilityINTEL(SPIRVModule *M, SPIRVId C,
SPIRVCapabilityKind K)
: SPIRVEntryNoId(M, 3), Condition(C), Kind(K) {
updateModuleVersion();
}
SPIRVConditionalCapabilityINTEL() : Condition(), Kind() {}
SPIRVId getCondition() const { return Condition; }
protected:
void encode(spv_ostream &O) const override {
getEncoder(O) << Condition << Kind;
}
void decode(std::istream &I) override {
getDecoder(I) >> Condition >> Kind;
Module->addConditionalCapability(Condition, Kind);
}
private:
SPIRVId Condition;
SPIRVCapabilityKind Kind;
};
class SPIRVConditionalCopyObjectINTEL : public SPIRVInstruction {
public:
const static Op OC = OpConditionalCopyObjectINTEL;
const static SPIRVWord FixedWordCount = 3;
// Complete constructor
SPIRVConditionalCopyObjectINTEL(SPIRVType *TheType, SPIRVId TheId,
const std::vector<SPIRVId> &TheConstituents,
SPIRVBasicBlock *TheBB)
: SPIRVInstruction(FixedWordCount + TheConstituents.size(), OC, TheType,
TheId, TheBB),
Constituents(TheConstituents) {
validate();
assert(TheBB && "Invalid BB");
}
// Incomplete constructor
SPIRVConditionalCopyObjectINTEL() : SPIRVInstruction(OC) {}
std::vector<SPIRVId> getOperandIds() { return Constituents; }
std::vector<SPIRVValue *> getOperands() override {
return getValues(Constituents);
}
protected:
void setWordCount(SPIRVWord TheWordCount) override {
SPIRVEntry::setWordCount(TheWordCount);
SPIRVCK(TheWordCount >= FixedWordCount, InvalidWordCount, "");
Constituents.resize(TheWordCount - FixedWordCount);
}
_SPIRV_DEF_ENCDEC3(Type, Id, Constituents)
void validate() const override {
SPIRVInstruction::validate();
size_t TypeOpCode = this->getType()->getOpCode();
assert(TypeOpCode != OpTypeVoid && "Conditional copy type cannot be void");
(void)(TypeOpCode);
assert(Constituents.size() % 2 == 0 &&
"Conditional copy requires condition-operand pairs");
assert(Constituents.size() >= 2 &&
"Conditional copy requires at least one condition-operand pair");
}
std::vector<SPIRVId> Constituents;
};
class SPIRVSpecConstantTargetINTEL : public SPIRVValue {
public:
constexpr static SPIRVWord FixedWC = 4;
constexpr static spv::Op OC = OpSpecConstantTargetINTEL;
// Complete constructor
SPIRVSpecConstantTargetINTEL(SPIRVModule *M, SPIRVType *TheType,
SPIRVId TheId, SPIRVWord TheTarget,
const std::vector<SPIRVWord> TheFeatures)
: SPIRVValue(M, TheFeatures.size() + FixedWC, OC, TheType, TheId) {
Features = TheFeatures;
Target = TheTarget;
NumWords = TheFeatures.size() + FixedWC;
validate();
}
// Incomplete constructor
SPIRVSpecConstantTargetINTEL() : SPIRVValue(OC), NumWords(), Target() {}
SPIRVWord getTarget() const { return Target; }
bool matchesDevice() {
std::optional<SPIRVWord> DeviceTarget = getModule()->getFnVarTarget();
std::vector<SPIRVWord> DeviceFeatures = getModule()->getFnVarFeatures();
bool Res = true;
if (DeviceTarget != std::nullopt && DeviceTarget.value() != Target) {
Res = false;
}
if (!DeviceFeatures.empty()) {
for (const auto &Feature : Features) {
if (std::find(DeviceFeatures.cbegin(), DeviceFeatures.cend(),
Feature) == DeviceFeatures.cend()) {
Res = false;
}
}
}
SPIRVDBG(
spvdbgs() << "[FnVar] match instr Target: ";
spvdbgs() << std::setw(4) << Target;
spvdbgs() << ", Features:"; if (Features.empty()) {
spvdbgs() << " none";
} else {
for (const auto &Feat : Features) {
spvdbgs() << " " << Feat;
}
} spvdbgs() << " | ID: %"
<< getId() << std::endl;
spvdbgs() << "[FnVar] device Target: "; if (DeviceTarget ==
std::nullopt) {
spvdbgs() << "none";
} else {
spvdbgs() << std::setw(4) << DeviceTarget.value();
} spvdbgs() << ", Features:";
for (const auto &Feat : DeviceFeatures) {
spvdbgs() << " " << Feat;
} spvdbgs()
<< std::endl;
spvdbgs() << "[FnVar] result: " << Res << std::endl;);
return Res;
}
protected:
_SPIRV_DEF_ENCDEC4(Type, Id, Target, Features);
void setWordCount(SPIRVWord WordCount) override {
SPIRVEntry::setWordCount(WordCount);
SPIRVCK(WordCount >= FixedWC, InvalidWordCount, "");
Features.resize(WordCount - FixedWC);
NumWords = WordCount - FixedWC;
}
private:
unsigned NumWords;
std::vector<SPIRVWord> Features;
SPIRVWord Target;
};
class SPIRVSpecConstantArchitectureINTEL : public SPIRVValue {
public:
constexpr static SPIRVWord FixedWC = 7;
constexpr static spv::Op OC = OpSpecConstantArchitectureINTEL;
// Complete constructor
SPIRVSpecConstantArchitectureINTEL(SPIRVModule *M, SPIRVType *TheType,
SPIRVId TheId, SPIRVWord TheCategory,
SPIRVWord TheFamily, spv::Op TheCmpOp,
SPIRVWord TheArchitecture)
: SPIRVValue(M, FixedWC, OC, TheType, TheId) {
Category = TheCategory;
Family = TheFamily;
CmpOp = TheCmpOp;
Architecture = TheArchitecture;
validate();
}
// Incomplete constructor
SPIRVSpecConstantArchitectureINTEL()
: SPIRVValue(OC), Category(), Family(), CmpOp(), Architecture() {}
SPIRVWord getCategory() { return Category; }
SPIRVWord getFamily() { return Family; }
spv::Op getCmpOp() { return CmpOp; }
SPIRVWord getArchitecture() { return Architecture; }
bool matchesDevice() {
std::optional<SPIRVWord> DeviceCategory = getModule()->getFnVarCategory();
std::optional<SPIRVWord> DeviceFamily = getModule()->getFnVarFamily();
std::optional<SPIRVWord> DeviceArchitecture = getModule()->getFnVarArch();
bool Res = true;
if (DeviceCategory != std::nullopt && DeviceCategory.value() != Category) {
Res = false;
}
if (DeviceFamily != std::nullopt && DeviceFamily.value() != Family) {
Res = false;
}
if (DeviceArchitecture != std::nullopt) {
switch (CmpOp) {
case OpIEqual:
Res = DeviceArchitecture == Architecture;
break;
case OpINotEqual:
Res = DeviceArchitecture != Architecture;
break;
case OpULessThan:
Res = DeviceArchitecture < Architecture;
break;
case OpULessThanEqual:
Res = DeviceArchitecture <= Architecture;
break;
case OpUGreaterThan:
Res = DeviceArchitecture > Architecture;
break;
case OpUGreaterThanEqual:
Res = DeviceArchitecture >= Architecture;
break;
default:
assert(false && "Invalid checked CmpOp");
Res = false;
break;
}
}
SPIRVDBG(
spvdbgs() << "[FnVar] match instr Category: " << std::setw(4)
<< Category
<< ", Family: " << std::setw(4) << Family << ", CmpOp: "
<< std::setw(4) << CmpOp << ", Architecture: " << std::setw(4)
<< Architecture << " | ID: %" << getId() << std::endl;
spvdbgs() << "[FnVar] device Category: "; if (DeviceCategory ==
std::nullopt) {
spvdbgs() << "none";
} else {
spvdbgs() << std::setw(4) << DeviceCategory.value();
} spvdbgs() << ", Family: ";
if (DeviceFamily == std::nullopt) { spvdbgs() << "none"; } else {
spvdbgs() << std::setw(4) << DeviceFamily.value();
} spvdbgs()
<< ", Architecture: ";
if (DeviceArchitecture == std::nullopt) { spvdbgs() << "none"; } else {
spvdbgs() << std::setw(4) << DeviceArchitecture.value();
} spvdbgs()
<< std::endl;
spvdbgs() << "[FnVar] result: " << Res << std::endl;);
return Res;
}
protected:
_SPIRV_DEF_ENCDEC6(Type, Id, Category, Family, CmpOp, Architecture);
private:
SPIRVWord Category;
SPIRVWord Family;
spv::Op CmpOp;
SPIRVWord Architecture;
};
class SPIRVSpecConstantCapabilitiesINTEL : public SPIRVValue {
public:
constexpr static SPIRVWord FixedWC = 3;
constexpr static spv::Op OC = OpSpecConstantCapabilitiesINTEL;
// Complete constructor
SPIRVSpecConstantCapabilitiesINTEL(
SPIRVModule *M, SPIRVType *TheType, SPIRVId TheId,
const std::vector<SPIRVWord> TheCapabilities)
: SPIRVValue(M, TheCapabilities.size() + FixedWC, OC, TheType, TheId) {
Capabilities = TheCapabilities;
NumWords = TheCapabilities.size() + FixedWC;
validate();
}
// Incomplete constructor
SPIRVSpecConstantCapabilitiesINTEL() : SPIRVValue(OC), NumWords() {}
std::vector<SPIRVWord> getCapabilities() const { return Capabilities; }
bool matchesDevice() {
std::vector<SPIRVWord> DeviceCapabilities =
getModule()->getFnVarCapabilities();
bool Res = true;
if (!DeviceCapabilities.empty()) {
for (const auto &Capability : Capabilities) {
if (std::find(DeviceCapabilities.cbegin(), DeviceCapabilities.cend(),
Capability) == DeviceCapabilities.cend()) {
Res = false;
}
}
}
SPIRVDBG(
spvdbgs() << "[FnVar] match instr Capabilities: ";
if (Capabilities.empty()) { spvdbgs() << "none"; } else {
for (const auto &Cap : Capabilities) {
spvdbgs() << " " << Cap;
}
} spvdbgs()
<< " | ID: %" << getId() << std::endl;
spvdbgs() << "[FnVar] device Capabilities: ";
for (const auto &Cap : DeviceCapabilities) {
spvdbgs() << " " << Cap;
} spvdbgs()
<< std::endl;
spvdbgs() << "[FnVar] result: " << Res << std::endl;);
return Res;
}
protected:
_SPIRV_DEF_ENCDEC3(Type, Id, Capabilities);
void validate() const override {
SPIRVValue::validate();
if (Capabilities.size() < 1) {
std::stringstream SS;
SS << "Id: " << Id << ", OpCode: " << OpCodeNameMap::map(OpCode)
<< ", Name: \"" << Name << "\". Expected at least one capability.\n";
getErrorLog().checkError(false, SPIRVEC_InvalidNumberOfOperands,
SS.str());
}
}
void setWordCount(SPIRVWord WordCount) override {
SPIRVEntry::setWordCount(WordCount);
SPIRVCK(WordCount >= FixedWC, InvalidWordCount, "");
Capabilities.resize(WordCount - FixedWC);
NumWords = WordCount - FixedWC;
}
private:
unsigned NumWords;
std::vector<SPIRVWord> Capabilities;
};
} // namespace SPIRV
#endif // SPIRV_LIBSPIRV_SPIRVFNVAR_H