Explorar el Código

Allow `ref` parameters for intrinsics (#5166)

We effectively have reference parameter passing for intrinsics, but we
accomplish it by extremely careful interpretation of `out` and `inout`
in specific contexts.

This change separates the two in our intrinsic defintiions. Parameters
marked `ref` become lvalue reference types in the intrinsic function
declarations but do not get annotated with `AR_QUAL_OUT`. This allows
us to differentate them as true reference parameters.

For the atomic result intrinsics this allows us to clear up some hacks
because we needed them to not be `out` parameters but we needed them to
be passed by reference.

I've left a comment in SemaHLSL where we match arguement types about a
further cleanup we should do to correct reference binding errors, but
that is a larger task that we should do separately.
Chris B hace 2 años
padre
commit
ea3623fdf7

+ 1 - 0
include/dxc/dxcapi.internal.h

@@ -24,6 +24,7 @@ typedef struct ID3D10Blob ID3D10Blob;
 // Intrinsic definitions.
 #define AR_QUAL_IN             0x0000000000000010ULL
 #define AR_QUAL_OUT            0x0000000000000020ULL
+#define AR_QUAL_REF            0x0000000000000040ULL
 #define AR_QUAL_CONST          0x0000000000000200ULL
 #define AR_QUAL_ROWMAJOR       0x0000000000000400ULL
 #define AR_QUAL_COLMAJOR       0x0000000000000800ULL

+ 1 - 1
tools/clang/include/clang/Basic/Specifiers.h

@@ -277,7 +277,7 @@ namespace hlsl {
   /// In/inout/out/uniform parameter modifier.
   class ParameterModifier {
   public:
-    enum Kind { In, InOut, Out, Uniform, Invalid };
+    enum Kind { In, InOut, Out, Uniform, Ref, Invalid };
 
     ParameterModifier() : m_Kind(Kind::In) { }
     ParameterModifier(enum Kind Kind) : m_Kind(Kind) { }

+ 11 - 73
tools/clang/lib/Sema/SemaHLSL.cpp

@@ -871,8 +871,6 @@ QualType GetOrCreateVectorSpecialization(ASTContext& context, Sema* sema,
 
 static const LPCSTR kBuiltinIntrinsicTableName = "op";
 
-static const unsigned kAtomicDstOperandIdx = 1;
-
 static const ArTypeObjectKind g_ScalarTT[] =
 {
   AR_TOBJ_SCALAR,
@@ -1835,6 +1833,8 @@ ParamModsFromIntrinsicArg(const HLSL_INTRINSIC_ARGUMENT *pArg) {
   if (pArg->qwUsage == AR_QUAL_OUT) {
     return hlsl::ParameterModifier(hlsl::ParameterModifier::Kind::Out);
   }
+  if (pArg->qwUsage == AR_QUAL_REF)
+    return hlsl::ParameterModifier(hlsl::ParameterModifier::Kind::Ref);
   DXASSERT(pArg->qwUsage & AR_QUAL_IN, "else usage is incorrect");
   return hlsl::ParameterModifier(hlsl::ParameterModifier::Kind::In);
 }
@@ -1864,52 +1864,6 @@ static bool IsBuiltinTable(LPCSTR tableName) {
   return tableName == kBuiltinIntrinsicTableName;
 }
 
-// Return true if the give <op> within the <tableName> namespace
-// maps to an atomic operation.
-// <acceptMethods> determines if atomic method operations will return true
-static bool IsAtomicOperation(LPCSTR tableName, IntrinsicOp op, bool acceptMethods=true) {
-  if (!IsBuiltinTable(tableName))
-    return false;
-  switch (op) {
-  case IntrinsicOp::IOP_InterlockedAdd:
-  case IntrinsicOp::IOP_InterlockedAnd:
-  case IntrinsicOp::IOP_InterlockedCompareExchange:
-  case IntrinsicOp::IOP_InterlockedCompareStore:
-  case IntrinsicOp::IOP_InterlockedCompareExchangeFloatBitwise:
-  case IntrinsicOp::IOP_InterlockedCompareStoreFloatBitwise:
-  case IntrinsicOp::IOP_InterlockedExchange:
-  case IntrinsicOp::IOP_InterlockedMax:
-  case IntrinsicOp::IOP_InterlockedMin:
-  case IntrinsicOp::IOP_InterlockedOr:
-  case IntrinsicOp::IOP_InterlockedXor:
-    return true;
-  case IntrinsicOp::MOP_InterlockedAdd:
-  case IntrinsicOp::MOP_InterlockedAnd:
-  case IntrinsicOp::MOP_InterlockedCompareExchange:
-  case IntrinsicOp::MOP_InterlockedCompareStore:
-  case IntrinsicOp::MOP_InterlockedExchange:
-  case IntrinsicOp::MOP_InterlockedMax:
-  case IntrinsicOp::MOP_InterlockedMin:
-  case IntrinsicOp::MOP_InterlockedOr:
-  case IntrinsicOp::MOP_InterlockedXor:
-  case IntrinsicOp::MOP_InterlockedAdd64:
-  case IntrinsicOp::MOP_InterlockedAnd64:
-  case IntrinsicOp::MOP_InterlockedCompareExchange64:
-  case IntrinsicOp::MOP_InterlockedCompareStore64:
-  case IntrinsicOp::MOP_InterlockedExchange64:
-  case IntrinsicOp::MOP_InterlockedMax64:
-  case IntrinsicOp::MOP_InterlockedMin64:
-  case IntrinsicOp::MOP_InterlockedOr64:
-  case IntrinsicOp::MOP_InterlockedXor64:
-  case IntrinsicOp::MOP_InterlockedExchangeFloat:
-  case IntrinsicOp::MOP_InterlockedCompareExchangeFloatBitwise:
-  case IntrinsicOp::MOP_InterlockedCompareStoreFloatBitwise:
-    return acceptMethods;
-  default:
-    return false;
-  }
-}
-
 static bool HasUnsignedOpcode(LPCSTR tableName, IntrinsicOp opcode) {
   return IsBuiltinTable(tableName) && HasUnsignedIntrinsicOpcode(opcode);
 }
@@ -1976,17 +1930,10 @@ FunctionDecl *AddHLSLIntrinsicFunction(
 
   InitParamMods(pIntrinsic, paramMods);
 
-  // Change dest address into reference type for atomic.
-  if (IsAtomicOperation(tableName, static_cast<IntrinsicOp>(pIntrinsic->Op))) {
-    DXASSERT(functionArgTypeCount > kAtomicDstOperandIdx,
-             "else operation was misrecognized");
-    functionArgQualTypes[kAtomicDstOperandIdx] =
-      context.getLValueReferenceType(functionArgQualTypes[kAtomicDstOperandIdx]);
-  }
-
   for (size_t i = 1; i < functionArgTypeCount; i++) {
     // Change out/inout param to reference type.
-    if (paramMods[i-1].isAnyOut()) {
+    if (paramMods[i - 1].isAnyOut() ||
+        paramMods[i - 1].GetKind() == hlsl::ParameterModifier::Kind::Ref) {
       QualType Ty = functionArgQualTypes[i];
       // Aggregate type will be indirect param convert to pointer type.
       // Don't need add reference for it.
@@ -6189,19 +6136,15 @@ bool HLSLExternalSource::MatchArguments(
 
     ASTContext &actx = m_sema->getASTContext();
     // Usage
-    if (pIntrinsicArg->qwUsage & AR_QUAL_OUT) {
-      if (pType.isConstant(actx)) {
-        // Can't use a const type in an out or inout parameter.
-        badArgIdx = std::min(badArgIdx, iArg);
-      }
-    }
 
-    // Catch invalid atomic dest parameters
-    if (iArg == kAtomicDstOperandIdx &&
-        IsAtomicOperation(tableName, builtinOp, false /*acceptMethods*/)) {
-      // This produces an error for bitfields that is a bit confusing
-      // because it says uint can't cast to uint
+    // Argument must be non-constant and non-bitfield for out, inout, and ref
+    // parameters because they may be treated as pass-by-reference.
+    // This is hacky. We should actually be handling this by failing reference
+    // binding in sema init with SK_BindReference*. That code path is currently
+    // hacked off for HLSL and less trivial to fix.
+    if (pIntrinsicArg->qwUsage & AR_QUAL_OUT || pIntrinsicArg->qwUsage & AR_QUAL_REF) {
       if (pType.isConstant(actx) || pCallArg->getObjectKind() == OK_BitField) {
+        // Can't use a const type in an out or inout parameter.
         badArgIdx = std::min(badArgIdx, iArg);
       }
     }
@@ -6493,11 +6436,6 @@ bool HLSLExternalSource::MatchArguments(
 
     DXASSERT(!pNewType.isNull(), "otherwise there's a branch in this function that fails to assign this");
     argTypes[i] = QualType(pNewType.getTypePtr(), quals);
-
-    // TODO: support out modifier
-    //if (pArgument->qwUsage & AR_QUAL_OUT) {
-    //  argTypes[i] = m_context->getLValueReferenceType(argTypes[i].withConst());
-    //}
   }
 
   // For variadic functions, we need to add the additional arguments here.

+ 55 - 0
tools/clang/test/HLSL/atomic-float-errors.hlsl

@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -fsyntax-only -ffreestanding -verify  %s
+
+// Verify that the first arg determines the overload and the others can be what they will
+
+groupshared int      resGI[256];
+groupshared uint64_t resGI64[256];
+RWBuffer<int>      resBI;
+RWBuffer<uint64_t> resBI64;
+
+RWByteAddressBuffer Rres;
+
+[numthreads(1,1,1)]
+void main( uint3 gtid : SV_GroupThreadID)
+{
+  uint a = gtid.x;
+  uint b = gtid.y;
+  uint c = gtid.z;
+  resGI[a] = a;
+  resGI64[a] = a;
+  resBI[a] = a;
+  resBI64[a] = a;
+
+  float fv = b - c;
+  float fv2 = b + c;
+  float ofv = 0;
+  int iv = b / c;
+  int iv2 = b * c;
+  int oiv = 0;
+  uint64_t bb = b;
+  uint64_t cc = c;
+  uint64_t lv = bb * cc;
+  uint64_t lv2 = bb / cc;
+  uint64_t olv = 0;
+
+  InterlockedCompareStoreFloatBitwise( resBI[a], iv, iv2 ); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'int' to 'float &' for 1st argument}}
+  InterlockedCompareStoreFloatBitwise( resBI64[a], lv, lv2); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'unsigned long long' to 'float' for 1st argument}}
+  InterlockedCompareStoreFloatBitwise( resGI[a], iv, iv2 ); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'int' to 'float' for 1st argument}}
+  InterlockedCompareStoreFloatBitwise( resGI64[a], lv, lv2); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'uint64_t' to 'float' for 1st argument}}
+
+  InterlockedCompareStoreFloatBitwise( resBI[a], fv, fv2 ); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'int' to 'float' for 1st argument}}
+  InterlockedCompareStoreFloatBitwise( resBI64[a], fv, fv2 ); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'unsigned long long' to 'float' for 1st argument}}
+  InterlockedCompareStoreFloatBitwise( resGI[a], fv, fv2 ); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'int' to 'float' for 1st argument}}
+  InterlockedCompareStoreFloatBitwise( resGI64[a], fv, fv2 ); // expected-error{{no matching function for call to 'InterlockedCompareStoreFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'uint64_t' to 'float' for 1st argument}}
+
+  InterlockedCompareExchangeFloatBitwise( resBI[a], iv, iv2, oiv ); // expected-error{{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'int' to 'float &' for 1st argument}}
+  InterlockedCompareExchangeFloatBitwise( resBI64[a], lv, lv2, olv); // expected-error{{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'unsigned long long' to 'float' for 1st argument}}
+  InterlockedCompareExchangeFloatBitwise( resGI[a], iv, iv2, oiv ); // expected-error {{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'int' to 'float' for 1st argument}}
+  InterlockedCompareExchangeFloatBitwise( resGI64[a], lv, lv2, olv); // expected-error{{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'uint64_t' to 'float' for 1st argument}}
+
+  InterlockedCompareExchangeFloatBitwise( resBI[a], fv, fv2, ofv ); // expected-error{{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'int' to 'float' for 1st argument}}
+  InterlockedCompareExchangeFloatBitwise( resBI64[a], fv, fv2, ofv ); // expected-error{{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'unsigned long long' to 'float' for 1st argument}}
+  InterlockedCompareExchangeFloatBitwise( resGI[a], fv, fv2, ofv ); // expected-error{{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'int' to 'float' for 1st argument}}
+  InterlockedCompareExchangeFloatBitwise( resGI64[a], fv, fv2, ofv ); // expected-error{{no matching function for call to 'InterlockedCompareExchangeFloatBitwise'}} expected-note{{candidate function not viable: no known conversion from 'uint64_t' to 'float' for 1st argument}}
+
+}

