Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/DXIL.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3231,7 +3231,10 @@ INSTR.LINALGMATRIXSCOPEREQLAYOUT2 %0 matrix with scope '%1'
INSTR.LINALGMATRIXUNSIGNEDFLOATTYPENOTALLOWED Float-like type '%0' must be signed
INSTR.LINALGMATRIXUSEMISMATCH %0 matrix use '%1' does not match expected use %2.
INSTR.LINALGMATRIXUSEMISMATCH2 %0 matrix use '%1' does not match expected use %2 or %3.
INSTR.LINALGMATRIXVECELEMCOUNTMISMATCH Return vector size '%0' must must size '%1' derived from input vector size and type.
INSTR.LINALGMATRIXVECELEMENTTYPEMISMATCH %0 vector element type '%1' must match %2 vector element type '%3'
INSTR.LINALGMATRIXVECTORTYPEMUSTMATCH %0 vector element type '%1' must match %2 matrix element type '%3'.
INSTR.LINALGMATRIXVECTORTYPEMUSTMATCHPACKED %0 vector element type '%1' must be i32 for %2 matrix with non-native element type '%3'.
INSTR.LINALGMETADATAMISSING %0 matrix must have well-formed metadata.
INSTR.MAYREORDERTHREADUNDEFCOHERENCEHINTPARAM Use of undef coherence hint or num coherence hint bits in MaybeReorderThread.
INSTR.MINPRECISIONNOTPRECISE Instructions marked precise may not refer to minprecision values.
Expand Down
87 changes: 84 additions & 3 deletions lib/DxilValidation/DxilValidation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,9 @@ ValidateConstantIntGetValue(CallInst *CI, Value *V, ValidationContext &ValCtx,
return cast<ConstantInt>(V)->getLimitedValue();
}

