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

Support operator sizeof (#2178)

To complement the new [RW]ByteAddressBuffer.Load/Store<T> feature, it's useful to have operator sizeof to compute offsets. This supports both type and expression forms of the operator, as in C++. The size returned is consistent with struct layouts in StructuredBuffer, but not necessarily with that of constant buffers, which use different rules. Using sizeof on resources or aggregates containing resources will result in a compile-time error. It is only supported for "blittable" types.
Tristan Labelle 6 жил өмнө
parent
commit
a6776de932

+ 4 - 0
tools/clang/include/clang/Basic/DiagnosticSemaKinds.td

@@ -7506,6 +7506,10 @@ def err_hlsl_requires_non_aggregate : Error<
   "scalar, vector, or matrix expected">;
   "scalar, vector, or matrix expected">;
 def err_hlsl_require_numeric_base_for_ctor : Error<
 def err_hlsl_require_numeric_base_for_ctor : Error<
   "constructors only defined for numeric base types">;
   "constructors only defined for numeric base types">;
+def err_hlsl_sizeof_literal : Error<
+  "invalid application of 'sizeof' to literal type %0">;
+def err_hlsl_sizeof_nonnumeric : Error<
+  "invalid application of 'sizeof' to non-numeric type %0">;
 def err_hlsl_typecheck_duplicate_matrix_components_not_mlvalue : Error< // Patterned after err_typecheck_duplicate_matrix_components_not_mlvalue
 def err_hlsl_typecheck_duplicate_matrix_components_not_mlvalue : Error< // Patterned after err_typecheck_duplicate_matrix_components_not_mlvalue
   "matrix is not assignable (contains duplicate components)">;
   "matrix is not assignable (contains duplicate components)">;
 def err_hlsl_typecheck_subscript_value : Error< // Patterned after err_typecheck_subscript_value
 def err_hlsl_typecheck_subscript_value : Error< // Patterned after err_typecheck_subscript_value

+ 5 - 0
tools/clang/include/clang/Sema/Sema.h

@@ -3786,6 +3786,11 @@ public:
   ExprResult CheckPlaceholderExpr(Expr *E);
   ExprResult CheckPlaceholderExpr(Expr *E);
   bool CheckVecStepExpr(Expr *E);
   bool CheckVecStepExpr(Expr *E);
 
 
+  // HLSL Change Begins
+  bool CheckHLSLUnaryExprOrTypeTraitOperand(QualType ExprType, SourceLocation Loc,
+                                            UnaryExprOrTypeTrait ExprKind);
+  // HLSL Change Ends
+
   bool CheckUnaryExprOrTypeTraitOperand(Expr *E, UnaryExprOrTypeTrait ExprKind);
   bool CheckUnaryExprOrTypeTraitOperand(Expr *E, UnaryExprOrTypeTrait ExprKind);
   bool CheckUnaryExprOrTypeTraitOperand(QualType ExprType, SourceLocation OpLoc,
   bool CheckUnaryExprOrTypeTraitOperand(QualType ExprType, SourceLocation OpLoc,
                                         SourceRange ExprRange,
                                         SourceRange ExprRange,

+ 4 - 1
tools/clang/lib/AST/HlslTypes.cpp

@@ -100,7 +100,10 @@ bool IsHLSLNumericOrAggregateOfNumericType(clang::QualType type) {
   } else if (type->isArrayType()) {
   } else if (type->isArrayType()) {
     return IsHLSLNumericOrAggregateOfNumericType(QualType(type->getArrayElementTypeNoTypeQual(), 0));
     return IsHLSLNumericOrAggregateOfNumericType(QualType(type->getArrayElementTypeNoTypeQual(), 0));
   }
   }
-  return Ty->isBuiltinType();
+
+  // Chars can only appear as part of strings, which we don't consider numeric.
+  const BuiltinType* BuiltinTy = dyn_cast<BuiltinType>(Ty);
+  return BuiltinTy != nullptr && BuiltinTy->getKind() != BuiltinType::Kind::Char_S;
 }
 }
 
 
 bool IsHLSLNumericUserDefinedType(clang::QualType type) {
 bool IsHLSLNumericUserDefinedType(clang::QualType type) {

+ 10 - 4
tools/clang/lib/Parse/ParseExpr.cpp

@@ -1137,7 +1137,7 @@ HLSLReservedKeyword:
   case tok::kw_vec_step:   // unary-expression: OpenCL 'vec_step' expression
   case tok::kw_vec_step:   // unary-expression: OpenCL 'vec_step' expression
   // unary-expression: '__builtin_omp_required_simd_align' '(' type-name ')'
   // unary-expression: '__builtin_omp_required_simd_align' '(' type-name ')'
   case tok::kw___builtin_omp_required_simd_align:
   case tok::kw___builtin_omp_required_simd_align:
-    if (getLangOpts().HLSL) { goto HLSLReservedKeyword; } // HLSL Change - not supported
+    if (getLangOpts().HLSL && Tok.getKind() != tok::kw_sizeof) { goto HLSLReservedKeyword; } // HLSL Change - not supported
     return ParseUnaryExprOrTypeTraitExpression();
     return ParseUnaryExprOrTypeTraitExpression();
   case tok::ampamp: {      // unary-expression: '&&' identifier
   case tok::ampamp: {      // unary-expression: '&&' identifier
     // HLSL Change Starts
     // HLSL Change Starts
@@ -1809,7 +1809,10 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
                                            bool &isCastExpr,
                                            bool &isCastExpr,
                                            ParsedType &CastTy,
                                            ParsedType &CastTy,
                                            SourceRange &CastRange) {
                                            SourceRange &CastRange) {
-  assert(!getLangOpts().HLSL && "not supported for HLSL - unreachable"); // HLSL Change
+  // HLSL Change Begin
+  assert((!getLangOpts().HLSL || OpTok.getKind() == tok::kw_sizeof)
+    && "not supported for HLSL - unreachable");
+  // HLSL Change End
 
 
   assert(OpTok.isOneOf(tok::kw_typeof, tok::kw_sizeof, tok::kw___alignof,
   assert(OpTok.isOneOf(tok::kw_typeof, tok::kw_sizeof, tok::kw___alignof,
                        tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step,
                        tok::kw_alignof, tok::kw__Alignof, tok::kw_vec_step,
@@ -1897,7 +1900,10 @@ Parser::ParseExprAfterUnaryExprOrTypeTrait(const Token &OpTok,
 /// [C++11] 'alignof' '(' type-id ')'
 /// [C++11] 'alignof' '(' type-id ')'
 /// \endverbatim
 /// \endverbatim
 ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
 ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
-  assert(!getLangOpts().HLSL && "not supported for HLSL - unreachable"); // HLSL Change
+  // HLSL Change Begin
+  assert((!getLangOpts().HLSL || Tok.getKind() == tok::kw_sizeof)
+    && "not supported for HLSL - unreachable");
+  // HLSL Change End
   assert(Tok.isOneOf(tok::kw_sizeof, tok::kw___alignof, tok::kw_alignof,
   assert(Tok.isOneOf(tok::kw_sizeof, tok::kw___alignof, tok::kw_alignof,
                      tok::kw__Alignof, tok::kw_vec_step,
                      tok::kw__Alignof, tok::kw_vec_step,
                      tok::kw___builtin_omp_required_simd_align) &&
                      tok::kw___builtin_omp_required_simd_align) &&
@@ -1906,7 +1912,7 @@ ExprResult Parser::ParseUnaryExprOrTypeTraitExpression() {
   ConsumeToken();
   ConsumeToken();
 
 
   // [C++11] 'sizeof' '...' '(' identifier ')'
   // [C++11] 'sizeof' '...' '(' identifier ')'
-  if (Tok.is(tok::ellipsis) && OpTok.is(tok::kw_sizeof)) {
+  if (Tok.is(tok::ellipsis) && OpTok.is(tok::kw_sizeof) && !getLangOpts().HLSL) { // HLSL Change
     SourceLocation EllipsisLoc = ConsumeToken();
     SourceLocation EllipsisLoc = ConsumeToken();
     SourceLocation LParenLoc, RParenLoc;
     SourceLocation LParenLoc, RParenLoc;
     IdentifierInfo *Name = nullptr;
     IdentifierInfo *Name = nullptr;

+ 29 - 0
tools/clang/lib/Sema/SemaExpr.cpp

@@ -3661,6 +3661,27 @@ static void warnOnSizeofOnArrayDecay(Sema &S, SourceLocation Loc, QualType T,
                                              << ICE->getSubExpr()->getType();
                                              << ICE->getSubExpr()->getType();
 }
 }
 
 
+// HLSL Change Begins
+bool Sema::CheckHLSLUnaryExprOrTypeTraitOperand(QualType ExprType, SourceLocation Loc,
+                                                UnaryExprOrTypeTrait ExprKind) {
+  assert(ExprKind == UnaryExprOrTypeTrait::UETT_SizeOf);
+
+  // "sizeof 42" is ill-defined because HLSL has literal int type which can decay to an int of any size.
+  const BuiltinType* BuiltinTy = ExprType->getAs<BuiltinType>();
+  if (BuiltinTy != nullptr && (BuiltinTy->getKind() == BuiltinType::LitInt || BuiltinTy->getKind() == BuiltinType::LitFloat)) {
+    Diag(Loc, diag::err_hlsl_sizeof_literal) << ExprType;
+    return true;
+  }
+
+  if (!hlsl::IsHLSLNumericOrAggregateOfNumericType(ExprType)) {
+    Diag(Loc, diag::err_hlsl_sizeof_nonnumeric) << ExprType;
+    return true;
+  }
+
+  return false;
+}
+// HLSL Change Ends
+
 /// \brief Check the constraints on expression operands to unary type expression
 /// \brief Check the constraints on expression operands to unary type expression
 /// and type traits.
 /// and type traits.
 ///
 ///
@@ -3702,6 +3723,10 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(Expr *E,
   ExprTy = E->getType();
   ExprTy = E->getType();
   assert(!ExprTy->isReferenceType());
   assert(!ExprTy->isReferenceType());
 
 
+  if (getLangOpts().HLSL && CheckHLSLUnaryExprOrTypeTraitOperand(ExprTy, E->getExprLoc(), ExprKind)) {
+    return true;
+  }
+
   if (ExprTy->isFunctionType()) {
   if (ExprTy->isFunctionType()) {
     Diag(E->getExprLoc(), diag::err_sizeof_alignof_function_type)
     Diag(E->getExprLoc(), diag::err_sizeof_alignof_function_type)
       << ExprKind << E->getSourceRange();
       << ExprKind << E->getSourceRange();
@@ -3776,6 +3801,10 @@ bool Sema::CheckUnaryExprOrTypeTraitOperand(QualType ExprType,
   if (const ReferenceType *Ref = ExprType->getAs<ReferenceType>())
   if (const ReferenceType *Ref = ExprType->getAs<ReferenceType>())
     ExprType = Ref->getPointeeType();
     ExprType = Ref->getPointeeType();
 
 
+  if (getLangOpts().HLSL && CheckHLSLUnaryExprOrTypeTraitOperand(ExprType, OpLoc, ExprKind)) {
+    return true;
+  }
+
   // C11 6.5.3.4/3, C++11 [expr.alignof]p3:
   // C11 6.5.3.4/3, C++11 [expr.alignof]p3:
   //   When alignof or _Alignof is applied to an array type, the result
   //   When alignof or _Alignof is applied to an array type, the result
   //   is the alignment of the element type.
   //   is the alignment of the element type.

+ 39 - 0
tools/clang/test/CodeGenHLSL/batch/expressions/operators/sizeof.hlsl

@@ -0,0 +1,39 @@
+// RUN: %dxc -E main -T vs_6_2 -HV 2018 -enable-16bit-types %s | FileCheck %s
+
+struct Empty {};
+
+AppendStructuredBuffer<int> buf;
+
+void main() {
+  // Test size and packing of scalar types, vectors and arrays all at once.
+
+  // CHECK: call void @dx.op.rawBufferStore.i32(i32 140, {{.*}}, i32 12, i32 undef
+  buf.Append(sizeof(int16_t3[2]));
+  // CHECK: call void @dx.op.rawBufferStore.i32(i32 140, {{.*}}, i32 12, i32 undef
+  buf.Append(sizeof(half3[2]));
+
+  // CHECK: call void @dx.op.rawBufferStore.i32(i32 140, {{.*}}, i32 24, i32 undef
+  buf.Append(sizeof(int3[2]));
+  // CHECK: call void @dx.op.rawBufferStore.i32(i32 140, {{.*}}, i32 24, i32 undef
+  buf.Append(sizeof(float3[2]));
+  // CHECK: call void @dx.op.rawBufferStore.i32(i32 140, {{.*}}, i32 24, i32 undef
+  buf.Append(sizeof(bool3[2]));
+
+  // CHECK: call void @dx.op.rawBufferStore.i32(i32 140, {{.*}}, i32 48, i32 undef
+  buf.Append(sizeof(int64_t3[2]));
+  // CHECK: call void @dx.op.rawBufferStore.i32(i32 140, {{.*}}, i32 48, i32 undef
+  buf.Append(sizeof(double3[2]));
+
+  // CHECK: call void @dx.op.rawBufferStore.i32(i32 140, {{.*}}, i32 0, i32 undef
+  buf.Append(sizeof(Empty[2]));
+
+  // CHECK: call void @dx.op.rawBufferStore.i32(i32 140, {{.*}}, i32 8, i32 undef
+  struct
+  {
+    int16_t i16;
+    // 2-byte padding
+    struct { float f32; } s; // Nested type
+    struct {} _; // Zero-sized field.
+  } complexStruct;
+  buf.Append(sizeof(complexStruct));
+}

+ 31 - 0
tools/clang/test/HLSL/sizeof.hlsl

@@ -0,0 +1,31 @@
+// RUN: %clang_cc1 -Wno-unused-value -fsyntax-only -ffreestanding -verify -verify-ignore-unexpected=note %s
+
+// Tests usage of the sizeof operator
+
+struct EmptyStruct {};
+struct SimpleStruct { int x; };
+struct StructWithResource { Buffer buf; int x; };
+
+void main()
+{
+  // Type vs expression argument
+  sizeof(int);
+  sizeof((int)0);
+
+  // Type shapes
+  sizeof(int);
+  sizeof(int2);
+  sizeof(int2x2);
+  sizeof(int[2]);
+  sizeof(SimpleStruct);
+  sizeof(EmptyStruct);
+
+  // Special types
+  sizeof(void); // expected-error {{invalid application of 'sizeof' to an incomplete type 'void'}}
+  sizeof 42; // expected-error {{invalid application of 'sizeof' to literal type 'literal int'}}
+  sizeof 42.0; // expected-error {{invalid application of 'sizeof' to literal type 'literal float'}}
+  sizeof ""; // expected-error {{invalid application of 'sizeof' to non-numeric type 'literal string'}}
+  sizeof(Buffer); // expected-error {{invalid application of 'sizeof' to non-numeric type 'Buffer'}}
+  sizeof(StructWithResource); // expected-error {{invalid application of 'sizeof' to non-numeric type 'StructWithResource'}}
+  sizeof(main); // expected-error {{invalid application of 'sizeof' to non-numeric type 'void ()'}}
+}

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

@@ -64,6 +64,7 @@ public:
   TEST_METHOD(RunScalarOperatorsAssignExactPrecision)
   TEST_METHOD(RunScalarOperatorsAssignExactPrecision)
   TEST_METHOD(RunScalarOperators)
   TEST_METHOD(RunScalarOperators)
   TEST_METHOD(RunScalarOperatorsExactPrecision)
   TEST_METHOD(RunScalarOperatorsExactPrecision)
+  TEST_METHOD(RunSizeof)
   TEST_METHOD(RunString)
   TEST_METHOD(RunString)
   TEST_METHOD(RunStructAssignments)
   TEST_METHOD(RunStructAssignments)
   TEST_METHOD(RunSubobjects)
   TEST_METHOD(RunSubobjects)
@@ -260,6 +261,10 @@ TEST_F(VerifierTest, RunScalarOperatorsExactPrecision) {
   CheckVerifiesHLSL(L"scalar-operators-exact-precision.hlsl");
   CheckVerifiesHLSL(L"scalar-operators-exact-precision.hlsl");
 }
 }
 
 
+TEST_F(VerifierTest, RunSizeof) {
+  CheckVerifiesHLSL(L"sizeof.hlsl");
+}
+
 TEST_F(VerifierTest, RunString) {
 TEST_F(VerifierTest, RunString) {
   CheckVerifiesHLSL(L"string.hlsl");
   CheckVerifiesHLSL(L"string.hlsl");
 }
 }