+ 50 - 0
tools/clang/test/HLSLFileCheck/hlsl/intrinsics/atomic/atomic.hlsl

@@ -2,6 +2,7 @@
 // RUN: %dxc -E main -DTYPE=int -T cs_6_0 %s | FileCheck %s
 // RUN: %dxc -E main -DTYPE=uint64_t -T cs_6_6 %s | FileCheck %s
 // RUN: %dxc -E main -DTYPE=int64_t -T cs_6_6 %s | FileCheck %s
+// RUN: %dxc -E main -DTYPE=int -T cs_6_0 -ast-dump-implicit %s | FileCheck %s -check-prefix=AST
 
 // CHECK: Compute Shader
 // CHECK: NumThreads=(8,8,1)
@@ -87,3 +88,52 @@ void main( uint GI : SV_GroupIndex, uint3 DTid : SV_DispatchThreadID )
 	// CHECK: atomicrmw
 	InterlockedAdd(shareMem[DTid.z].r, 1);
 }
+
+// AST: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit InterlockedAdd 'void (unsigned long long &, unsigned long long)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'unsigned long long &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'unsigned long long'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op" "" 27
+
+// AST: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit used InterlockedAdd 'void (int &, unsigned int)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'int &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'unsigned int'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op" "" 27
+
+// AST: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit InterlockedAdd 'void (unsigned long long &, unsigned long long, long long &)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'unsigned long long &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'unsigned long long'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} original 'long long &&__restrict'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op" "" 27
+
+// AST: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit used InterlockedAdd 'void (int &, unsigned int, int &)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'int &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'unsigned int'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} original 'int &&__restrict'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op" "" 27
+
+// AST: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit InterlockedCompareStore 'void (unsigned long long &, unsigned long long, unsigned long long)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'unsigned long long &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} compare 'unsigned long long'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'unsigned long long'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op" "" 31
+
+// AST: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit used InterlockedCompareStore 'void (int &, unsigned int, unsigned int)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'int &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} compare 'unsigned int'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'unsigned int'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op" "" 31
+
+// AST: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit InterlockedCompareExchange 'void (unsigned long long &, unsigned long long, unsigned long long, long long &)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'unsigned long long &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} compare 'unsigned long long'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'unsigned long long'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} original 'long long &&__restrict'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op" "" 29
+
+// AST: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit used InterlockedCompareExchange 'void (int &, unsigned int, unsigned int, int &)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'int &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} compare 'unsigned int'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'unsigned int'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} original 'int &&__restrict'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op"
+// "" 29

