Skip to content

Commit

Permalink
[tint][ir][val] Add operand and result checks for MemberBuiltinCall
Browse files Browse the repository at this point in the history
Since core IR does not declare any member builtins, tests have been
added to the backend specific unittests for coverage.

Fixes: 395822789
Change-Id: I74281c101777fd708bfb4fd9c26201ae179e0b95
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/225638
Auto-Submit: Ryan Harrison <[email protected]>
Commit-Queue: Ryan Harrison <[email protected]>
Reviewed-by: dan sinclair <[email protected]>
  • Loading branch information
zoddicus authored and Dawn LUCI CQ committed Feb 12, 2025
1 parent c06c766 commit 157857f
Show file tree
Hide file tree
Showing 5 changed files with 543 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/tint/lang/core/ir/member_builtin_call.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ class MemberBuiltinCall : public Castable<MemberBuiltinCall, Call> {
/// The base offset in Operands() for the args
static constexpr size_t kArgsOperandOffset = 1;

/// The fixed number of results returned by this instruction
static constexpr size_t kNumResults = 1;

/// Constructor (no results, no operands)
/// @param id the instruction id
explicit MemberBuiltinCall(Id id);
Expand Down
7 changes: 7 additions & 0 deletions src/tint/lang/core/ir/validator.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2843,6 +2843,13 @@ void Validator::CheckBuiltinCall(const BuiltinCall* call) {
}

void Validator::CheckMemberBuiltinCall(const MemberBuiltinCall* call) {
// This check cannot be more precise, since until intrinsic lookup below, it is unknown what
// number of operands are expected, but still need to enforce things are in scope,
// have types, etc.
if (!CheckResults(call, MemberBuiltinCall::kNumResults) || !CheckOperands(call)) {
return;
}

auto args = Vector<const core::type::Type*, 8>({call->Object()->Type()});
for (auto* arg : call->Args()) {
args.Push(arg->Type());
Expand Down
116 changes: 116 additions & 0 deletions src/tint/lang/glsl/ir/member_builtin_call_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -118,5 +118,121 @@ SB = struct @align(4) {
)");
}

TEST_F(IR_GlslMemberBuiltinCallTest, Valid) {
auto* sb = ty.Struct(mod.symbols.New("SB"), {
{mod.symbols.New("a"), ty.array<u32>()},
});
auto* var = b.Var("v", storage, sb, core::Access::kReadWrite);
var->SetBindingPoint(0, 0);
b.ir.root_block->Append(var);

auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
b.Append(func->Block(), [&] {
auto* access = b.Access(ty.ptr<storage, array<u32>, read_write>(), var, 0_u);
b.MemberCall<MemberBuiltinCall>(mod.Types().i32(), BuiltinFn::kLength, access);
b.Return(func);
});

auto res = core::ir::Validate(mod);
ASSERT_EQ(res, Success);
}

TEST_F(IR_GlslMemberBuiltinCallTest, MissingResult) {
auto* sb = ty.Struct(mod.symbols.New("SB"), {
{mod.symbols.New("a"), ty.array<u32>()},
});
auto* var = b.Var("v", storage, sb, core::Access::kReadWrite);
var->SetBindingPoint(0, 0);
b.ir.root_block->Append(var);

auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
b.Append(func->Block(), [&] {
auto* access = b.Access(ty.ptr<storage, array<u32>, read_write>(), var, 0_u);
auto* m = b.MemberCall<MemberBuiltinCall>(mod.Types().i32(), BuiltinFn::kLength, access);
m->ClearResults();
b.Return(func);
});

auto res = core::ir::Validate(mod);
ASSERT_NE(res, Success);
EXPECT_EQ(res.Failure().reason.Str(),
R"(:12:16 error: length: expected exactly 1 results, got 0
undef = %3.length
^^^^^^
:10:3 note: in block
$B2: {
^^^
note: # Disassembly
SB = struct @align(4) {
a:array<u32> @offset(0)
}
$B1: { # root
%v:ptr<storage, SB, read_write> = var @binding_point(0, 0)
}
%foo = @fragment func():void {
$B2: {
%3:ptr<storage, array<u32>, read_write> = access %v, 0u
undef = %3.length
ret
}
}
)");
}

TEST_F(IR_GlslMemberBuiltinCallTest, TooManyArgs) {
auto* sb = ty.Struct(mod.symbols.New("SB"), {
{mod.symbols.New("a"), ty.array<u32>()},
});
auto* var = b.Var("v", storage, sb, core::Access::kReadWrite);
var->SetBindingPoint(0, 0);
b.ir.root_block->Append(var);

auto* func = b.Function("foo", ty.void_(), core::ir::Function::PipelineStage::kFragment);
b.Append(func->Block(), [&] {
auto* access = b.Access(ty.ptr<storage, array<u32>, read_write>(), var, 0_u);
b.MemberCall<MemberBuiltinCall>(mod.Types().i32(), BuiltinFn::kLength, access, 0_u);
b.Return(func);
});

auto res = core::ir::Validate(mod);
ASSERT_NE(res, Success);
EXPECT_EQ(
res.Failure().reason.Str(),
R"(:12:17 error: length: no matching call to 'length(ptr<storage, array<u32>, read_write>, u32)'
1 candidate function:
• 'length(ptr<storage, array<T>, A> ✓ ) -> i32' where:
✗ overload expects 1 argument, call passed 2 arguments
%4:i32 = %3.length 0u
^^^^^^
:10:3 note: in block
$B2: {
^^^
note: # Disassembly
SB = struct @align(4) {
a:array<u32> @offset(0)
}
$B1: { # root
%v:ptr<storage, SB, read_write> = var @binding_point(0, 0)
}
%foo = @fragment func():void {
$B2: {
%3:ptr<storage, array<u32>, read_write> = access %v, 0u
%4:i32 = %3.length 0u
ret
}
}
)");
}

} // namespace
} // namespace tint::glsl::ir
Loading

0 comments on commit 157857f

Please sign in to comment.