tint/resolver: Allow texture 'offset' to be const-expr

This allows the value to be declared in a `const` expression, and to use arithmetic.

Fixed: tint:1636
Change-Id: Ie641a9d4183429c79c91605cd4df78f569be3579
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/105623
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
Commit-Queue: Ben Clayton <bclayton@google.com>
This commit is contained in:
Ben Clayton
2022-10-13 17:06:29 +00:00
committed by Dawn LUCI CQ
parent d5139b4463
commit 559a248233
87 changed files with 256 additions and 130 deletions

View File

@@ -260,8 +260,7 @@ TEST_P(BuiltinTextureConstExprArgValidationTest, Immediate) {
// a vector constructor.
bool is_vector = arg_to_replace->Is<ast::CallExpression>();
// Make the expression to be replaced, reachable. This keeps the resolver
// happy.
// Make the expression to be replaced, reachable. This keeps the resolver happy.
WrapInFunction(arg_to_replace);
arg_to_replace = expr(Source{{12, 34}}, *this);
@@ -310,13 +309,65 @@ TEST_P(BuiltinTextureConstExprArgValidationTest, GlobalConst) {
auto args = overload.args(this);
auto*& arg_to_replace = (param.position == Position::kFirst) ? args.Front() : args.Back();
// Make the expression to be replaced, reachable. This keeps the resolver
// happy.
// BuildTextureVariable() uses a Literal for scalars, and a CallExpression for
// a vector constructor.
bool is_vector = arg_to_replace->Is<ast::CallExpression>();
// Make the expression to be replaced, reachable. This keeps the resolver happy.
WrapInFunction(arg_to_replace);
arg_to_replace = Expr(Source{{12, 34}}, "G");
// Call the builtin with the constexpr argument replaced
// Call the builtin with the constant-expression argument replaced
Func("func", utils::Empty, ty.void_(),
utils::Vector{
CallStmt(Call(overload.function, args)),
},
utils::Vector{
Stage(ast::PipelineStage::kFragment),
});
if (expr.invalid_index == Constexpr::kValid) {
EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
EXPECT_FALSE(r()->Resolve());
std::stringstream err;
if (is_vector) {
err << "12:34 error: each component of the " << param.name
<< " argument must be at least " << param.min << " and at most " << param.max
<< ". " << param.name << " component " << expr.invalid_index << " is "
<< std::to_string(expr.values[static_cast<size_t>(expr.invalid_index)]);
} else {
err << "12:34 error: the " << param.name << " argument must be at least " << param.min
<< " and at most " << param.max << ". " << param.name << " is "
<< std::to_string(expr.values[static_cast<size_t>(expr.invalid_index)]);
}
EXPECT_EQ(r()->error(), err.str());
}
}
TEST_P(BuiltinTextureConstExprArgValidationTest, GlobalVar) {
auto& p = GetParam();
auto overload = std::get<0>(p);
auto param = std::get<1>(p);
auto expr = std::get<2>(p);
// Build the global texture and sampler variables
overload.BuildTextureVariable(this);
overload.BuildSamplerVariable(this);
// Build the module-scope var 'G' with the offset value
GlobalVar("G", expr({}, *this), ast::AddressSpace::kPrivate);
auto args = overload.args(this);
auto*& arg_to_replace = (param.position == Position::kFirst) ? args.Front() : args.Back();
// Make the expression to be replaced, reachable. This keeps the resolver happy.
WrapInFunction(arg_to_replace);
arg_to_replace = Expr(Source{{12, 34}}, "G");
// Call the builtin with the constant-expression argument replaced
Func("func", utils::Empty, ty.void_(),
utils::Vector{
CallStmt(Call(overload.function, args)),

View File

@@ -1663,50 +1663,29 @@ bool Validator::TextureBuiltinFunction(const sem::Call* call) const {
std::string name = sem::str(usage);
auto* arg = call->Arguments()[index];
if (auto values = arg->ConstantValue()) {
// Assert that the constant values are of the expected type.
if (!values->Type()->is_integer_scalar_or_vector()) {
TINT_ICE(Resolver, diagnostics_)
<< "failed to resolve '" + func_name + "' " << name << " parameter type";
return false;
}
// Currently const_expr is restricted to literals and type constructors.
// Check that that's all we have for the parameter.
bool is_const_expr = true;
ast::TraverseExpressions(
arg->Declaration(), diagnostics_, [&](const ast::Expression* e) {
if (e->IsAnyOf<ast::LiteralExpression, ast::CallExpression>()) {
return ast::TraverseAction::Descend;
}
is_const_expr = false;
return ast::TraverseAction::Stop;
});
if (is_const_expr) {
if (auto* vector = builtin->Parameters()[index]->Type()->As<sem::Vector>()) {
for (size_t i = 0; i < vector->Width(); i++) {
auto value = values->Index(i)->As<AInt>();
if (value < min || value > max) {
AddError("each component of the " + name +
" argument must be at least " + std::to_string(min) +
" and at most " + std::to_string(max) + ". " + name +
" component " + std::to_string(i) + " is " +
std::to_string(value),
arg->Declaration()->source);
return false;
}
}
} else {
auto value = values->As<AInt>();
if (auto* vector = values->Type()->As<sem::Vector>()) {
for (size_t i = 0; i < vector->Width(); i++) {
auto value = values->Index(i)->As<AInt>();
if (value < min || value > max) {
AddError("the " + name + " argument must be at least " +
AddError("each component of the " + name + " argument must be at least " +
std::to_string(min) + " and at most " + std::to_string(max) +
". " + name + " is " + std::to_string(value),
". " + name + " component " + std::to_string(i) + " is " +
std::to_string(value),
arg->Declaration()->source);
return false;
}
}
return true;
} else {
auto value = values->As<AInt>();
if (value < min || value > max) {
AddError("the " + name + " argument must be at least " + std::to_string(min) +
" and at most " + std::to_string(max) + ". " + name + " is " +
std::to_string(value),
arg->Declaration()->source);
return false;
}
}
return true;
}
AddError("the " + name + " argument must be a const-expression",
arg->Declaration()->source);