[llvm] 153dee3 - [SPIR-V](6/6) Add the module analysis pass and the simplest tests
Michal Paszkowski via llvm-commits
llvm-commits at lists.llvm.org
Tue Apr 19 16:28:16 PDT 2022
Author: Ilia Diachkov
Date: 2022-04-20T01:10:25+02:00
New Revision: 153dee34f16105dc0db6eb6d5bc617b4e5a31bc6
URL: https://github.com/llvm/llvm-project/commit/153dee34f16105dc0db6eb6d5bc617b4e5a31bc6
DIFF: https://github.com/llvm/llvm-project/commit/153dee34f16105dc0db6eb6d5bc617b4e5a31bc6.diff
LOG: [SPIR-V](6/6) Add the module analysis pass and the simplest tests
This patch adds one SPIRV analysis pass and extends AsmPrinter. It is
essential for minimum SPIR-V output. Also it adds several simplest tests
to show that the target basically works.
Differential Revision: https://reviews.llvm.org/D116465
Authors: Aleksandr Bezzubikov, Lewis Crawford, Ilia Diachkov,
Michal Paszkowski, Andrey Tretyakov, Konrad Trifunovic
Co-authored-by: Aleksandr Bezzubikov <zuban32s at gmail.com>
Co-authored-by: Ilia Diachkov <iliya.diyachkov at intel.com>
Co-authored-by: Michal Paszkowski <michal.paszkowski at outlook.com>
Co-authored-by: Andrey Tretyakov <andrey1.tretyakov at intel.com>
Co-authored-by: Konrad Trifunovic <konrad.trifunovic at intel.com>
Added:
llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
llvm/test/CodeGen/SPIRV/function/identity-function.ll
llvm/test/CodeGen/SPIRV/function/trivial-function-definition.ll
llvm/test/CodeGen/SPIRV/function/trivial-function-with-attributes.ll
llvm/test/CodeGen/SPIRV/function/trivial-function-with-call.ll
llvm/test/CodeGen/SPIRV/lit.local.cfg
llvm/test/CodeGen/SPIRV/metadata-opencl.ll
llvm/test/CodeGen/SPIRV/transcoding/readonly.ll
Modified:
llvm/lib/Target/SPIRV/CMakeLists.txt
llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
llvm/lib/Target/SPIRV/SPIRV.h
llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
llvm/lib/Target/SPIRV/SPIRVMCInstLower.cpp
llvm/lib/Target/SPIRV/SPIRVMCInstLower.h
llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
Removed:
################################################################################
diff --git a/llvm/lib/Target/SPIRV/CMakeLists.txt b/llvm/lib/Target/SPIRV/CMakeLists.txt
index ab7e850172c2f..0c52fed54eb5b 100644
--- a/llvm/lib/Target/SPIRV/CMakeLists.txt
+++ b/llvm/lib/Target/SPIRV/CMakeLists.txt
@@ -21,6 +21,7 @@ add_llvm_target(SPIRVCodeGen
SPIRVISelLowering.cpp
SPIRVLegalizerInfo.cpp
SPIRVMCInstLower.cpp
+ SPIRVModuleAnalysis.cpp
SPIRVRegisterBankInfo.cpp
SPIRVRegisterInfo.cpp
SPIRVSubtarget.cpp
diff --git a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
index dd3a5f0e82d50..3105baa02c90c 100644
--- a/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/MCTargetDesc/SPIRVInstPrinter.cpp
@@ -11,6 +11,8 @@
//===----------------------------------------------------------------------===//
#include "SPIRVInstPrinter.h"
+#include "SPIRV.h"
+#include "SPIRVBaseInfo.h"
#include "llvm/CodeGen/Register.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCExpr.h"
@@ -63,7 +65,113 @@ void SPIRVInstPrinter::recordOpExtInstImport(const MCInst *MI) {
void SPIRVInstPrinter::printInst(const MCInst *MI, uint64_t Address,
StringRef Annot, const MCSubtargetInfo &STI,
raw_ostream &OS) {
+ const unsigned OpCode = MI->getOpcode();
printInstruction(MI, Address, OS);
+
+ if (OpCode == SPIRV::OpDecorate) {
+ printOpDecorate(MI, OS);
+ } else if (OpCode == SPIRV::OpExtInstImport) {
+ recordOpExtInstImport(MI);
+ } else if (OpCode == SPIRV::OpExtInst) {
+ printOpExtInst(MI, OS);
+ } else {
+ // Print any extra operands for variadic instructions.
+ MCInstrDesc MCDesc = MII.get(OpCode);
+ if (MCDesc.isVariadic()) {
+ const unsigned NumFixedOps = MCDesc.getNumOperands();
+ const unsigned LastFixedIndex = NumFixedOps - 1;
+ const int FirstVariableIndex = NumFixedOps;
+ if (NumFixedOps > 0 &&
+ MCDesc.OpInfo[LastFixedIndex].OperandType == MCOI::OPERAND_UNKNOWN) {
+ // For instructions where a custom type (not reg or immediate) comes as
+ // the last operand before the variable_ops. This is usually a StringImm
+ // operand, but there are a few other cases.
+ switch (OpCode) {
+ case SPIRV::OpTypeImage:
+ OS << ' ';
+ printAccessQualifier(MI, FirstVariableIndex, OS);
+ break;
+ case SPIRV::OpVariable:
+ OS << ' ';
+ printOperand(MI, FirstVariableIndex, OS);
+ break;
+ case SPIRV::OpEntryPoint: {
+ // Print the interface ID operands, skipping the name's string
+ // literal.
+ printRemainingVariableOps(MI, NumFixedOps, OS, false, true);
+ break;
+ }
+ case SPIRV::OpExecutionMode:
+ case SPIRV::OpExecutionModeId:
+ case SPIRV::OpLoopMerge: {
+ // Print any literals after the OPERAND_UNKNOWN argument normally.
+ printRemainingVariableOps(MI, NumFixedOps, OS);
+ break;
+ }
+ default:
+ break; // printStringImm has already been handled
+ }
+ } else {
+ // For instructions with no fixed ops or a reg/immediate as the final
+ // fixed operand, we can usually print the rest with "printOperand", but
+ // check for a few cases with custom types first.
+ switch (OpCode) {
+ case SPIRV::OpLoad:
+ case SPIRV::OpStore:
+ OS << ' ';
+ printMemoryOperand(MI, FirstVariableIndex, OS);
+ printRemainingVariableOps(MI, FirstVariableIndex + 1, OS);
+ break;
+ case SPIRV::OpImageSampleImplicitLod:
+ case SPIRV::OpImageSampleDrefImplicitLod:
+ case SPIRV::OpImageSampleProjImplicitLod:
+ case SPIRV::OpImageSampleProjDrefImplicitLod:
+ case SPIRV::OpImageFetch:
+ case SPIRV::OpImageGather:
+ case SPIRV::OpImageDrefGather:
+ case SPIRV::OpImageRead:
+ case SPIRV::OpImageWrite:
+ case SPIRV::OpImageSparseSampleImplicitLod:
+ case SPIRV::OpImageSparseSampleDrefImplicitLod:
+ case SPIRV::OpImageSparseSampleProjImplicitLod:
+ case SPIRV::OpImageSparseSampleProjDrefImplicitLod:
+ case SPIRV::OpImageSparseFetch:
+ case SPIRV::OpImageSparseGather:
+ case SPIRV::OpImageSparseDrefGather:
+ case SPIRV::OpImageSparseRead:
+ case SPIRV::OpImageSampleFootprintNV:
+ OS << ' ';
+ printImageOperand(MI, FirstVariableIndex, OS);
+ printRemainingVariableOps(MI, NumFixedOps + 1, OS);
+ break;
+ case SPIRV::OpCopyMemory:
+ case SPIRV::OpCopyMemorySized: {
+ const unsigned NumOps = MI->getNumOperands();
+ for (unsigned i = NumFixedOps; i < NumOps; ++i) {
+ OS << ' ';
+ printMemoryOperand(MI, i, OS);
+ if (MI->getOperand(i).getImm() &
+ static_cast<unsigned>(SPIRV::MemoryOperand::Aligned)) {
+ assert(i + 1 < NumOps && "Missing alignment operand");
+ OS << ' ';
+ printOperand(MI, i + 1, OS);
+ i += 1;
+ }
+ }
+ break;
+ }
+ case SPIRV::OpConstantI:
+ case SPIRV::OpConstantF:
+ printOpConstantVarOps(MI, NumFixedOps, OS);
+ break;
+ default:
+ printRemainingVariableOps(MI, NumFixedOps, OS);
+ break;
+ }
+ }
+ }
+ }
+
printAnnotation(OS, Annot);
}
@@ -72,7 +180,42 @@ void SPIRVInstPrinter::printOpExtInst(const MCInst *MI, raw_ostream &O) {
}
void SPIRVInstPrinter::printOpDecorate(const MCInst *MI, raw_ostream &O) {
- llvm_unreachable("Unimplemented printOpDecorate");
+ // The fixed operands have already been printed, so just need to decide what
+ // type of decoration operands to print based on the Decoration type.
+ MCInstrDesc MCDesc = MII.get(MI->getOpcode());
+ unsigned NumFixedOps = MCDesc.getNumOperands();
+
+ if (NumFixedOps != MI->getNumOperands()) {
+ auto DecOp = MI->getOperand(NumFixedOps - 1);
+ auto Dec = static_cast<SPIRV::Decoration>(DecOp.getImm());
+
+ O << ' ';
+
+ switch (Dec) {
+ case SPIRV::Decoration::BuiltIn:
+ printBuiltIn(MI, NumFixedOps, O);
+ break;
+ case SPIRV::Decoration::UniformId:
+ printScope(MI, NumFixedOps, O);
+ break;
+ case SPIRV::Decoration::FuncParamAttr:
+ printFunctionParameterAttribute(MI, NumFixedOps, O);
+ break;
+ case SPIRV::Decoration::FPRoundingMode:
+ printFPRoundingMode(MI, NumFixedOps, O);
+ break;
+ case SPIRV::Decoration::FPFastMathMode:
+ printFPFastMathMode(MI, NumFixedOps, O);
+ break;
+ case SPIRV::Decoration::LinkageAttributes:
+ case SPIRV::Decoration::UserSemantic:
+ printStringImm(MI, NumFixedOps, O);
+ break;
+ default:
+ printRemainingVariableOps(MI, NumFixedOps, O, true);
+ break;
+ }
+ }
}
static void printExpr(const MCExpr *Expr, raw_ostream &O) {
@@ -111,7 +254,35 @@ void SPIRVInstPrinter::printOperand(const MCInst *MI, unsigned OpNo,
void SPIRVInstPrinter::printStringImm(const MCInst *MI, unsigned OpNo,
raw_ostream &O) {
- llvm_unreachable("Unimplemented printStringImm");
+ const unsigned NumOps = MI->getNumOperands();
+ unsigned StrStartIndex = OpNo;
+ while (StrStartIndex < NumOps) {
+ if (MI->getOperand(StrStartIndex).isReg())
+ break;
+
+ std::string Str = getSPIRVStringOperand(*MI, OpNo);
+ if (StrStartIndex != OpNo)
+ O << ' '; // Add a space if we're starting a new string/argument.
+ O << '"';
+ for (char c : Str) {
+ if (c == '"')
+ O.write('\\'); // Escape " characters (might break for complex UTF-8).
+ O.write(c);
+ }
+ O << '"';
+
+ unsigned numOpsInString = (Str.size() / 4) + 1;
+ StrStartIndex += numOpsInString;
+
+ // Check for final Op of "OpDecorate %x %stringImm %linkageAttribute".
+ if (MI->getOpcode() == SPIRV::OpDecorate &&
+ MI->getOperand(1).getImm() ==
+ static_cast<unsigned>(SPIRV::Decoration::LinkageAttributes)) {
+ O << ' ';
+ printLinkageType(MI, StrStartIndex, O);
+ break;
+ }
+ }
}
void SPIRVInstPrinter::printExtInst(const MCInst *MI, unsigned OpNo,
@@ -119,39 +290,267 @@ void SPIRVInstPrinter::printExtInst(const MCInst *MI, unsigned OpNo,
llvm_unreachable("Unimplemented printExtInst");
}
-// Methods for printing textual names of SPIR-V enums.
-#define GEN_INSTR_PRINTER_IMPL(EnumName) \
- void SPIRVInstPrinter::print##EnumName(const MCInst *MI, unsigned OpNo, \
- raw_ostream &O) { \
- llvm_unreachable("Unimplemented print" #EnumName); \
- }
-GEN_INSTR_PRINTER_IMPL(Capability)
-GEN_INSTR_PRINTER_IMPL(SourceLanguage)
-GEN_INSTR_PRINTER_IMPL(ExecutionModel)
-GEN_INSTR_PRINTER_IMPL(AddressingModel)
-GEN_INSTR_PRINTER_IMPL(MemoryModel)
-GEN_INSTR_PRINTER_IMPL(ExecutionMode)
-GEN_INSTR_PRINTER_IMPL(StorageClass)
-GEN_INSTR_PRINTER_IMPL(Dim)
-GEN_INSTR_PRINTER_IMPL(SamplerAddressingMode)
-GEN_INSTR_PRINTER_IMPL(SamplerFilterMode)
-GEN_INSTR_PRINTER_IMPL(ImageFormat)
-GEN_INSTR_PRINTER_IMPL(ImageChannelOrder)
-GEN_INSTR_PRINTER_IMPL(ImageChannelDataType)
-GEN_INSTR_PRINTER_IMPL(ImageOperand)
-GEN_INSTR_PRINTER_IMPL(FPFastMathMode)
-GEN_INSTR_PRINTER_IMPL(FPRoundingMode)
-GEN_INSTR_PRINTER_IMPL(LinkageType)
-GEN_INSTR_PRINTER_IMPL(AccessQualifier)
-GEN_INSTR_PRINTER_IMPL(FunctionParameterAttribute)
-GEN_INSTR_PRINTER_IMPL(Decoration)
-GEN_INSTR_PRINTER_IMPL(BuiltIn)
-GEN_INSTR_PRINTER_IMPL(SelectionControl)
-GEN_INSTR_PRINTER_IMPL(LoopControl)
-GEN_INSTR_PRINTER_IMPL(FunctionControl)
-GEN_INSTR_PRINTER_IMPL(MemorySemantics)
-GEN_INSTR_PRINTER_IMPL(MemoryOperand)
-GEN_INSTR_PRINTER_IMPL(Scope)
-GEN_INSTR_PRINTER_IMPL(GroupOperation)
-GEN_INSTR_PRINTER_IMPL(KernelEnqueueFlags)
-GEN_INSTR_PRINTER_IMPL(KernelProfilingInfo)
+void SPIRVInstPrinter::printCapability(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::Capability e =
+ static_cast<SPIRV::Capability>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getCapabilityName(e);
+ }
+}
+
+void SPIRVInstPrinter::printSourceLanguage(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::SourceLanguage e =
+ static_cast<SPIRV::SourceLanguage>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getSourceLanguageName(e);
+ }
+}
+
+void SPIRVInstPrinter::printExecutionModel(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::ExecutionModel e =
+ static_cast<SPIRV::ExecutionModel>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getExecutionModelName(e);
+ }
+}
+
+void SPIRVInstPrinter::printAddressingModel(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::AddressingModel e =
+ static_cast<SPIRV::AddressingModel>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getAddressingModelName(e);
+ }
+}
+
+void SPIRVInstPrinter::printMemoryModel(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::MemoryModel e =
+ static_cast<SPIRV::MemoryModel>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getMemoryModelName(e);
+ }
+}
+
+void SPIRVInstPrinter::printExecutionMode(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::ExecutionMode e =
+ static_cast<SPIRV::ExecutionMode>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getExecutionModeName(e);
+ }
+}
+
+void SPIRVInstPrinter::printStorageClass(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::StorageClass e =
+ static_cast<SPIRV::StorageClass>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getStorageClassName(e);
+ }
+}
+
+void SPIRVInstPrinter::printDim(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::Dim e = static_cast<SPIRV::Dim>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getDimName(e);
+ }
+}
+
+void SPIRVInstPrinter::printSamplerAddressingMode(const MCInst *MI,
+ unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::SamplerAddressingMode e = static_cast<SPIRV::SamplerAddressingMode>(
+ MI->getOperand(OpNo).getImm());
+ O << SPIRV::getSamplerAddressingModeName(e);
+ }
+}
+
+void SPIRVInstPrinter::printSamplerFilterMode(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::SamplerFilterMode e =
+ static_cast<SPIRV::SamplerFilterMode>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getSamplerFilterModeName(e);
+ }
+}
+
+void SPIRVInstPrinter::printImageFormat(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::ImageFormat e =
+ static_cast<SPIRV::ImageFormat>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getImageFormatName(e);
+ }
+}
+
+void SPIRVInstPrinter::printImageChannelOrder(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::ImageChannelOrder e =
+ static_cast<SPIRV::ImageChannelOrder>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getImageChannelOrderName(e);
+ }
+}
+
+void SPIRVInstPrinter::printImageChannelDataType(const MCInst *MI,
+ unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::ImageChannelDataType e =
+ static_cast<SPIRV::ImageChannelDataType>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getImageChannelDataTypeName(e);
+ }
+}
+
+void SPIRVInstPrinter::printImageOperand(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ unsigned e = static_cast<unsigned>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getImageOperandName(e);
+ }
+}
+
+void SPIRVInstPrinter::printFPFastMathMode(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ unsigned e = static_cast<unsigned>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getFPFastMathModeName(e);
+ }
+}
+
+void SPIRVInstPrinter::printFPRoundingMode(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::FPRoundingMode e =
+ static_cast<SPIRV::FPRoundingMode>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getFPRoundingModeName(e);
+ }
+}
+
+void SPIRVInstPrinter::printLinkageType(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::LinkageType e =
+ static_cast<SPIRV::LinkageType>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getLinkageTypeName(e);
+ }
+}
+
+void SPIRVInstPrinter::printAccessQualifier(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::AccessQualifier e =
+ static_cast<SPIRV::AccessQualifier>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getAccessQualifierName(e);
+ }
+}
+
+void SPIRVInstPrinter::printFunctionParameterAttribute(const MCInst *MI,
+ unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::FunctionParameterAttribute e =
+ static_cast<SPIRV::FunctionParameterAttribute>(
+ MI->getOperand(OpNo).getImm());
+ O << SPIRV::getFunctionParameterAttributeName(e);
+ }
+}
+
+void SPIRVInstPrinter::printDecoration(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::Decoration e =
+ static_cast<SPIRV::Decoration>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getDecorationName(e);
+ }
+}
+
+void SPIRVInstPrinter::printBuiltIn(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::BuiltIn e =
+ static_cast<SPIRV::BuiltIn>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getBuiltInName(e);
+ }
+}
+
+void SPIRVInstPrinter::printSelectionControl(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ unsigned e = static_cast<unsigned>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getSelectionControlName(e);
+ }
+}
+
+void SPIRVInstPrinter::printLoopControl(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ unsigned e = static_cast<unsigned>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getLoopControlName(e);
+ }
+}
+
+void SPIRVInstPrinter::printFunctionControl(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ unsigned e = static_cast<unsigned>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getFunctionControlName(e);
+ }
+}
+
+void SPIRVInstPrinter::printMemorySemantics(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ unsigned e = static_cast<unsigned>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getMemorySemanticsName(e);
+ }
+}
+
+void SPIRVInstPrinter::printMemoryOperand(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ unsigned e = static_cast<unsigned>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getMemoryOperandName(e);
+ }
+}
+
+void SPIRVInstPrinter::printScope(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::Scope e = static_cast<SPIRV::Scope>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getScopeName(e);
+ }
+}
+
+void SPIRVInstPrinter::printGroupOperation(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::GroupOperation e =
+ static_cast<SPIRV::GroupOperation>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getGroupOperationName(e);
+ }
+}
+
+void SPIRVInstPrinter::printKernelEnqueueFlags(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::KernelEnqueueFlags e =
+ static_cast<SPIRV::KernelEnqueueFlags>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getKernelEnqueueFlagsName(e);
+ }
+}
+
+void SPIRVInstPrinter::printKernelProfilingInfo(const MCInst *MI, unsigned OpNo,
+ raw_ostream &O) {
+ if (OpNo < MI->getNumOperands()) {
+ SPIRV::KernelProfilingInfo e =
+ static_cast<SPIRV::KernelProfilingInfo>(MI->getOperand(OpNo).getImm());
+ O << SPIRV::getKernelProfilingInfoName(e);
+ }
+}
diff --git a/llvm/lib/Target/SPIRV/SPIRV.h b/llvm/lib/Target/SPIRV/SPIRV.h
index 31e2c39e7b6f6..c5740025e6c8b 100644
--- a/llvm/lib/Target/SPIRV/SPIRV.h
+++ b/llvm/lib/Target/SPIRV/SPIRV.h
@@ -23,6 +23,8 @@ InstructionSelector *
createSPIRVInstructionSelector(const SPIRVTargetMachine &TM,
const SPIRVSubtarget &Subtarget,
const RegisterBankInfo &RBI);
+
+void initializeSPIRVModuleAnalysisPass(PassRegistry &);
} // namespace llvm
#endif // LLVM_LIB_TARGET_SPIRV_SPIRV_H
diff --git a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
index 64a1be2f980e0..04527da695ac6 100644
--- a/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVAsmPrinter.cpp
@@ -15,8 +15,12 @@
#include "SPIRV.h"
#include "SPIRVInstrInfo.h"
#include "SPIRVMCInstLower.h"
+#include "SPIRVModuleAnalysis.h"
+#include "SPIRVSubtarget.h"
#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
#include "TargetInfo/SPIRVTargetInfo.h"
+#include "llvm/ADT/DenseMap.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineConstantPool.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
@@ -29,36 +33,75 @@
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/raw_ostream.h"
+
using namespace llvm;
#define DEBUG_TYPE "asm-printer"
namespace {
class SPIRVAsmPrinter : public AsmPrinter {
-
public:
explicit SPIRVAsmPrinter(TargetMachine &TM,
std::unique_ptr<MCStreamer> Streamer)
- : AsmPrinter(TM, std::move(Streamer)) {}
+ : AsmPrinter(TM, std::move(Streamer)), ST(nullptr), TII(nullptr) {}
+ bool ModuleSectionsEmitted;
+ const SPIRVSubtarget *ST;
+ const SPIRVInstrInfo *TII;
StringRef getPassName() const override { return "SPIRV Assembly Printer"; }
void printOperand(const MachineInstr *MI, int OpNum, raw_ostream &O);
-
bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
const char *ExtraCode, raw_ostream &O) override;
- void emitInstruction(const MachineInstr *MI) override;
+ void outputMCInst(MCInst &Inst);
+ void outputInstruction(const MachineInstr *MI);
+ void outputModuleSection(SPIRV::ModuleSectionType MSType);
+ void outputEntryPoints();
+ void outputDebugSourceAndStrings(const Module &M);
+ void outputOpMemoryModel();
+ void outputOpFunctionEnd();
+ void outputExtFuncDecls();
+ void outputModuleSections();
+ void emitInstruction(const MachineInstr *MI) override;
void emitFunctionEntryLabel() override {}
void emitFunctionHeader() override;
void emitFunctionBodyStart() override {}
- void emitBasicBlockStart(const MachineBasicBlock &MBB) override {}
+ void emitFunctionBodyEnd() override;
+ void emitBasicBlockStart(const MachineBasicBlock &MBB) override;
void emitBasicBlockEnd(const MachineBasicBlock &MBB) override {}
void emitGlobalVariable(const GlobalVariable *GV) override {}
+ void emitOpLabel(const MachineBasicBlock &MBB);
+ void emitEndOfAsmFile(Module &M) override;
+ bool doInitialization(Module &M) override;
+
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+ SPIRV::ModuleAnalysisInfo *MAI;
};
} // namespace
+void SPIRVAsmPrinter::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.addRequired<SPIRVModuleAnalysis>();
+ AU.addPreserved<SPIRVModuleAnalysis>();
+ AsmPrinter::getAnalysisUsage(AU);
+}
+
+// If the module has no functions, we need output global info anyway.
+void SPIRVAsmPrinter::emitEndOfAsmFile(Module &M) {
+ if (ModuleSectionsEmitted == false) {
+ outputModuleSections();
+ ModuleSectionsEmitted = true;
+ }
+}
+
void SPIRVAsmPrinter::emitFunctionHeader() {
+ if (ModuleSectionsEmitted == false) {
+ outputModuleSections();
+ ModuleSectionsEmitted = true;
+ }
+ // Get the subtarget from the current MachineFunction.
+ ST = &MF->getSubtarget<SPIRVSubtarget>();
+ TII = ST->getInstrInfo();
const Function &F = MF->getFunction();
if (isVerbose()) {
@@ -71,6 +114,38 @@ void SPIRVAsmPrinter::emitFunctionHeader() {
MF->setSection(Section);
}
+void SPIRVAsmPrinter::outputOpFunctionEnd() {
+ MCInst FunctionEndInst;
+ FunctionEndInst.setOpcode(SPIRV::OpFunctionEnd);
+ outputMCInst(FunctionEndInst);
+}
+
+// Emit OpFunctionEnd at the end of MF and clear BBNumToRegMap.
+void SPIRVAsmPrinter::emitFunctionBodyEnd() {
+ outputOpFunctionEnd();
+ MAI->BBNumToRegMap.clear();
+}
+
+void SPIRVAsmPrinter::emitOpLabel(const MachineBasicBlock &MBB) {
+ MCInst LabelInst;
+ LabelInst.setOpcode(SPIRV::OpLabel);
+ LabelInst.addOperand(MCOperand::createReg(MAI->getOrCreateMBBRegister(MBB)));
+ outputMCInst(LabelInst);
+}
+
+void SPIRVAsmPrinter::emitBasicBlockStart(const MachineBasicBlock &MBB) {
+ // If it's the first MBB in MF, it has OpFunction and OpFunctionParameter, so
+ // OpLabel should be output after them.
+ if (MBB.getNumber() == MF->front().getNumber()) {
+ for (const MachineInstr &MI : MBB)
+ if (MI.getOpcode() == SPIRV::OpFunction)
+ return;
+ // TODO: this case should be checked by the verifier.
+ report_fatal_error("OpFunction is expected in the front MBB of MF");
+ }
+ emitOpLabel(MBB);
+}
+
void SPIRVAsmPrinter::printOperand(const MachineInstr *MI, int OpNum,
raw_ostream &O) {
const MachineOperand &MO = MI->getOperand(OpNum);
@@ -122,12 +197,148 @@ bool SPIRVAsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNo,
return false;
}
-void SPIRVAsmPrinter::emitInstruction(const MachineInstr *MI) {
+static bool isFuncOrHeaderInstr(const MachineInstr *MI,
+ const SPIRVInstrInfo *TII) {
+ return TII->isHeaderInstr(*MI) || MI->getOpcode() == SPIRV::OpFunction ||
+ MI->getOpcode() == SPIRV::OpFunctionParameter;
+}
+
+void SPIRVAsmPrinter::outputMCInst(MCInst &Inst) {
+ OutStreamer->emitInstruction(Inst, *OutContext.getSubtargetInfo());
+}
+void SPIRVAsmPrinter::outputInstruction(const MachineInstr *MI) {
SPIRVMCInstLower MCInstLowering;
MCInst TmpInst;
- MCInstLowering.lower(MI, TmpInst);
- EmitToStreamer(*OutStreamer, TmpInst);
+ MCInstLowering.lower(MI, TmpInst, MAI);
+ outputMCInst(TmpInst);
+}
+
+void SPIRVAsmPrinter::emitInstruction(const MachineInstr *MI) {
+ if (!MAI->getSkipEmission(MI))
+ outputInstruction(MI);
+
+ // Output OpLabel after OpFunction and OpFunctionParameter in the first MBB.
+ const MachineInstr *NextMI = MI->getNextNode();
+ if (!MAI->hasMBBRegister(*MI->getParent()) && isFuncOrHeaderInstr(MI, TII) &&
+ (!NextMI || !isFuncOrHeaderInstr(NextMI, TII))) {
+ assert(MI->getParent()->getNumber() == MF->front().getNumber() &&
+ "OpFunction is not in the front MBB of MF");
+ emitOpLabel(*MI->getParent());
+ }
+}
+
+void SPIRVAsmPrinter::outputModuleSection(SPIRV::ModuleSectionType MSType) {
+ for (MachineInstr *MI : MAI->getMSInstrs(MSType))
+ outputInstruction(MI);
+}
+
+void SPIRVAsmPrinter::outputDebugSourceAndStrings(const Module &M) {
+ // Output OpSource.
+ MCInst Inst;
+ Inst.setOpcode(SPIRV::OpSource);
+ Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->SrcLang)));
+ Inst.addOperand(
+ MCOperand::createImm(static_cast<unsigned>(MAI->SrcLangVersion)));
+ outputMCInst(Inst);
+}
+
+void SPIRVAsmPrinter::outputOpMemoryModel() {
+ MCInst Inst;
+ Inst.setOpcode(SPIRV::OpMemoryModel);
+ Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->Addr)));
+ Inst.addOperand(MCOperand::createImm(static_cast<unsigned>(MAI->Mem)));
+ outputMCInst(Inst);
+}
+
+// Before the OpEntryPoints' output, we need to add the entry point's
+// interfaces. The interface is a list of IDs of global OpVariable instructions.
+// These declare the set of global variables from a module that form
+// the interface of this entry point.
+void SPIRVAsmPrinter::outputEntryPoints() {
+ // Find all OpVariable IDs with required StorageClass.
+ DenseSet<Register> InterfaceIDs;
+ for (MachineInstr *MI : MAI->GlobalVarList) {
+ assert(MI->getOpcode() == SPIRV::OpVariable);
+ auto SC = static_cast<SPIRV::StorageClass>(MI->getOperand(2).getImm());
+ // Before version 1.4, the interface's storage classes are limited to
+ // the Input and Output storage classes. Starting with version 1.4,
+ // the interface's storage classes are all storage classes used in
+ // declaring all global variables referenced by the entry point call tree.
+ if (ST->getSPIRVVersion() >= 14 || SC == SPIRV::StorageClass::Input ||
+ SC == SPIRV::StorageClass::Output) {
+ MachineFunction *MF = MI->getMF();
+ Register Reg = MAI->getRegisterAlias(MF, MI->getOperand(0).getReg());
+ InterfaceIDs.insert(Reg);
+ }
+ }
+
+ // Output OpEntryPoints adding interface args to all of them.
+ for (MachineInstr *MI : MAI->getMSInstrs(SPIRV::MB_EntryPoints)) {
+ SPIRVMCInstLower MCInstLowering;
+ MCInst TmpInst;
+ MCInstLowering.lower(MI, TmpInst, MAI);
+ for (Register Reg : InterfaceIDs) {
+ assert(Reg.isValid());
+ TmpInst.addOperand(MCOperand::createReg(Reg));
+ }
+ outputMCInst(TmpInst);
+ }
+}
+
+void SPIRVAsmPrinter::outputExtFuncDecls() {
+ // Insert OpFunctionEnd after each declaration.
+ SmallVectorImpl<MachineInstr *>::iterator
+ I = MAI->getMSInstrs(SPIRV::MB_ExtFuncDecls).begin(),
+ E = MAI->getMSInstrs(SPIRV::MB_ExtFuncDecls).end();
+ for (; I != E; ++I) {
+ outputInstruction(*I);
+ if ((I + 1) == E || (*(I + 1))->getOpcode() == SPIRV::OpFunction)
+ outputOpFunctionEnd();
+ }
+}
+
+void SPIRVAsmPrinter::outputModuleSections() {
+ const Module *M = MMI->getModule();
+ // Get the global subtarget to output module-level info.
+ ST = static_cast<const SPIRVTargetMachine &>(TM).getSubtargetImpl();
+ TII = ST->getInstrInfo();
+ MAI = &SPIRVModuleAnalysis::MAI;
+ assert(ST && TII && MAI && M && "Module analysis is required");
+ // Output instructions according to the Logical Layout of a Module:
+ // TODO: 1,2. All OpCapability instructions, then optional OpExtension
+ // instructions.
+ // TODO: 3. Optional OpExtInstImport instructions.
+ // 4. The single required OpMemoryModel instruction.
+ outputOpMemoryModel();
+ // 5. All entry point declarations, using OpEntryPoint.
+ outputEntryPoints();
+ // 6. Execution-mode declarations, using OpExecutionMode or OpExecutionModeId.
+ // TODO:
+ // 7a. Debug: all OpString, OpSourceExtension, OpSource, and
+ // OpSourceContinued, without forward references.
+ outputDebugSourceAndStrings(*M);
+ // 7b. Debug: all OpName and all OpMemberName.
+ outputModuleSection(SPIRV::MB_DebugNames);
+ // 7c. Debug: all OpModuleProcessed instructions.
+ outputModuleSection(SPIRV::MB_DebugModuleProcessed);
+ // 8. All annotation instructions (all decorations).
+ outputModuleSection(SPIRV::MB_Annotations);
+ // 9. All type declarations (OpTypeXXX instructions), all constant
+ // instructions, and all global variable declarations. This section is
+ // the first section to allow use of: OpLine and OpNoLine debug information;
+ // non-semantic instructions with OpExtInst.
+ outputModuleSection(SPIRV::MB_TypeConstVars);
+ // 10. All function declarations (functions without a body).
+ outputExtFuncDecls();
+ // 11. All function definitions (functions with a body).
+ // This is done in regular function output.
+}
+
+bool SPIRVAsmPrinter::doInitialization(Module &M) {
+ ModuleSectionsEmitted = false;
+ // We need to call the parent's one explicitly.
+ return AsmPrinter::doInitialization(M);
}
// Force static initialization.
diff --git a/llvm/lib/Target/SPIRV/SPIRVMCInstLower.cpp b/llvm/lib/Target/SPIRV/SPIRVMCInstLower.cpp
index b5a053c624d81..8e4ab973bf07c 100644
--- a/llvm/lib/Target/SPIRV/SPIRVMCInstLower.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVMCInstLower.cpp
@@ -12,24 +12,38 @@
//===----------------------------------------------------------------------===//
#include "SPIRVMCInstLower.h"
+#include "SPIRV.h"
+#include "SPIRVModuleAnalysis.h"
+#include "SPIRVUtils.h"
#include "llvm/CodeGen/MachineInstr.h"
#include "llvm/IR/Constants.h"
using namespace llvm;
-void SPIRVMCInstLower::lower(const MachineInstr *MI, MCInst &OutMI) const {
+void SPIRVMCInstLower::lower(const MachineInstr *MI, MCInst &OutMI,
+ SPIRV::ModuleAnalysisInfo *MAI) const {
OutMI.setOpcode(MI->getOpcode());
-
+ const MachineFunction *MF = MI->getMF();
for (unsigned i = 0, e = MI->getNumOperands(); i != e; ++i) {
const MachineOperand &MO = MI->getOperand(i);
-
MCOperand MCOp;
switch (MO.getType()) {
default:
llvm_unreachable("unknown operand type");
- case MachineOperand::MO_Register:
- MCOp = MCOperand::createReg(MO.getReg());
+ case MachineOperand::MO_GlobalAddress: {
+ Register FuncReg = MAI->getFuncReg(MO.getGlobal()->getGlobalIdentifier());
+ assert(FuncReg.isValid() && "Cannot find function Id");
+ MCOp = MCOperand::createReg(FuncReg);
+ break;
+ }
+ case MachineOperand::MO_MachineBasicBlock:
+ MCOp = MCOperand::createReg(MAI->getOrCreateMBBRegister(*MO.getMBB()));
+ break;
+ case MachineOperand::MO_Register: {
+ Register NewReg = MAI->getRegisterAlias(MF, MO.getReg());
+ MCOp = MCOperand::createReg(NewReg.isValid() ? NewReg : MO.getReg());
break;
+ }
case MachineOperand::MO_Immediate:
MCOp = MCOperand::createImm(MO.getImm());
break;
diff --git a/llvm/lib/Target/SPIRV/SPIRVMCInstLower.h b/llvm/lib/Target/SPIRV/SPIRVMCInstLower.h
index 3ea6f2a882d42..66fa903e11c30 100644
--- a/llvm/lib/Target/SPIRV/SPIRVMCInstLower.h
+++ b/llvm/lib/Target/SPIRV/SPIRVMCInstLower.h
@@ -14,11 +14,15 @@
namespace llvm {
class MCInst;
class MachineInstr;
+namespace SPIRV {
+class ModuleAnalysisInfo;
+} // namespace SPIRV
// This class is used to lower a MachineInstr into an MCInst.
class LLVM_LIBRARY_VISIBILITY SPIRVMCInstLower {
public:
- void lower(const MachineInstr *MI, MCInst &OutMI) const;
+ void lower(const MachineInstr *MI, MCInst &OutMI,
+ SPIRV::ModuleAnalysisInfo *MAI) const;
};
} // namespace llvm
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
new file mode 100644
index 0000000000000..fa78dd7942c6b
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.cpp
@@ -0,0 +1,250 @@
+//===- SPIRVModuleAnalysis.cpp - analysis of global instrs & regs - C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// The analysis collects instructions that should be output at the module level
+// and performs the global register numbering.
+//
+// The results of this analysis are used in AsmPrinter to rename registers
+// globally and to output required instructions at the module level.
+//
+//===----------------------------------------------------------------------===//
+
+#include "SPIRVModuleAnalysis.h"
+#include "SPIRV.h"
+#include "SPIRVGlobalRegistry.h"
+#include "SPIRVSubtarget.h"
+#include "SPIRVTargetMachine.h"
+#include "SPIRVUtils.h"
+#include "TargetInfo/SPIRVTargetInfo.h"
+#include "llvm/CodeGen/MachineModuleInfo.h"
+#include "llvm/CodeGen/TargetPassConfig.h"
+
+using namespace llvm;
+
+#define DEBUG_TYPE "spirv-module-analysis"
+
+char llvm::SPIRVModuleAnalysis::ID = 0;
+
+namespace llvm {
+void initializeSPIRVModuleAnalysisPass(PassRegistry &);
+} // namespace llvm
+
+INITIALIZE_PASS(SPIRVModuleAnalysis, DEBUG_TYPE, "SPIRV module analysis", true,
+ true)
+
+// Retrieve an unsigned from an MDNode with a list of them as operands.
+static unsigned getMetadataUInt(MDNode *MdNode, unsigned OpIndex,
+ unsigned DefaultVal = 0) {
+ if (MdNode && OpIndex < MdNode->getNumOperands()) {
+ const auto &Op = MdNode->getOperand(OpIndex);
+ return mdconst::extract<ConstantInt>(Op)->getZExtValue();
+ }
+ return DefaultVal;
+}
+
+void SPIRVModuleAnalysis::setBaseInfo(const Module &M) {
+ MAI.MaxID = 0;
+ for (int i = 0; i < SPIRV::NUM_MODULE_SECTIONS; i++)
+ MAI.MS[i].clear();
+ MAI.RegisterAliasTable.clear();
+ MAI.InstrsToDelete.clear();
+ MAI.FuncNameMap.clear();
+ MAI.GlobalVarList.clear();
+
+ // TODO: determine memory model and source language from the configuratoin.
+ MAI.Mem = SPIRV::MemoryModel::OpenCL;
+ MAI.SrcLang = SPIRV::SourceLanguage::OpenCL_C;
+ unsigned PtrSize = ST->getPointerSize();
+ MAI.Addr = PtrSize == 32 ? SPIRV::AddressingModel::Physical32
+ : PtrSize == 64 ? SPIRV::AddressingModel::Physical64
+ : SPIRV::AddressingModel::Logical;
+ // Get the OpenCL version number from metadata.
+ // TODO: support other source languages.
+ MAI.SrcLangVersion = 0;
+ if (auto VerNode = M.getNamedMetadata("opencl.ocl.version")) {
+ // Construct version literal according to OpenCL 2.2 environment spec.
+ auto VersionMD = VerNode->getOperand(0);
+ unsigned MajorNum = getMetadataUInt(VersionMD, 0, 2);
+ unsigned MinorNum = getMetadataUInt(VersionMD, 1);
+ unsigned RevNum = getMetadataUInt(VersionMD, 2);
+ MAI.SrcLangVersion = 0 | (MajorNum << 16) | (MinorNum << 8) | RevNum;
+ }
+}
+
+// True if there is an instruction in the MS list with all the same operands as
+// the given instruction has (after the given starting index).
+// TODO: maybe it needs to check Opcodes too.
+static bool findSameInstrInMS(const MachineInstr &A,
+ SPIRV::ModuleSectionType MSType,
+ SPIRV::ModuleAnalysisInfo &MAI,
+ bool UpdateRegAliases,
+ unsigned StartOpIndex = 0) {
+ for (const auto *B : MAI.MS[MSType]) {
+ const unsigned NumAOps = A.getNumOperands();
+ if (NumAOps == B->getNumOperands() && A.getNumDefs() == B->getNumDefs()) {
+ bool AllOpsMatch = true;
+ for (unsigned i = StartOpIndex; i < NumAOps && AllOpsMatch; ++i) {
+ if (A.getOperand(i).isReg() && B->getOperand(i).isReg()) {
+ Register RegA = A.getOperand(i).getReg();
+ Register RegB = B->getOperand(i).getReg();
+ AllOpsMatch = MAI.getRegisterAlias(A.getMF(), RegA) ==
+ MAI.getRegisterAlias(B->getMF(), RegB);
+ } else {
+ AllOpsMatch = A.getOperand(i).isIdenticalTo(B->getOperand(i));
+ }
+ }
+ if (AllOpsMatch) {
+ if (UpdateRegAliases) {
+ assert(A.getOperand(0).isReg() && B->getOperand(0).isReg());
+ Register LocalReg = A.getOperand(0).getReg();
+ Register GlobalReg =
+ MAI.getRegisterAlias(B->getMF(), B->getOperand(0).getReg());
+ MAI.setRegisterAlias(A.getMF(), LocalReg, GlobalReg);
+ }
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+// Look for IDs declared with Import linkage, and map the imported name string
+// to the register defining that variable (which will usually be the result of
+// an OpFunction). This lets us call externally imported functions using
+// the correct ID registers.
+void SPIRVModuleAnalysis::collectFuncNames(MachineInstr &MI,
+ const Function &F) {
+ if (MI.getOpcode() == SPIRV::OpDecorate) {
+ // If it's got Import linkage.
+ auto Dec = MI.getOperand(1).getImm();
+ if (Dec == static_cast<unsigned>(SPIRV::Decoration::LinkageAttributes)) {
+ auto Lnk = MI.getOperand(MI.getNumOperands() - 1).getImm();
+ if (Lnk == static_cast<unsigned>(SPIRV::LinkageType::Import)) {
+ // Map imported function name to function ID register.
+ std::string Name = getStringImm(MI, 2);
+ Register Target = MI.getOperand(0).getReg();
+ // TODO: check defs from
diff erent MFs.
+ MAI.FuncNameMap[Name] = MAI.getRegisterAlias(MI.getMF(), Target);
+ }
+ }
+ } else if (MI.getOpcode() == SPIRV::OpFunction) {
+ // Record all internal OpFunction declarations.
+ Register Reg = MI.defs().begin()->getReg();
+ Register GlobalReg = MAI.getRegisterAlias(MI.getMF(), Reg);
+ assert(GlobalReg.isValid());
+ // TODO: check that it does not conflict with existing entries.
+ MAI.FuncNameMap[F.getGlobalIdentifier()] = GlobalReg;
+ }
+}
+
+// Collect the given instruction in the specified MS. We assume global register
+// numbering has already occurred by this point. We can directly compare reg
+// arguments when detecting duplicates.
+static void collectOtherInstr(MachineInstr &MI, SPIRV::ModuleAnalysisInfo &MAI,
+ SPIRV::ModuleSectionType MSType,
+ bool IsConstOrType = false) {
+ MAI.setSkipEmission(&MI);
+ if (findSameInstrInMS(MI, MSType, MAI, IsConstOrType, IsConstOrType ? 1 : 0))
+ return; // Found a duplicate, so don't add it.
+ // No duplicates, so add it.
+ MAI.MS[MSType].push_back(&MI);
+}
+
+// Some global instructions make reference to function-local ID regs, so cannot
+// be correctly collected until these registers are globally numbered.
+void SPIRVModuleAnalysis::processOtherInstrs(const Module &M) {
+ for (auto F = M.begin(), E = M.end(); F != E; ++F) {
+ if ((*F).isDeclaration())
+ continue;
+ MachineFunction *MF = MMI->getMachineFunction(*F);
+ assert(MF);
+ unsigned FCounter = 0;
+ for (MachineBasicBlock &MBB : *MF)
+ for (MachineInstr &MI : MBB) {
+ if (MI.getOpcode() == SPIRV::OpFunction)
+ FCounter++;
+ if (MAI.getSkipEmission(&MI))
+ continue;
+ const unsigned OpCode = MI.getOpcode();
+ const bool IsFuncOrParm =
+ OpCode == SPIRV::OpFunction || OpCode == SPIRV::OpFunctionParameter;
+ const bool IsConstOrType =
+ TII->isConstantInstr(MI) || TII->isTypeDeclInstr(MI);
+ if (OpCode == SPIRV::OpName || OpCode == SPIRV::OpMemberName) {
+ collectOtherInstr(MI, MAI, SPIRV::MB_DebugNames);
+ } else if (OpCode == SPIRV::OpEntryPoint) {
+ collectOtherInstr(MI, MAI, SPIRV::MB_EntryPoints);
+ } else if (TII->isDecorationInstr(MI)) {
+ collectOtherInstr(MI, MAI, SPIRV::MB_Annotations);
+ collectFuncNames(MI, *F);
+ } else if (IsConstOrType || (FCounter > 1 && IsFuncOrParm)) {
+ // Now OpSpecConstant*s are not in DT,
+ // but they need to be collected anyway.
+ enum SPIRV::ModuleSectionType Type =
+ IsFuncOrParm ? SPIRV::MB_ExtFuncDecls : SPIRV::MB_TypeConstVars;
+ collectOtherInstr(MI, MAI, Type, IsConstOrType);
+ } else if (OpCode == SPIRV::OpFunction) {
+ collectFuncNames(MI, *F);
+ }
+ }
+ }
+}
+
+// Number registers in all functions globally from 0 onwards and store
+// the result in global register alias table.
+void SPIRVModuleAnalysis::numberRegistersGlobally(const Module &M) {
+ for (auto F = M.begin(), E = M.end(); F != E; ++F) {
+ if ((*F).isDeclaration())
+ continue;
+ MachineFunction *MF = MMI->getMachineFunction(*F);
+ assert(MF);
+ for (MachineBasicBlock &MBB : *MF) {
+ for (MachineInstr &MI : MBB) {
+ for (MachineOperand &Op : MI.operands()) {
+ if (!Op.isReg())
+ continue;
+ Register Reg = Op.getReg();
+ if (MAI.hasRegisterAlias(MF, Reg))
+ continue;
+ Register NewReg = Register::index2VirtReg(MAI.getNextID());
+ MAI.setRegisterAlias(MF, Reg, NewReg);
+ }
+ }
+ }
+ }
+}
+
+struct SPIRV::ModuleAnalysisInfo SPIRVModuleAnalysis::MAI;
+
+void SPIRVModuleAnalysis::getAnalysisUsage(AnalysisUsage &AU) const {
+ AU.addRequired<TargetPassConfig>();
+ AU.addRequired<MachineModuleInfoWrapperPass>();
+}
+
+bool SPIRVModuleAnalysis::runOnModule(Module &M) {
+ SPIRVTargetMachine &TM =
+ getAnalysis<TargetPassConfig>().getTM<SPIRVTargetMachine>();
+ ST = TM.getSubtargetImpl();
+ GR = ST->getSPIRVGlobalRegistry();
+ TII = ST->getInstrInfo();
+
+ MMI = &getAnalysis<MachineModuleInfoWrapperPass>().getMMI();
+
+ setBaseInfo(M);
+
+ // TODO: Process type/const/global var/func decl instructions, number their
+ // destination registers from 0 to N, collect Extensions and Capabilities.
+
+ // Number rest of registers from N+1 onwards.
+ numberRegistersGlobally(M);
+
+ // Collect OpName, OpEntryPoint, OpDecorate etc, process other instructions.
+ processOtherInstrs(M);
+
+ return false;
+}
diff --git a/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
new file mode 100644
index 0000000000000..1bef13d458c10
--- /dev/null
+++ b/llvm/lib/Target/SPIRV/SPIRVModuleAnalysis.h
@@ -0,0 +1,137 @@
+//===- SPIRVModuleAnalysis.h - analysis of global instrs & regs -*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// The analysis collects instructions that should be output at the module level
+// and performs the global register numbering.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H
+#define LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H
+
+#include "MCTargetDesc/SPIRVBaseInfo.h"
+#include "SPIRVSubtarget.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringMap.h"
+
+namespace llvm {
+class MachineFunction;
+class MachineModuleInfo;
+
+namespace SPIRV {
+// The enum contains logical module sections for the instruction collection.
+enum ModuleSectionType {
+ // MB_Capabilities, MB_Extensions, MB_ExtInstImports, MB_MemoryModel,
+ MB_EntryPoints, // All OpEntryPoint instructions (if any).
+ // MB_ExecutionModes, MB_DebugSourceAndStrings,
+ MB_DebugNames, // All OpName and OpMemberName intrs.
+ MB_DebugModuleProcessed, // All OpModuleProcessed instructions.
+ MB_Annotations, // OpDecorate, OpMemberDecorate etc.
+ MB_TypeConstVars, // OpTypeXXX, OpConstantXXX, and global OpVariables.
+ MB_ExtFuncDecls, // OpFunction etc. to declare for external funcs.
+ NUM_MODULE_SECTIONS // Total number of sections requiring basic blocks.
+};
+
+using InstrList = SmallVector<MachineInstr *>;
+// Maps a local register to the corresponding global alias.
+using LocalToGlobalRegTable = std::map<Register, Register>;
+using RegisterAliasMapTy =
+ std::map<const MachineFunction *, LocalToGlobalRegTable>;
+
+// The struct contains results of the module analysis and methods
+// to access them.
+struct ModuleAnalysisInfo {
+ SPIRV::MemoryModel Mem;
+ SPIRV::AddressingModel Addr;
+ SPIRV::SourceLanguage SrcLang;
+ unsigned SrcLangVersion;
+ // Contains the list of all global OpVariables in the module.
+ SmallVector<MachineInstr *, 4> GlobalVarList;
+ // Maps function names to coresponding function ID registers.
+ StringMap<Register> FuncNameMap;
+ // The set contains machine instructions which are necessary
+ // for correct MIR but will not be emitted in function bodies.
+ DenseSet<MachineInstr *> InstrsToDelete;
+ // The table contains global aliases of local registers for each machine
+ // function. The aliases are used to substitute local registers during
+ // code emission.
+ RegisterAliasMapTy RegisterAliasTable;
+ // The counter holds the maximum ID we have in the module.
+ unsigned MaxID;
+ // The array contains lists of MIs for each module section.
+ InstrList MS[NUM_MODULE_SECTIONS];
+ // The table maps MBB number to SPIR-V unique ID register.
+ DenseMap<int, Register> BBNumToRegMap;
+
+ Register getFuncReg(std::string FuncName) {
+ auto FuncReg = FuncNameMap.find(FuncName);
+ assert(FuncReg != FuncNameMap.end() && "Cannot find function Id");
+ return FuncReg->second;
+ }
+ InstrList &getMSInstrs(unsigned MSType) { return MS[MSType]; }
+ void setSkipEmission(MachineInstr *MI) { InstrsToDelete.insert(MI); }
+ bool getSkipEmission(const MachineInstr *MI) {
+ return InstrsToDelete.contains(MI);
+ }
+ void setRegisterAlias(const MachineFunction *MF, Register Reg,
+ Register AliasReg) {
+ RegisterAliasTable[MF][Reg] = AliasReg;
+ }
+ Register getRegisterAlias(const MachineFunction *MF, Register Reg) {
+ auto RI = RegisterAliasTable[MF].find(Reg);
+ if (RI == RegisterAliasTable[MF].end()) {
+ return Register(0);
+ }
+ return RegisterAliasTable[MF][Reg];
+ }
+ bool hasRegisterAlias(const MachineFunction *MF, Register Reg) {
+ return RegisterAliasTable.find(MF) != RegisterAliasTable.end() &&
+ RegisterAliasTable[MF].find(Reg) != RegisterAliasTable[MF].end();
+ }
+ unsigned getNextID() { return MaxID++; }
+ bool hasMBBRegister(const MachineBasicBlock &MBB) {
+ return BBNumToRegMap.find(MBB.getNumber()) != BBNumToRegMap.end();
+ }
+ // Convert MBB's number to corresponding ID register.
+ Register getOrCreateMBBRegister(const MachineBasicBlock &MBB) {
+ auto f = BBNumToRegMap.find(MBB.getNumber());
+ if (f != BBNumToRegMap.end())
+ return f->second;
+ Register NewReg = Register::index2VirtReg(getNextID());
+ BBNumToRegMap[MBB.getNumber()] = NewReg;
+ return NewReg;
+ }
+};
+} // namespace SPIRV
+
+struct SPIRVModuleAnalysis : public ModulePass {
+ static char ID;
+
+public:
+ SPIRVModuleAnalysis() : ModulePass(ID) {}
+
+ bool runOnModule(Module &M) override;
+ void getAnalysisUsage(AnalysisUsage &AU) const override;
+ static struct SPIRV::ModuleAnalysisInfo MAI;
+
+private:
+ void setBaseInfo(const Module &M);
+ template <typename T> void collectTypesConstsVars();
+ void processDefInstrs(const Module &M);
+ void collectFuncNames(MachineInstr &MI, const Function &F);
+ void processOtherInstrs(const Module &M);
+ void numberRegistersGlobally(const Module &M);
+
+ const SPIRVSubtarget *ST;
+ SPIRVGlobalRegistry *GR;
+ const SPIRVInstrInfo *TII;
+ MachineModuleInfo *MMI;
+};
+} // namespace llvm
+#endif // LLVM_LIB_TARGET_SPIRV_SPIRVMODULEANALYSIS_H
diff --git a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
index a9fd5e2d581c1..e705de56c5bfd 100644
--- a/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
+++ b/llvm/lib/Target/SPIRV/SPIRVTargetMachine.cpp
@@ -40,6 +40,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSPIRVTarget() {
PassRegistry &PR = *PassRegistry::getPassRegistry();
initializeGlobalISel(PR);
+ initializeSPIRVModuleAnalysisPass(PR);
}
static std::string computeDataLayout(const Triple &TT) {
diff --git a/llvm/test/CodeGen/SPIRV/function/identity-function.ll b/llvm/test/CodeGen/SPIRV/function/identity-function.ll
new file mode 100644
index 0000000000000..ebf1fffc0dd41
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/function/identity-function.ll
@@ -0,0 +1,19 @@
+; RUN: llc -O0 %s -o - | FileCheck %s
+
+target triple = "spirv32-unknown-unknown"
+
+; CHECK-DAG: OpName [[VALUE:%.+]] "value"
+; CHECK-DAG: OpName [[IDENTITY:%.+]] "identity"
+
+; CHECK: [[INT:%.+]] = OpTypeInt 32
+; CHECK: [[FN:%.+]] = OpTypeFunction [[INT]] [[INT]]
+
+; CHECK: [[IDENTITY]] = OpFunction [[INT]] None [[FN]]
+; CHECK-NEXT: [[VALUE]] = OpFunctionParameter [[INT]]
+; CHECK-NEXT: {{%.+}} = OpLabel
+; CHECK-NEXT: OpReturnValue [[VALUE]]
+; CHECK-NEXT: OpFunctionEnd
+
+define i32 @identity(i32 %value) {
+ ret i32 %value
+}
diff --git a/llvm/test/CodeGen/SPIRV/function/trivial-function-definition.ll b/llvm/test/CodeGen/SPIRV/function/trivial-function-definition.ll
new file mode 100644
index 0000000000000..488ec3f710bb3
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/function/trivial-function-definition.ll
@@ -0,0 +1,27 @@
+; RUN: llc -O0 %s -o - | FileCheck %s
+
+target triple = "spirv32-unknown-unknown"
+
+; Debug info:
+; CHECK: OpName [[FOO:%.+]] "foo"
+
+; Types:
+; CHECK: [[VOID:%.+]] = OpTypeVoid
+; CHECK: [[FN:%.+]] = OpTypeFunction [[VOID]]
+
+; Functions:
+; CHECK: [[FOO]] = OpFunction [[VOID]] None [[FN]]
+; CHECK-NOT: OpFunctionParameter
+; NOTE: In 2.4, it isn't explicitly written that a function always has a least
+; one block. In fact, 2.4.11 seems to imply that there are at least two
+; blocks in functions with a body, but that doesn't make much sense.
+; However, in order to distinguish between function declaration and
+; definition, a function needs at least one block, hence why this test
+; expects one OpLabel + OpReturn.
+; CHECK: OpLabel
+; CHECK: OpReturn
+; CHECK-NOT: OpLabel
+; CHECK: OpFunctionEnd
+define void @foo() {
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/function/trivial-function-with-attributes.ll b/llvm/test/CodeGen/SPIRV/function/trivial-function-with-attributes.ll
new file mode 100644
index 0000000000000..df61f2c6ebab6
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/function/trivial-function-with-attributes.ll
@@ -0,0 +1,74 @@
+; RUN: llc -O0 %s -o - | FileCheck %s
+
+target triple = "spirv32-unknown-unknown"
+
+; FIXME: Are there any attributes that would make the IR invalid for SPIR-V?
+
+; Names:
+; CHECK-DAG: OpName [[FN1:%.+]] "fn1"
+; CHECK-DAG: OpName [[FN2:%.+]] "fn2"
+; CHECK-DAG: OpName [[FN3:%.+]] "fn3"
+; CHECK-DAG: OpName [[FN4:%.+]] "fn4"
+; CHECK-DAG: OpName [[FN5:%.+]] "fn5"
+; CHECK-DAG: OpName [[FN6:%.+]] "fn6"
+; CHECK-DAG: OpName [[FN7:%.+]] "fn7"
+
+; CHECK-NOT: DAG-FENCE
+
+; Types:
+; CHECK: [[VOID:%.+]] = OpTypeVoid
+; CHECK: [[FN:%.+]] = OpTypeFunction [[VOID]]
+
+
+; Functions:
+
+define void @fn1() noinline {
+ ret void
+}
+; CHECK: [[FN1]] = OpFunction [[VOID]] DontInline [[FN]]
+; CHECK-NOT: OpFunctionParameter
+; CHECK: OpFunctionEnd
+
+
+attributes #0 = { noinline }
+define void @fn2() #0 {
+ ret void
+}
+; CHECK: [[FN2]] = OpFunction [[VOID]] DontInline [[FN]]
+; CHECK: OpFunctionEnd
+
+
+define void @fn3() alwaysinline {
+ ret void
+}
+; CHECK: [[FN3]] = OpFunction [[VOID]] Inline [[FN]]
+; CHECK: OpFunctionEnd
+
+
+; NOTE: inlinehint is not an actual requirement.
+define void @fn4() inlinehint {
+ ret void
+}
+; CHECK: [[FN4]] = OpFunction [[VOID]] None [[FN]]
+; CHECK: OpFunctionEnd
+
+
+define void @fn5() readnone {
+ ret void
+}
+; CHECK: [[FN5]] = OpFunction [[VOID]] Pure [[FN]]
+; CHECK: OpFunctionEnd
+
+
+define void @fn6() readonly {
+ ret void
+}
+; CHECK: [[FN6]] = OpFunction [[VOID]] Const [[FN]]
+; CHECK: OpFunctionEnd
+
+
+define void @fn7() alwaysinline readnone {
+ ret void
+}
+; CHECK: [[FN7]] = OpFunction [[VOID]] Inline|Pure [[FN]]
+; CHECK: OpFunctionEnd
diff --git a/llvm/test/CodeGen/SPIRV/function/trivial-function-with-call.ll b/llvm/test/CodeGen/SPIRV/function/trivial-function-with-call.ll
new file mode 100644
index 0000000000000..fbff919c16eef
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/function/trivial-function-with-call.ll
@@ -0,0 +1,30 @@
+; RUN: llc -O0 %s -o - | FileCheck %s
+
+target triple = "spirv32-unknown-unknown"
+
+; Debug info:
+; CHECK: OpName [[FOO:%.+]] "foo"
+; CHECK: OpName [[BAR:%.+]] "bar"
+
+; Types:
+; CHECK-DAG: [[I32:%.+]] = OpTypeInt 32
+; CHECK-DAG: [[VOID:%.+]] = OpTypeVoid
+; CHECK-DAG: [[FNVOID:%.+]] = OpTypeFunction [[VOID]] [[I32]]
+; CHECK-DAG: [[FNI32:%.+]] = OpTypeFunction [[I32]] [[I32]]
+; Function decl:
+; CHECK: [[BAR]] = OpFunction [[I32]] None [[FNI32]]
+; CHECK-NEXT: OpFunctionParameter [[I32]]
+; CHECK-NEXT: OpFunctionEnd
+declare i32 @bar(i32 %x)
+; Function def:
+; CHECK: [[FOO]] = OpFunction [[VOID]] None [[FNVOID]]
+; CHECK: OpFunctionParameter
+; CHECK: OpLabel
+; CHECK: OpFunctionCall [[I32]] [[BAR]]
+; CHECK: OpReturn
+; CHECK-NOT: OpLabel
+; CHECK: OpFunctionEnd
+define spir_func void @foo(i32 %x) {
+ %call1 = call spir_func i32 @bar(i32 %x)
+ ret void
+}
diff --git a/llvm/test/CodeGen/SPIRV/lit.local.cfg b/llvm/test/CodeGen/SPIRV/lit.local.cfg
new file mode 100644
index 0000000000000..5c9b0f15271cd
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/lit.local.cfg
@@ -0,0 +1,2 @@
+if not 'SPIRV' in config.root.targets:
+ config.unsupported = True
diff --git a/llvm/test/CodeGen/SPIRV/metadata-opencl.ll b/llvm/test/CodeGen/SPIRV/metadata-opencl.ll
new file mode 100644
index 0000000000000..1e275d208d4dc
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/metadata-opencl.ll
@@ -0,0 +1,29 @@
+; RUN: split-file %s %t
+; RUN: llc -O0 %t/metadata-opencl12.ll -o - | FileCheck %t/metadata-opencl12.ll
+; RUN: llc -O0 %t/metadata-opencl20.ll -o - | FileCheck %t/metadata-opencl20.ll
+; RUN: llc -O0 %t/metadata-opencl22.ll -o - | FileCheck %t/metadata-opencl22.ll
+
+;--- metadata-opencl12.ll
+target triple = "spirv32-unknown-unknown"
+
+!opencl.ocl.version = !{!0}
+!0 = !{i32 1, i32 2}
+
+; We assume the SPIR-V 2.2 environment spec's version format: 0|Maj|Min|Rev|
+; CHECK: OpSource OpenCL_C 66048
+;--- metadata-opencl20.ll
+target triple = "spirv32-unknown-unknown"
+
+!opencl.ocl.version = !{!0}
+!0 = !{i32 2, i32 0}
+
+; We assume the SPIR-V 2.2 environment spec's version format: 0|Maj|Min|Rev|
+; CHECK: OpSource OpenCL_C 131072
+;--- metadata-opencl22.ll
+target triple = "spirv32-unknown-unknown"
+
+!opencl.ocl.version = !{!0}
+!0 = !{i32 2, i32 2}
+
+; We assume the SPIR-V 2.2 environment spec's version format: 0|Maj|Min|Rev|
+; CHECK: OpSource OpenCL_C 131584
diff --git a/llvm/test/CodeGen/SPIRV/transcoding/readonly.ll b/llvm/test/CodeGen/SPIRV/transcoding/readonly.ll
new file mode 100644
index 0000000000000..032837c13ba57
--- /dev/null
+++ b/llvm/test/CodeGen/SPIRV/transcoding/readonly.ll
@@ -0,0 +1,27 @@
+; RUN: llc -O0 %s -o - | FileCheck %s --check-prefix=CHECK-SPIRV
+
+; CHECK-SPIRV: OpDecorate %[[PARAM:[0-9]+]] FuncParamAttr NoWrite
+; CHECK-SPIRV: %[[PARAM]] = OpFunctionParameter %{{.*}}
+
+; ModuleID = 'readonly.bc'
+source_filename = "readonly.cpp"
+target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024-n8:16:32:64"
+target triple = "spirv64-unknown-unknown"
+
+; Function Attrs: norecurse nounwind readonly willreturn
+define dso_local spir_kernel void @_ZTSZ4mainE15kernel_function(i32 addrspace(1)* readonly %_arg_) local_unnamed_addr #0 {
+entry:
+ ret void
+}
+
+attributes #0 = { norecurse nounwind }
+
+!llvm.module.flags = !{!0}
+!opencl.spir.version = !{!1}
+!spirv.Source = !{!2}
+!llvm.ident = !{!3}
+
+!0 = !{i32 1, !"wchar_size", i32 4}
+!1 = !{i32 1, i32 2}
+!2 = !{i32 4, i32 100000}
+!3 = !{!"clang version 13.0.0 (https://github.com/intel/llvm.git)"}
More information about the llvm-commits
mailing list