[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