+ 45 - 0
tools/clang/test/HLSLFileCheck/hlsl/intrinsics/atomic/atomic_float.hlsl

@@ -1,6 +1,7 @@
 // RUN: %dxc -E CSMain -T cs_6_6 %s | FileCheck %s -check-prefix=GSCHECK
 // RUN: %dxc -T ps_6_6 -DMEMTYPE=RWBuffer %s | FileCheck %s -check-prefixes=CHECK,TYCHECK
 // RUN: %dxc -T ps_6_6 -DMEMTYPE=RWStructuredBuffer %s | FileCheck %s -check-prefix=CHECK
+// RUN: %dxc -E CSMain -T cs_6_6 -ast-dump-implicit %s | FileCheck %s -check-prefix=AST
 
 #ifdef MEMTYPE
 MEMTYPE<float>     resF;
@@ -123,3 +124,47 @@ void CSMain( uint3 gtid : SV_GroupThreadID, uint ix : SV_GroupIndex)
 {
   output[ix] = dotest(gtid.x, gtid.y, gtid.z);
 }
+
+
+// AST: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit InterlockedExchange 'void (unsigned long long &, long long, long long &)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'unsigned long long &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'long long'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} original 'long long &&__restrict'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op" "" 33
+
+// AST: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit used InterlockedExchange 'void (float &, float, float &)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'float &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'float'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} original 'float &&__restrict'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op" "" 33
+
+// AST: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit used InterlockedExchange 'void (int &, unsigned int, int &)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'int &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'unsigned int'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} original 'int &&__restrict'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op" "" 33
+
+// AST: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit used InterlockedExchange 'void (long long &, unsigned long long, unsigned long long &)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'long long &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'unsigned long long'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} original 'unsigned long long &&__restrict'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op" "" 33
+
+// AST: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit used InterlockedExchange 'void (long long &, long long, long long &)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'long long &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'long long'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} original 'long long &&__restrict'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op" "" 33
+
+// AST: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit used InterlockedCompareStoreFloatBitwise 'void (float &, float, float)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'float &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} compare 'float'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'float'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op" "" 32
+
+// AST-NEXT: FunctionDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} implicit used InterlockedCompareExchangeFloatBitwise 'void (float &, float, float, float &)' extern
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} result 'float &'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} compare 'float'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} value 'float'
+// AST-NEXT: |-ParmVarDecl {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} original 'float &&__restrict'
+// AST-NEXT: `-HLSLIntrinsicAttr {{0x[0-9a-fA-F]+}} {{[<>a-z ]+}} Implicit "op" "" 30

