2
0
Эх сурвалжийг харах

Insert cast when types are identical to HLSL but not to clang (#4631)

It's possible to get a value of a type `unsigned long` in HLSL, such as
the result of sizeof(), while the uint type is `unsigned int` instead.
This leads to the situation where no cast is inserted because the types
look identical when translated down to ArBasicKind, which can lead to an
assert during codegen, such as when passing sizeof() result to a function
call parameter.

This change preserves the original canonical type ptr from clang and
sets the conversion type when these differ, even when ArBasicKind is
identical.
Tex Riddell 3 жил өмнө
parent
commit
4672db64b3

+ 30 - 0
tools/clang/lib/Sema/SemaHLSL.cpp

@@ -708,6 +708,7 @@ BITWISE_ENUM_OPS(TYPE_CONVERSION_REMARKS)
 struct ArTypeInfo {
   ArTypeObjectKind ShapeKind;      // The shape of the type (basic, matrix, etc.)
   ArBasicKind EltKind;             // The primitive type of elements in this type.
+  const clang::Type *EltTy;        // Canonical element type ptr
   ArBasicKind ObjKind;             // The object type for this type (textures, buffers, etc.)
   UINT uRows;
   UINT uCols;
@@ -4308,6 +4309,22 @@ public:
     return QualType(type->getCanonicalTypeUnqualified()->getTypePtr(), 0);
   }
 
+  /// <summary>Given a Clang type, return the QualType for its element, drilling through any array/vector/matrix.</summary>
+  QualType GetTypeElementType(QualType type)
+  {
+    type = GetStructuralForm(type);
+    ArTypeObjectKind kind = GetTypeObjectKind(type);
+    if (kind == AR_TOBJ_MATRIX || kind == AR_TOBJ_VECTOR) {
+      type = GetMatrixOrVectorElementType(type);
+    } else if (kind == AR_TOBJ_STRING) {
+      // return original type even if it's an array (string literal)
+    } else if (type->isArrayType()) {
+      const ArrayType* arrayType = type->getAsArrayTypeUnsafe();
+      type = GetTypeElementType(arrayType->getElementType());
+    }
+    return type;
+  }
+
   /// <summary>Given a Clang type, return the ArBasicKind classification for its contents.</summary>
   ArBasicKind GetTypeElementKind(QualType type)
   {
@@ -6779,6 +6796,7 @@ void HLSLExternalSource::CollectInfo(QualType type, ArTypeInfo* pTypeInfo)
   //       Try to inline that here, making it cheaper to use this function
   //       when retrieving multiple properties.
   pTypeInfo->ObjKind = GetTypeElementKind(type);
+  pTypeInfo->EltTy = GetTypeElementType(type)->getCanonicalTypeUnqualified()->getTypePtr();
   pTypeInfo->EltKind = pTypeInfo->ObjKind;
   pTypeInfo->ShapeKind = GetTypeObjectKind(type);
   GetRowsAndColsForAny(type, pTypeInfo->uRows, pTypeInfo->uCols);
@@ -8899,6 +8917,18 @@ static bool ConvertComponent(ArTypeInfo TargetInfo, ArTypeInfo SourceInfo,
           ComponentConversion = ICK_Floating_Integral;
       }
     }
+  } else if (TargetInfo.EltTy != SourceInfo.EltTy) {
+    // Types are identical in HLSL, but not identical in clang,
+    // such as unsigned long vs. unsigned int.
+    // Add conversion based on the type.
+    if (IS_BASIC_AINT(TargetInfo.EltKind))
+      ComponentConversion = ICK_Integral_Conversion;
+    else if (IS_BASIC_FLOAT(TargetInfo.EltKind))
+      ComponentConversion = ICK_Floating_Conversion;
+    else {
+      DXASSERT(false, "unhandled case for conversion that's identical in HLSL, but not in clang");
+      return false;
+    }
   }
 
   return true;

+ 20 - 0
tools/clang/test/HLSLFileCheck/hlsl/types/cast/implicit-cast-almost-identical.hlsl

@@ -0,0 +1,20 @@
+// RUN: %dxc -T vs_6_0 -ast-dump %s | FileCheck %s
+// RUN: %dxc -T vs_6_0 %s | FileCheck -check-prefix=CHECKIR %s
+
+// sizeof() results in unsigned long, while various HLSL intrinsics take unsigned int.
+// These types are identical in HLSL (uint), but not in clang.
+// A conversion step is still necessary to prevent this case from causing
+// assert when checking function arguments during codegen.
+
+// CHECK: CallExpr
+// CHECK-NEXT: ImplicitCastExpr
+// CHECK-NEXT: DeclRefExpr
+// CHECK-NEXT: ImplicitCastExpr
+// CHECK-SAME: IntegralCast
+// CHECK-NEXT: UnaryExprOrTypeTraitExpr
+
+// CHECKIR: call void @dx.op.storeOutput.i32(i32 5, i32 0, i32 0, i8 0, i32 4)
+
+uint main() : OUT {
+  return abs(sizeof(float));
+}