Files
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

313 lines
11 KiB
C++

//===- SPIRVInstruction.cpp -Class to represent SPIR-V instruction - C++ --===//
//
// The LLVM/SPIRV Translator
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
// Copyright (c) 2014 Advanced Micro Devices, Inc. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal with the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimers.
// Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimers in the documentation
// and/or other materials provided with the distribution.
// Neither the names of Advanced Micro Devices, Inc., nor the names of its
// contributors may be used to endorse or promote products derived from this
// Software without specific prior written permission.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH
// THE SOFTWARE.
//
//===----------------------------------------------------------------------===//
/// \file
///
/// This file implements SPIR-V instructions.
///
//===----------------------------------------------------------------------===//
#include "SPIRVInstruction.h"
#include "SPIRVBasicBlock.h"
#include "SPIRVFunction.h"
#include "SPIRVInternal.h"
#include <unordered_set>
namespace SPIRV {
// Complete constructor for instruction with type and id
SPIRVInstruction::SPIRVInstruction(unsigned TheWordCount, Op TheOC,
SPIRVType *TheType, SPIRVId TheId,
SPIRVBasicBlock *TheBB)
: SPIRVValue(TheBB->getModule(), TheWordCount, TheOC, TheType, TheId),
BB(TheBB), DebugScope(nullptr) {
SPIRVInstruction::validate();
}
SPIRVInstruction::SPIRVInstruction(unsigned TheWordCount, Op TheOC,
SPIRVType *TheType, SPIRVId TheId,
SPIRVBasicBlock *TheBB, SPIRVModule *TheBM)
: SPIRVValue(TheBM, TheWordCount, TheOC, TheType, TheId), BB(TheBB),
DebugScope(nullptr) {
SPIRVInstruction::validate();
}
// Complete constructor for instruction with id but no type
SPIRVInstruction::SPIRVInstruction(unsigned TheWordCount, Op TheOC,
SPIRVId TheId, SPIRVBasicBlock *TheBB)
: SPIRVValue(TheBB->getModule(), TheWordCount, TheOC, TheId), BB(TheBB),
DebugScope(nullptr) {
SPIRVInstruction::validate();
}
// Complete constructor for instruction without type and id
SPIRVInstruction::SPIRVInstruction(unsigned TheWordCount, Op TheOC,
SPIRVBasicBlock *TheBB)
: SPIRVValue(TheBB->getModule(), TheWordCount, TheOC), BB(TheBB),
DebugScope(nullptr) {
SPIRVInstruction::validate();
}
// Complete constructor for instruction with type but no id
SPIRVInstruction::SPIRVInstruction(unsigned TheWordCount, Op TheOC,
SPIRVType *TheType, SPIRVBasicBlock *TheBB)
: SPIRVValue(TheBB->getModule(), TheWordCount, TheOC, TheType), BB(TheBB),
DebugScope(nullptr) {
SPIRVInstruction::validate();
}
// Special constructor for debug instruction
SPIRVInstruction::SPIRVInstruction(unsigned TheWordCount, Op TheOC,
SPIRVType *TheType, SPIRVId TheId,
SPIRVModule *TheBM, SPIRVBasicBlock *TheBB)
: SPIRVValue(TheBM, TheWordCount, TheOC, TheType, TheId), BB(TheBB),
DebugScope(nullptr) {
SPIRVInstruction::validate();
}
void SPIRVInstruction::setParent(SPIRVBasicBlock *TheBB) {
assert(TheBB && "Invalid BB");
if (BB == TheBB)
return;
assert(BB == NULL && "BB cannot change parent");
BB = TheBB;
}
void SPIRVInstruction::setScope(SPIRVEntry *Scope) {
assert(Scope && Scope->getOpCode() == OpLabel && "Invalid scope");
setParent(static_cast<SPIRVBasicBlock *>(Scope));
}
SPIRVFunctionCall::SPIRVFunctionCall(SPIRVId TheId, SPIRVFunction *TheFunction,
const std::vector<SPIRVWord> &TheArgs,
SPIRVBasicBlock *BB)
: SPIRVFunctionCallGeneric(TheFunction->getFunctionType()->getReturnType(),
TheId, TheArgs, BB),
FunctionId(TheFunction->getId()) {
validate();
}
void SPIRVFunctionCall::validate() const {
SPIRVFunctionCallGeneric::validate();
}
SPIRVFunctionPointerCallINTEL::SPIRVFunctionPointerCallINTEL(
SPIRVId TheId, SPIRVValue *TheCalledValue, SPIRVType *TheReturnType,
const std::vector<SPIRVWord> &TheArgs, SPIRVBasicBlock *BB)
: SPIRVFunctionCallGeneric(TheReturnType, TheId, TheArgs, BB),
CalledValueId(TheCalledValue->getId()) {
validate();
}
void SPIRVFunctionPointerCallINTEL::validate() const {
SPIRVFunctionCallGeneric::validate();
}
// ToDo: Each instruction should implement this function
std::vector<SPIRVValue *> SPIRVInstruction::getOperands() {
std::vector<SPIRVValue *> Empty;
assert(0 && "not supported");
return Empty;
}
std::vector<SPIRVType *>
SPIRVInstruction::getOperandTypes(const std::vector<SPIRVValue *> &Ops) {
std::vector<SPIRVType *> Tys;
for (auto &I : Ops) {
SPIRVType *Ty = nullptr;
if (I->getOpCode() == OpFunction)
Ty = reinterpret_cast<SPIRVFunction *>(I)->getFunctionType();
else if (I->getOpCode() == OpTypeCooperativeMatrixKHR)
Ty = reinterpret_cast<SPIRVType *>(I);
else
Ty = I->getType();
Tys.push_back(Ty);
}
return Tys;
}
void SPIRVImageInstBase::setOpWords(const std::vector<SPIRVWord> &OpsArg) {
std::vector<SPIRVWord> Ops = OpsArg;
// If the Image Operands field has the SignExtend or ZeroExtend bit set,
// either raise the minimum SPIR-V version to 1.4, or drop the operand
// if SPIR-V 1.4 cannot be emitted.
size_t ImgOpsIndex = getImageOperandsIndex(OpCode);
if (ImgOpsIndex != ~0U && ImgOpsIndex < Ops.size()) {
SPIRVWord ImgOps = Ops[ImgOpsIndex];
unsigned SignZeroExtMasks = ImageOperandsMask::ImageOperandsSignExtendMask |
ImageOperandsMask::ImageOperandsZeroExtendMask;
if (ImgOps & SignZeroExtMasks) {
SPIRVModule *M = getModule();
if (M->isAllowedToUseVersion(VersionNumber::SPIRV_1_4)) {
M->setMinSPIRVVersion(VersionNumber::SPIRV_1_4);
} else {
// Drop SignExtend/ZeroExtend if we cannot use SPIR-V 1.4.
Ops[ImgOpsIndex] &= ~SignZeroExtMasks;
if (Ops[ImgOpsIndex] == 0) {
// Drop the Image Operands if SignExtend/ZeroExtend was the only
// bit set.
Ops.pop_back();
}
}
}
}
SPIRVInstTemplateBase::setOpWords(Ops);
}
bool SPIRVImageInstBase::hasImageOperand(ImageOperandsMask Mask) const {
size_t ImgOpsIndex = getImageOperandsIndex(OpCode);
if (ImgOpsIndex == ~0U)
return false;
if (getOpWords().size() <= ImgOpsIndex)
return false;
return getOpWord(ImgOpsIndex) & Mask;
}
bool isSpecConstantOpAllowedOp(Op OC) {
static SPIRVWord Table[] = {
OpSConvert,
OpFConvert,
OpConvertFToS,
OpConvertSToF,
OpConvertFToU,
OpConvertUToF,
OpUConvert,
OpConvertPtrToU,
OpConvertUToPtr,
OpGenericCastToPtr,
OpPtrCastToGeneric,
OpCrossWorkgroupCastToPtrINTEL,
OpPtrCastToCrossWorkgroupINTEL,
OpBitcast,
OpQuantizeToF16,
OpSNegate,
OpNot,
OpIAdd,
OpISub,
OpIMul,
OpUDiv,
OpSDiv,
OpUMod,
OpSRem,
OpSMod,
OpShiftRightLogical,
OpShiftRightArithmetic,
OpShiftLeftLogical,
OpBitwiseOr,
OpBitwiseXor,
OpBitwiseAnd,
OpFNegate,
OpFAdd,
OpFSub,
OpFMul,
OpFDiv,
OpFRem,
OpFMod,
OpVectorShuffle,
OpCompositeExtract,
OpCompositeInsert,
OpLogicalOr,
OpLogicalAnd,
OpLogicalNot,
OpLogicalEqual,
OpLogicalNotEqual,
OpSelect,
OpIEqual,
OpINotEqual,
OpULessThan,
OpSLessThan,
OpUGreaterThan,
OpSGreaterThan,
OpULessThanEqual,
OpSLessThanEqual,
OpUGreaterThanEqual,
OpSGreaterThanEqual,
OpAccessChain,
OpInBoundsAccessChain,
OpPtrAccessChain,
OpInBoundsPtrAccessChain,
OpUntypedAccessChainKHR,
OpUntypedInBoundsAccessChainKHR,
OpUntypedPtrAccessChainKHR,
OpUntypedInBoundsPtrAccessChainKHR,
};
static std::unordered_set<SPIRVWord> Allow(std::begin(Table),
std::end(Table));
return Allow.count(OC);
}
SPIRVSpecConstantOp *createSpecConstantOpInst(SPIRVInstruction *Inst) {
auto OC = Inst->getOpCode();
assert(isSpecConstantOpAllowedOp(OC) &&
"Op code not allowed for OpSpecConstantOp");
std::vector<SPIRVWord> Ops;
// CompositeExtract/Insert operations use zero-based numbering for their
// indexes (containted in instruction operands). All their operands are
// Literals, so we can pass them as is for further handling.
if (OC == OpCompositeExtract || OC == OpCompositeInsert) {
auto *SPIRVInst = static_cast<SPIRVInstTemplateBase *>(Inst);
Ops = SPIRVInst->getOpWords();
} else {
Ops = Inst->getIds(Inst->getOperands());
}
Ops.insert(Ops.begin(), OC);
return static_cast<SPIRVSpecConstantOp *>(SPIRVSpecConstantOp::create(
OpSpecConstantOp, Inst->getType(), Inst->getId(), Ops, nullptr,
Inst->getModule()));
}
SPIRVInstruction *createInstFromSpecConstantOp(SPIRVSpecConstantOp *Inst) {
assert(Inst->getOpCode() == OpSpecConstantOp && "Not OpSpecConstantOp");
auto Ops = Inst->getOpWords();
auto OC = static_cast<Op>(Ops[0]);
assert(isSpecConstantOpAllowedOp(OC) &&
"Op code not allowed for OpSpecConstantOp");
auto *Const = Inst->getOperand(1);
// LLVM would eliminate a bitcast from a function pointer in a constexpr
// context. Cut this short here to avoid necessity to align address spaces
if (OC == OpBitcast && Const->getOpCode() == OpConstantFunctionPointerINTEL)
return static_cast<SPIRVInstruction *>(Const);
Ops.erase(Ops.begin(), Ops.begin() + 1);
auto *BM = Inst->getModule();
auto *RetInst = SPIRVInstTemplateBase::create(
OC, Inst->getType(), Inst->getId(), Ops, nullptr, BM);
// Instruction that creates from OpSpecConstantOp has the same Id
BM->insertEntryNoId(RetInst);
return RetInst;
}
} // namespace SPIRV