+ 0 - 87
tools/clang/test/HLSLFileCheck/hlsl/intrinsics/atomic/atomic_float_errors.hlsl

@@ -1,87 +0,0 @@
-// RUN: %dxc -T cs_6_6 %s | FileCheck %s
-
-// Verify that the first arg determines the overload and the others can be what they will
-
-groupshared int      resGI[256];
-groupshared uint64_t resGI64[256];
-RWBuffer<int>      resBI;
-RWBuffer<uint64_t> resBI64;
-
-RWByteAddressBuffer Rres;
-
-[numthreads(1,1,1)]
-void main( uint3 gtid : SV_GroupThreadID)
-{
-  uint a = gtid.x;
-  uint b = gtid.y;
-  uint c = gtid.z;
-  resGI[a] = a;
-  resGI64[a] = a;
-  resBI[a] = a;
-  resBI64[a] = a;
-
-  float fv = b - c;
-  float fv2 = b + c;
-  float ofv = 0;
-  int iv = b / c;
-  int iv2 = b * c;
-  int oiv = 0;
-  uint64_t bb = b;
-  uint64_t cc = c;
-  uint64_t lv = bb * cc;
-  uint64_t lv2 = bb / cc;
-  uint64_t olv = 0;
-
-  // CHECK: error: no matching function for call to 'InterlockedCompareStoreFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'int' to 'float &' for 1st argument
-  // CHECK: error: no matching function for call to 'InterlockedCompareStoreFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'unsigned long long' to 'float' for 1st argument
-  // CHECK: error: no matching function for call to 'InterlockedCompareStoreFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'int' to 'float' for 1st argument
-  // CHECK: error: no matching function for call to 'InterlockedCompareStoreFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'uint64_t' to 'float' for 1st argument
-  InterlockedCompareStoreFloatBitwise( resBI[a], iv, iv2 );
-  InterlockedCompareStoreFloatBitwise( resBI64[a], lv, lv2);
-  InterlockedCompareStoreFloatBitwise( resGI[a], iv, iv2 );
-  InterlockedCompareStoreFloatBitwise( resGI64[a], lv, lv2);
-
-  // CHECK: error: no matching function for call to 'InterlockedCompareStoreFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'int' to 'float' for 1st argument
-  // CHECK: error: no matching function for call to 'InterlockedCompareStoreFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'unsigned long long' to 'float' for 1st argument
-  // CHECK: error: no matching function for call to 'InterlockedCompareStoreFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'int' to 'float' for 1st argument
-  // CHECK: error: no matching function for call to 'InterlockedCompareStoreFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'uint64_t' to 'float' for 1st argument
-  InterlockedCompareStoreFloatBitwise( resBI[a], fv, fv2 );
-  InterlockedCompareStoreFloatBitwise( resBI64[a], fv, fv2 );
-  InterlockedCompareStoreFloatBitwise( resGI[a], fv, fv2 );
-  InterlockedCompareStoreFloatBitwise( resGI64[a], fv, fv2 );
-
-  // CHECK: error: no matching function for call to 'InterlockedCompareExchangeFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'int' to 'float &' for 1st argument
-  // CHECK: error: no matching function for call to 'InterlockedCompareExchangeFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'unsigned long long' to 'float' for 1st argument
-  // CHECK: error: no matching function for call to 'InterlockedCompareExchangeFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'int' to 'float' for 1st argument
-  // CHECK: error: no matching function for call to 'InterlockedCompareExchangeFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'uint64_t' to 'float' for 1st argument
-  InterlockedCompareExchangeFloatBitwise( resBI[a], iv, iv2, oiv );
-  InterlockedCompareExchangeFloatBitwise( resBI64[a], lv, lv2, olv);
-  InterlockedCompareExchangeFloatBitwise( resGI[a], iv, iv2, oiv );
-  InterlockedCompareExchangeFloatBitwise( resGI64[a], lv, lv2, olv);
-
-  // CHECK: error: no matching function for call to 'InterlockedCompareExchangeFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'int' to 'float' for 1st argument
-  // CHECK: error: no matching function for call to 'InterlockedCompareExchangeFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'unsigned long long' to 'float' for 1st argument
-  // CHECK: error: no matching function for call to 'InterlockedCompareExchangeFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'int' to 'float' for 1st argument
-  // CHECK: error: no matching function for call to 'InterlockedCompareExchangeFloatBitwise'
-  // CHECK: note: candidate function not viable: no known conversion from 'uint64_t' to 'float' for 1st argument
-  InterlockedCompareExchangeFloatBitwise( resBI[a], fv, fv2, ofv );
-  InterlockedCompareExchangeFloatBitwise( resBI64[a], fv, fv2, ofv );
-  InterlockedCompareExchangeFloatBitwise( resGI[a], fv, fv2, ofv );
-  InterlockedCompareExchangeFloatBitwise( resGI64[a], fv, fv2, ofv );
-
-}

