Author: Deric C. Date: 2026-02-18T19:05:21Z New Revision: 76ddcdf1202fac52ba9b1c630289553055d2f480
URL: https://github.com/llvm/llvm-project/commit/76ddcdf1202fac52ba9b1c630289553055d2f480 DIFF: https://github.com/llvm/llvm-project/commit/76ddcdf1202fac52ba9b1c630289553055d2f480.diff LOG: [Sema][HLSL][Matrix] Make matrices initializable by a single vector and vice-versa (#177486) Fixes #169561 The problem was that the logic for determining whether or not to convert constructor syntax into list initialization did not account for cases where the constructor only had a single argument. Thus, the constructor for a matrix with a single vector, or a vector with a single matrix did not convert to list initialization despite being valid (in DXC). This PR allows constructor syntax to be converted into list initialization when the number of arguments in the constructor is 1, but if and only if the destination type is a matrix/vector and the source type is a vector/matrix. Added: clang/test/CodeGenHLSL/BasicFeatures/MatrixToAndFromVectorConstructors.hlsl Modified: clang/lib/Sema/SemaInit.cpp clang/test/SemaHLSL/Types/BuiltinMatrix/MatrixSplatErrors.hlsl Removed: ################################################################################ diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp index 498ffd0887630..b79c22603494c 100644 --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -6947,10 +6947,39 @@ void InitializationSequence::InitializeFrom(Sema &S, // For HLSL ext vector types we allow list initialization behavior for C++ // functional cast expressions which look like constructor syntax. This is // accomplished by converting initialization arguments to InitListExpr. - if (S.getLangOpts().HLSL && Args.size() > 1 && - (DestType->isExtVectorType() || DestType->isConstantMatrixType()) && - (SourceType.isNull() || - !Context.hasSameUnqualifiedType(SourceType, DestType))) { + auto ShouldTryListInitialization = [&]() -> bool { + // Only try list initialization for HLSL. + if (!S.getLangOpts().HLSL) + return false; + + bool DestIsVec = DestType->isExtVectorType(); + bool DestIsMat = DestType->isConstantMatrixType(); + + // If the destination type is neither a vector nor a matrix, then don't try + // list initialization. + if (!DestIsVec && !DestIsMat) + return false; + + // If there is only a single source argument, then only try list + // initialization if initializing a matrix with a vector or vice versa. + if (Args.size() == 1) { + assert(!SourceType.isNull() && + "Source QualType should not be null when arg size is exactly 1"); + bool SourceIsVec = SourceType->isExtVectorType(); + bool SourceIsMat = SourceType->isConstantMatrixType(); + + if (DestIsMat && !SourceIsVec) + return false; + if (DestIsVec && !SourceIsMat) + return false; + } + + // Try list initialization if the source type is null or if the + // destination and source types diff er. + return SourceType.isNull() || + !Context.hasSameUnqualifiedType(SourceType, DestType); + }; + if (ShouldTryListInitialization()) { InitListExpr *ILE = new (Context) InitListExpr(S.getASTContext(), Args.front()->getBeginLoc(), Args, Args.back()->getEndLoc()); diff --git a/clang/test/CodeGenHLSL/BasicFeatures/MatrixToAndFromVectorConstructors.hlsl b/clang/test/CodeGenHLSL/BasicFeatures/MatrixToAndFromVectorConstructors.hlsl new file mode 100644 index 0000000000000..8b1fb9038bedd --- /dev/null +++ b/clang/test/CodeGenHLSL/BasicFeatures/MatrixToAndFromVectorConstructors.hlsl @@ -0,0 +1,121 @@ +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -disable-llvm-passes -emit-llvm -finclude-default-header -o - -fmatrix-memory-layout=column-major %s | FileCheck %s --check-prefixes=CHECK,COL-CHECK +// RUN: %clang_cc1 -triple dxil-pc-shadermodel6.0-library -disable-llvm-passes -emit-llvm -finclude-default-header -o - -fmatrix-memory-layout=row-major %s | FileCheck %s --check-prefixes=CHECK,ROW-CHECK + +// CHECK-LABEL: define hidden noundef nofpclass(nan inf) <4 x float> @_Z2fnu11matrix_typeILm2ELm2EfE( +// CHECK-SAME: <4 x float> noundef nofpclass(nan inf) [[M:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[M_ADDR:%.*]] = alloca [2 x <2 x float>], align 4 +// CHECK-NEXT: [[V:%.*]] = alloca <4 x float>, align 16 +// CHECK-NEXT: store <4 x float> [[M]], ptr [[M_ADDR]], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = load <4 x float>, ptr [[M_ADDR]], align 4 +// CHECK-NEXT: [[MATRIXEXT:%.*]] = extractelement <4 x float> [[TMP0]], i32 0 +// CHECK-NEXT: [[VECINIT:%.*]] = insertelement <4 x float> poison, float [[MATRIXEXT]], i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load <4 x float>, ptr [[M_ADDR]], align 4 +// COL-CHECK-NEXT: [[MATRIXEXT1:%.*]] = extractelement <4 x float> [[TMP1]], i32 2 +// ROW-CHECK-NEXT: [[MATRIXEXT1:%.*]] = extractelement <4 x float> [[TMP1]], i32 1 +// CHECK-NEXT: [[VECINIT2:%.*]] = insertelement <4 x float> [[VECINIT]], float [[MATRIXEXT1]], i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load <4 x float>, ptr [[M_ADDR]], align 4 +// COL-CHECK-NEXT: [[MATRIXEXT3:%.*]] = extractelement <4 x float> [[TMP2]], i32 1 +// ROW-CHECK-NEXT: [[MATRIXEXT3:%.*]] = extractelement <4 x float> [[TMP2]], i32 2 +// CHECK-NEXT: [[VECINIT4:%.*]] = insertelement <4 x float> [[VECINIT2]], float [[MATRIXEXT3]], i32 2 +// CHECK-NEXT: [[TMP3:%.*]] = load <4 x float>, ptr [[M_ADDR]], align 4 +// CHECK-NEXT: [[MATRIXEXT5:%.*]] = extractelement <4 x float> [[TMP3]], i32 3 +// CHECK-NEXT: [[VECINIT6:%.*]] = insertelement <4 x float> [[VECINIT4]], float [[MATRIXEXT5]], i32 3 +// CHECK-NEXT: store <4 x float> [[VECINIT6]], ptr [[V]], align 16 +// CHECK-NEXT: [[TMP4:%.*]] = load <4 x float>, ptr [[V]], align 16 +// CHECK-NEXT: ret <4 x float> [[TMP4]] +// +float4 fn(float2x2 m) { + float4 v = m; + return v; +} + +// CHECK-LABEL: define hidden noundef <4 x i32> @_Z2fnDv4_i( +// CHECK-SAME: <4 x i32> noundef [[V:%.*]]) #[[ATTR0:[0-9]+]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[V_ADDR:%.*]] = alloca <4 x i32>, align 16 +// CHECK-NEXT: [[M:%.*]] = alloca [2 x <2 x i32>], align 4 +// CHECK-NEXT: store <4 x i32> [[V]], ptr [[V_ADDR]], align 16 +// CHECK-NEXT: [[TMP0:%.*]] = load <4 x i32>, ptr [[V_ADDR]], align 16 +// CHECK-NEXT: [[VECEXT:%.*]] = extractelement <4 x i32> [[TMP0]], i64 0 +// CHECK-NEXT: [[VECINIT:%.*]] = insertelement <4 x i32> poison, i32 [[VECEXT]], i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load <4 x i32>, ptr [[V_ADDR]], align 16 +// CHECK-NEXT: [[VECEXT1:%.*]] = extractelement <4 x i32> [[TMP1]], i64 2 +// CHECK-NEXT: [[VECINIT2:%.*]] = insertelement <4 x i32> [[VECINIT]], i32 [[VECEXT1]], i32 1 +// CHECK-NEXT: [[TMP2:%.*]] = load <4 x i32>, ptr [[V_ADDR]], align 16 +// CHECK-NEXT: [[VECEXT3:%.*]] = extractelement <4 x i32> [[TMP2]], i64 1 +// CHECK-NEXT: [[VECINIT4:%.*]] = insertelement <4 x i32> [[VECINIT2]], i32 [[VECEXT3]], i32 2 +// CHECK-NEXT: [[TMP3:%.*]] = load <4 x i32>, ptr [[V_ADDR]], align 16 +// CHECK-NEXT: [[VECEXT5:%.*]] = extractelement <4 x i32> [[TMP3]], i64 3 +// CHECK-NEXT: [[VECINIT6:%.*]] = insertelement <4 x i32> [[VECINIT4]], i32 [[VECEXT5]], i32 3 +// CHECK-NEXT: store <4 x i32> [[VECINIT6]], ptr [[M]], align 4 +// CHECK-NEXT: [[TMP4:%.*]] = load <4 x i32>, ptr [[M]], align 4 +// CHECK-NEXT: ret <4 x i32> [[TMP4]] +// +int2x2 fn(int4 v) { + int2x2 m = v; + return m; +} + +// CHECK-LABEL: define hidden noundef <2 x i32> @_Z3fn1Dv2_i( +// CHECK-SAME: <2 x i32> noundef [[V:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[V_ADDR:%.*]] = alloca <2 x i32>, align 8 +// CHECK-NEXT: store <2 x i32> [[V]], ptr [[V_ADDR]], align 8 +// CHECK-NEXT: [[TMP0:%.*]] = load <2 x i32>, ptr [[V_ADDR]], align 8 +// CHECK-NEXT: [[VECEXT:%.*]] = extractelement <2 x i32> [[TMP0]], i64 0 +// CHECK-NEXT: [[VECINIT:%.*]] = insertelement <2 x i32> poison, i32 [[VECEXT]], i32 0 +// CHECK-NEXT: [[TMP1:%.*]] = load <2 x i32>, ptr [[V_ADDR]], align 8 +// CHECK-NEXT: [[VECEXT1:%.*]] = extractelement <2 x i32> [[TMP1]], i64 1 +// CHECK-NEXT: [[VECINIT2:%.*]] = insertelement <2 x i32> [[VECINIT]], i32 [[VECEXT1]], i32 1 +// CHECK-NEXT: ret <2 x i32> [[VECINIT2]] +// +int1x2 fn1(int2 v) { + return v; +} + +// CHECK-LABEL: define hidden noundef <3 x i1> @_Z3fn2Dv3_b( +// CHECK-SAME: <3 x i1> noundef [[B:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// CHECK-NEXT: [[B_ADDR:%.*]] = alloca <3 x i32>, align 16 +// CHECK-NEXT: [[TMP0:%.*]] = zext <3 x i1> [[B]] to <3 x i32> +// CHECK-NEXT: store <3 x i32> [[TMP0]], ptr [[B_ADDR]], align 16 +// CHECK-NEXT: [[TMP1:%.*]] = load <3 x i32>, ptr [[B_ADDR]], align 16 +// CHECK-NEXT: [[LOADEDV:%.*]] = trunc <3 x i32> [[TMP1]] to <3 x i1> +// CHECK-NEXT: [[VECEXT:%.*]] = extractelement <3 x i1> [[LOADEDV]], i64 0 +// CHECK-NEXT: [[VECINIT:%.*]] = insertelement <3 x i1> poison, i1 [[VECEXT]], i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load <3 x i32>, ptr [[B_ADDR]], align 16 +// CHECK-NEXT: [[LOADEDV1:%.*]] = trunc <3 x i32> [[TMP2]] to <3 x i1> +// CHECK-NEXT: [[VECEXT2:%.*]] = extractelement <3 x i1> [[LOADEDV1]], i64 1 +// CHECK-NEXT: [[VECINIT3:%.*]] = insertelement <3 x i1> [[VECINIT]], i1 [[VECEXT2]], i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load <3 x i32>, ptr [[B_ADDR]], align 16 +// CHECK-NEXT: [[LOADEDV4:%.*]] = trunc <3 x i32> [[TMP3]] to <3 x i1> +// CHECK-NEXT: [[VECEXT5:%.*]] = extractelement <3 x i1> [[LOADEDV4]], i64 2 +// CHECK-NEXT: [[VECINIT6:%.*]] = insertelement <3 x i1> [[VECINIT3]], i1 [[VECEXT5]], i32 2 +// CHECK-NEXT: ret <3 x i1> [[VECINIT6]] +// +bool3x1 fn2(bool3 b) { + return b; +} + +// CHECK-LABEL: define hidden noundef <3 x i32> @_Z3fn3u11matrix_typeILm1ELm3EbE( +// CHECK-SAME: <3 x i1> noundef [[B:%.*]]) #[[ATTR0]] { +// CHECK-NEXT: [[ENTRY:.*:]] +// COL-CHECK-NEXT: [[B_ADDR:%.*]] = alloca [3 x <1 x i32>], align 4 +// ROW-CHECK-NEXT: [[B_ADDR:%.*]] = alloca [1 x <3 x i32>], align 4 +// CHECK-NEXT: [[TMP0:%.*]] = zext <3 x i1> [[B]] to <3 x i32> +// CHECK-NEXT: store <3 x i32> [[TMP0]], ptr [[B_ADDR]], align 4 +// CHECK-NEXT: [[TMP1:%.*]] = load <3 x i32>, ptr [[B_ADDR]], align 4 +// CHECK-NEXT: [[MATRIXEXT:%.*]] = extractelement <3 x i32> [[TMP1]], i32 0 +// CHECK-NEXT: [[VECINIT:%.*]] = insertelement <3 x i32> poison, i32 [[MATRIXEXT]], i32 0 +// CHECK-NEXT: [[TMP2:%.*]] = load <3 x i32>, ptr [[B_ADDR]], align 4 +// CHECK-NEXT: [[MATRIXEXT1:%.*]] = extractelement <3 x i32> [[TMP2]], i32 1 +// CHECK-NEXT: [[VECINIT2:%.*]] = insertelement <3 x i32> [[VECINIT]], i32 [[MATRIXEXT1]], i32 1 +// CHECK-NEXT: [[TMP3:%.*]] = load <3 x i32>, ptr [[B_ADDR]], align 4 +// CHECK-NEXT: [[MATRIXEXT3:%.*]] = extractelement <3 x i32> [[TMP3]], i32 2 +// CHECK-NEXT: [[VECINIT4:%.*]] = insertelement <3 x i32> [[VECINIT2]], i32 [[MATRIXEXT3]], i32 2 +// CHECK-NEXT: ret <3 x i32> [[VECINIT4]] +// +int3 fn3(bool1x3 b) { + return b; +} diff --git a/clang/test/SemaHLSL/Types/BuiltinMatrix/MatrixSplatErrors.hlsl b/clang/test/SemaHLSL/Types/BuiltinMatrix/MatrixSplatErrors.hlsl index 0c2e53d382180..fc3f8e7adc050 100644 --- a/clang/test/SemaHLSL/Types/BuiltinMatrix/MatrixSplatErrors.hlsl +++ b/clang/test/SemaHLSL/Types/BuiltinMatrix/MatrixSplatErrors.hlsl @@ -1,8 +1,13 @@ // RUN: %clang_cc1 -triple dxil-pc-shadermodel6.6-library -finclude-default-header -std=hlsl202x -verify %s -void SplatOfVectortoMat(int4 V){ +void SplatOfUndersizedVectortoMat(int3 V){ int2x2 M = V; - // expected-error@-1 {{cannot initialize a variable of type 'int2x2' (aka 'matrix<int, 2, 2>') with an lvalue of type 'int4' (aka 'vector<int, 4>')}} + // expected-error@-1 {{too few initializers in list for type 'int2x2' (aka 'matrix<int, 2, 2>') (expected 4 but found 3)}} +} + +void SplatOfOversizedVectortoMat(int3 V){ + int1x2 M = V; + // expected-error@-1 {{too many initializers in list for type 'int1x2' (aka 'matrix<int, 1, 2>') (expected 2 but found 3)}} } void SplatOfMattoMat(int4x3 N){ _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
