Browse Source

[spirv] Handle static global and local variables (#541)

These variables should be using the Private storage class.
And local static variables should be initialized once.
Lei Zhang 8 years ago
parent
commit
1da87c6aaf

+ 8 - 2
docs/SPIR-V.rst

@@ -235,7 +235,13 @@ Variables are defined in HLSL using the following `syntax <https://msdn.microsof
 Storage class
 Storage class
 +++++++++++++
 +++++++++++++
 
 
-[TODO]
+Normal local variables (without any modifier) will be placed in the ``Function`` SPIR-V storage class.
+
+``static``
+~~~~~~~~~~
+
+- Global variables with ``static`` modifier will be placed in the ``Private`` SPIR-V storage class. Initalizers of such global variables will be translated into SPIR-V ``OpVariable`` initializers if possible; otherwise, they will be initialized at the very beginning of the entry function using SPIR-V ``OpStore``.
+- Local variables with ``static`` modifier will also be placed in the ``Private`` SPIR-V storage class. initializers of such local variables will also be translated into SPIR-V ``OpVariable`` initializers if possible; otherwise, they will be initialized at the very beginning of the enclosing function. To make sure that such a local variable is only initialized once, a second boolean variable of the ``Private`` SPIR-V storage class will be generated to mark its initialization status.
 
 
 Type modifier
 Type modifier
 +++++++++++++
 +++++++++++++
@@ -456,7 +462,7 @@ For a function ``f`` which has a parameter of type ``T``, the generated SPIR-V s
 This approach gives us unified handling of function parameters and local variables: both of them are accessed via load/store instructions.
 This approach gives us unified handling of function parameters and local variables: both of them are accessed via load/store instructions.
 
 
 Intrinsic functions
 Intrinsic functions
------------------
+-------------------
 
 
 The following intrinsic HLSL functions are currently supported:
 The following intrinsic HLSL functions are currently supported:
 
 

+ 19 - 8
tools/clang/include/clang/SPIRV/ModuleBuilder.h

@@ -54,12 +54,15 @@ public:
 
 
   /// \brief Registers a function parameter of the given pointer type in the
   /// \brief Registers a function parameter of the given pointer type in the
   /// current function and returns its <result-id>.
   /// current function and returns its <result-id>.
-  uint32_t addFnParameter(uint32_t ptrType, llvm::StringRef name = "");
+  uint32_t addFnParam(uint32_t ptrType, llvm::StringRef name = "");
 
 
-  /// \brief Creates a local variable of the given pointer type in the current
+  /// \brief Creates a local variable of the given type in the current
   /// function and returns its <result-id>.
   /// function and returns its <result-id>.
-  uint32_t addFnVariable(uint32_t ptrType, llvm::StringRef name = "",
-                         llvm::Optional<uint32_t> init = llvm::None);
+  ///
+  /// The corresponding pointer type of the given type will be constructed in
+  /// this method for the variable itself.
+  uint32_t addFnVar(uint32_t valueType, llvm::StringRef name = "",
+                    llvm::Optional<uint32_t> init = llvm::None);
 
 
   /// \brief Ends building of the current function. Returns true of success,
   /// \brief Ends building of the current function. Returns true of success,
   /// false on failure. All basic blocks constructed from the beginning or
   /// false on failure. All basic blocks constructed from the beginning or
@@ -208,15 +211,22 @@ public:
   ///
   ///
   /// The corresponding pointer type of the given type will be constructed in
   /// The corresponding pointer type of the given type will be constructed in
   /// this method for the variable itself.
   /// this method for the variable itself.
-  uint32_t addStageIOVariable(uint32_t type, spv::StorageClass storageClass);
+  uint32_t addStageIOVar(uint32_t type, spv::StorageClass storageClass);
 
 
   /// \brief Adds a stage builtin variable whose value is of the given type.
   /// \brief Adds a stage builtin variable whose value is of the given type.
   ///
   ///
   /// The corresponding pointer type of the given type will be constructed in
   /// The corresponding pointer type of the given type will be constructed in
   /// this method for the variable itself.
   /// this method for the variable itself.
-  uint32_t addStageBuiltinVariable(uint32_t type,
-                                   spv::StorageClass storageClass,
-                                   spv::BuiltIn);
+  uint32_t addStageBuiltinVar(uint32_t type, spv::StorageClass storageClass,
+                              spv::BuiltIn);
+
+  /// \brief Adds a file/module visible variable. This variable will have
+  /// Private storage class.
+  ///
+  /// The corresponding pointer type of the given type will be constructed in
+  /// this method for the variable itself.
+  uint32_t addFileVar(uint32_t valueType, llvm::StringRef name = "",
+                      llvm::Optional<uint32_t> init = llvm::None);
 
 
   /// \brief Decorates the given target <result-id> with the given location.
   /// \brief Decorates the given target <result-id> with the given location.
   void decorateLocation(uint32_t targetId, uint32_t location);
   void decorateLocation(uint32_t targetId, uint32_t location);
@@ -242,6 +252,7 @@ public:
   uint32_t getConstantFloat32(float value);
   uint32_t getConstantFloat32(float value);
   uint32_t getConstantComposite(uint32_t typeId,
   uint32_t getConstantComposite(uint32_t typeId,
                                 llvm::ArrayRef<uint32_t> constituents);
                                 llvm::ArrayRef<uint32_t> constituents);
+  uint32_t getConstantNull(uint32_t type);
 
 
 private:
 private:
   /// \brief Map from basic blocks' <label-id> to their structured
   /// \brief Map from basic blocks' <label-id> to their structured

+ 22 - 20
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -21,23 +21,26 @@ namespace spirv {
 bool DeclResultIdMapper::createStageVarFromFnReturn(
 bool DeclResultIdMapper::createStageVarFromFnReturn(
     const FunctionDecl *funcDecl) {
     const FunctionDecl *funcDecl) {
   // SemanticDecl for the return value is attached to the FunctionDecl.
   // SemanticDecl for the return value is attached to the FunctionDecl.
-  return createStageVariables(funcDecl, true);
+  return createStageVars(funcDecl, true);
 }
 }
 
 
 bool DeclResultIdMapper::createStageVarFromFnParam(
 bool DeclResultIdMapper::createStageVarFromFnParam(
     const ParmVarDecl *paramDecl) {
     const ParmVarDecl *paramDecl) {
-  return createStageVariables(paramDecl, false);
+  return createStageVars(paramDecl, false);
 }
 }
 
 
 void DeclResultIdMapper::registerDeclResultId(const NamedDecl *symbol,
 void DeclResultIdMapper::registerDeclResultId(const NamedDecl *symbol,
                                               uint32_t resultId) {
                                               uint32_t resultId) {
   auto sc = spv::StorageClass::Function;
   auto sc = spv::StorageClass::Function;
-  // TODO: need to fix the storage class for other cases
   if (const auto *varDecl = dyn_cast<VarDecl>(symbol)) {
   if (const auto *varDecl = dyn_cast<VarDecl>(symbol)) {
-    if (!varDecl->isLocalVarDecl()) {
-      // Global variables are by default constant. But the default behavior
-      // can be changed via command line option.
+    if (varDecl->isExternallyVisible()) {
+      // TODO: Global variables are by default constant. But the default
+      // behavior can be changed via command line option. So Uniform may
+      // not be the correct storage class.
       sc = spv::StorageClass::Uniform;
       sc = spv::StorageClass::Uniform;
+    } else if (!varDecl->hasLocalStorage()) {
+      // File scope variables
+      sc = spv::StorageClass::Private;
     }
     }
   }
   }
   normalDecls[symbol] = {resultId, sc};
   normalDecls[symbol] = {resultId, sc};
@@ -141,7 +144,7 @@ DeclResultIdMapper::resolveStorageClass(const Decl *decl) const {
   return resolver.get();
   return resolver.get();
 }
 }
 
 
-std::vector<uint32_t> DeclResultIdMapper::collectStageVariables() const {
+std::vector<uint32_t> DeclResultIdMapper::collectStageVars() const {
   std::vector<uint32_t> vars;
   std::vector<uint32_t> vars;
 
 
   for (const auto &var : stageVars)
   for (const auto &var : stageVars)
@@ -175,8 +178,8 @@ DeclResultIdMapper::getFnParamOrRetType(const DeclaratorDecl *decl) const {
   return decl->getType();
   return decl->getType();
 }
 }
 
 
-bool DeclResultIdMapper::createStageVariables(const DeclaratorDecl *decl,
-                                              bool forRet) {
+bool DeclResultIdMapper::createStageVars(const DeclaratorDecl *decl,
+                                         bool forRet) {
   QualType type = getFnParamOrRetType(decl);
   QualType type = getFnParamOrRetType(decl);
 
 
   if (type->isVoidType()) {
   if (type->isVoidType()) {
@@ -228,7 +231,7 @@ bool DeclResultIdMapper::createStageVariables(const DeclaratorDecl *decl,
 
 
     // Recursively handle all the fields.
     // Recursively handle all the fields.
     for (const auto *field : structDecl->fields()) {
     for (const auto *field : structDecl->fields()) {
-      if (!createStageVariables(field, forRet))
+      if (!createStageVars(field, forRet))
         return false;
         return false;
     }
     }
   }
   }
@@ -261,13 +264,13 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar) {
   case hlsl::Semantic::Kind::Position: {
   case hlsl::Semantic::Kind::Position: {
     switch (sigPointKind) {
     switch (sigPointKind) {
     case hlsl::SigPoint::Kind::VSIn:
     case hlsl::SigPoint::Kind::VSIn:
-      return theBuilder.addStageIOVariable(type, sc);
+      return theBuilder.addStageIOVar(type, sc);
     case hlsl::SigPoint::Kind::VSOut:
     case hlsl::SigPoint::Kind::VSOut:
       stageVar->setIsSpirvBuiltin();
       stageVar->setIsSpirvBuiltin();
-      return theBuilder.addStageBuiltinVariable(type, sc, BuiltIn::Position);
+      return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::Position);
     case hlsl::SigPoint::Kind::PSIn:
     case hlsl::SigPoint::Kind::PSIn:
       stageVar->setIsSpirvBuiltin();
       stageVar->setIsSpirvBuiltin();
-      return theBuilder.addStageBuiltinVariable(type, sc, BuiltIn::FragCoord);
+      return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::FragCoord);
     default:
     default:
       emitError("semantic Position for SigPoint %0 unimplemented yet")
       emitError("semantic Position for SigPoint %0 unimplemented yet")
           << stageVar->getSigPoint()->GetName();
           << stageVar->getSigPoint()->GetName();
@@ -277,7 +280,7 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar) {
   // According to DXIL spec, the VertexID SV can only be used by VSIn.
   // According to DXIL spec, the VertexID SV can only be used by VSIn.
   case hlsl::Semantic::Kind::VertexID:
   case hlsl::Semantic::Kind::VertexID:
     stageVar->setIsSpirvBuiltin();
     stageVar->setIsSpirvBuiltin();
-    return theBuilder.addStageBuiltinVariable(type, sc, BuiltIn::VertexIndex);
+    return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::VertexIndex);
   // According to DXIL spec, the InstanceID SV can  be used by VSIn, VSOut,
   // According to DXIL spec, the InstanceID SV can  be used by VSIn, VSOut,
   // HSCPIn, HSCPOut, DSCPIn, DSOut, GSVIn, GSOut, PSIn.
   // HSCPIn, HSCPOut, DSCPIn, DSOut, GSVIn, GSOut, PSIn.
   // According to Vulkan spec, the InstanceIndex can only be used by VSIn.
   // According to Vulkan spec, the InstanceIndex can only be used by VSIn.
@@ -285,12 +288,11 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar) {
     switch (sigPointKind) {
     switch (sigPointKind) {
     case hlsl::SigPoint::Kind::VSIn:
     case hlsl::SigPoint::Kind::VSIn:
       stageVar->setIsSpirvBuiltin();
       stageVar->setIsSpirvBuiltin();
-      return theBuilder.addStageBuiltinVariable(type, sc,
-                                                BuiltIn::InstanceIndex);
+      return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::InstanceIndex);
     case hlsl::SigPoint::Kind::VSOut:
     case hlsl::SigPoint::Kind::VSOut:
-      return theBuilder.addStageIOVariable(type, sc);
+      return theBuilder.addStageIOVar(type, sc);
     case hlsl::SigPoint::Kind::PSIn:
     case hlsl::SigPoint::Kind::PSIn:
-      return theBuilder.addStageIOVariable(type, sc);
+      return theBuilder.addStageIOVar(type, sc);
     default:
     default:
       emitError("semantic InstanceID for SigPoint %0 unimplemented yet")
       emitError("semantic InstanceID for SigPoint %0 unimplemented yet")
           << stageVar->getSigPoint()->GetName();
           << stageVar->getSigPoint()->GetName();
@@ -300,7 +302,7 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar) {
   // According to DXIL spec, the Depth SV can only be used by PSOut.
   // According to DXIL spec, the Depth SV can only be used by PSOut.
   case hlsl::Semantic::Kind::Depth:
   case hlsl::Semantic::Kind::Depth:
     stageVar->setIsSpirvBuiltin();
     stageVar->setIsSpirvBuiltin();
-    return theBuilder.addStageBuiltinVariable(type, sc, BuiltIn::FragDepth);
+    return theBuilder.addStageBuiltinVar(type, sc, BuiltIn::FragDepth);
   // According to DXIL spec, the Target SV can only be used by PSOut.
   // According to DXIL spec, the Target SV can only be used by PSOut.
   // There is no corresponding builtin decoration in SPIR-V. So generate normal
   // There is no corresponding builtin decoration in SPIR-V. So generate normal
   // Vulkan stage input/output variables.
   // Vulkan stage input/output variables.
@@ -308,7 +310,7 @@ uint32_t DeclResultIdMapper::createSpirvStageVar(StageVar *stageVar) {
   // An arbitrary semantic is defined by users. Generate normal Vulkan stage
   // An arbitrary semantic is defined by users. Generate normal Vulkan stage
   // input/output variables.
   // input/output variables.
   case hlsl::Semantic::Kind::Arbitrary: {
   case hlsl::Semantic::Kind::Arbitrary: {
-    return theBuilder.addStageIOVariable(type, sc);
+    return theBuilder.addStageIOVar(type, sc);
     // TODO: patch constant function in hull shader
     // TODO: patch constant function in hull shader
   }
   }
   default:
   default:

+ 2 - 2
tools/clang/lib/SPIRV/DeclResultIdMapper.h

@@ -145,7 +145,7 @@ public:
 
 
   /// \brief Returns all defined stage (builtin/input/ouput) variables in this
   /// \brief Returns all defined stage (builtin/input/ouput) variables in this
   /// mapper.
   /// mapper.
-  std::vector<uint32_t> collectStageVariables() const;
+  std::vector<uint32_t> collectStageVars() const;
 
 
   /// \brief Decorates all stage input and output variables with proper
   /// \brief Decorates all stage input and output variables with proper
   /// location.
   /// location.
@@ -171,7 +171,7 @@ private:
   /// and returns true on success.
   /// and returns true on success.
   ///
   ///
   /// Assumes the decl has semantic attached to itself or to its fields.
   /// Assumes the decl has semantic attached to itself or to its fields.
-  bool createStageVariables(const DeclaratorDecl *decl, bool forReturnValue);
+  bool createStageVars(const DeclaratorDecl *decl, bool forReturnValue);
 
 
   /// Creates the SPIR-V variable instruction for the given StageVar and returns
   /// Creates the SPIR-V variable instruction for the given StageVar and returns
   /// the <result-id>. Also sets whether the StageVar is a SPIR-V builtin and
   /// the <result-id>. Also sets whether the StageVar is a SPIR-V builtin and

+ 26 - 8
tools/clang/lib/SPIRV/ModuleBuilder.cpp

@@ -54,7 +54,7 @@ uint32_t ModuleBuilder::beginFunction(uint32_t funcType, uint32_t returnType,
   return fId;
   return fId;
 }
 }
 
 
-uint32_t ModuleBuilder::addFnParameter(uint32_t ptrType, llvm::StringRef name) {
+uint32_t ModuleBuilder::addFnParam(uint32_t ptrType, llvm::StringRef name) {
   assert(theFunction && "found detached parameter");
   assert(theFunction && "found detached parameter");
 
 
   const uint32_t paramId = theContext.takeNextId();
   const uint32_t paramId = theContext.takeNextId();
@@ -64,10 +64,11 @@ uint32_t ModuleBuilder::addFnParameter(uint32_t ptrType, llvm::StringRef name) {
   return paramId;
   return paramId;
 }
 }
 
 
-uint32_t ModuleBuilder::addFnVariable(uint32_t ptrType, llvm::StringRef name,
-                                      llvm::Optional<uint32_t> init) {
+uint32_t ModuleBuilder::addFnVar(uint32_t varType, llvm::StringRef name,
+                                 llvm::Optional<uint32_t> init) {
   assert(theFunction && "found detached local variable");
   assert(theFunction && "found detached local variable");
 
 
+  const uint32_t ptrType = getPointerType(varType, spv::StorageClass::Function);
   const uint32_t varId = theContext.takeNextId();
   const uint32_t varId = theContext.takeNextId();
   theFunction->addVariable(ptrType, varId, init);
   theFunction->addVariable(ptrType, varId, init);
   theModule.addDebugName(varId, name);
   theModule.addDebugName(varId, name);
@@ -310,8 +311,8 @@ uint32_t ModuleBuilder::getGLSLExtInstSet() {
   return glslExtSetId;
   return glslExtSetId;
 }
 }
 
 
-uint32_t ModuleBuilder::addStageIOVariable(uint32_t type,
-                                           spv::StorageClass storageClass) {
+uint32_t ModuleBuilder::addStageIOVar(uint32_t type,
+                                      spv::StorageClass storageClass) {
   const uint32_t pointerType = getPointerType(type, storageClass);
   const uint32_t pointerType = getPointerType(type, storageClass);
   const uint32_t varId = theContext.takeNextId();
   const uint32_t varId = theContext.takeNextId();
   instBuilder.opVariable(pointerType, varId, storageClass, llvm::None).x();
   instBuilder.opVariable(pointerType, varId, storageClass, llvm::None).x();
@@ -319,9 +320,8 @@ uint32_t ModuleBuilder::addStageIOVariable(uint32_t type,
   return varId;
   return varId;
 }
 }
 
 
-uint32_t ModuleBuilder::addStageBuiltinVariable(uint32_t type,
-                                                spv::StorageClass sc,
-                                                spv::BuiltIn builtin) {
+uint32_t ModuleBuilder::addStageBuiltinVar(uint32_t type, spv::StorageClass sc,
+                                           spv::BuiltIn builtin) {
   const uint32_t pointerType = getPointerType(type, sc);
   const uint32_t pointerType = getPointerType(type, sc);
   const uint32_t varId = theContext.takeNextId();
   const uint32_t varId = theContext.takeNextId();
   instBuilder.opVariable(pointerType, varId, sc, llvm::None).x();
   instBuilder.opVariable(pointerType, varId, sc, llvm::None).x();
@@ -334,6 +334,17 @@ uint32_t ModuleBuilder::addStageBuiltinVariable(uint32_t type,
   return varId;
   return varId;
 }
 }
 
 
+uint32_t ModuleBuilder::addFileVar(uint32_t type, llvm::StringRef name,
+                                   llvm::Optional<uint32_t> init) {
+  const uint32_t pointerType = getPointerType(type, spv::StorageClass::Private);
+  const uint32_t varId = theContext.takeNextId();
+  instBuilder.opVariable(pointerType, varId, spv::StorageClass::Private, init)
+      .x();
+  theModule.addVariable(std::move(constructSite));
+  theModule.addDebugName(varId, name);
+  return varId;
+}
+
 void ModuleBuilder::decorateLocation(uint32_t targetId, uint32_t location) {
 void ModuleBuilder::decorateLocation(uint32_t targetId, uint32_t location) {
   const Decoration *d =
   const Decoration *d =
       Decoration::getLocation(theContext, location, llvm::None);
       Decoration::getLocation(theContext, location, llvm::None);
@@ -451,6 +462,13 @@ ModuleBuilder::getConstantComposite(uint32_t typeId,
   return constId;
   return constId;
 }
 }
 
 
+uint32_t ModuleBuilder::getConstantNull(uint32_t typeId) {
+  const Constant *constant = Constant::getNull(theContext, typeId);
+  const uint32_t constId = theContext.getResultIdForConstant(constant);
+  theModule.addConstant(constant, constId);
+  return constId;
+}
+
 BasicBlock *ModuleBuilder::getBasicBlock(uint32_t labelId) {
 BasicBlock *ModuleBuilder::getBasicBlock(uint32_t labelId) {
   auto it = basicBlocks.find(labelId);
   auto it = basicBlocks.find(labelId);
   if (it == basicBlocks.end()) {
   if (it == basicBlocks.end()) {

+ 85 - 41
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -153,6 +153,8 @@ void SPIRVEmitter::HandleTranslationUnit(ASTContext &context) {
       if (funcDecl->getName() == entryFunctionName) {
       if (funcDecl->getName() == entryFunctionName) {
         workQueue.insert(funcDecl);
         workQueue.insert(funcDecl);
       }
       }
+    } else if (auto *varDecl = dyn_cast<VarDecl>(decl)) {
+      doVarDecl(varDecl);
     }
     }
   }
   }
 
 
@@ -164,8 +166,7 @@ void SPIRVEmitter::HandleTranslationUnit(ASTContext &context) {
   }
   }
 
 
   theBuilder.addEntryPoint(getSpirvShaderStage(shaderModel), entryFunctionId,
   theBuilder.addEntryPoint(getSpirvShaderStage(shaderModel), entryFunctionId,
-                           entryFunctionName,
-                           declIdMapper.collectStageVariables());
+                           entryFunctionName, declIdMapper.collectStageVars());
 
 
   AddExecutionModeForEntryPoint(entryFunctionId);
   AddExecutionModeForEntryPoint(entryFunctionId);
 
 
@@ -348,7 +349,7 @@ void SPIRVEmitter::doFunctionDecl(const FunctionDecl *decl) {
 
 
   const llvm::StringRef funcName = decl->getName();
   const llvm::StringRef funcName = decl->getName();
 
 
-  uint32_t funcId;
+  uint32_t funcId = 0;
 
 
   if (funcName == entryFunctionName) {
   if (funcName == entryFunctionName) {
     // First create stage variables for the entry point.
     // First create stage variables for the entry point.
@@ -394,7 +395,7 @@ void SPIRVEmitter::doFunctionDecl(const FunctionDecl *decl) {
     for (uint32_t i = 0; i < decl->getNumParams(); ++i) {
     for (uint32_t i = 0; i < decl->getNumParams(); ++i) {
       const ParmVarDecl *paramDecl = decl->getParamDecl(i);
       const ParmVarDecl *paramDecl = decl->getParamDecl(i);
       const uint32_t paramId =
       const uint32_t paramId =
-          theBuilder.addFnParameter(paramTypes[i], paramDecl->getName());
+          theBuilder.addFnParam(paramTypes[i], paramDecl->getName());
       declIdMapper.registerDeclResultId(paramDecl, paramId);
       declIdMapper.registerDeclResultId(paramDecl, paramId);
     }
     }
   }
   }
@@ -404,6 +405,12 @@ void SPIRVEmitter::doFunctionDecl(const FunctionDecl *decl) {
     const uint32_t entryLabel = theBuilder.createBasicBlock("bb.entry");
     const uint32_t entryLabel = theBuilder.createBasicBlock("bb.entry");
     theBuilder.setInsertPoint(entryLabel);
     theBuilder.setInsertPoint(entryLabel);
 
 
+    // Initialize all global variables at the beginning of the entry function
+    if (funcId == entryFunctionId)
+      for (const VarDecl *varDecl : toInitGloalVars)
+        theBuilder.createStore(declIdMapper.getDeclResultId(varDecl),
+                               doExpr(varDecl->getInit()));
+
     // Process all statments in the body.
     // Process all statments in the body.
     doStmt(decl->getBody());
     doStmt(decl->getBody());
 
 
@@ -420,43 +427,59 @@ void SPIRVEmitter::doFunctionDecl(const FunctionDecl *decl) {
 }
 }
 
 
 void SPIRVEmitter::doVarDecl(const VarDecl *decl) {
 void SPIRVEmitter::doVarDecl(const VarDecl *decl) {
-  if (decl->isLocalVarDecl()) {
-    const uint32_t ptrType =
-        theBuilder.getPointerType(typeTranslator.translateType(decl->getType()),
-                                  spv::StorageClass::Function);
+  // The contents in externally visible variables can be updated via the
+  // pipeline. They should be handled differently from file and function scope
+  // variables.
+  // File scope variables (static "global" and "local" variables) belongs to
+  // the Private storage class, while function scope variables (normal "local"
+  // variables) belongs to the Function storage class.
+  if (!decl->isExternallyVisible()) {
+    // We already know the variable is not externally visible here. If it does
+    // not have local storage, it should be file scope variable.
+    const bool isFileScopeVar = !decl->hasLocalStorage();
+    const uint32_t varType = typeTranslator.translateType(decl->getType());
 
 
     // Handle initializer. SPIR-V requires that "initializer must be an <id>
     // Handle initializer. SPIR-V requires that "initializer must be an <id>
     // from a constant instruction or a global (module scope) OpVariable
     // from a constant instruction or a global (module scope) OpVariable
     // instruction."
     // instruction."
-    llvm::Optional<uint32_t> constInitializer = llvm::None;
-    uint32_t varInitializer = 0;
+    llvm::Optional<uint32_t> constInit;
     if (decl->hasInit()) {
     if (decl->hasInit()) {
-      const Expr *declInit = decl->getInit();
-
       // First try to evaluate the initializer as a constant expression
       // First try to evaluate the initializer as a constant expression
       Expr::EvalResult evalResult;
       Expr::EvalResult evalResult;
-      if (declInit->EvaluateAsRValue(evalResult, astContext) &&
+      if (decl->getInit()->EvaluateAsRValue(evalResult, astContext) &&
           !evalResult.HasSideEffects) {
           !evalResult.HasSideEffects) {
-        constInitializer = llvm::Optional<uint32_t>(
+        constInit = llvm::Optional<uint32_t>(
             translateAPValue(evalResult.Val, decl->getType()));
             translateAPValue(evalResult.Val, decl->getType()));
       }
       }
-
-      // If we cannot evaluate the initializer as a constant expression, we'll
-      // need use OpStore to write the initializer to the variable.
-      if (!constInitializer.hasValue()) {
-        varInitializer = doExpr(declInit);
-      }
+    } else if (isFileScopeVar) {
+      // For static variables, if no initializers are provided, we should
+      // initialize them to zero values.
+      constInit = llvm::Optional<uint32_t>(theBuilder.getConstantNull(varType));
     }
     }
 
 
     const uint32_t varId =
     const uint32_t varId =
-        theBuilder.addFnVariable(ptrType, decl->getName(), constInitializer);
+        isFileScopeVar
+            ? theBuilder.addFileVar(varType, decl->getName(), constInit)
+            : theBuilder.addFnVar(varType, decl->getName(), constInit);
     declIdMapper.registerDeclResultId(decl, varId);
     declIdMapper.registerDeclResultId(decl, varId);
 
 
-    if (varInitializer) {
-      theBuilder.createStore(varId, varInitializer);
+    // If we cannot evaluate the initializer as a constant expression, we'll
+    // need to use OpStore to write the initializer to the variable.
+    // Also we should only evaluate the initializer once for a static variable.
+    if (decl->hasInit() && !constInit.hasValue()) {
+      if (isFileScopeVar) {
+        if (decl->isStaticLocal()) {
+          initOnce(decl->getName(), varId, decl->getInit());
+        } else {
+          // Defer to initialize these global variables at the beginning of the
+          // entry function.
+          toInitGloalVars.push_back(decl);
+        }
+      } else {
+        theBuilder.createStore(varId, doExpr(decl->getInit()));
+      }
     }
     }
   } else {
   } else {
-    // TODO: handle global variables
     emitError("Global variables are not supported yet.");
     emitError("Global variables are not supported yet.");
   }
   }
 }
 }
@@ -972,11 +995,8 @@ uint32_t SPIRVEmitter::doCallExpr(const CallExpr *callExpr) {
     for (const auto *arg : callExpr->arguments()) {
     for (const auto *arg : callExpr->arguments()) {
       // We need to create variables for holding the values to be used as
       // We need to create variables for holding the values to be used as
       // arguments. The variables themselves are of pointer types.
       // arguments. The variables themselves are of pointer types.
-      const uint32_t ptrType = theBuilder.getPointerType(
-          typeTranslator.translateType(arg->getType()),
-          spv::StorageClass::Function);
-
-      const uint32_t tempVarId = theBuilder.addFnVariable(ptrType);
+      const uint32_t varType = typeTranslator.translateType(arg->getType());
+      const uint32_t tempVarId = theBuilder.addFnVar(varType);
       theBuilder.createStore(tempVarId, doExpr(arg));
       theBuilder.createStore(tempVarId, doExpr(arg));
 
 
       params.push_back(tempVarId);
       params.push_back(tempVarId);
@@ -1218,9 +1238,7 @@ uint32_t SPIRVEmitter::doCXXOperatorCallExpr(const CXXOperatorCallExpr *expr) {
       // all indices are contant integers.
       // all indices are contant integers.
       if (!baseExpr->isGLValue()) {
       if (!baseExpr->isGLValue()) {
         const uint32_t compositeType = typeTranslator.translateType(baseType);
         const uint32_t compositeType = typeTranslator.translateType(baseType);
-        const uint32_t ptrType = theBuilder.getPointerType(
-            compositeType, spv::StorageClass::Function);
-        const uint32_t tempVar = theBuilder.addFnVariable(ptrType, "temp.var");
+        const uint32_t tempVar = theBuilder.addFnVar(compositeType, "temp.var");
         theBuilder.createStore(tempVar, base);
         theBuilder.createStore(tempVar, base);
         base = tempVar;
         base = tempVar;
       }
       }
@@ -1665,6 +1683,35 @@ uint32_t SPIRVEmitter::processBinaryOp(const Expr *lhs, const Expr *rhs,
   return 0;
   return 0;
 }
 }
 
 
+void SPIRVEmitter::initOnce(std::string varName, uint32_t varPtr,
+                            const Expr *varInit) {
+  const uint32_t boolType = theBuilder.getBoolType();
+  varName = "init.done." + varName;
+
+  // Create a file/module visible variable to hold the initialization state.
+  const uint32_t initDoneVar = theBuilder.addFileVar(
+      boolType, varName, theBuilder.getConstantBool(false));
+
+  const uint32_t condition = theBuilder.createLoad(boolType, initDoneVar);
+
+  const uint32_t thenBB = theBuilder.createBasicBlock("if.true");
+  const uint32_t mergeBB = theBuilder.createBasicBlock("if.merge");
+
+  theBuilder.createConditionalBranch(condition, thenBB, mergeBB, mergeBB);
+  theBuilder.addSuccessor(thenBB);
+  theBuilder.addSuccessor(mergeBB);
+  theBuilder.setMergeTarget(mergeBB);
+
+  theBuilder.setInsertPoint(thenBB);
+  // Do initialization and mark done
+  theBuilder.createStore(varPtr, doExpr(varInit));
+  theBuilder.createStore(initDoneVar, theBuilder.getConstantBool(true));
+  theBuilder.createBranch(mergeBB);
+  theBuilder.addSuccessor(mergeBB);
+
+  theBuilder.setInsertPoint(mergeBB);
+}
+
 bool SPIRVEmitter::isVectorShuffle(const Expr *expr) {
 bool SPIRVEmitter::isVectorShuffle(const Expr *expr) {
   // TODO: the following check is essentially duplicated from
   // TODO: the following check is essentially duplicated from
   // doHLSLVectorElementExpr. Should unify them.
   // doHLSLVectorElementExpr. Should unify them.
@@ -2118,9 +2165,8 @@ uint32_t SPIRVEmitter::processMatrixBinaryOp(const Expr *lhs, const Expr *rhs,
   case BO_DivAssign:
   case BO_DivAssign:
   case BO_RemAssign: {
   case BO_RemAssign: {
     const uint32_t vecType = typeTranslator.getComponentVectorType(lhsType);
     const uint32_t vecType = typeTranslator.getComponentVectorType(lhsType);
-    const auto actOnEachVec = [this, spvOp, rhsVal](uint32_t index,
-                                                    uint32_t vecType,
-                                                    uint32_t lhsVec) {
+    const auto actOnEachVec = [this, spvOp, rhsVal](
+        uint32_t index, uint32_t vecType, uint32_t lhsVec) {
       // For each vector of lhs, we need to load the corresponding vector of
       // For each vector of lhs, we need to load the corresponding vector of
       // rhs and do the operation on them.
       // rhs and do the operation on them.
       const uint32_t rhsVec =
       const uint32_t rhsVec =
@@ -2486,9 +2532,8 @@ uint32_t SPIRVEmitter::processIntrinsicFloatSign(const CallExpr *callExpr) {
 
 
   // For matrices, we can perform the instruction on each vector of the matrix.
   // For matrices, we can perform the instruction on each vector of the matrix.
   if (TypeTranslator::isSpirvAcceptableMatrixType(argType)) {
   if (TypeTranslator::isSpirvAcceptableMatrixType(argType)) {
-    const auto actOnEachVec = [this, glslInstSetId](uint32_t /*index*/,
-                                                    uint32_t vecType,
-                                                    uint32_t curRowId) {
+    const auto actOnEachVec = [this, glslInstSetId](
+        uint32_t /*index*/, uint32_t vecType, uint32_t curRowId) {
       return theBuilder.createExtInst(vecType, glslInstSetId,
       return theBuilder.createExtInst(vecType, glslInstSetId,
                                       GLSLstd450::GLSLstd450FSign, {curRowId});
                                       GLSLstd450::GLSLstd450FSign, {curRowId});
     };
     };
@@ -2516,9 +2561,8 @@ uint32_t SPIRVEmitter::processIntrinsicUsingGLSLInst(
     // instruction on each vector of the matrix.
     // instruction on each vector of the matrix.
     if (actPerRowForMatrices &&
     if (actPerRowForMatrices &&
         TypeTranslator::isSpirvAcceptableMatrixType(arg->getType())) {
         TypeTranslator::isSpirvAcceptableMatrixType(arg->getType())) {
-      const auto actOnEachVec = [this, glslInstSetId,
-                                 opcode](uint32_t /*index*/, uint32_t vecType,
-                                         uint32_t curRowId) {
+      const auto actOnEachVec = [this, glslInstSetId, opcode](
+          uint32_t /*index*/, uint32_t vecType, uint32_t curRowId) {
         return theBuilder.createExtInst(vecType, glslInstSetId, opcode,
         return theBuilder.createExtInst(vecType, glslInstSetId, opcode,
                                         {curRowId});
                                         {curRowId});
       };
       };

+ 8 - 0
tools/clang/lib/SPIRV/SPIRVEmitter.h

@@ -15,6 +15,7 @@
 #define LLVM_CLANG_LIB_SPIRV_SPIRVEMITTER_H
 #define LLVM_CLANG_LIB_SPIRV_SPIRVEMITTER_H
 
 
 #include <stack>
 #include <stack>
+#include <string>
 #include <utility>
 #include <utility>
 #include <vector>
 #include <vector>
 
 
@@ -115,6 +116,9 @@ private:
                            uint32_t *lhsResultId = nullptr,
                            uint32_t *lhsResultId = nullptr,
                            const spv::Op mandateGenOpcode = spv::Op::Max);
                            const spv::Op mandateGenOpcode = spv::Op::Max);
 
 
+  /// Generates SPIR-V instructions to initialize the given variable once.
+  void initOnce(std::string varName, uint32_t varPtr, const Expr *varInit);
+
   /// Returns true if the given expression will be translated into a vector
   /// Returns true if the given expression will be translated into a vector
   /// shuffle instruction in SPIR-V.
   /// shuffle instruction in SPIR-V.
   ///
   ///
@@ -414,6 +418,10 @@ private:
   /// The current function under traversal.
   /// The current function under traversal.
   const FunctionDecl *curFunction;
   const FunctionDecl *curFunction;
 
 
+  /// Global variables that should be initialized once at the begining of the
+  /// entry function.
+  llvm::SmallVector<const VarDecl *, 4> toInitGloalVars;
+
   /// For loops, while loops, and switch statements may encounter "break"
   /// For loops, while loops, and switch statements may encounter "break"
   /// statements that alter their control flow. At any point the break statement
   /// statements that alter their control flow. At any point the break statement
   /// is observed, the control flow jumps to the inner-most scope's merge block.
   /// is observed, the control flow jumps to the inner-most scope's merge block.

+ 9 - 3
tools/clang/test/CodeGenSPIRV/spirv.storage-class.hlsl

@@ -5,15 +5,21 @@ struct VSOut {
     float4 out2: D;
     float4 out2: D;
 };
 };
 
 
+static float4 sgVar; // Private
+
 // CHECK: [[input:%\d+]] = OpVariable %_ptr_Input_v4float Input
 // CHECK: [[input:%\d+]] = OpVariable %_ptr_Input_v4float Input
 
 
-VSOut main(float4 input: A, uint index: B) {
-    VSOut ret;
+VSOut main(float4 input: A /* Input */, uint index: B /* Input */) {
+    static float4 slVar; // Private
+
+    VSOut ret; // Function
 
 
 // CHECK:      {{%\d+}} = OpAccessChain %_ptr_Input_float [[input]] {{%\d+}}
 // CHECK:      {{%\d+}} = OpAccessChain %_ptr_Input_float [[input]] {{%\d+}}
+// CHECK:      {{%\d+}} = OpAccessChain %_ptr_Private_float %sgVar {{%\d+}}
+// CHECK:      {{%\d+}} = OpAccessChain %_ptr_Private_float %slVar {{%\d+}}
 // CHECK:      [[lhs:%\d+]] = OpAccessChain %_ptr_Function_v4float %ret %int_0
 // CHECK:      [[lhs:%\d+]] = OpAccessChain %_ptr_Function_v4float %ret %int_0
 // CHECK-NEXT: {{%\d+}} = OpAccessChain %_ptr_Function_float [[lhs]] {{%\d+}}
 // CHECK-NEXT: {{%\d+}} = OpAccessChain %_ptr_Function_float [[lhs]] {{%\d+}}
-    ret.out1[index] = input[index];
+    ret.out1[index] = input[index] + sgVar[index] + slVar[index];
 
 
     return ret;
     return ret;
 }
 }

+ 50 - 0
tools/clang/test/CodeGenSPIRV/var.static.hlsl

@@ -0,0 +1,50 @@
+// Run: %dxc -T vs_6_0 -E main
+
+// CHECK: [[v3b0:%\d+]] = OpConstantNull %v3bool
+// CHECK: [[v4f0:%\d+]] = OpConstantNull %v4float
+
+// CHECK: %ga = OpVariable %_ptr_Private_int Private %int_6
+static int ga = 6;
+// CHECK: %gb = OpVariable %_ptr_Private_v3bool Private [[v3b0]]
+static bool3 gb;
+// The front end has no const evaluation support for HLSL specific types.
+// So the following will ends up trying to create an OpStore into gc. We emit
+// those initialization code at the beginning of the entry function.
+// TODO: optimize this to emit initializer directly: need to fix either the
+// general const evaluation in the front end or add const evaluation in our
+// InitListHandler.
+static float2x2 gc = {1, 2, 3, 4};
+
+// CHECK: [[input:%\d+]] = OpVariable %_ptr_Input_int Input
+
+// CHECK: %a = OpVariable %_ptr_Private_uint Private %uint_5
+// CHECK: %b = OpVariable %_ptr_Private_v4float Private [[v4f0]]
+// CHECK: %c = OpVariable %_ptr_Private_int Private
+
+// CHECK: %init_done_c = OpVariable %_ptr_Private_bool Private %false
+
+int main(int input: A) : B {
+// CHECK-LABEL: %bb_entry = OpLabel
+
+    // initialization of gc
+// CHECK:      [[v2f12:%\d+]] = OpCompositeConstruct %v2float %float_1 %float_2
+// CHECK-NEXT: [[v2f34:%\d+]] = OpCompositeConstruct %v2float %float_3 %float_4
+// CHECK-NEXT: [[mat1234:%\d+]] = OpCompositeConstruct %mat2v2float [[v2f12]] [[v2f34]]
+// CHECK-NEXT: OpStore %gc [[mat1234]]
+
+    static uint a = 5;    // const init
+    static float4 b;      // no init
+
+// CHECK-NEXT: [[initdonec:%\d+]] = OpLoad %bool %init_done_c
+// CHECK-NEXT: OpSelectionMerge %if_merge None
+// CHECK-NEXT: OpBranchConditional [[initdonec]] %if_true %if_merge
+// CHECK-NEXT: %if_true = OpLabel
+// CHECK-NEXT: [[initc:%\d+]] = OpLoad %int [[input]]
+// CHECK-NEXT: OpStore %c [[initc]]
+// CHECK-NEXT: OpStore %init_done_c %true
+// CHECK-NEXT: OpBranch %if_merge
+    static int c = input; // var init
+// CHECK-NEXT: %if_merge = OpLabel
+
+    return input;
+}

+ 1 - 0
tools/clang/unittests/SPIRV/CodeGenSPIRVTest.cpp

@@ -50,6 +50,7 @@ TEST_F(FileTest, VarInitMatrixMxN) { runFileTest("var.init.matrix.mxn.hlsl"); }
 TEST_F(FileTest, VarInitMatrixMx1) { runFileTest("var.init.matrix.mx1.hlsl"); }
 TEST_F(FileTest, VarInitMatrixMx1) { runFileTest("var.init.matrix.mx1.hlsl"); }
 TEST_F(FileTest, VarInitMatrix1xN) { runFileTest("var.init.matrix.1xn.hlsl"); }
 TEST_F(FileTest, VarInitMatrix1xN) { runFileTest("var.init.matrix.1xn.hlsl"); }
 TEST_F(FileTest, VarInitMatrix1x1) { runFileTest("var.init.matrix.1x1.hlsl"); }
 TEST_F(FileTest, VarInitMatrix1x1) { runFileTest("var.init.matrix.1x1.hlsl"); }
+TEST_F(FileTest, StaticVar) { runFileTest("var.static.hlsl"); }
 
 
 // For prefix/postfix increment/decrement
 // For prefix/postfix increment/decrement
 TEST_F(FileTest, UnaryOpPrefixIncrement) {
 TEST_F(FileTest, UnaryOpPrefixIncrement) {