[tint][ir][val] Check that conversion for specific types exists
This CL introduces looking up conversion entries in the intrinsic table
for the convert call. It does not enforce that entry is a converter (not
a constructor), so is less restrictive then the intended final state.
A follow up CL will add the 'is converter' check and update various
tests.
Bug: 389589696
Change-Id: I2726e6546545fa5cb719343f04e3c286290b727a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/222115
Auto-Submit: Ryan Harrison <rharrison@chromium.org>
Commit-Queue: James Price <jrprice@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/ir/validator.cc b/src/tint/lang/core/ir/validator.cc
index 9458b1d..d1ff4fe 100644
--- a/src/tint/lang/core/ir/validator.cc
+++ b/src/tint/lang/core/ir/validator.cc
@@ -2878,7 +2878,45 @@
}
void Validator::CheckConvert(const Convert* convert) {
- CheckResultsAndOperands(convert, Convert::kNumResults, Convert::kNumOperands);
+ if (!CheckResultsAndOperands(convert, Convert::kNumResults, Convert::kNumOperands)) {
+ return;
+ }
+
+ auto* result_type = convert->Result(0)->Type();
+ auto* value_type = convert->Operand(Convert::kValueOperandOffset)->Type();
+
+ intrinsic::CtorConv conv_ty;
+ Vector<const core::type::Type*, 1> template_type;
+ tint::Switch(
+ result_type, //
+ [&](const core::type::I32*) { conv_ty = intrinsic::CtorConv::kI32; }, //
+ [&](const core::type::U32*) { conv_ty = intrinsic::CtorConv::kU32; }, //
+ [&](const core::type::F32*) { conv_ty = intrinsic::CtorConv::kF32; }, //
+ [&](const core::type::F16*) { conv_ty = intrinsic::CtorConv::kF16; }, //
+ [&](const core::type::Bool*) { conv_ty = intrinsic::CtorConv::kBool; }, //
+ [&](const core::type::Vector* v) {
+ conv_ty = intrinsic::VectorCtorConv(v->Width());
+ template_type.Push(v->Type());
+ },
+ [&](const core::type::Matrix* m) {
+ conv_ty = intrinsic::MatrixCtorConv(m->Columns(), m->Rows());
+ template_type.Push(m->Type());
+ },
+ [&](Default) { conv_ty = intrinsic::CtorConv::kNone; });
+
+ if (conv_ty == intrinsic::CtorConv::kNone) {
+ AddError(convert) << "not defined for result type, " << StyledText{}
+ << style::Type(result_type->FriendlyName());
+ return;
+ }
+
+ auto table = intrinsic::Table<intrinsic::Dialect>(type_mgr_, symbols_);
+ auto match =
+ table.Lookup(conv_ty, template_type, Vector{value_type}, core::EvaluationStage::kOverride);
+ if (match != Success) {
+ AddError(convert) << match.Failure();
+ return;
+ }
}
void Validator::CheckDiscard(const tint::core::ir::Discard* discard) {
diff --git a/src/tint/lang/core/ir/validator_test.cc b/src/tint/lang/core/ir/validator_test.cc
index 0f5e595..8bfeca0 100644
--- a/src/tint/lang/core/ir/validator_test.cc
+++ b/src/tint/lang/core/ir/validator_test.cc
@@ -627,6 +627,236 @@
)");
}
+TEST_F(IR_ValidatorTest, Convert_ScalarToScalar) {
+ auto* f = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam("p", ty.f32());
+ f->AppendParam(p);
+ b.Append(f->Block(), [&] {
+ b.Convert(ty.i32(), p);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_ValidatorTest, Convert_ScalarToVec) {
+ auto* f = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam("p", ty.f32());
+ f->AppendParam(p);
+ b.Append(f->Block(), [&] {
+ b.Convert(ty.vec2<f32>(), p);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ // There is a vector constructor that fills all elements with the scalar,
+ // this will start failing when checking for kConvertor is added.
+ ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_ValidatorTest, Convert_ScalarToMat) {
+ auto* f = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam("p", ty.f32());
+ f->AppendParam(p);
+ b.Append(f->Block(), [&] {
+ b.Convert(ty.mat2x3<f32>(), p);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_THAT(
+ res.Failure().reason.Str(),
+ testing::HasSubstr(":3:22 error: convert: no matching constructor for 'mat2x3<f32>(f32)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_VecToVec) {
+ auto* f = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam("p", ty.vec2<u32>());
+ f->AppendParam(p);
+ b.Append(f->Block(), [&] {
+ b.Convert(ty.vec2<f32>(), p);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_ValidatorTest, Convert_VecToVec_WidthMismatch) {
+ auto* f = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam("p", ty.vec2<u32>());
+ f->AppendParam(p);
+ b.Append(f->Block(), [&] {
+ b.Convert(ty.vec4<f32>(), p);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_THAT(res.Failure().reason.Str(),
+ testing::HasSubstr(
+ ":3:20 error: convert: no matching constructor for 'vec4<f32>(vec2<u32>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_VecToScalar) {
+ auto* f = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam("p", ty.vec2<u32>());
+ f->AppendParam(p);
+ b.Append(f->Block(), [&] {
+ b.Convert(ty.f32(), p);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_THAT(
+ res.Failure().reason.Str(),
+ testing::HasSubstr(":3:14 error: convert: no matching constructor for 'f32(vec2<u32>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_VecToMat) {
+ auto* f = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam("p", ty.vec2<u32>());
+ f->AppendParam(p);
+ b.Append(f->Block(), [&] {
+ b.Convert(ty.mat3x2<f32>(), p);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_THAT(res.Failure().reason.Str(),
+ testing::HasSubstr(
+ ":3:22 error: convert: no matching constructor for 'mat3x2<f32>(vec2<u32>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_MatToMat) {
+ auto* f = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam("p", ty.mat4x4<f32>());
+ f->AppendParam(p);
+ b.Append(f->Block(), [&] {
+ b.Convert(ty.mat4x4<f16>(), p);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_ValidatorTest, Convert_MatToMat_ShapeMismatch) {
+ auto* f = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam("p", ty.mat4x4<f32>());
+ f->AppendParam(p);
+ b.Append(f->Block(), [&] {
+ b.Convert(ty.mat2x2<f32>(), p);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_THAT(
+ res.Failure().reason.Str(),
+ testing::HasSubstr(
+ ":3:22 error: convert: no matching constructor for 'mat2x2<f32>(mat4x4<f32>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_MatToScalar) {
+ auto* f = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam("p", ty.mat4x4<f32>());
+ f->AppendParam(p);
+ b.Append(f->Block(), [&] {
+ b.Convert(ty.f32(), p);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_THAT(
+ res.Failure().reason.Str(),
+ testing::HasSubstr(":3:14 error: convert: no matching constructor for 'f32(mat4x4<f32>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_MatToVec) {
+ auto* f = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam("p", ty.mat4x4<u32>());
+ f->AppendParam(p);
+ b.Append(f->Block(), [&] {
+ b.Convert(ty.vec4<f32>(), p);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_THAT(res.Failure().reason.Str(),
+ testing::HasSubstr(
+ ":3:20 error: convert: no matching constructor for 'vec4<f32>(mat4x4<u32>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_4xU8ToU32) {
+ auto* f = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam("p", ty.vec4<u8>());
+ f->AppendParam(p);
+ b.Append(f->Block(), [&] {
+ b.Convert(ty.u32(), p);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod, Capabilities{Capability::kAllow8BitIntegers});
+ ASSERT_NE(res, Success);
+ EXPECT_THAT(
+ res.Failure().reason.Str(),
+ testing::HasSubstr(":3:14 error: convert: no matching constructor for 'u32(vec4<u8>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_U32To4xU8) {
+ auto* f = b.Function("f", ty.void_());
+ auto* p = b.FunctionParam("p", ty.u32());
+ f->AppendParam(p);
+ b.Append(f->Block(), [&] {
+ b.Convert(ty.vec4<u8>(), p);
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod, Capabilities{Capability::kAllow8BitIntegers});
+ ASSERT_NE(res, Success);
+ EXPECT_THAT(
+ res.Failure().reason.Str(),
+ testing::HasSubstr(":3:19 error: convert: no matching constructor for 'vec4<u8>(u32)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_PtrToVal) {
+ auto* f = b.Function("f", ty.void_());
+ b.Append(f->Block(), [&] {
+ auto* v = b.Var("v", 0_u);
+ b.Convert(ty.u32(), v->Result(0));
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_THAT(res.Failure().reason.Str(),
+ testing::HasSubstr(":4:14 error: convert: no matching constructor for "
+ "'u32(ptr<function, u32, read_write>)'"));
+}
+
+TEST_F(IR_ValidatorTest, Convert_PtrToPtr) {
+ auto* f = b.Function("f", ty.void_());
+ b.Append(f->Block(), [&] {
+ auto* v = b.Var("v", 0_u);
+ b.Convert(v->Result(0)->Type(), v->Result(0));
+ b.Return(f);
+ });
+
+ auto res = ir::Validate(mod);
+ ASSERT_NE(res, Success);
+ EXPECT_THAT(
+ res.Failure().reason.Str(),
+ testing::HasSubstr(
+ ":4:41 error: convert: not defined for result type, 'ptr<function, u32, read_write>'"));
+}
+
TEST_F(IR_ValidatorTest, Block_NoTerminator) {
b.Function("my_func", ty.void_());
diff --git a/src/tint/lang/core/ir/validator_type_test.cc b/src/tint/lang/core/ir/validator_type_test.cc
index ff9bde3..c1cc6f9 100644
--- a/src/tint/lang/core/ir/validator_type_test.cc
+++ b/src/tint/lang/core/ir/validator_type_test.cc
@@ -748,16 +748,16 @@
TEST_F(IR_ValidatorTest, Int8Type_InstructionOperand_NotAllowed) {
auto* fn = b.Function("my_func", ty.void_());
b.Append(fn->Block(), [&] {
- b.Convert(ty.i32(), u8(1));
+ b.Let("l", u8(1));
b.Return(fn);
});
auto res = ir::Validate(mod);
ASSERT_NE(res, Success);
EXPECT_EQ(res.Failure().reason.Str(),
- R"(:3:22 error: convert: 8-bit integer types are not permitted
- %2:i32 = convert 1u8
- ^^^
+ R"(:3:5 error: let: 8-bit integer types are not permitted
+ %l:u8 = let 1u8
+ ^^^^^
:2:3 note: in block
$B1: {
@@ -766,7 +766,7 @@
note: # Disassembly
%my_func = func():void {
$B1: {
- %2:i32 = convert 1u8
+ %l:u8 = let 1u8
ret
}
}
@@ -776,7 +776,7 @@
TEST_F(IR_ValidatorTest, Int8Type_InstructionOperand_Allowed) {
auto* fn = b.Function("my_func", ty.void_());
b.Append(fn->Block(), [&] {
- b.Convert(ty.i32(), u8(1));
+ b.Let("l", u8(1));
b.Return(fn);
});
diff --git a/src/tint/lang/glsl/writer/convert_test.cc b/src/tint/lang/glsl/writer/convert_test.cc
index b3e6df6..c438070 100644
--- a/src/tint/lang/glsl/writer/convert_test.cc
+++ b/src/tint/lang/glsl/writer/convert_test.cc
@@ -36,7 +36,7 @@
TEST_F(GlslWriterTest, ConvertU32) {
auto* f = b.Function("a", ty.u32());
b.Append(f->Block(), [&] {
- auto* v = b.Var("v", 2_i);
+ auto* v = b.Let("v", 2_i);
b.Return(f, b.Convert(ty.u32(), v));
});
diff --git a/src/tint/lang/hlsl/writer/convert_test.cc b/src/tint/lang/hlsl/writer/convert_test.cc
index 525fc33..df9a508 100644
--- a/src/tint/lang/hlsl/writer/convert_test.cc
+++ b/src/tint/lang/hlsl/writer/convert_test.cc
@@ -36,7 +36,7 @@
TEST_F(HlslWriterTest, ConvertU32) {
auto* f = b.Function("a", ty.u32());
b.Append(f->Block(), [&] {
- auto* v = b.Var("v", 2_i);
+ auto* v = b.Let("v", 2_i);
b.Return(f, b.Convert(ty.u32(), v));
});