+ 5 - 0
tools/clang/unittests/HLSL/VerifierTest.cpp

@@ -36,6 +36,7 @@ public:
 
   TEST_METHOD(RunArrayIndexOutOfBounds)
   TEST_METHOD(RunArrayLength)
+  TEST_METHOD(RunAtomicFloatErrors)
   TEST_METHOD(RunAttributes)
   TEST_METHOD(RunBuiltinTypesNoInheritance)
   TEST_METHOD(RunConstExpr)
@@ -189,6 +190,10 @@ TEST_F(VerifierTest, RunArrayLength) {
   CheckVerifiesHLSL(L"array-length.hlsl");
 }
 
+TEST_F(VerifierTest, RunAtomicFloatErrors) {
+  CheckVerifiesHLSL(L"atomic-float-errors.hlsl");
+}
+
 TEST_F(VerifierTest, RunAttributes) {
   CheckVerifiesHLSL(L"attributes.hlsl");
 }

+ 33 - 33
utils/hct/gen_intrin_main.txt

@@ -148,41 +148,41 @@ $type1 [[rn]] fwidth(in float_like<> x);
 void [[]] GroupMemoryBarrier() : syncsharedmemory;
 void [[]] GroupMemoryBarrierWithGroupSync() : syncgroupandsharedmemory;
 // 64-bit integers interlocks