static void ValidateLinAlgComponentType(CallInst *CI, DXIL::ComponentType CT,
// Determines if a ComponentType is allowed in LinAlg builtins.
// Returns true when the CT is allowed.
static bool ValidateLinAlgComponentType(CallInst *CI, DXIL::ComponentType CT,
ValidationContext &ValCtx,
StringRef SourceName) {
switch (CT) {
Expand All @@ -1007,12 +1009,12 @@ static void ValidateLinAlgComponentType(CallInst *CI, DXIL::ComponentType CT,
case DXIL::ComponentType::F32:
case DXIL::ComponentType::F64:
case DXIL::ComponentType::BFloat16:
break;
return true;
default:
ValCtx.EmitInstrFormatError(CI,
ValidationRule::InstrLinAlgIllegalComponentType,
{ComponentTypeToString(CT), SourceName});
break;
return false;
}
}

Expand Down Expand Up @@ -1547,6 +1549,85 @@ static void ValidateLinAlgMatrixAccumulateToMemory(CallInst *CI,

static void ValidateLinAlgConvert(CallInst *CI, ValidationContext &ValCtx) {
ValidateLinAlgOpParameters(CI, ValCtx);
Comment thread
V-FEXrt marked this conversation as resolved.
DxilInst_LinAlgConvert Op(CI);

VectorType *RetVecTy = cast<VectorType>(CI->getType());
VectorType *InVecTy = cast<VectorType>(Op.get_inputVector()->getType());
bool IsComponentTypeValid = true;

// Input interp must be a immarg of allowed ComponentType
std::optional<uint64_t> InputInterpV =
ValidateConstantIntGetValue(CI, Op.get_inputInterpretation(), ValCtx,
"InputInterpretation", "LinAlgConvert");
if (!InputInterpV)
return;
DXIL::ComponentType InputInterp =
static_cast<DXIL::ComponentType>(*InputInterpV);
IsComponentTypeValid &= ValidateLinAlgComponentType(CI, InputInterp, ValCtx,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A nit: if component type is invalid at this point, we will continue and attempt to validate the constant-ness of the output interp value.
One could argue we should return upon the first validation error.
Is this behavior intentional?

i.e., does a

if (!IsComponentTypeValid)
    return;

belong here?

"InputInterpretation");

// Output interp must be a immarg of allowed ComponentType
std::optional<uint64_t> OutputInterpV =
ValidateConstantIntGetValue(CI, Op.get_outputInterpretation(), ValCtx,
"OutputInterpretation", "LinAlgConvert");
if (!OutputInterpV)
return;
DXIL::ComponentType OutputInterp =
static_cast<DXIL::ComponentType>(*OutputInterpV);
IsComponentTypeValid &= ValidateLinAlgComponentType(CI, OutputInterp, ValCtx,
"OutputInterpretation");

// The remaining validations only give reasonable errors when assuming valid
// ComponentTypes. Stop early to minimze noise/avoid being unhelpful
if (!IsComponentTypeValid)
return;

bool IsNativeInputInterp = IsComponentTypeNative(InputInterp);
bool IsNativeOutputInterp = IsComponentTypeNative(OutputInterp);

// If input interp is native then input vector element type must match that
// type
if (IsNativeInputInterp &&
!IsComponentTypeSameNativeType(InputInterp, InVecTy->getElementType()))
ValCtx.EmitInstrFormatError(
CI, ValidationRule::InstrLinAlgMatrixVectorTypeMustMatch,
{"Input", TypeToString(InVecTy->getElementType()),
"InputInterpretation", ComponentTypeToString(InputInterp)});

// If input interp is non-native then input vector element type must be i32
if (!IsNativeInputInterp && !InVecTy->getElementType()->isIntegerTy(32))
ValCtx.EmitInstrFormatError(
CI, ValidationRule::InstrLinAlgMatrixVectorTypeMustMatchPacked,
{"Input", TypeToString(InVecTy->getElementType()),
"InputInterpretation", ComponentTypeToString(InputInterp)});

// If output interp is native then output vector element type must match that
// type
if (IsNativeOutputInterp &&
!IsComponentTypeSameNativeType(OutputInterp, RetVecTy->getElementType()))
ValCtx.EmitInstrFormatError(
CI, ValidationRule::InstrLinAlgMatrixVectorTypeMustMatch,
{"Output", TypeToString(RetVecTy->getElementType()),
"OutputInterpretation", ComponentTypeToString(OutputInterp)});

// If output interp is non-native then output vector element type must be i32
if (!IsNativeOutputInterp && !RetVecTy->getElementType()->isIntegerTy(32))
ValCtx.EmitInstrFormatError(
CI, ValidationRule::InstrLinAlgMatrixVectorTypeMustMatchPacked,
{"Output", TypeToString(RetVecTy->getElementType()),
"OutputInterpretation", ComponentTypeToString(OutputInterp)});

// output vector length must be properly converted from input length
unsigned InputElemCount =
InVecTy->getNumElements() * ComponentTypeElementsPerScalar(InputInterp);
unsigned OutputElemPerScalar = ComponentTypeElementsPerScalar(OutputInterp);
unsigned ExpectedOutVecSize =
(InputElemCount + OutputElemPerScalar - 1) / OutputElemPerScalar;
if (RetVecTy->getNumElements() != ExpectedOutVecSize)
ValCtx.EmitInstrFormatError(
CI, ValidationRule::InstrLinAlgMatrixVecElemCountMismatch,
{std::to_string(RetVecTy->getNumElements()),
std::to_string(ExpectedOutVecSize)});
}

static void
Expand Down
17 changes: 17 additions & 0 deletions lib/DxilValidation/DxilValidationUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -747,4 +747,21 @@ bool IsComponentTypeSameNativeType(DXIL::ComponentType CT, llvm::Type *Ty) {
}
}

bool IsComponentTypeNative(DXIL::ComponentType CT) {
switch (CT) {
case DXIL::ComponentType::I16:
case DXIL::ComponentType::U16:
case DXIL::ComponentType::I32:
case DXIL::ComponentType::U32:
case DXIL::ComponentType::I64:
case DXIL::ComponentType::U64:
case DXIL::ComponentType::F16:
case DXIL::ComponentType::F32:
case DXIL::ComponentType::F64:
return true;
default:
return false;
}
}

} // namespace hlsl
2 changes: 2 additions & 0 deletions lib/DxilValidation/DxilValidationUtils.h
Original file line number Diff line number Diff line change
Expand Up @@ -167,4 +167,6 @@ llvm::StringRef MatrixLayoutToString(DXIL::MatrixLayout ML);
std::string TypeToString(llvm::Type *Ty);

bool IsComponentTypeSameNativeType(DXIL::ComponentType CT, llvm::Type *Ty);

bool IsComponentTypeNative(DXIL::ComponentType CT);
} // namespace hlsl
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ void main() {
// CHECK-LABEL: define void @main()

// CHECK: %{{.*}} = call <4 x i32> @dx.op.linAlgConvert.v4i32.v4f32
// CHECK-SAME: (i32 -2147483618, <4 x float> <float 9.000000e+00, float 8.000000e+00, float 7.000000e+00, float 6.000000e+00>, i32 1, i32 2)
// CHECK-SAME: (i32 -2147483618, <4 x float> <float 9.000000e+00, float 8.000000e+00, float 7.000000e+00, float 6.000000e+00>, i32 9, i32 4)
// CHECK-SAME: ; LinAlgConvert(inputVector,inputInterpretation,outputInterpretation)

// CHECK2: call void @"dx.hl.op..void (i32, <4 x i32>*, <4 x float>, i32, i32)"
// CHECK2-SAME: (i32 422, <4 x i32>* %result1, <4 x float> %{{.*}}, i32 1, i32 2)
// CHECK2-SAME: (i32 422, <4 x i32>* %result1, <4 x float> %{{.*}}, i32 9, i32 4)
float4 vec1 = {9.0, 8.0, 7.0, 6.0};
int4 result1;
__builtin_LinAlg_Convert(result1, vec1, 1, 2);
__builtin_LinAlg_Convert(result1, vec1, 9, 4);

// CHECK: %{{.*}} = call <4 x i64> @dx.op.linAlgConvert.v4i64.v4f64
// CHECK-SAME: (i32 -2147483618, <4 x double> <double 9.000000e+00, double 8.000000e+00, double 7.000000e+00, double 6.000000e+00>, i32 1, i32 2)
// CHECK-SAME: (i32 -2147483618, <4 x double> <double 9.000000e+00, double 8.000000e+00, double 7.000000e+00, double 6.000000e+00>, i32 10, i32 7)
// CHECK-SAME: ; LinAlgConvert(inputVector,inputInterpretation,outputInterpretation)

// CHECK2: call void @"dx.hl.op..void (i32, <4 x i64>*, <4 x double>, i32, i32)"
// CHECK2-SAME: (i32 422, <4 x i64>* %result2, <4 x double> %{{.*}}, i32 1, i32 2)
// CHECK2-SAME: (i32 422, <4 x i64>* %result2, <4 x double> %{{.*}}, i32 10, i32 7)
double4 vec2 = {9.0, 8.0, 7.0, 6.0};
vector<int64_t, 4> result2;
__builtin_LinAlg_Convert(result2, vec2, 1, 2);
__builtin_LinAlg_Convert(result2, vec2, 10, 7);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
; REQUIRES: dxil-1-10
; RUN: not %dxv %s 2>&1 | FileCheck %s

target datalayout = "e-m:e-p:32:32-i1:32-i8:8-i16:16-i32:32-i64:64-f16:16-f32:32-f64:64-n8:16:32:64"
target triple = "dxil-ms-dx"

%dx.types.Handle = type { i8* }
%dx.types.ResBind = type { i32, i32, i32, i8 }
%dx.types.ResourceProperties = type { i32, i32 }
%dx.types.ResRet.i32 = type { i32, i32, i32, i32, i32 }
%struct.ByteAddressBuffer = type { i32 }

define void @main() {
%1 = call %dx.types.Handle @dx.op.createHandleFromBinding(i32 217, %dx.types.ResBind zeroinitializer, i32 0, i1 false) ; CreateHandleFromBinding(bind,index,nonUniformIndex)
%2 = call %dx.types.Handle @dx.op.annotateHandle(i32 216, %dx.types.Handle %1, %dx.types.ResourceProperties { i32 11, i32 0 }) ; AnnotateHandle(res,props) resource: ByteAddressBuffer
%3 = call %dx.types.ResRet.i32 @dx.op.rawBufferLoad.i32(i32 139, %dx.types.Handle %2, i32 0, i32 undef, i8 1, i32 4) ; RawBufferLoad(srv,index,elementOffset,mask,alignment)
%4 = extractvalue %dx.types.ResRet.i32 %3, 0

; okay
%5 = call <8 x i32> @dx.op.linAlgConvert.v8i32.v8i32(i32 -2147483618, <8 x i32> <i32 9, i32 8, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2>, i32 4, i32 4) ; LinAlgConvert(inputVector,inputInterpretation,outputInterpretation)

; CHECK: Function: main: error: InputInterpretation of LinAlgConvert must be an immediate constant.
; CHECK-NEXT: note: at {{.*}} @dx.op.linAlgConvert.v8i32.v8i32
%6 = call <8 x i32> @dx.op.linAlgConvert.v8i32.v8i32(i32 -2147483618, <8 x i32> <i32 9, i32 8, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2>, i32 %4, i32 4) ; LinAlgConvert(inputVector,inputInterpretation,outputInterpretation)

; CHECK-NEXT: Function: main: error: OutputInterpretation of LinAlgConvert must be an immediate constant.
; CHECK-NEXT: note: at {{.*}} @dx.op.linAlgConvert.v8i32.v8i32
%7 = call <8 x i32> @dx.op.linAlgConvert.v8i32.v8i32(i32 -2147483618, <8 x i32> <i32 9, i32 8, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2>, i32 4, i32 %4) ; LinAlgConvert(inputVector,inputInterpretation,outputInterpretation)

; CHECK-NEXT: Function: main: error: Component type 'Invalid' from InputInterpretation not allowed in LinAlg Matrix operations.
; CHECK-NEXT: note: at {{.*}} @dx.op.linAlgConvert.v8i32.v8i32
%8 = call <8 x i32> @dx.op.linAlgConvert.v8i32.v8i32(i32 -2147483618, <8 x i32> <i32 9, i32 8, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2>, i32 0, i32 4) ; LinAlgConvert(inputVector,inputInterpretation,outputInterpretation)

; CHECK-NEXT: Function: main: error: Component type 'Invalid' from OutputInterpretation not allowed in LinAlg Matrix operations.
; CHECK-NEXT: note: at {{.*}} @dx.op.linAlgConvert.v8i32.v8i32
%9 = call <8 x i32> @dx.op.linAlgConvert.v8i32.v8i32(i32 -2147483618, <8 x i32> <i32 9, i32 8, i32 7, i32 6, i32 5, i32 4, i32 3, i32 2>, i32 4, i32 0) ; LinAlgConvert(inputVector,inputInterpretation,outputInterpretation)

; CHECK-NEXT: Function: main: error: Input vector element type 'float' must match InputInterpretation matrix element type 'I32'.
; CHECK-NEXT: note: at {{.*}} @dx.op.linAlgConvert.v8i32.v8f32
%10 = call <8 x i32> @dx.op.linAlgConvert.v8i32.v8f32(i32 -2147483618, <8 x float> <float 9.000000e+00, float 8.000000e+00, float 7.000000e+00, float 6.000000e+00, float 5.000000e+00, float 4.000000e+00, float 3.000000e+00, float 2.000000e+00>, i32 4, i32 4) ; LinAlgConvert(inputVector,inputInterpretation,outputInterpretation)

; CHECK-NEXT: Function: main: error: Output vector element type 'i32' must match OutputInterpretation matrix element type 'F32'.
; CHECK-NEXT: note: at {{.*}} @dx.op.linAlgConvert.v8i32.v8f32
%11 = call <8 x i32> @dx.op.linAlgConvert.v8i32.v8f32(i32 -2147483618, <8 x float> <float 9.000000e+00, float 8.000000e+00, float 7.000000e+00, float 6.000000e+00, float 5.000000e+00, float 4.000000e+00, float 3.000000e+00, float 2.000000e+00>, i32 9, i32 9) ; LinAlgConvert(inputVector,inputInterpretation,outputInterpretation)

; CHECK-NEXT: Function: main: error: Input vector element type 'float' must be i32 for InputInterpretation matrix with non-native element type 'F8_E4M3FN'.
; CHECK-NEXT: note: at {{.*}} @dx.op.linAlgConvert.v32i32.v8f32
%12 = call <32 x i32> @dx.op.linAlgConvert.v32i32.v8f32(i32 -2147483618, <8 x float> <float 9.000000e+00, float 8.000000e+00, float 7.000000e+00, float 6.000000e+00, float 5.000000e+00, float 4.000000e+00, float 3.000000e+00, float 2.000000e+00>, i32 21, i32 4) ; LinAlgConvert(inputVector,inputInterpretation,outputInterpretation)

; CHECK-NEXT: Function: main: error: Return vector size '32' must must size '2' derived from input vector size and type.
; CHECK-NEXT: note: at {{.*}} @dx.op.linAlgConvert.v32i32.v8f32
%13 = call <32 x i32> @dx.op.linAlgConvert.v32i32.v8f32(i32 -2147483618, <8 x float> <float 9.000000e+00, float 8.000000e+00, float 7.000000e+00, float 6.000000e+00, float 5.000000e+00, float 4.000000e+00, float 3.000000e+00, float 2.000000e+00>, i32 9, i32 21) ; LinAlgConvert(inputVector,inputInterpretation,outputInterpretation)

; CHECK-NEXT: Function: main: error: Output vector element type 'float' must be i32 for OutputInterpretation matrix with non-native element type 'F8_E4M3FN'.
; CHECK-NEXT: note: at {{.*}} @dx.op.linAlgConvert.v2f32.v8f32
%14 = call <2 x float> @dx.op.linAlgConvert.v2f32.v8f32(i32 -2147483618, <8 x float> <float 9.000000e+00, float 8.000000e+00, float 7.000000e+00, float 6.000000e+00, float 5.000000e+00, float 4.000000e+00, float 3.000000e+00, float 2.000000e+00>, i32 9, i32 21) ; LinAlgConvert(inputVector,inputInterpretation,outputInterpretation)

; CHECK-NEXT: Validation failed.
ret void
}

; Function Attrs: nounwind readonly
declare %dx.types.ResRet.i32 @dx.op.rawBufferLoad.i32(i32, %dx.types.Handle, i32, i32, i8, i32) #0

; Function Attrs: nounwind
declare <8 x i32> @dx.op.linAlgConvert.v8i32.v8i32(i32, <8 x i32>, i32, i32) #1

; Function Attrs: nounwind
declare <8 x i32> @dx.op.linAlgConvert.v8i32.v8f32(i32, <8 x float>, i32, i32) #1

; Function Attrs: nounwind
declare <32 x i32> @dx.op.linAlgConvert.v32i32.v8f32(i32, <8 x float>, i32, i32) #1

; Function Attrs: nounwind
declare <2 x float> @dx.op.linAlgConvert.v2f32.v8f32(i32, <8 x float>, i32, i32) #1

; Function Attrs: nounwind readnone
declare %dx.types.Handle @dx.op.annotateHandle(i32, %dx.types.Handle, %dx.types.ResourceProperties) #2

; Function Attrs: nounwind readnone
declare %dx.types.Handle @dx.op.createHandleFromBinding(i32, %dx.types.ResBind, i32, i1) #2

attributes #0 = { nounwind readonly }
attributes #1 = { nounwind }
attributes #2 = { nounwind readnone }

!llvm.ident = !{!0}
!dx.version = !{!1}
!dx.valver = !{!1}
!dx.shaderModel = !{!2}
!dx.resources = !{!3}
!dx.entryPoints = !{!6}

!0 = !{!"dxc(private) 1.9.0.5484 (linalg-vali-convert, 2832abc07)"}
!1 = !{i32 1, i32 10}
!2 = !{!"cs", i32 6, i32 10}
!3 = !{!4, null, null, null}
!4 = !{!5}
!5 = !{i32 0, %struct.ByteAddressBuffer* undef, !"", i32 0, i32 0, i32 1, i32 11, i32 0, null}
!6 = !{void ()* @main, !"main", null, !3, !7}
!7 = !{i32 0, i64 8388624, i32 4, !8}
!8 = !{i32 1, i32 1, i32 1}

12 changes: 12 additions & 0 deletions utils/hct/hctdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -8743,6 +8743,18 @@ def build_valrules(self):
"Instr.LinAlgMatrixGSMemMustBeLargeEnough",
"Groupshared memory holds '%0' scalars but must hold at least '%1' scalars.",
)
self.add_valrule(
"Instr.LinAlgMatrixVectorTypeMustMatch",
"%0 vector element type '%1' must match %2 matrix element type '%3'.",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Surprised this diagnostic doesn't already exist / can't be merged with another pre-existing diagnostic.
Can't see anything this can be merged with.

)
self.add_valrule(
"Instr.LinAlgMatrixVectorTypeMustMatchPacked",
"%0 vector element type '%1' must be i32 for %2 matrix with non-native element type '%3'."
)
self.add_valrule(
"Instr.LinAlgMatrixVecElemCountMismatch",
"Return vector size '%0' must must size '%1' derived from input vector size and type.",
)

# Some legacy rules:
# - space is only supported for shader targets 5.1 and higher
Expand Down
Loading