Browse Source

[spirv] Change SPIRVEmitter methods to return structs (#659)

This allows us to convey more information about the evaluation
(constness, storage class, layout rule, etc.) than before
(only result id).

This sets the foundation for propagating relaxed precision info
later.
Lei Zhang 8 years ago
parent
commit
04e269a0e3

+ 6 - 113
tools/clang/lib/SPIRV/DeclResultIdMapper.cpp

@@ -65,7 +65,7 @@ DeclResultIdMapper::getDeclSpirvInfo(const NamedDecl *decl) const {
   return nullptr;
 }
 
-uint32_t DeclResultIdMapper::getDeclResultId(const NamedDecl *decl) {
+SpirvEvalInfo DeclResultIdMapper::getDeclResultId(const NamedDecl *decl) {
   if (const auto *info = getDeclSpirvInfo(decl))
     if (info->indexInCTBuffer >= 0) {
       // If this is a VarDecl inside a HLSLBufferDecl, we need to do an extra
@@ -80,11 +80,14 @@ uint32_t DeclResultIdMapper::getDeclResultId(const NamedDecl *decl) {
           // According to the Vulkan spec, cbuffer should follow standrad
           // uniform buffer layout, which GLSL std140 rules statisfies.
           LayoutRule::GLSLStd140);
-      return theBuilder.createAccessChain(
+
+      const uint32_t elemId = theBuilder.createAccessChain(
           theBuilder.getPointerType(varType, info->storageClass),
           info->resultId, {theBuilder.getConstantInt32(info->indexInCTBuffer)});
+
+      return {elemId, info->storageClass, info->layoutRule};
     } else {
-      return info->resultId;
+      return {info->resultId, info->storageClass, info->layoutRule};
     }
 
   assert(false && "found unregistered decl");
@@ -264,116 +267,6 @@ uint32_t DeclResultIdMapper::getCounterId(const VarDecl *decl) {
   return 0;
 }
 
-namespace {
-/// A class for resolving the storage info (storage class and memory layout) of
-/// a given Decl or Expr.
-class StorageInfoResolver : public RecursiveASTVisitor<StorageInfoResolver> {
-public:
-  explicit StorageInfoResolver(const DeclResultIdMapper &mapper)
-      : declIdMapper(mapper), baseDecl(nullptr) {}
-
-  bool TraverseMemberExpr(MemberExpr *expr) {
-    // For MemberExpr, the storage info should follow the base.
-    return TraverseStmt(expr->getBase());
-  }
-
-  bool TravereArraySubscriptExpr(ArraySubscriptExpr *expr) {
-    // For ArraySubscriptExpr, the storage info should follow the array object.
-    return TraverseStmt(expr->getBase());
-  }
-
-  bool TraverseCXXOperatorCallExpr(CXXOperatorCallExpr *expr) {
-    // For operator[], the storage info should follow the object.
-    if (expr->getOperator() == OverloadedOperatorKind::OO_Subscript)
-      return TraverseStmt(expr->getArg(0));
-
-    // TODO: the following may not be correct for all other operator calls.
-    for (uint32_t i = 0; i < expr->getNumArgs(); ++i)
-      if (!TraverseStmt(expr->getArg(i)))
-        return false;
-
-    return true;
-  }
-
-  bool TraverseCXXMemberCallExpr(CXXMemberCallExpr *expr) {
-    // For method calls, the storage info should follow the object.
-    return TraverseStmt(expr->getImplicitObjectArgument());
-  }
-
-  // For querying the storage info of a remapped decl
-
-  // Semantics may be attached to FunctionDecl, ParmVarDecl, and FieldDecl.
-  // We create stage variables for them and we may need to query the storage
-  // classes of these stage variables.
-  bool VisitFunctionDecl(FunctionDecl *decl) { return processDecl(decl); }
-  bool VisitFieldDecl(FieldDecl *decl) { return processDecl(decl); }
-  bool VisitParmVarDecl(ParmVarDecl *decl) { return processDecl(decl); }
-
-  // For querying the storage info of a normal decl
-
-  // Normal decls should be referred in expressions.
-  bool VisitDeclRefExpr(DeclRefExpr *expr) {
-    return processDecl(expr->getDecl());
-  }
-
-  bool processDecl(NamedDecl *decl) {
-    // Calling C++ methods like operator[] is also represented as DeclRefExpr.
-    if (isa<CXXMethodDecl>(decl))
-      return true;
-
-    if (!baseDecl) {
-      baseDecl = decl;
-      return true;
-    }
-
-    // Two different decls referenced in the expression: this expression stands
-    // for a derived temporary object, for which case we use the Function
-    // storage class and no layout rules.
-    // Turn baseDecl to nullptr so we return the proper info and stop further
-    // traversing.
-    baseDecl = nullptr;
-    return false;
-  }
-
-  spv::StorageClass getStorageClass() const {
-    if (const auto *info = declIdMapper.getDeclSpirvInfo(baseDecl))
-      return info->storageClass;
-
-    // No Decl referenced. This is probably a temporary expression.
-    return spv::StorageClass::Function;
-  }
-
-  LayoutRule getLayoutRule() const {
-    if (const auto *info = declIdMapper.getDeclSpirvInfo(baseDecl))
-      return info->layoutRule;
-
-    // No Decl referenced. This is probably a temporary expression.
-    return LayoutRule::Void;
-  }
-
-private:
-  const DeclResultIdMapper &declIdMapper;
-  const NamedDecl *baseDecl;
-};
-} // namespace
-
-spv::StorageClass
-DeclResultIdMapper::resolveStorageInfo(const Expr *expr,
-                                       LayoutRule *rule) const {
-  auto resolver = StorageInfoResolver(*this);
-  resolver.TraverseStmt(const_cast<Expr *>(expr));
-  if (rule)
-    *rule = resolver.getLayoutRule();
-  return resolver.getStorageClass();
-}
-
-spv::StorageClass
-DeclResultIdMapper::resolveStorageClass(const Decl *decl) const {
-  auto resolver = StorageInfoResolver(*this);
-  resolver.TraverseDecl(const_cast<Decl *>(decl));
-  return resolver.getStorageClass();
-}
-
 std::vector<uint32_t> DeclResultIdMapper::collectStageVars() const {
   std::vector<uint32_t> vars;
 

+ 4 - 10
tools/clang/lib/SPIRV/DeclResultIdMapper.h

@@ -10,6 +10,7 @@
 #ifndef LLVM_CLANG_LIB_SPIRV_DECLRESULTIDMAPPER_H
 #define LLVM_CLANG_LIB_SPIRV_DECLRESULTIDMAPPER_H
 
+#include <tuple>
 #include <vector>
 
 #include "dxc/HLSL/DxilSemantic.h"
@@ -24,6 +25,7 @@
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/Twine.h"
 
+#include "SpirvEvalInfo.h"
 #include "TypeTranslator.h"
 
 namespace clang {
@@ -194,10 +196,10 @@ public:
   /// Returns nullptr if no such decl was previously registered.
   const DeclSpirvInfo *getDeclSpirvInfo(const NamedDecl *decl) const;
 
-  /// \brief Returns the <result-id> for the given decl.
+  /// \brief Returns the information for the given decl.
   ///
   /// This method will panic if the given decl is not registered.
-  uint32_t getDeclResultId(const NamedDecl *decl);
+  SpirvEvalInfo getDeclResultId(const NamedDecl *decl);
 
   /// \brief Returns the <result-id> for the given function if already
   /// registered; otherwise, treats the given function as a normal decl and
@@ -208,14 +210,6 @@ public:
   /// {Append|Consume}StructuredBuffer variable.
   uint32_t getCounterId(const VarDecl *decl);
 
-  /// Returns the storage class for the given expression. If rule is not
-  /// nullptr, also writes the layout rule into it.
-  /// The expression is expected to be an lvalue. Otherwise this method may
-  /// panic.
-  spv::StorageClass resolveStorageInfo(const Expr *expr,
-                                       LayoutRule *rule = nullptr) const;
-  spv::StorageClass resolveStorageClass(const Decl *decl) const;
-
   /// \brief Returns all defined stage (builtin/input/ouput) variables in this
   /// mapper.
   std::vector<uint32_t> collectStageVars() const;

+ 183 - 184
tools/clang/lib/SPIRV/SPIRVEmitter.cpp

@@ -321,9 +321,8 @@ void SPIRVEmitter::doStmt(const Stmt *stmt,
   }
 }
 
-uint32_t SPIRVEmitter::doExpr(const Expr *expr) {
+SpirvEvalInfo SPIRVEmitter::doExpr(const Expr *expr) {
   if (const auto *delRefExpr = dyn_cast<DeclRefExpr>(expr)) {
-    // Returns the <result-id> of the referenced Decl.
     return declIdMapper.getDeclResultId(delRefExpr->getFoundDecl());
   }
 
@@ -346,15 +345,17 @@ uint32_t SPIRVEmitter::doExpr(const Expr *expr) {
 
   if (const auto *boolLiteral = dyn_cast<CXXBoolLiteralExpr>(expr)) {
     const bool value = boolLiteral->getValue();
-    return theBuilder.getConstantBool(value);
+    return SpirvEvalInfo::withConst(theBuilder.getConstantBool(value));
   }
 
   if (const auto *intLiteral = dyn_cast<IntegerLiteral>(expr)) {
-    return translateAPInt(intLiteral->getValue(), expr->getType());
+    return SpirvEvalInfo::withConst(
+        translateAPInt(intLiteral->getValue(), expr->getType()));
   }
 
   if (const auto *floatLiteral = dyn_cast<FloatingLiteral>(expr)) {
-    return translateAPFloat(floatLiteral->getValue(), expr->getType());
+    return SpirvEvalInfo::withConst(
+        translateAPFloat(floatLiteral->getValue(), expr->getType()));
   }
 
   // CompoundAssignOperator is a subclass of BinaryOperator. It should be
@@ -397,16 +398,17 @@ uint32_t SPIRVEmitter::doExpr(const Expr *expr) {
   }
 
   emitError("Expr '%0' is not supported yet.") << expr->getStmtClassName();
-  // TODO: handle other expressions
   return 0;
 }
 
-uint32_t SPIRVEmitter::loadIfGLValue(const Expr *expr) {
-  const uint32_t result = doExpr(expr);
+SpirvEvalInfo SPIRVEmitter::loadIfGLValue(const Expr *expr) {
+  auto info = doExpr(expr);
   if (expr->isGLValue())
-    return theBuilder.createLoad(getType(expr), result);
+    info.resultId = theBuilder.createLoad(
+        typeTranslator.translateType(expr->getType(), info.layoutRule),
+        info.resultId);
 
-  return result;
+  return info;
 }
 
 uint32_t SPIRVEmitter::castToType(uint32_t value, QualType fromType,
@@ -454,7 +456,7 @@ void SPIRVEmitter::doFunctionDecl(const FunctionDecl *decl) {
     // Non-entry functions are added to the work queue following function
     // calls. We have already assigned <result-id>s for it when translating
     // its call site. Query it here.
-    funcId = declIdMapper.getDeclResultId(decl);
+    funcId = declIdMapper.getDeclResultId(decl).resultId;
   }
 
   if (!needsLegalization &&
@@ -582,10 +584,7 @@ void SPIRVEmitter::doVarDecl(const VarDecl *decl) {
           toInitGloalVars.push_back(decl);
         }
       } else {
-        LayoutRule rhsLayout = LayoutRule::Void;
-        (void)declIdMapper.resolveStorageInfo(decl->getInit(), &rhsLayout);
-        storeValue(varId, loadIfGLValue(decl->getInit()), decl->getType(),
-                   spv::StorageClass::Function, LayoutRule::Void, rhsLayout);
+        storeValue(varId, loadIfGLValue(decl->getInit()), decl->getType());
       }
     }
   } else {
@@ -1112,35 +1111,35 @@ void SPIRVEmitter::doSwitchStmt(const SwitchStmt *switchStmt,
     processSwitchStmtUsingIfStmts(switchStmt);
 }
 
-uint32_t SPIRVEmitter::doArraySubscriptExpr(const ArraySubscriptExpr *expr) {
+SpirvEvalInfo
+SPIRVEmitter::doArraySubscriptExpr(const ArraySubscriptExpr *expr) {
   llvm::SmallVector<uint32_t, 4> indices;
   const auto *base = collectArrayStructIndices(expr, &indices);
+  auto info = doExpr(base);
 
-  LayoutRule rule = LayoutRule::Void;
-  const auto sc = declIdMapper.resolveStorageInfo(base, &rule);
   const uint32_t ptrType = theBuilder.getPointerType(
-      typeTranslator.translateType(expr->getType(), rule), sc);
+      typeTranslator.translateType(expr->getType(), info.layoutRule),
+      info.storageClass);
+  info.resultId = theBuilder.createAccessChain(ptrType, info, indices);
 
-  return theBuilder.createAccessChain(ptrType, doExpr(base), indices);
+  return info;
 }
 
-uint32_t SPIRVEmitter::doBinaryOperator(const BinaryOperator *expr) {
+SpirvEvalInfo SPIRVEmitter::doBinaryOperator(const BinaryOperator *expr) {
   const auto opcode = expr->getOpcode();
 
   // Handle assignment first since we need to evaluate rhs before lhs.
   // For other binary operations, we need to evaluate lhs before rhs.
   if (opcode == BO_Assign) {
-    LayoutRule rhsLayout = LayoutRule::Void;
-    (void)declIdMapper.resolveStorageInfo(expr->getRHS(), &rhsLayout);
     return processAssignment(expr->getLHS(), loadIfGLValue(expr->getRHS()),
-                             false, /*lhsPtr*/ 0, rhsLayout);
+                             false);
   }
 
   // Try to optimize floatMxN * float and floatN * float case
   if (opcode == BO_Mul) {
-    if (const uint32_t result = tryToGenFloatMatrixScale(expr))
+    if (const SpirvEvalInfo result = tryToGenFloatMatrixScale(expr))
       return result;
-    if (const uint32_t result = tryToGenFloatVectorScale(expr))
+    if (const SpirvEvalInfo result = tryToGenFloatVectorScale(expr))
       return result;
   }
 
@@ -1148,7 +1147,7 @@ uint32_t SPIRVEmitter::doBinaryOperator(const BinaryOperator *expr) {
   return processBinaryOp(expr->getLHS(), expr->getRHS(), opcode, resultType);
 }
 
-uint32_t SPIRVEmitter::doCallExpr(const CallExpr *callExpr) {
+SpirvEvalInfo SPIRVEmitter::doCallExpr(const CallExpr *callExpr) {
   if (const auto *operatorCall = dyn_cast<CXXOperatorCallExpr>(callExpr))
     return doCXXOperatorCallExpr(operatorCall);
 
@@ -1244,13 +1243,13 @@ uint32_t SPIRVEmitter::processCall(const CallExpr *callExpr) {
   return 0;
 }
 
-uint32_t SPIRVEmitter::doCastExpr(const CastExpr *expr) {
+SpirvEvalInfo SPIRVEmitter::doCastExpr(const CastExpr *expr) {
   const Expr *subExpr = expr->getSubExpr();
   const QualType toType = expr->getType();
 
   switch (expr->getCastKind()) {
   case CastKind::CK_LValueToRValue: {
-    const uint32_t fromValue = doExpr(subExpr);
+    auto info = doExpr(subExpr);
     if (isVectorShuffle(subExpr) || isa<ExtMatrixElementExpr>(subExpr) ||
         isBufferTextureIndexing(dyn_cast<CXXOperatorCallExpr>(subExpr)) ||
         isTextureMipsSampleIndexing(dyn_cast<CXXOperatorCallExpr>(subExpr))) {
@@ -1262,13 +1261,14 @@ uint32_t SPIRVEmitter::doCastExpr(const CastExpr *expr) {
       // should have already happened after creating access chain for each
       // element. For (RW)Buffer/RWTexture element accessing, load should have
       // already happened using OpImageFetch.
-
-      return fromValue;
+      return info;
     }
 
     // Using lvalue as rvalue means we need to OpLoad the contents from
     // the parameter/variable first.
-    return theBuilder.createLoad(getType(expr), fromValue);
+    info.resultId = theBuilder.createLoad(
+        typeTranslator.translateType(expr->getType(), info.layoutRule), info);
+    return info;
   }
   case CastKind::CK_NoOp:
     return doExpr(subExpr);
@@ -1363,16 +1363,16 @@ uint32_t SPIRVEmitter::doCastExpr(const CastExpr *expr) {
     if (colCount == 1)
       return createVectorSplat(subExpr, rowCount);
 
-    bool isConstVec = false;
-    const uint32_t vecSplat = createVectorSplat(subExpr, colCount, &isConstVec);
+    const auto vecSplat = createVectorSplat(subExpr, colCount);
     if (rowCount == 1)
       return vecSplat;
 
     const uint32_t matType = typeTranslator.translateType(toType);
     llvm::SmallVector<uint32_t, 4> vectors(size_t(rowCount), vecSplat);
 
-    if (isConstVec) {
-      return theBuilder.getConstantComposite(matType, vectors);
+    if (vecSplat.isConst) {
+      return SpirvEvalInfo::withConst(
+          theBuilder.getConstantComposite(matType, vectors));
     } else {
       return theBuilder.createCompositeConstruct(matType, vectors);
     }
@@ -1399,25 +1399,24 @@ uint32_t SPIRVEmitter::doCastExpr(const CastExpr *expr) {
   }
 }
 
-uint32_t
+SpirvEvalInfo
 SPIRVEmitter::doCompoundAssignOperator(const CompoundAssignOperator *expr) {
   const auto opcode = expr->getOpcode();
 
   // Try to optimize floatMxN *= float and floatN *= float case
   if (opcode == BO_MulAssign) {
-    if (const uint32_t result = tryToGenFloatMatrixScale(expr))
+    if (const SpirvEvalInfo result = tryToGenFloatMatrixScale(expr))
       return result;
-    if (const uint32_t result = tryToGenFloatVectorScale(expr))
+    if (const SpirvEvalInfo result = tryToGenFloatVectorScale(expr))
       return result;
   }
 
   const auto *rhs = expr->getRHS();
   const auto *lhs = expr->getLHS();
 
-  uint32_t lhsPtr = 0;
+  SpirvEvalInfo lhsPtr = 0;
   const uint32_t resultType = typeTranslator.translateType(expr->getType());
-  const uint32_t result =
-      processBinaryOp(lhs, rhs, opcode, resultType, &lhsPtr);
+  const auto result = processBinaryOp(lhs, rhs, opcode, resultType, &lhsPtr);
   return processAssignment(lhs, result, true, lhsPtr);
 }
 
@@ -1551,7 +1550,8 @@ SPIRVEmitter::processBufferTextureGetDimensions(const CXXMemberCallExpr *expr) {
   }
 
   const uint32_t query =
-      lod ? theBuilder.createBinaryOp(spv::Op::OpImageQuerySizeLod,
+      lod
+          ? theBuilder.createBinaryOp(spv::Op::OpImageQuerySizeLod,
                                       resultTypeId, objectId, lod)
           : theBuilder.createUnaryOp(spv::Op::OpImageQuerySize, resultTypeId,
                                      objectId);
@@ -1688,7 +1688,7 @@ uint32_t SPIRVEmitter::processByteAddressBufferLoadStore(
   uint32_t resultId = 0;
   const auto object = expr->getImplicitObjectArgument();
   const auto type = object->getType();
-  const uint32_t objectId = doExpr(object);
+  const auto objectInfo = doExpr(object);
   assert(numWords >= 1 && numWords <= 4);
   if (doStore) {
     assert(typeTranslator.isRWByteAddressBuffer(type));
@@ -1720,8 +1720,8 @@ uint32_t SPIRVEmitter::processByteAddressBufferLoadStore(
   // runtimeArray). The second index passed to OpAccessChain should be
   // the address.
   const uint32_t uintTypeId = theBuilder.getUint32Type();
-  const uint32_t ptrType = theBuilder.getPointerType(
-      uintTypeId, declIdMapper.resolveStorageInfo(object));
+  const uint32_t ptrType =
+      theBuilder.getPointerType(uintTypeId, objectInfo.storageClass);
   const uint32_t constUint0 = theBuilder.getConstantUint32(0);
 
   if (doStore) {
@@ -1743,12 +1743,12 @@ uint32_t SPIRVEmitter::processByteAddressBufferLoadStore(
 
       // Store the word to the right address at the output.
       const uint32_t storePtr = theBuilder.createAccessChain(
-          ptrType, objectId, {constUint0, curStoreAddress});
+          ptrType, objectInfo, {constUint0, curStoreAddress});
       theBuilder.createStore(storePtr, curValue);
     }
   } else {
-    uint32_t loadPtr =
-        theBuilder.createAccessChain(ptrType, objectId, {constUint0, address});
+    uint32_t loadPtr = theBuilder.createAccessChain(ptrType, objectInfo,
+                                                    {constUint0, address});
     resultId = theBuilder.createLoad(uintTypeId, loadPtr);
     if (numWords > 1) {
       // Load word 2, 3, and 4 where necessary. Use OpCompositeConstruct to
@@ -1759,7 +1759,7 @@ uint32_t SPIRVEmitter::processByteAddressBufferLoadStore(
         const uint32_t offset = theBuilder.getConstantUint32(wordCounter - 1);
         const uint32_t newAddress = theBuilder.createBinaryOp(
             spv::Op::OpIAdd, addressTypeId, address, offset);
-        loadPtr = theBuilder.createAccessChain(ptrType, objectId,
+        loadPtr = theBuilder.createAccessChain(ptrType, objectInfo,
                                                {constUint0, newAddress});
         values.push_back(theBuilder.createLoad(uintTypeId, loadPtr));
       }
@@ -1771,7 +1771,7 @@ uint32_t SPIRVEmitter::processByteAddressBufferLoadStore(
   return resultId;
 }
 
-uint32_t
+SpirvEvalInfo
 SPIRVEmitter::processStructuredBufferLoad(const CXXMemberCallExpr *expr) {
   if (expr->getNumArgs() == 2) {
     emitError("Load(int, int) unimplemented for (RW)StructuredBuffer");
@@ -1779,19 +1779,22 @@ SPIRVEmitter::processStructuredBufferLoad(const CXXMemberCallExpr *expr) {
   }
 
   const auto *buffer = expr->getImplicitObjectArgument();
+  auto info = doExpr(buffer);
+
   const QualType structType =
       hlsl::GetHLSLResourceResultType(buffer->getType());
   const uint32_t ptrType = theBuilder.getPointerType(
-      typeTranslator.translateType(structType, LayoutRule::GLSLStd430),
-      declIdMapper.resolveStorageInfo(buffer));
+      typeTranslator.translateType(structType, info.layoutRule),
+      info.storageClass);
 
   const uint32_t zero = theBuilder.getConstantInt32(0);
   const uint32_t index = doExpr(expr->getArg(0));
 
-  return theBuilder.createAccessChain(ptrType, doExpr(buffer), {zero, index});
+  info.resultId = theBuilder.createAccessChain(ptrType, info, {zero, index});
+  return info;
 }
 
-uint32_t
+SpirvEvalInfo
 SPIRVEmitter::processACSBufferAppendConsume(const CXXMemberCallExpr *expr) {
   const bool isAppend = expr->getNumArgs() == 1;
 
@@ -1822,24 +1825,21 @@ SPIRVEmitter::processACSBufferAppendConsume(const CXXMemberCallExpr *expr) {
     index = theBuilder.createBinaryOp(spv::Op::OpISub, u32Type, prevIndex, one);
   }
 
-  LayoutRule lhsLayout = LayoutRule::Void;
-  const auto lhsSc = declIdMapper.resolveStorageInfo(object, &lhsLayout);
+  auto bufferInfo = declIdMapper.getDeclResultId(buffer);
 
   const auto bufferElemTy = hlsl::GetHLSLResourceResultType(object->getType());
   const uint32_t bufferElemType =
-      typeTranslator.translateType(bufferElemTy, lhsLayout);
+      typeTranslator.translateType(bufferElemTy, bufferInfo.layoutRule);
   // Get the pointer inside the {Append|Consume}StructuredBuffer
   const uint32_t bufferElemPtrType =
-      theBuilder.getPointerType(bufferElemType, lhsSc);
+      theBuilder.getPointerType(bufferElemType, bufferInfo.storageClass);
   const uint32_t bufferElemPtr = theBuilder.createAccessChain(
-      bufferElemPtrType, declIdMapper.getDeclResultId(buffer), {zero, index});
+      bufferElemPtrType, bufferInfo.resultId, {zero, index});
 
   if (isAppend) {
-    LayoutRule rhsLayout = LayoutRule::Void;
-    (void)declIdMapper.resolveStorageInfo(expr->getArg(0), &rhsLayout);
     // Write out the value
-    storeValue(bufferElemPtr, doExpr(expr->getArg(0)), bufferElemTy, lhsSc,
-               lhsLayout, rhsLayout);
+    bufferInfo.resultId = bufferElemPtr;
+    storeValue(bufferInfo, doExpr(expr->getArg(0)), bufferElemTy);
 
     return 0;
   } else {
@@ -1847,13 +1847,15 @@ SPIRVEmitter::processACSBufferAppendConsume(const CXXMemberCallExpr *expr) {
     // of .Consume() is not labelled as xvalue. That will cause OpLoad
     // instruction missing. Load directly here.
     if (bufferElemTy->isStructureType())
-      return bufferElemPtr;
+      bufferInfo.resultId = bufferElemPtr;
     else
-      return theBuilder.createLoad(bufferElemType, bufferElemPtr);
+      bufferInfo.resultId =
+          theBuilder.createLoad(bufferElemType, bufferElemPtr);
+    return bufferInfo;
   }
 }
 
-uint32_t SPIRVEmitter::doCXXMemberCallExpr(const CXXMemberCallExpr *expr) {
+SpirvEvalInfo SPIRVEmitter::doCXXMemberCallExpr(const CXXMemberCallExpr *expr) {
   const FunctionDecl *callee = expr->getDirectCallee();
 
   llvm::StringRef group;
@@ -1867,8 +1869,9 @@ uint32_t SPIRVEmitter::doCXXMemberCallExpr(const CXXMemberCallExpr *expr) {
   return processCall(expr);
 }
 
-uint32_t SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
-                                                  hlsl::IntrinsicOp opcode) {
+SpirvEvalInfo
+SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
+                                         hlsl::IntrinsicOp opcode) {
   using namespace hlsl;
 
   // Handles the offset argument. If there exists an offset argument, writes the
@@ -2022,7 +2025,7 @@ uint32_t SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
 
     if (TypeTranslator::isTexture(objectType)) {
       // .Load() has a second optional paramter for offset.
-      const auto locationId = doExpr(location);
+      const uint32_t locationId = doExpr(location);
       uint32_t constOffset = 0, varOffset = 0;
       uint32_t coordinate = 0, lod = 0;
       // For Texture Load() functions, the location parameter is a vector that
@@ -2089,7 +2092,8 @@ uint32_t SPIRVEmitter::processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
   return 0;
 }
 
-uint32_t SPIRVEmitter::doCXXOperatorCallExpr(const CXXOperatorCallExpr *expr) {
+SpirvEvalInfo
+SPIRVEmitter::doCXXOperatorCallExpr(const CXXOperatorCallExpr *expr) {
   { // Handle Buffer/RWBuffer/Texture/RWTexture indexing
     const Expr *baseExpr = nullptr;
     const Expr *indexExpr = nullptr;
@@ -2114,7 +2118,7 @@ uint32_t SPIRVEmitter::doCXXOperatorCallExpr(const CXXOperatorCallExpr *expr) {
   llvm::SmallVector<uint32_t, 4> indices;
   const Expr *baseExpr = collectArrayStructIndices(expr, &indices);
 
-  uint32_t base = doExpr(baseExpr);
+  auto base = doExpr(baseExpr);
   if (indices.empty())
     return base; // For indexing into size-1 vectors and 1xN matrices
   // If we are indexing into a rvalue, to use OpAccessChain, we first need
@@ -2129,18 +2133,18 @@ uint32_t SPIRVEmitter::doCXXOperatorCallExpr(const CXXOperatorCallExpr *expr) {
     base = tempVar;
   }
 
-  LayoutRule rule = LayoutRule::Void;
-  const auto sc = declIdMapper.resolveStorageInfo(baseExpr, &rule);
   const uint32_t ptrType = theBuilder.getPointerType(
-      typeTranslator.translateType(expr->getType(), rule), sc);
+      typeTranslator.translateType(expr->getType(), base.layoutRule),
+      base.storageClass);
+  base.resultId = theBuilder.createAccessChain(ptrType, base, indices);
 
-  return theBuilder.createAccessChain(ptrType, base, indices);
+  return base;
 }
 
-uint32_t
+SpirvEvalInfo
 SPIRVEmitter::doExtMatrixElementExpr(const ExtMatrixElementExpr *expr) {
   const Expr *baseExpr = expr->getBase();
-  const uint32_t base = doExpr(baseExpr);
+  const auto baseInfo = doExpr(baseExpr);
   const auto accessor = expr->getEncodedElementAccess();
   const uint32_t elemType = typeTranslator.translateType(
       hlsl::GetHLSLMatElementType(baseExpr->getType()));
@@ -2170,19 +2174,19 @@ SPIRVEmitter::doExtMatrixElementExpr(const ExtMatrixElementExpr *expr) {
       for (uint32_t i = 0; i < indices.size(); ++i)
         indices[i] = theBuilder.getConstantInt32(indices[i]);
 
-      const uint32_t ptrType = theBuilder.getPointerType(
-          elemType, declIdMapper.resolveStorageInfo(baseExpr));
+      const uint32_t ptrType =
+          theBuilder.getPointerType(elemType, baseInfo.storageClass);
       if (!indices.empty()) {
         // Load the element via access chain
-        elem = theBuilder.createAccessChain(ptrType, base, indices);
+        elem = theBuilder.createAccessChain(ptrType, baseInfo, indices);
       } else {
         // The matrix is of size 1x1. No need to use access chain, base should
         // be the source pointer.
-        elem = base;
+        elem = baseInfo;
       }
       elem = theBuilder.createLoad(elemType, elem);
     } else { // e.g., (mat1 + mat2)._m11
-      elem = theBuilder.createCompositeExtract(elemType, base, indices);
+      elem = theBuilder.createCompositeExtract(elemType, baseInfo, indices);
     }
     elements.push_back(elem);
   }
@@ -2194,7 +2198,7 @@ SPIRVEmitter::doExtMatrixElementExpr(const ExtMatrixElementExpr *expr) {
   return theBuilder.createCompositeConstruct(vecType, elements);
 }
 
-uint32_t
+SpirvEvalInfo
 SPIRVEmitter::doHLSLVectorElementExpr(const HLSLVectorElementExpr *expr) {
   const Expr *baseExpr = nullptr;
   hlsl::VectorMemberAccessPositions accessor;
@@ -2229,11 +2233,12 @@ SPIRVEmitter::doHLSLVectorElementExpr(const HLSLVectorElementExpr *expr) {
     // instead of the original base here since we can have something like
     // v.xyyz to turn a lvalue v into rvalue.
     if (expr->getBase()->isGLValue()) { // E.g., v.x;
-      const uint32_t ptrType = theBuilder.getPointerType(
-          type, declIdMapper.resolveStorageInfo(baseExpr));
+      const auto baseInfo = doExpr(baseExpr);
+      const uint32_t ptrType =
+          theBuilder.getPointerType(type, baseInfo.storageClass);
       const uint32_t index = theBuilder.getConstantInt32(accessor.Swz0);
       // We need a lvalue here. Do not try to load.
-      return theBuilder.createAccessChain(ptrType, doExpr(baseExpr), {index});
+      return theBuilder.createAccessChain(ptrType, baseInfo, {index});
     } else { // E.g., (v + w).x;
       // The original base vector may not be a rvalue. Need to load it if
       // it is lvalue since ImplicitCastExpr (LValueToRValue) will be missing
@@ -2270,30 +2275,31 @@ SPIRVEmitter::doHLSLVectorElementExpr(const HLSLVectorElementExpr *expr) {
   return theBuilder.createVectorShuffle(type, baseVal, baseVal, selectors);
 }
 
-uint32_t SPIRVEmitter::doInitListExpr(const InitListExpr *expr) {
+SpirvEvalInfo SPIRVEmitter::doInitListExpr(const InitListExpr *expr) {
   if (const uint32_t id = tryToEvaluateAsConst(expr))
     return id;
 
   return InitListHandler(*this).process(expr);
 }
 
-uint32_t SPIRVEmitter::doMemberExpr(const MemberExpr *expr) {
+SpirvEvalInfo SPIRVEmitter::doMemberExpr(const MemberExpr *expr) {
   llvm::SmallVector<uint32_t, 4> indices;
   const Expr *base = collectArrayStructIndices(expr, &indices);
+  auto info = doExpr(base);
 
-  LayoutRule rule = LayoutRule::Void;
-  const auto sc = declIdMapper.resolveStorageInfo(base, &rule);
   const uint32_t ptrType = theBuilder.getPointerType(
-      typeTranslator.translateType(expr->getType(), rule), sc);
+      typeTranslator.translateType(expr->getType(), info.layoutRule),
+      info.storageClass);
+  info.resultId = theBuilder.createAccessChain(ptrType, info, indices);
 
-  return theBuilder.createAccessChain(ptrType, doExpr(base), indices);
+  return info;
 }
 
-uint32_t SPIRVEmitter::doUnaryOperator(const UnaryOperator *expr) {
+SpirvEvalInfo SPIRVEmitter::doUnaryOperator(const UnaryOperator *expr) {
   const auto opcode = expr->getOpcode();
   const auto *subExpr = expr->getSubExpr();
   const auto subType = subExpr->getType();
-  const auto subValue = doExpr(subExpr);
+  auto subValue = doExpr(subExpr);
   const auto subTypeId = typeTranslator.translateType(subType);
 
   switch (opcode) {
@@ -2351,19 +2357,6 @@ uint32_t SPIRVEmitter::doUnaryOperator(const UnaryOperator *expr) {
   return 0;
 }
 
-uint32_t SPIRVEmitter::getType(const Expr *expr) {
-  LayoutRule rule = LayoutRule::Void;
-  (void)declIdMapper.resolveStorageInfo(expr, &rule);
-  return typeTranslator.translateType(expr->getType(), rule);
-}
-
-uint32_t SPIRVEmitter::getPointerType(const Expr *expr) {
-  LayoutRule rule = LayoutRule::Void;
-  const auto sc = declIdMapper.resolveStorageInfo(expr, &rule);
-  return theBuilder.getPointerType(
-      typeTranslator.translateType(expr->getType(), rule), sc);
-}
-
 spv::Op SPIRVEmitter::translateOp(BinaryOperator::Opcode op, QualType type) {
   const bool isSintType = isSintOrVecMatOfSintType(type);
   const bool isUintType = isUintOrVecMatOfUintType(type);
@@ -2485,45 +2478,42 @@ case BO_##kind : {                                                             \
   return spv::Op::OpNop;
 }
 
-uint32_t SPIRVEmitter::processAssignment(const Expr *lhs, const uint32_t rhs,
-                                         const bool isCompoundAssignment,
-                                         uint32_t lhsPtr,
-                                         LayoutRule rhsLayout) {
+SpirvEvalInfo SPIRVEmitter::processAssignment(const Expr *lhs,
+                                              const SpirvEvalInfo &rhs,
+                                              const bool isCompoundAssignment,
+                                              SpirvEvalInfo lhsPtr) {
   // Assigning to vector swizzling should be handled differently.
-  if (const uint32_t result = tryToAssignToVectorElements(lhs, rhs)) {
+  if (const SpirvEvalInfo result = tryToAssignToVectorElements(lhs, rhs))
     return result;
-  }
+
   // Assigning to matrix swizzling should be handled differently.
-  if (const uint32_t result = tryToAssignToMatrixElements(lhs, rhs)) {
+  if (const SpirvEvalInfo result = tryToAssignToMatrixElements(lhs, rhs))
     return result;
-  }
+
   // Assigning to a RWBuffer/RWTexture should be handled differently.
-  if (const uint32_t result = tryToAssignToRWBufferRWTexture(lhs, rhs)) {
+  if (const SpirvEvalInfo result = tryToAssignToRWBufferRWTexture(lhs, rhs))
     return result;
-  }
 
   // Normal assignment procedure
-  if (!lhsPtr)
+
+  if (!lhsPtr.resultId)
     lhsPtr = doExpr(lhs);
 
-  LayoutRule lhsLayout = LayoutRule::Void;
-  const auto lhsSc = declIdMapper.resolveStorageInfo(lhs, &lhsLayout);
-  storeValue(lhsPtr, rhs, lhs->getType(), lhsSc, lhsLayout, rhsLayout);
+  storeValue(lhsPtr, rhs, lhs->getType());
 
   // Plain assignment returns a rvalue, while compound assignment returns
   // lvalue.
   return isCompoundAssignment ? lhsPtr : rhs;
 }
 
-void SPIRVEmitter::storeValue(const uint32_t lhsPtr, const uint32_t rhsVal,
-                              const QualType valType,
-                              const spv::StorageClass lhsSc,
-                              const LayoutRule lhsLayout,
-                              const LayoutRule rhsLayout) {
+void SPIRVEmitter::storeValue(const SpirvEvalInfo &lhsPtr,
+                              const SpirvEvalInfo &rhsVal,
+                              const QualType valType) {
   // If lhs and rhs has the same memory layout, we should be safe to load
   // from rhs and directly store into lhs and avoid decomposing rhs.
   // TODO: is this optimization always correct?
-  if (lhsLayout == rhsLayout || typeTranslator.isScalarType(valType) ||
+  if (lhsPtr.layoutRule == rhsVal.layoutRule ||
+      typeTranslator.isScalarType(valType) ||
       typeTranslator.isVectorType(valType) ||
       typeTranslator.isMxNMatrix(valType)) {
     theBuilder.createStore(lhsPtr, rhsVal);
@@ -2538,16 +2528,17 @@ void SPIRVEmitter::storeValue(const uint32_t lhsPtr, const uint32_t rhsVal,
       assert(field);
 
       const auto subRhsValType =
-          typeTranslator.translateType(field->getType(), rhsLayout);
+          typeTranslator.translateType(field->getType(), rhsVal.layoutRule);
       const auto subRhsVal =
           theBuilder.createCompositeExtract(subRhsValType, rhsVal, {index});
       const auto subLhsPtrType = theBuilder.getPointerType(
-          typeTranslator.translateType(field->getType(), lhsLayout), lhsSc);
+          typeTranslator.translateType(field->getType(), lhsPtr.layoutRule),
+          lhsPtr.storageClass);
       const auto subLhsPtr = theBuilder.createAccessChain(
           subLhsPtrType, lhsPtr, {theBuilder.getConstantUint32(index)});
 
-      storeValue(subLhsPtr, subRhsVal, field->getType(), lhsSc, lhsLayout,
-                 rhsLayout);
+      storeValue(lhsPtr.substResultId(subLhsPtr),
+                 rhsVal.substResultId(subRhsVal), field->getType());
       ++index;
     }
   } else if (const auto *arrayType =
@@ -2559,26 +2550,28 @@ void SPIRVEmitter::storeValue(const uint32_t lhsPtr, const uint32_t rhsVal,
 
     for (uint32_t i = 0; i < size; ++i) {
       const auto subRhsValType =
-          typeTranslator.translateType(elemType, rhsLayout);
+          typeTranslator.translateType(elemType, rhsVal.layoutRule);
       const auto subRhsVal =
           theBuilder.createCompositeExtract(subRhsValType, rhsVal, {i});
       const auto subLhsPtrType = theBuilder.getPointerType(
-          typeTranslator.translateType(elemType, lhsLayout), lhsSc);
+          typeTranslator.translateType(elemType, lhsPtr.layoutRule),
+          lhsPtr.storageClass);
       const auto subLhsPtr = theBuilder.createAccessChain(
           subLhsPtrType, lhsPtr, {theBuilder.getConstantUint32(i)});
 
-      storeValue(subLhsPtr, subRhsVal, elemType, lhsSc, lhsLayout, rhsLayout);
+      storeValue(lhsPtr.substResultId(subLhsPtr),
+                 rhsVal.substResultId(subRhsVal), elemType);
     }
   } else {
     emitError("storing value of type %0 unimplemented") << valType;
   }
 }
 
-uint32_t SPIRVEmitter::processBinaryOp(const Expr *lhs, const Expr *rhs,
-                                       const BinaryOperatorKind opcode,
-                                       const uint32_t resultType,
-                                       uint32_t *lhsResultId,
-                                       const spv::Op mandateGenOpcode) {
+SpirvEvalInfo SPIRVEmitter::processBinaryOp(const Expr *lhs, const Expr *rhs,
+                                            const BinaryOperatorKind opcode,
+                                            const uint32_t resultType,
+                                            SpirvEvalInfo *lhsInfo,
+                                            const spv::Op mandateGenOpcode) {
   // If the operands are of matrix type, we need to dispatch the operation
   // onto each element vector iff the operands are not degenerated matrices
   // and we don't have a matrix specific SPIR-V instruction for the operation.
@@ -2599,7 +2592,7 @@ uint32_t SPIRVEmitter::processBinaryOp(const Expr *lhs, const Expr *rhs,
                             ? translateOp(opcode, lhs->getType())
                             : mandateGenOpcode;
 
-  uint32_t rhsVal, lhsPtr, lhsVal;
+  SpirvEvalInfo rhsVal = 0, lhsPtr = 0, lhsVal = 0;
   if (BinaryOperator::isCompoundAssignmentOp(opcode)) {
     // Evalute rhs before lhs
     rhsVal = doExpr(rhs);
@@ -2616,8 +2609,8 @@ uint32_t SPIRVEmitter::processBinaryOp(const Expr *lhs, const Expr *rhs,
     rhsVal = doExpr(rhs);
   }
 
-  if (lhsResultId)
-    *lhsResultId = lhsPtr;
+  if (lhsInfo)
+    *lhsInfo = lhsPtr;
 
   switch (opcode) {
   case BO_Add:
@@ -2647,8 +2640,13 @@ uint32_t SPIRVEmitter::processBinaryOp(const Expr *lhs, const Expr *rhs,
   case BO_OrAssign:
   case BO_XorAssign:
   case BO_ShlAssign:
-  case BO_ShrAssign:
-    return theBuilder.createBinaryOp(spvOp, resultType, lhsVal, rhsVal);
+  case BO_ShrAssign: {
+    const auto result =
+        theBuilder.createBinaryOp(spvOp, resultType, lhsVal, rhsVal);
+    return lhsVal.isRelaxedPrecision || rhsVal.isRelaxedPrecision
+               ? SpirvEvalInfo::withRelaxedPrecision(result)
+               : result;
+  }
   case BO_Assign:
     llvm_unreachable("assignment should not be handled here");
   default:
@@ -2830,8 +2828,8 @@ void SPIRVEmitter::condenseVectorElementExpr(
   }
 }
 
-uint32_t SPIRVEmitter::createVectorSplat(const Expr *scalarExpr, uint32_t size,
-                                         bool *resultIsConstant) {
+SpirvEvalInfo SPIRVEmitter::createVectorSplat(const Expr *scalarExpr,
+                                              uint32_t size) {
   bool isConstVal = false;
   uint32_t scalarVal = 0;
 
@@ -2843,19 +2841,19 @@ uint32_t SPIRVEmitter::createVectorSplat(const Expr *scalarExpr, uint32_t size,
     scalarVal = doExpr(scalarExpr);
   }
 
-  if (resultIsConstant)
-    *resultIsConstant = isConstVal;
-
   // Just return the scalar value for vector splat with size 1
   if (size == 1)
-    return scalarVal;
+    return isConstVal ? SpirvEvalInfo::withConst(scalarVal) : scalarVal;
 
   const uint32_t vecType = theBuilder.getVecType(
       typeTranslator.translateType(scalarExpr->getType()), size);
   llvm::SmallVector<uint32_t, 4> elements(size_t(size), scalarVal);
 
   if (isConstVal) {
-    return theBuilder.getConstantComposite(vecType, elements);
+    // TODO: we are saying the constant has Function storage class here.
+    // Should find a more meaningful one.
+    return SpirvEvalInfo::withConst(
+        theBuilder.getConstantComposite(vecType, elements));
   } else {
     return theBuilder.createCompositeConstruct(vecType, elements);
   }
@@ -2886,7 +2884,8 @@ void SPIRVEmitter::splitVecLastElement(QualType vecType, uint32_t vec,
       theBuilder.createCompositeExtract(elemTypeId, vec, {count - 1});
 }
 
-uint32_t SPIRVEmitter::tryToGenFloatVectorScale(const BinaryOperator *expr) {
+SpirvEvalInfo
+SPIRVEmitter::tryToGenFloatVectorScale(const BinaryOperator *expr) {
   const QualType type = expr->getType();
   // We can only translate floatN * float into OpVectorTimesScalar.
   // So the result type must be floatN.
@@ -2907,8 +2906,8 @@ uint32_t SPIRVEmitter::tryToGenFloatVectorScale(const BinaryOperator *expr) {
       if (cast->getCastKind() == CK_HLSLVectorSplat) {
         const uint32_t vecType = typeTranslator.translateType(expr->getType());
         if (isa<CompoundAssignOperator>(expr)) {
-          uint32_t lhsPtr = 0;
-          const uint32_t result =
+          SpirvEvalInfo lhsPtr = 0;
+          const auto result =
               processBinaryOp(lhs, cast->getSubExpr(), expr->getOpcode(),
                               vecType, &lhsPtr, spv::Op::OpVectorTimesScalar);
           return processAssignment(lhs, result, true, lhsPtr);
@@ -2938,7 +2937,8 @@ uint32_t SPIRVEmitter::tryToGenFloatVectorScale(const BinaryOperator *expr) {
   return 0;
 }
 
-uint32_t SPIRVEmitter::tryToGenFloatMatrixScale(const BinaryOperator *expr) {
+SpirvEvalInfo
+SPIRVEmitter::tryToGenFloatMatrixScale(const BinaryOperator *expr) {
   const QualType type = expr->getType();
   // We can only translate floatMxN * float into OpMatrixTimesScalar.
   // So the result type must be floatMxN.
@@ -2968,8 +2968,8 @@ uint32_t SPIRVEmitter::tryToGenFloatMatrixScale(const BinaryOperator *expr) {
         const uint32_t matType = typeTranslator.translateType(expr->getType());
         const spv::Op opcode = selectOpcode(lhsType);
         if (isa<CompoundAssignOperator>(expr)) {
-          uint32_t lhsPtr = 0;
-          const uint32_t result =
+          SpirvEvalInfo lhsPtr = 0;
+          const auto result =
               processBinaryOp(lhs, cast->getSubExpr(), expr->getOpcode(),
                               matType, &lhsPtr, opcode);
           return processAssignment(lhs, result, true, lhsPtr);
@@ -2999,8 +2999,9 @@ uint32_t SPIRVEmitter::tryToGenFloatMatrixScale(const BinaryOperator *expr) {
   return 0;
 }
 
-uint32_t SPIRVEmitter::tryToAssignToVectorElements(const Expr *lhs,
-                                                   const uint32_t rhs) {
+SpirvEvalInfo
+SPIRVEmitter::tryToAssignToVectorElements(const Expr *lhs,
+                                          const SpirvEvalInfo &rhs) {
   // Assigning to a vector swizzling lhs is tricky if we are neither
   // writing to one element nor all elements in their original order.
   // Under such cases, we need to create a new vector swizzling involving
@@ -3066,8 +3067,9 @@ uint32_t SPIRVEmitter::tryToAssignToVectorElements(const Expr *lhs,
   return rhs;
 }
 
-uint32_t SPIRVEmitter::tryToAssignToRWBufferRWTexture(const Expr *lhs,
-                                                      uint32_t rhs) {
+SpirvEvalInfo
+SPIRVEmitter::tryToAssignToRWBufferRWTexture(const Expr *lhs,
+                                             const SpirvEvalInfo &rhs) {
   const Expr *baseExpr = nullptr;
   const Expr *indexExpr = nullptr;
   const auto lhsExpr = dyn_cast<CXXOperatorCallExpr>(lhs);
@@ -3081,14 +3083,15 @@ uint32_t SPIRVEmitter::tryToAssignToRWBufferRWTexture(const Expr *lhs,
   return 0;
 }
 
-uint32_t SPIRVEmitter::tryToAssignToMatrixElements(const Expr *lhs,
-                                                   uint32_t rhs) {
+SpirvEvalInfo
+SPIRVEmitter::tryToAssignToMatrixElements(const Expr *lhs,
+                                          const SpirvEvalInfo &rhs) {
   const auto *lhsExpr = dyn_cast<ExtMatrixElementExpr>(lhs);
   if (!lhsExpr)
     return 0;
 
   const Expr *baseMat = lhsExpr->getBase();
-  const uint32_t base = doExpr(baseMat);
+  const auto &base = doExpr(baseMat);
   const QualType elemType = hlsl::GetHLSLMatElementType(baseMat->getType());
   const uint32_t elemTypeId = typeTranslator.translateType(elemType);
 
@@ -3122,15 +3125,15 @@ uint32_t SPIRVEmitter::tryToAssignToMatrixElements(const Expr *lhs,
     if (accessor.Count > 1)
       rhsElem = theBuilder.createCompositeExtract(elemTypeId, rhs, {i});
 
-    const uint32_t ptrType = theBuilder.getPointerType(
-        elemTypeId, declIdMapper.resolveStorageInfo(baseMat));
+    const uint32_t ptrType =
+        theBuilder.getPointerType(elemTypeId, base.storageClass);
 
     // If the lhs is actually a matrix of size 1x1, we don't need the access
     // chain. base is already the dest pointer.
     uint32_t lhsElemPtr = base;
     if (!indices.empty()) {
       // Load the element via access chain
-      lhsElemPtr = theBuilder.createAccessChain(ptrType, base, indices);
+      lhsElemPtr = theBuilder.createAccessChain(ptrType, lhsElemPtr, indices);
     }
 
     theBuilder.createStore(lhsElemPtr, rhsElem);
@@ -3166,8 +3169,9 @@ uint32_t SPIRVEmitter::processEachVectorInMatrix(
       typeTranslator.translateType(matType), vectors);
 }
 
-uint32_t SPIRVEmitter::processMatrixBinaryOp(const Expr *lhs, const Expr *rhs,
-                                             const BinaryOperatorKind opcode) {
+SpirvEvalInfo
+SPIRVEmitter::processMatrixBinaryOp(const Expr *lhs, const Expr *rhs,
+                                    const BinaryOperatorKind opcode) {
   // TODO: some code are duplicated from processBinaryOp. Try to unify them.
   const auto lhsType = lhs->getType();
   assert(TypeTranslator::isSpirvAcceptableMatrixType(lhsType));
@@ -4186,8 +4190,7 @@ uint32_t SPIRVEmitter::processIntrinsicSaturate(const CallExpr *callExpr) {
     const uint32_t vecZero = getVecValueZero(elemType, numCols);
     const uint32_t vecOne = getVecValueOne(elemType, numCols);
     const auto actOnEachVec = [this, vecZero, vecOne, glslInstSetId](
-                                  uint32_t /*index*/, uint32_t vecType,
-                                  uint32_t curRowId) {
+        uint32_t /*index*/, uint32_t vecType, uint32_t curRowId) {
       return theBuilder.createExtInst(vecType, glslInstSetId,
                                       GLSLstd450::GLSLstd450FClamp,
                                       {curRowId, vecZero, vecOne});
@@ -4237,9 +4240,8 @@ uint32_t SPIRVEmitter::processIntrinsicUsingSpirvInst(
     // instruction on each vector of the matrix.
     if (actPerRowForMatrices &&
         TypeTranslator::isSpirvAcceptableMatrixType(arg->getType())) {
-      const auto actOnEachVec = [this, opcode](uint32_t /*index*/,
-                                               uint32_t vecType,
-                                               uint32_t curRowId) {
+      const auto actOnEachVec = [this, opcode](
+          uint32_t /*index*/, uint32_t vecType, uint32_t curRowId) {
         return theBuilder.createUnaryOp(opcode, vecType, {curRowId});
       };
       return processEachVectorInMatrix(arg, argId, actOnEachVec);
@@ -4253,9 +4255,8 @@ uint32_t SPIRVEmitter::processIntrinsicUsingSpirvInst(
     // instruction on each vector of the matrix.
     if (actPerRowForMatrices &&
         TypeTranslator::isSpirvAcceptableMatrixType(arg0->getType())) {
-      const auto actOnEachVec = [this, opcode, arg1Id](uint32_t index,
-                                                       uint32_t vecType,
-                                                       uint32_t arg0RowId) {
+      const auto actOnEachVec = [this, opcode, arg1Id](
+          uint32_t index, uint32_t vecType, uint32_t arg0RowId) {
         const uint32_t arg1RowId =
             theBuilder.createCompositeExtract(vecType, arg1Id, {index});
         return theBuilder.createBinaryOp(opcode, vecType, arg0RowId, arg1RowId);
@@ -4283,9 +4284,8 @@ uint32_t SPIRVEmitter::processIntrinsicUsingGLSLInst(
     // instruction on each vector of the matrix.
     if (actPerRowForMatrices &&
         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,
                                         {curRowId});
       };
@@ -4300,9 +4300,8 @@ uint32_t SPIRVEmitter::processIntrinsicUsingGLSLInst(
     // instruction on each vector of the matrix.
     if (actPerRowForMatrices &&
         TypeTranslator::isSpirvAcceptableMatrixType(arg0->getType())) {
-      const auto actOnEachVec = [this, glslInstSetId, opcode,
-                                 arg1Id](uint32_t index, uint32_t vecType,
-                                         uint32_t arg0RowId) {
+      const auto actOnEachVec = [this, glslInstSetId, opcode, arg1Id](
+          uint32_t index, uint32_t vecType, uint32_t arg0RowId) {
         const uint32_t arg1RowId =
             theBuilder.createCompositeExtract(vecType, arg1Id, {index});
         return theBuilder.createExtInst(vecType, glslInstSetId, opcode,

+ 46 - 52
tools/clang/lib/SPIRV/SPIRVEmitter.h

@@ -33,6 +33,7 @@
 #include "llvm/ADT/SetVector.h"
 
 #include "DeclResultIdMapper.h"
+#include "SpirvEvalInfo.h"
 #include "TypeTranslator.h"
 
 namespace clang {
@@ -44,7 +45,7 @@ namespace spirv {
 /// through the AST is done manually instead of using ASTConsumer's harness.
 class SPIRVEmitter : public ASTConsumer {
 public:
-  explicit SPIRVEmitter(CompilerInstance &ci, const EmitSPIRVOptions &options);
+  SPIRVEmitter(CompilerInstance &ci, const EmitSPIRVOptions &options);
 
   void HandleTranslationUnit(ASTContext &context) override;
 
@@ -55,7 +56,7 @@ public:
 
   void doDecl(const Decl *decl);
   void doStmt(const Stmt *stmt, llvm::ArrayRef<const Attr *> attrs = {});
-  uint32_t doExpr(const Expr *expr);
+  SpirvEvalInfo doExpr(const Expr *expr);
 
   /// Processes the given expression and emits SPIR-V instructions. If the
   /// result is a GLValue, does an additional load.
@@ -65,7 +66,7 @@ public:
   /// not be wrapped in ImplicitCastExpr (LValueToRValue) when appearing in
   /// HLSLVectorElementExpr since the generated HLSLVectorElementExpr itself can
   /// be lvalue or rvalue.
-  uint32_t loadIfGLValue(const Expr *expr);
+  SpirvEvalInfo loadIfGLValue(const Expr *expr);
 
   /// Casts the given value from fromType to toType. fromType and toType should
   /// both be scalar or vector types of the same size.
@@ -87,29 +88,21 @@ private:
   void doDoStmt(const DoStmt *, llvm::ArrayRef<const Attr *> attrs = {});
   void doContinueStmt(const ContinueStmt *);
 
-  uint32_t doArraySubscriptExpr(const ArraySubscriptExpr *expr);
-  uint32_t doBinaryOperator(const BinaryOperator *expr);
-  uint32_t doCallExpr(const CallExpr *callExpr);
-  uint32_t doCastExpr(const CastExpr *expr);
-  uint32_t doCompoundAssignOperator(const CompoundAssignOperator *expr);
+  SpirvEvalInfo doArraySubscriptExpr(const ArraySubscriptExpr *expr);
+  SpirvEvalInfo doBinaryOperator(const BinaryOperator *expr);
+  SpirvEvalInfo doCallExpr(const CallExpr *callExpr);
+  SpirvEvalInfo doCastExpr(const CastExpr *expr);
+  SpirvEvalInfo doCompoundAssignOperator(const CompoundAssignOperator *expr);
   uint32_t doConditionalOperator(const ConditionalOperator *expr);
-  uint32_t doCXXMemberCallExpr(const CXXMemberCallExpr *expr);
-  uint32_t doCXXOperatorCallExpr(const CXXOperatorCallExpr *expr);
-  uint32_t doExtMatrixElementExpr(const ExtMatrixElementExpr *expr);
-  uint32_t doHLSLVectorElementExpr(const HLSLVectorElementExpr *expr);
-  uint32_t doInitListExpr(const InitListExpr *expr);
-  uint32_t doMemberExpr(const MemberExpr *expr);
-  uint32_t doUnaryOperator(const UnaryOperator *expr);
+  SpirvEvalInfo doCXXMemberCallExpr(const CXXMemberCallExpr *expr);
+  SpirvEvalInfo doCXXOperatorCallExpr(const CXXOperatorCallExpr *expr);
+  SpirvEvalInfo doExtMatrixElementExpr(const ExtMatrixElementExpr *expr);
+  SpirvEvalInfo doHLSLVectorElementExpr(const HLSLVectorElementExpr *expr);
+  SpirvEvalInfo doInitListExpr(const InitListExpr *expr);
+  SpirvEvalInfo doMemberExpr(const MemberExpr *expr);
+  SpirvEvalInfo doUnaryOperator(const UnaryOperator *expr);
 
 private:
-  /// Returns the proper type for the given expr. This method is used to please
-  /// expressions derived from global resource variables, when we must construct
-  /// the type with correct layout decorations.
-  uint32_t getType(const Expr *expr);
-
-  /// Returns the proper pointer type for the given expr. Similar to the above.
-  uint32_t getPointerType(const Expr *expr);
-
   /// Translates the given frontend binary operator into its SPIR-V equivalent
   /// taking consideration of the operand type.
   spv::Op translateOp(BinaryOperator::Opcode op, QualType type);
@@ -121,25 +114,24 @@ private:
   /// Generates the necessary instructions for assigning rhs to lhs. If lhsPtr
   /// is not zero, it will be used as the pointer from lhs instead of evaluating
   /// lhs again.
-  uint32_t processAssignment(const Expr *lhs, uint32_t rhs,
-                             bool isCompoundAssignment, uint32_t lhsPtr = 0,
-                             LayoutRule rhsLayout = LayoutRule::Void);
+  SpirvEvalInfo processAssignment(const Expr *lhs, const SpirvEvalInfo &rhs,
+                                  bool isCompoundAssignment,
+                                  SpirvEvalInfo lhsPtr = 0);
 
   /// Generates SPIR-V instructions to store rhsVal into lhsPtr. This will be
   /// recursive if valType is a composite type.
-  void storeValue(uint32_t lhsPtr, uint32_t rhsVal, QualType valType,
-                  spv::StorageClass lhsSc, LayoutRule lhsLayout,
-                  LayoutRule rhsLayout);
+  void storeValue(const SpirvEvalInfo &lhsPtr, const SpirvEvalInfo &rhsVal,
+                  QualType valType);
 
   /// Generates the necessary instructions for conducting the given binary
   /// operation on lhs and rhs. If lhsResultId is not nullptr, the evaluated
   /// pointer from lhs during the process will be written into it. If
   /// mandateGenOpcode is not spv::Op::Max, it will used as the SPIR-V opcode
   /// instead of deducing from Clang frontend opcode.
-  uint32_t processBinaryOp(const Expr *lhs, const Expr *rhs,
-                           BinaryOperatorKind opcode, uint32_t resultType,
-                           uint32_t *lhsResultId = nullptr,
-                           spv::Op mandateGenOpcode = spv::Op::Max);
+  SpirvEvalInfo processBinaryOp(const Expr *lhs, const Expr *rhs,
+                                BinaryOperatorKind opcode, uint32_t resultType,
+                                SpirvEvalInfo *lhsInfo = nullptr,
+                                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);
@@ -167,9 +159,10 @@ private:
   /// success, writes base texture object into *base if base is not nullptr,
   /// writes the index into *index if index is not nullptr, and writes the mip
   /// level (lod) to *lod if lod is not nullptr.
-  bool SPIRVEmitter::isTextureMipsSampleIndexing(
-      const CXXOperatorCallExpr *indexExpr, const Expr **base = nullptr,
-      const Expr **index = nullptr, const Expr **lod = nullptr);
+  bool isTextureMipsSampleIndexing(const CXXOperatorCallExpr *indexExpr,
+                                   const Expr **base = nullptr,
+                                   const Expr **index = nullptr,
+                                   const Expr **lod = nullptr);
 
   /// Condenses a sequence of HLSLVectorElementExpr starting from the given
   /// expr into one. Writes the original base into *basePtr and the condensed
@@ -182,8 +175,7 @@ private:
   /// the given scalarExpr. The generated vector will have the same element
   /// type as scalarExpr and of the given size. If resultIsConstant is not
   /// nullptr, writes true to it if the generated instruction is a constant.
-  uint32_t createVectorSplat(const Expr *scalarExpr, uint32_t size,
-                             bool *resultIsConstant = nullptr);
+  SpirvEvalInfo createVectorSplat(const Expr *scalarExpr, uint32_t size);
 
   /// Splits the given vector into the last element and the rest (as a new
   /// vector).
@@ -193,12 +185,12 @@ private:
   /// Translates a floatN * float multiplication into SPIR-V instructions and
   /// returns the <result-id>. Returns 0 if the given binary operation is not
   /// floatN * float.
-  uint32_t tryToGenFloatVectorScale(const BinaryOperator *expr);
+  SpirvEvalInfo tryToGenFloatVectorScale(const BinaryOperator *expr);
 
   /// Translates a floatMxN * float multiplication into SPIR-V instructions and
   /// returns the <result-id>. Returns 0 if the given binary operation is not
   /// floatMxN * float.
-  uint32_t tryToGenFloatMatrixScale(const BinaryOperator *expr);
+  SpirvEvalInfo tryToGenFloatMatrixScale(const BinaryOperator *expr);
 
   /// Tries to emit instructions for assigning to the given vector element
   /// accessing expression. Returns 0 if the trial fails and no instructions
@@ -207,16 +199,19 @@ private:
   /// This method handles the cases that we are writing to neither one element
   /// or all elements in their original order. For other cases, 0 will be
   /// returned and the normal assignment process should be used.
-  uint32_t tryToAssignToVectorElements(const Expr *lhs, const uint32_t rhs);
+  SpirvEvalInfo tryToAssignToVectorElements(const Expr *lhs,
+                                            const SpirvEvalInfo &rhs);
 
   /// Tries to emit instructions for assigning to the given matrix element
   /// accessing expression. Returns 0 if the trial fails and no instructions
   /// are generated.
-  uint32_t tryToAssignToMatrixElements(const Expr *lhs, uint32_t rhs);
+  SpirvEvalInfo tryToAssignToMatrixElements(const Expr *lhs,
+                                            const SpirvEvalInfo &rhs);
 
   /// Tries to emit instructions for assigning to the given RWBuffer/RWTexture
   /// object. Returns 0 if the trial fails and no instructions are generated.
-  uint32_t tryToAssignToRWBufferRWTexture(const Expr *lhs, uint32_t rhs);
+  SpirvEvalInfo tryToAssignToRWBufferRWTexture(const Expr *lhs,
+                                               const SpirvEvalInfo &rhs);
 
   /// Processes each vector within the given matrix by calling actOnEachVector.
   /// matrixVal should be the loaded value of the matrix. actOnEachVector takes
@@ -231,8 +226,8 @@ private:
   /// operation on lhs and rhs.
   ///
   /// This method expects that both lhs and rhs are SPIR-V acceptable matrices.
-  uint32_t processMatrixBinaryOp(const Expr *lhs, const Expr *rhs,
-                                 const BinaryOperatorKind opcode);
+  SpirvEvalInfo processMatrixBinaryOp(const Expr *lhs, const Expr *rhs,
+                                      const BinaryOperatorKind opcode);
 
   /// Collects all indices (SPIR-V constant values) from consecutive MemberExprs
   /// or ArraySubscriptExprs or operator[] calls and writes into indices.
@@ -308,10 +303,6 @@ private:
   uint32_t processIntrinsicUsingGLSLInst(const CallExpr *, GLSLstd450 instr,
                                          bool canOperateOnMatrix);
 
-  /// Processes the given intrinsic member call.
-  uint32_t processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
-                                      hlsl::IntrinsicOp opcode);
-
   /// Processes the given intrinsic function call using the given SPIR-V
   /// instruction. If the given instruction cannot operate on matrices, it
   /// performs the instruction on each row of the matrix and uses composite
@@ -319,6 +310,10 @@ private:
   uint32_t processIntrinsicUsingSpirvInst(const CallExpr *, spv::Op,
                                           bool canOperateOnMatrix);
 
+  /// Processes the given intrinsic member call.
+  SpirvEvalInfo processIntrinsicMemberCall(const CXXMemberCallExpr *expr,
+                                           hlsl::IntrinsicOp opcode);
+
 private:
   /// Returns the <result-id> for constant value 0 of the given type.
   uint32_t getValueZero(QualType type);
@@ -489,18 +484,17 @@ private:
 
   /// \brief Generates an OpAccessChain instruction for the given
   /// (RW)StructuredBuffer.Load() method call.
-  uint32_t processStructuredBufferLoad(const CXXMemberCallExpr *expr);
+  SpirvEvalInfo processStructuredBufferLoad(const CXXMemberCallExpr *expr);
 
   /// \brief Generates SPIR-V instructions for the .Append()/.Consume() call on
   /// the given {Append|Consume}StructuredBuffer. Returns the <result-id> of
   /// the loaded value for .Consume; returns zero for .Append().
-  uint32_t processACSBufferAppendConsume(const CXXMemberCallExpr *expr);
+  SpirvEvalInfo processACSBufferAppendConsume(const CXXMemberCallExpr *expr);
 
   /// \brief Returns the calculated level-of-detail (a single float value) for
   /// the given texture. Handles intrinsic HLSL CalculateLevelOfDetail function.
   uint32_t processTextureLevelOfDetail(const CXXMemberCallExpr *expr);
 
-private:
   /// \brief Queries the given (RW)Buffer/(RW)Texture image in the given expr
   /// for the requested information. Based on the dimension of the image, the
   /// following info can be queried: width, height, depth, number of mipmap

+ 103 - 0
tools/clang/lib/SPIRV/SpirvEvalInfo.h

@@ -0,0 +1,103 @@
+//===------- SpirvEvalInfo.h - SPIR-V Evaluation Info -----------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//===----------------------------------------------------------------------===//
+//
+//  This file defines a struct containing SPIR-V information from evaluating
+//  a Clang AST node.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_SPIRV_SPIRVEVALINFO_H
+#define LLVM_CLANG_LIB_SPIRV_SPIRVEVALINFO_H
+
+#include "spirv/1.0/spirv.hpp11"
+
+namespace clang {
+namespace spirv {
+
+/// Memory layout rules
+enum class LayoutRule {
+  Void,
+  GLSLStd140,
+  GLSLStd430,
+};
+
+/// Struct contains SPIR-V information from evaluating a Clang AST node.
+///
+/// We need to report more information than just the <result-id> for SPIR-V:
+/// * Constness: enables generating SPIR-V constant instructions
+/// * Storage class: for getting correct (pointer) types
+/// * Layout rule: for getting correct (pointer) types
+/// * Relaxed precision: for emitting PrelaxedPrecision decoration
+///
+/// For most cases, the evaluation result of a Clang AST node will just be
+/// a SPIR-V <result-id> that is not a constant, with storage class Function,
+/// layout rule Void, and not relaxed precision.
+///
+/// The evaluation result of a Clang Expr can be considered as SPIR-V constant
+/// as long as all its operands are SPIR-V constants and we are creating a
+/// composite out of them.
+///
+/// We derive storage class or layout rule from a base variable. Access chains
+/// and load/store/insertion/extraction of the base variable retains the same
+/// storage class or layout rule. Other operations will likely turn the storage
+/// class into Function and layout rule into Void.
+///
+/// Relaxed precision decorated on a variable propagates to all operations
+/// involving that variable.
+///
+/// This struct has utilities to allow implicit constructing from or converting
+/// to a uint32_t value (for <result-id>) since that's the most commonly used
+/// information out of evaluating a Clang AST node. When doing such construction
+/// or conversion, all other information are defaulted to what are meaningful
+/// for a function temporary variable.
+struct SpirvEvalInfo {
+  /// Constructor from various evaluation information
+  SpirvEvalInfo(uint32_t id)
+      : resultId(id), isConst(false), storageClass(spv::StorageClass::Function),
+        layoutRule(LayoutRule::Void), isRelaxedPrecision(false) {}
+  SpirvEvalInfo(uint32_t id, spv::StorageClass sc, LayoutRule rule)
+      : resultId(id), isConst(false), storageClass(sc), layoutRule(rule),
+        isRelaxedPrecision(false) {}
+
+  static SpirvEvalInfo withConst(uint32_t id) {
+    SpirvEvalInfo info = id;
+    info.isConst = true;
+    return info;
+  }
+
+  static SpirvEvalInfo withRelaxedPrecision(uint32_t id) {
+    SpirvEvalInfo info = id;
+    info.isRelaxedPrecision = true;
+    return info;
+  }
+
+  /// Returns a new SpirvEvalInfo with an updated SPIR-V <result-id>. Keeps the
+  /// other fields the same.
+  SpirvEvalInfo substResultId(uint32_t newId) const {
+    SpirvEvalInfo info = *this;
+    info.resultId = newId;
+    return info;
+  }
+
+  /// Handy implicit conversion to get the <result-id>.
+  operator uint32_t() const { return resultId; }
+
+  /// Handly implicit conversion to test whether the <result-id> is valid.
+  operator bool() const { return resultId != 0; }
+
+  uint32_t resultId; ///< SPIR-V <result-id>
+  bool isConst;      ///< Whether the <result-id> is for a SPIR-V constant
+  spv::StorageClass storageClass;
+  LayoutRule layoutRule;
+  bool isRelaxedPrecision;
+};
+
+} // end namespace spirv
+} // end namespace clang
+
+#endif

+ 2 - 7
tools/clang/lib/SPIRV/TypeTranslator.h

@@ -16,16 +16,11 @@
 #include "clang/Basic/Diagnostic.h"
 #include "clang/SPIRV/ModuleBuilder.h"
 
+#include "SpirvEvalInfo.h"
+
 namespace clang {
 namespace spirv {
 
-/// Memory layout rules
-enum class LayoutRule {
-  Void,
-  GLSLStd140,
-  GLSLStd430,
-};
-
 /// The class responsible to translate Clang frontend types into SPIR-V type
 /// instructions.
 ///