-void [[]] InterlockedAdd(in int64_only result, in u64 value);
-void [[]] InterlockedAdd(in int64_only result, in u64 value, out any_int64 original) : interlockedadd_immediate;
-void [[unsigned_op=InterlockedUMin,overload=0]] InterlockedMin(in int64_only result, in any_int64 value) : interlockedmin;
-void [[unsigned_op=InterlockedUMin,overload=0]] InterlockedMin(in int64_only result, in any_int64 value, out any_int64 original) : interlockedmin_immediate;
-void [[unsigned_op=InterlockedUMax,overload=0]] InterlockedMax(in int64_only result, in any_int64 value) : interlockedmax;
-void [[unsigned_op=InterlockedUMax,overload=0]] InterlockedMax(in int64_only result, in any_int64 value, out any_int64 original) : interlockedmax_immediate;
-void [[]] InterlockedAnd(in int64_only result, in u64 value);
-void [[]] InterlockedAnd(in int64_only result, in u64 value, out any_int64 original) : interlockedand_immediate;
-void [[]] InterlockedOr(in int64_only result, in u64 value);
-void [[]] InterlockedOr(in int64_only result, in u64 value, out any_int64 original) : interlockedor_immediate;
-void [[]] InterlockedXor(in int64_only result, in u64 value);
-void [[]] InterlockedXor(in int64_only result, in u64 value, out any_int64 original) : interlockedxor_immediate;
-void [[]] InterlockedCompareStore(in int64_only result, in u64 compare, in u64 value);
-void [[]] InterlockedExchange(in int64_only result, in any_int64 value, out any_int64 original);
-void [[]] InterlockedCompareExchange(in int64_only result, in u64 compare, in u64 value, out any_int64 original);
+void [[]] InterlockedAdd(ref int64_only result, in u64 value);
+void [[]] InterlockedAdd(ref int64_only result, in u64 value, out any_int64 original) : interlockedadd_immediate;
+void [[unsigned_op=InterlockedUMin,overload=0]] InterlockedMin(ref int64_only result, in any_int64 value) : interlockedmin;
+void [[unsigned_op=InterlockedUMin,overload=0]] InterlockedMin(ref int64_only result, in any_int64 value, out any_int64 original) : interlockedmin_immediate;
+void [[unsigned_op=InterlockedUMax,overload=0]] InterlockedMax(ref int64_only result, in any_int64 value) : interlockedmax;
+void [[unsigned_op=InterlockedUMax,overload=0]] InterlockedMax(ref int64_only result, in any_int64 value, out any_int64 original) : interlockedmax_immediate;
+void [[]] InterlockedAnd(ref int64_only result, in u64 value);
+void [[]] InterlockedAnd(ref int64_only result, in u64 value, out any_int64 original) : interlockedand_immediate;
+void [[]] InterlockedOr(ref int64_only result, in u64 value);
+void [[]] InterlockedOr(ref int64_only result, in u64 value, out any_int64 original) : interlockedor_immediate;
+void [[]] InterlockedXor(ref int64_only result, in u64 value);
+void [[]] InterlockedXor(ref int64_only result, in u64 value, out any_int64 original) : interlockedxor_immediate;
+void [[]] InterlockedCompareStore(ref int64_only result, in u64 compare, in u64 value);
+void [[]] InterlockedExchange(ref int64_only result, in any_int64 value, out any_int64 original);
+void [[]] InterlockedCompareExchange(ref int64_only result, in u64 compare, in u64 value, out any_int64 original);
 // floating point interlocks
-void [[]] InterlockedExchange(in float32_only result, in float value, out float original);
-void [[]] InterlockedCompareStoreFloatBitwise(in float32_only result, in float compare, in float value);
-void [[]] InterlockedCompareExchangeFloatBitwise(in float32_only result, in float compare, in float value, out float original);
+void [[]] InterlockedExchange(ref float32_only result, in float value, out float original);
+void [[]] InterlockedCompareStoreFloatBitwise(ref float32_only result, in float compare, in float value);
+void [[]] InterlockedCompareExchangeFloatBitwise(ref float32_only result, in float compare, in float value, out float original);
 // 32-bit integer interlocks
-void [[]] InterlockedAdd(in int32_only result, in uint value);
-void [[]] InterlockedAdd(in int32_only result, in uint value, out any_int32 original) : interlockedadd_immediate;
-void [[unsigned_op=InterlockedUMin,overload=0]] InterlockedMin(in int32_only result, in any_int32 value) : interlockedmin;
-void [[unsigned_op=InterlockedUMin,overload=0]] InterlockedMin(in int32_only result, in any_int32 value, out any_int32 original) : interlockedmin_immediate;
-void [[unsigned_op=InterlockedUMax,overload=0]] InterlockedMax(in int32_only result, in any_int32 value) : interlockedmax;
-void [[unsigned_op=InterlockedUMax,overload=0]] InterlockedMax(in int32_only result, in any_int32 value, out any_int32  original) : interlockedmax_immediate;
-void [[]] InterlockedAnd(in int32_only result, in uint value);
-void [[]] InterlockedAnd(in int32_only result, in uint value, out any_int32 original) : interlockedand_immediate;
-void [[]] InterlockedOr(in int32_only result, in uint value);
-void [[]] InterlockedOr(in int32_only result, in uint value, out any_int32 original) : interlockedor_immediate;
-void [[]] InterlockedXor(in int32_only result, in uint value);
-void [[]] InterlockedXor(in int32_only result, in uint value, out any_int32 original) : interlockedxor_immediate;
-void [[]] InterlockedCompareStore(in int32_only result, in uint compare, in uint value);
-void [[]] InterlockedExchange(in int32_only result, in uint value, out any_int32 original);
-void [[]] InterlockedCompareExchange(in int32_only result, in uint compare, in uint value, out any_int32 original);
+void [[]] InterlockedAdd(ref int32_only result, in uint value);
+void [[]] InterlockedAdd(ref int32_only result, in uint value, out any_int32 original) : interlockedadd_immediate;
+void [[unsigned_op=InterlockedUMin,overload=0]] InterlockedMin(ref int32_only result, in any_int32 value) : interlockedmin;
+void [[unsigned_op=InterlockedUMin,overload=0]] InterlockedMin(ref int32_only result, in any_int32 value, out any_int32 original) : interlockedmin_immediate;
+void [[unsigned_op=InterlockedUMax,overload=0]] InterlockedMax(ref int32_only result, in any_int32 value) : interlockedmax;
+void [[unsigned_op=InterlockedUMax,overload=0]] InterlockedMax(ref int32_only result, in any_int32 value, out any_int32  original) : interlockedmax_immediate;
+void [[]] InterlockedAnd(ref int32_only result, in uint value);
+void [[]] InterlockedAnd(ref int32_only result, in uint value, out any_int32 original) : interlockedand_immediate;
+void [[]] InterlockedOr(ref int32_only result, in uint value);
+void [[]] InterlockedOr(ref int32_only result, in uint value, out any_int32 original) : interlockedor_immediate;
+void [[]] InterlockedXor(ref int32_only result, in uint value);
+void [[]] InterlockedXor(ref int32_only result, in uint value, out any_int32 original) : interlockedxor_immediate;
+void [[]] InterlockedCompareStore(ref int32_only result, in uint compare, in uint value);
+void [[]] InterlockedExchange(ref int32_only result, in uint value, out any_int32 original);
+void [[]] InterlockedCompareExchange(ref int32_only result, in uint compare, in uint value, out any_int32 original);
 $match<1, 0> bool<> [[rn]] isfinite(in float<> x);
 $match<1, 0> bool<> [[rn]] isinf(in float<> x);
 $match<1, 0> bool<> [[rn]] isnan(in float<> x);

+ 1 - 0
utils/hct/hctdb.py

@@ -2953,6 +2953,7 @@ class db_hlsl(object):
         self.param_qual = {
             "in": "AR_QUAL_IN",
             "inout": "AR_QUAL_IN | AR_QUAL_OUT",
+            "ref" : "AR_QUAL_REF",
             "out": "AR_QUAL_OUT",
             "col_major": "AR_QUAL_COLMAJOR",
             "row_major": "AR_QUAL_ROWMAJOR"}