blob: 6cc6af1c5a2b631bcc746a410856472003cfcd5e [file] [log] [blame]
Austin Engcc2516a2023-10-17 20:57:54 +00001// Copyright 2023 The Dawn & Tint Authors
dan sinclair5ccafa42023-06-03 14:57:42 +00002//
Austin Engcc2516a2023-10-17 20:57:54 +00003// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
dan sinclair5ccafa42023-06-03 14:57:42 +00005//
Austin Engcc2516a2023-10-17 20:57:54 +00006// 1. Redistributions of source code must retain the above copyright notice, this
7// list of conditions and the following disclaimer.
dan sinclair5ccafa42023-06-03 14:57:42 +00008//
Austin Engcc2516a2023-10-17 20:57:54 +00009// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12//
13// 3. Neither the name of the copyright holder nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
dan sinclair5ccafa42023-06-03 14:57:42 +000027
dan sinclair97c37272023-07-24 17:11:53 +000028#include "src/tint/lang/core/ir/validator.h"
dan sinclair5ccafa42023-06-03 14:57:42 +000029
Ben Claytonc951b862024-05-22 07:36:00 +000030#include <algorithm>
Ben Clayton7711bfc2024-03-28 17:38:16 +000031#include <cstdint>
dan sinclairfbc4ce72024-10-21 19:25:39 +000032#include <functional>
dan sinclairb9846432023-06-07 21:52:57 +000033#include <memory>
dan sinclaircff18f32023-06-05 21:36:29 +000034#include <string>
Ben Claytonc951b862024-05-22 07:36:00 +000035#include <string_view>
dan sinclair5ccafa42023-06-03 14:57:42 +000036#include <utility>
37
Ben Clayton6a56e272023-09-06 02:01:07 +000038#include "src/tint/lang/core/intrinsic/table.h"
dan sinclair97c37272023-07-24 17:11:53 +000039#include "src/tint/lang/core/ir/access.h"
40#include "src/tint/lang/core/ir/binary.h"
41#include "src/tint/lang/core/ir/bitcast.h"
Ben Clayton833b8922024-05-01 21:07:24 +000042#include "src/tint/lang/core/ir/block_param.h"
dan sinclair97c37272023-07-24 17:11:53 +000043#include "src/tint/lang/core/ir/break_if.h"
Ben Clayton833b8922024-05-01 21:07:24 +000044#include "src/tint/lang/core/ir/constant.h"
dan sinclair97c37272023-07-24 17:11:53 +000045#include "src/tint/lang/core/ir/construct.h"
46#include "src/tint/lang/core/ir/continue.h"
Ben Clayton7b35ff12024-05-13 16:44:35 +000047#include "src/tint/lang/core/ir/control_instruction.h"
dan sinclair97c37272023-07-24 17:11:53 +000048#include "src/tint/lang/core/ir/convert.h"
49#include "src/tint/lang/core/ir/core_builtin_call.h"
dan sinclair650bc3e2024-06-11 23:26:53 +000050#include "src/tint/lang/core/ir/disassembler.h"
dan sinclair97c37272023-07-24 17:11:53 +000051#include "src/tint/lang/core/ir/discard.h"
52#include "src/tint/lang/core/ir/exit_if.h"
53#include "src/tint/lang/core/ir/exit_loop.h"
54#include "src/tint/lang/core/ir/exit_switch.h"
55#include "src/tint/lang/core/ir/function.h"
Ben Clayton833b8922024-05-01 21:07:24 +000056#include "src/tint/lang/core/ir/function_param.h"
dan sinclair97c37272023-07-24 17:11:53 +000057#include "src/tint/lang/core/ir/if.h"
Ben Clayton7b35ff12024-05-13 16:44:35 +000058#include "src/tint/lang/core/ir/instruction.h"
Ben Clayton833b8922024-05-01 21:07:24 +000059#include "src/tint/lang/core/ir/instruction_result.h"
dan sinclair97c37272023-07-24 17:11:53 +000060#include "src/tint/lang/core/ir/let.h"
61#include "src/tint/lang/core/ir/load.h"
62#include "src/tint/lang/core/ir/load_vector_element.h"
63#include "src/tint/lang/core/ir/loop.h"
James Pricec34ca5f2024-06-11 18:24:06 +000064#include "src/tint/lang/core/ir/member_builtin_call.h"
dan sinclair97c37272023-07-24 17:11:53 +000065#include "src/tint/lang/core/ir/multi_in_block.h"
66#include "src/tint/lang/core/ir/next_iteration.h"
Ryan Harrison2cbfbd42024-09-23 16:59:01 +000067#include "src/tint/lang/core/ir/referenced_module_vars.h"
dan sinclair97c37272023-07-24 17:11:53 +000068#include "src/tint/lang/core/ir/return.h"
69#include "src/tint/lang/core/ir/store.h"
70#include "src/tint/lang/core/ir/store_vector_element.h"
71#include "src/tint/lang/core/ir/switch.h"
72#include "src/tint/lang/core/ir/swizzle.h"
73#include "src/tint/lang/core/ir/terminate_invocation.h"
74#include "src/tint/lang/core/ir/unary.h"
75#include "src/tint/lang/core/ir/unreachable.h"
Ryan Harrison71f1b392024-08-08 13:27:46 +000076#include "src/tint/lang/core/ir/unused.h"
dan sinclair97c37272023-07-24 17:11:53 +000077#include "src/tint/lang/core/ir/user_call.h"
78#include "src/tint/lang/core/ir/var.h"
dan sinclair352f8c82023-07-21 00:40:07 +000079#include "src/tint/lang/core/type/bool.h"
Ryan Harrison2cbfbd42024-09-23 16:59:01 +000080#include "src/tint/lang/core/type/f32.h"
James Priced31f5e42024-08-01 04:47:58 +000081#include "src/tint/lang/core/type/i8.h"
Ben Clayton120ca8e2024-03-28 16:22:29 +000082#include "src/tint/lang/core/type/memory_view.h"
dan sinclair352f8c82023-07-21 00:40:07 +000083#include "src/tint/lang/core/type/pointer.h"
Ben Clayton120ca8e2024-03-28 16:22:29 +000084#include "src/tint/lang/core/type/reference.h"
Ben Clayton7711bfc2024-03-28 17:38:16 +000085#include "src/tint/lang/core/type/type.h"
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +000086#include "src/tint/lang/core/type/u32.h"
James Priced31f5e42024-08-01 04:47:58 +000087#include "src/tint/lang/core/type/u8.h"
dan sinclair352f8c82023-07-21 00:40:07 +000088#include "src/tint/lang/core/type/vector.h"
James Price17d6eda2023-07-21 15:08:02 +000089#include "src/tint/lang/core/type/void.h"
Ben Clayton833b8922024-05-01 21:07:24 +000090#include "src/tint/utils/containers/hashset.h"
Ben Clayton7b35ff12024-05-13 16:44:35 +000091#include "src/tint/utils/containers/predicates.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000092#include "src/tint/utils/containers/reverse.h"
Ben Clayton6a56e272023-09-06 02:01:07 +000093#include "src/tint/utils/containers/transform.h"
Ben Claytonc951b862024-05-22 07:36:00 +000094#include "src/tint/utils/diagnostic/diagnostic.h"
Ben Clayton833b8922024-05-01 21:07:24 +000095#include "src/tint/utils/ice/ice.h"
96#include "src/tint/utils/macros/defer.h"
Ben Claytonc951b862024-05-22 07:36:00 +000097#include "src/tint/utils/result/result.h"
98#include "src/tint/utils/rtti/castable.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000099#include "src/tint/utils/rtti/switch.h"
Ben Clayton425cb422024-05-01 15:43:55 +0000100#include "src/tint/utils/text/styled_text.h"
Ben Clayton710b62f2024-02-26 20:24:06 +0000101#include "src/tint/utils/text/text_style.h"
dan sinclair5ccafa42023-06-03 14:57:42 +0000102
James Pricedb46be12023-08-01 17:15:35 +0000103/// If set to 1 then the Tint will dump the IR when validating.
104#define TINT_DUMP_IR_WHEN_VALIDATING 0
105#if TINT_DUMP_IR_WHEN_VALIDATING
106#include <iostream>
James Price2ee45462024-05-03 17:37:14 +0000107#include "src/tint/utils/text/styled_text_printer.h"
James Pricedb46be12023-08-01 17:15:35 +0000108#endif
109
dan sinclairce6dffe2023-08-14 21:01:40 +0000110using namespace tint::core::fluent_types; // NOLINT
111
dan sinclair6f138fe2023-08-15 21:29:34 +0000112namespace tint::core::ir {
dan sinclair5ccafa42023-06-03 14:57:42 +0000113
dan sinclairfbc4ce72024-10-21 19:25:39 +0000114struct ValidatedType {
115 const type::Type* ty;
116 Capabilities caps;
117};
118
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000119namespace {
120
Ben Clayton7b35ff12024-05-13 16:44:35 +0000121/// @returns the parent block of @p block
122const Block* ParentBlockOf(const Block* block) {
123 if (auto* parent = block->Parent()) {
124 return parent->Block();
125 }
126 return nullptr;
127}
128
129/// @returns true if @p block directly or transitively holds the instruction @p inst
130bool TransitivelyHolds(const Block* block, const Instruction* inst) {
131 for (auto* b = inst->Block(); b; b = ParentBlockOf(b)) {
132 if (b == block) {
133 return true;
134 }
135 }
136 return false;
137}
138
Ryan Harrisone5c5c652024-10-01 22:23:03 +0000139/// @returns true if @p attr contains both a location and builtin decoration
Ryan Harrisonceb3a5c52024-09-26 21:39:25 +0000140bool HasLocationAndBuiltin(const tint::core::IOAttributes& attr) {
141 return attr.builtin.has_value() && attr.location.has_value();
142}
143
Ryan Harrison6a8b16c2024-10-09 21:49:35 +0000144/// @returns true if @p attr contains one of location or builtin decoration
145bool HasLocationOrBuiltin(const tint::core::IOAttributes& attr) {
146 return attr.builtin.has_value() || attr.location.has_value();
Ryan Harrisona7b805c2024-10-04 18:02:16 +0000147}
148
Ryan Harrisonee7a5b12024-10-02 22:03:03 +0000149/// @return true if @param attr does not have invariant decoration or if it also has position
150/// decoration
151bool InvariantOnlyIfAlsoPosition(const tint::core::IOAttributes& attr) {
152 return !attr.invariant || attr.builtin == BuiltinValue::kPosition;
153}
154
Ryan Harrisone5c5c652024-10-01 22:23:03 +0000155/// @returns true if @p ty meets the basic function parameter rules (i.e. one of constructible,
156/// pointer, sampler or texture).
157///
158/// Note: Does not handle corner cases like if certain capabilities are
159/// enabled.
160bool IsValidFunctionParamType(const core::type::Type* ty) {
161 return ty->IsConstructible() || ty->Is<type::Pointer>() || ty->Is<type::Texture>() ||
162 ty->Is<type::Sampler>();
163}
164
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +0000165/// @returns true if @p ty is a non-struct and decorated with @builtin(position), or if it is a
166/// struct and one of its members is decorated, otherwise false.
167/// @param attr attributes attached to data
168/// @param ty type of the data being tested
169bool IsPositionPresent(const IOAttributes& attr, const core::type::Type* ty) {
170 if (auto* ty_struct = ty->As<core::type::Struct>()) {
171 for (const auto* mem : ty_struct->Members()) {
172 if (mem->Attributes().builtin == BuiltinValue::kPosition) {
173 return true;
174 }
175 }
176 return false;
177 }
178
179 return attr.builtin == BuiltinValue::kPosition;
180}
181
Ryan Harrison6a8b16c2024-10-09 21:49:35 +0000182/// Utility for running checks on attributes.
183/// If the type that the attributes are attached to is a struct, the check is run over the members,
184/// otherwise it run on the attributes directly.
185///
186/// @param msg_anchor what to associate errors with, i.e. the 'foo' of AddError(foo)
187/// @param ty_attr the directly attached attributes
188/// @param ty the type of the thing that the attributes are attached to
189/// @param is_not_struct_impl has the signature 'void(const MSG_ANCHOR*, const IOAttributes&)' and
190/// is called when @p ty is not a struct
191/// @param is_struct_impl has the signature 'void(const MSG_ANCHOR*, const IOAttributes&)' and is
192/// called when @p ty is a struct
193template <typename MSG_ANCHOR, typename IS_NOT_STRUCT, typename IS_STRUCT>
194void CheckIOAttributes(const MSG_ANCHOR* msg_anchor,
195 const IOAttributes& ty_attr,
196 const core::type::Type* ty,
197 IS_NOT_STRUCT&& is_not_struct_impl,
198 IS_STRUCT&& is_struct_impl) {
199 if (auto* ty_struct = ty->As<core::type::Struct>()) {
200 for (const auto* mem : ty_struct->Members()) {
201 is_struct_impl(msg_anchor, mem->Attributes());
202 }
203 } else {
204 is_not_struct_impl(msg_anchor, ty_attr);
205 }
206}
207
208/// Helper for calling CheckIOAttributes on a function return
209/// @param func function whose return is to be tested
210/// See @ref CheckIOAttributes for more details
211template <typename IS_NOT_STRUCT, typename IS_STRUCT>
212void CheckFunctionReturnAttributes(const Function* func,
213 IS_NOT_STRUCT&& is_not_struct_impl,
214 IS_STRUCT&& is_struct_impl) {
215 CheckIOAttributes(func, func->ReturnAttributes(), func->ReturnType(),
216 std::forward<IS_NOT_STRUCT>(is_not_struct_impl),
217 std::forward<IS_STRUCT>(is_struct_impl));
218}
219
220/// Helper for calling CheckIOAttributes on a function param
221/// @param param function param to be tested
222/// See @ref CheckIOAttributes for more details
223template <typename IS_NOT_STRUCT, typename IS_STRUCT>
224void CheckFunctionParamAttributes(const FunctionParam* param,
225 IS_NOT_STRUCT&& is_not_struct_impl,
226 IS_STRUCT&& is_struct_impl) {
227 CheckIOAttributes(param, param->Attributes(), param->Type(),
228 std::forward<IS_NOT_STRUCT>(is_not_struct_impl),
229 std::forward<IS_STRUCT>(is_struct_impl));
230}
231
232/// Utility for running checks on attributes and type.
233/// If the type that the attributes are attached to is a struct, the check is run over the members,
234/// otherwise it run on the attributes directly.
235///
236/// @param msg_anchor what to associate errors with, i.e. the 'foo' of AddError(foo)
237/// @param ty_attr the directly attached attributes
238/// @param ty the type of the thing that the attributes are attached to
239/// @param is_not_struct_impl has the signature 'void(const MSG_ANCHOR*, const IOAttributes&, const
240/// core::type::Type* ty)'
241/// and is called when @p ty is not a struct
242/// @param is_struct_impl has the signature 'void(const MSG_ANCHOR*, const IOAttributes&, const
243/// core::type::Type* ty)'
244/// and is called when @p ty is a struct
245template <typename MSG_ANCHOR, typename IS_NOT_STRUCT, typename IS_STRUCT>
246void CheckIOAttributesAndType(const MSG_ANCHOR* msg_anchor,
247 const IOAttributes& ty_attr,
248 const core::type::Type* ty,
249 IS_NOT_STRUCT&& is_not_struct_impl,
250 IS_STRUCT&& is_struct_impl) {
251 if (auto* ty_struct = ty->As<core::type::Struct>()) {
252 for (const auto* mem : ty_struct->Members()) {
253 is_struct_impl(msg_anchor, mem->Attributes(), mem->Type());
254 }
255 } else {
256 is_not_struct_impl(msg_anchor, ty_attr, ty);
257 }
258}
259
Ryan Harrison96ea4742024-10-29 18:02:45 +0000260// Wrapper for CheckIOAttributesAndType, when the struct and non-struct impl are the same
261/// See @ref IOAttributesAndType for more details
262template <typename MSG_ANCHOR, typename IMPL>
263void CheckIOAttributesAndType(const MSG_ANCHOR* msg_anchor,
264 const IOAttributes& ty_attr,
265 const core::type::Type* ty,
266 IMPL&& impl) {
267 CheckIOAttributesAndType(msg_anchor, ty_attr, ty, impl, impl);
268}
269
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +0000270/// Helper for calling IOAttributesAndType on a function param
271/// @param param function param to be tested
272/// See @ref IOAttributesAndType for more details
273template <typename IS_NOT_STRUCT, typename IS_STRUCT>
274void CheckFunctionParamAttributesAndType(const FunctionParam* param,
275 IS_NOT_STRUCT&& is_not_struct_impl,
276 IS_STRUCT&& is_struct_impl) {
277 CheckIOAttributesAndType(param, param->Attributes(), param->Type(),
278 std::forward<IS_NOT_STRUCT>(is_not_struct_impl),
279 std::forward<IS_STRUCT>(is_struct_impl));
280}
281
Ryan Harrison96ea4742024-10-29 18:02:45 +0000282/// Helper for calling IOAttributesAndType on a function param
283/// @param param function param to be tested
284/// See @ref IOAttributesAndType for more details
285template <typename IMPL>
286void CheckFunctionParamAttributesAndType(const FunctionParam* param, IMPL&& impl) {
287 CheckIOAttributesAndType(param, param->Attributes(), param->Type(), std::forward<IMPL>(impl));
288}
289
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +0000290/// Helper for calling IOAttributesAndType on a function return
291/// @param func function's return to be tested
292/// See @ref IOAttributesAndType for more details
Ryan Harrison6a8b16c2024-10-09 21:49:35 +0000293template <typename IS_NOT_STRUCT, typename IS_STRUCT>
294void CheckFunctionReturnAttributesAndType(const Function* func,
295 IS_NOT_STRUCT&& is_not_struct_impl,
296 IS_STRUCT&& is_struct_impl) {
297 CheckIOAttributesAndType(func, func->ReturnAttributes(), func->ReturnType(),
298 std::forward<IS_NOT_STRUCT>(is_not_struct_impl),
299 std::forward<IS_STRUCT>(is_struct_impl));
300}
301
Ryan Harrison96ea4742024-10-29 18:02:45 +0000302/// Helper for calling IOAttributesAndType on a function return
303/// @param func function's return to be tested
304/// See @ref IOAttributesAndType for more details
305template <typename IMPL>
306void CheckFunctionReturnAttributesAndType(const Function* func, IMPL&& impl) {
307 CheckIOAttributesAndType(func, func->ReturnAttributes(), func->ReturnType(),
308 std::forward<IMPL>(impl));
309}
310
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +0000311/// A BuiltinChecker is the interface used to check that a usage of a builtin attribute meets the
312/// basic spec rules, i.e. correct shader stage, data type, and IO direction.
313/// It does not test more sophisticated rules like location and builtins being mutually exclusive or
314/// the correct capabilities are enabled.
315struct BuiltinChecker {
316 /// User friendly name to print in logging messages
317 const char* name;
318
319 /// What type of entry point is this builtin legal for
320 EnumSet<Function::PipelineStage> stages;
321
322 enum IODirection : uint8_t { kInput, kOutput };
323 /// Is this expected to be a param going into the entry point or a result coming out
324 IODirection direction;
325
326 /// Implements logic for checking if the given type is valid or not
327 using TypeCheckFn = bool(const core::type::Type* type);
328
329 /// @see #TypeCheckFn
330 TypeCheckFn* const type_check;
331
332 /// Message that should logged if the type check fails
333 const char* type_error;
334};
335
336std::string_view ToString(BuiltinChecker::IODirection value) {
337 switch (value) {
338 case BuiltinChecker::IODirection::kInput:
339 return "input";
340 case BuiltinChecker::IODirection::kOutput:
341 return "output";
342 }
343 TINT_ICE() << "Unknown enum passed to ToString(BuiltinChecker::IODirection)";
344}
345
346constexpr BuiltinChecker kPointSizeChecker{
347 /* name */ "__point_size",
348 /* stages */ EnumSet<Function::PipelineStage>(Function::PipelineStage::kVertex),
349 /* direction */ BuiltinChecker::IODirection::kOutput,
350 /* type_check */ [](const core::type::Type* ty) -> bool { return ty->Is<core::type::F32>(); },
351 /* type_error */ "__point_size must be a f32",
352};
353
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +0000354constexpr BuiltinChecker kFragDepthChecker{
355 /* name */ "frag_depth",
356 /* stages */ EnumSet<Function::PipelineStage>(Function::PipelineStage::kFragment),
357 /* direction */ BuiltinChecker::IODirection::kOutput,
358 /* type_check */ [](const core::type::Type* ty) -> bool { return ty->Is<core::type::F32>(); },
359 /* type_error */ "frag_depth must be a f32",
360};
361
362constexpr BuiltinChecker kFrontFacingChecker{
363 /* name */ "front_facing",
364 /* stages */ EnumSet<Function::PipelineStage>(Function::PipelineStage::kFragment),
365 /* direction */ BuiltinChecker::IODirection::kInput,
366 /* type_check */ [](const core::type::Type* ty) -> bool { return ty->Is<core::type::Bool>(); },
367 /* type_error */ "front_facing must be a bool",
368};
369
370constexpr BuiltinChecker kGlobalInvocationIdChecker{
371 /* name */ "global_invocation_id",
372 /* stages */ EnumSet<Function::PipelineStage>(Function::PipelineStage::kCompute),
373 /* direction */ BuiltinChecker::IODirection::kInput,
374 /* type_check */
375 [](const core::type::Type* ty) -> bool {
376 return ty->IsUnsignedIntegerVector() && ty->Elements().count == 3;
377 },
378 /* type_error */ "global_invocation_id must be an vec3<u32>",
379};
380
381constexpr BuiltinChecker kInstanceIndexChecker{
382 /* name */ "instance_index",
383 /* stages */ EnumSet<Function::PipelineStage>(Function::PipelineStage::kVertex),
384 /* direction */ BuiltinChecker::IODirection::kInput,
385 /* type_check */ [](const core::type::Type* ty) -> bool { return ty->Is<core::type::U32>(); },
386 /* type_error */ "instance_index must be an u32",
387};
388
389constexpr BuiltinChecker kLocalInvocationIdChecker{
390 /* name */ "local_invocation_id",
391 /* stages */ EnumSet<Function::PipelineStage>(Function::PipelineStage::kCompute),
392 /* direction */ BuiltinChecker::IODirection::kInput,
393 /* type_check */
394 [](const core::type::Type* ty) -> bool {
395 return ty->IsUnsignedIntegerVector() && ty->Elements().count == 3;
396 },
397 /* type_error */ "local_invocation_id must be an vec3<u32>",
398};
399
400constexpr BuiltinChecker kLocalInvocationIndexChecker{
401 /* name */ "local_invocation_index",
402 /* stages */ EnumSet<Function::PipelineStage>(Function::PipelineStage::kCompute),
403 /* direction */ BuiltinChecker::IODirection::kInput,
404 /* type_check */ [](const core::type::Type* ty) -> bool { return ty->Is<core::type::U32>(); },
405 /* type_error */ "local_invocation_index must be an u32",
406};
407
408constexpr BuiltinChecker kNumWorkgroupsChecker{
409 /* name */ "num_workgroups",
410 /* stages */ EnumSet<Function::PipelineStage>(Function::PipelineStage::kCompute),
411 /* direction */ BuiltinChecker::IODirection::kInput,
412 /* type_check */
413 [](const core::type::Type* ty) -> bool {
414 return ty->IsUnsignedIntegerVector() && ty->Elements().count == 3;
415 },
416 /* type_error */ "num_workgroups must be an vec3<u32>",
417};
418
419constexpr BuiltinChecker kSampleIndexChecker{
420 /* name */ "sample_index",
421 /* stages */ EnumSet<Function::PipelineStage>(Function::PipelineStage::kFragment),
422 /* direction */ BuiltinChecker::IODirection::kInput,
423 /* type_check */ [](const core::type::Type* ty) -> bool { return ty->Is<core::type::U32>(); },
424 /* type_error */ "sample_index must be an u32",
425};
426
427constexpr BuiltinChecker kSubgroupInvocationIdChecker{
428 /* name */ "subgroup_invocation_id",
429 /* stages */
430 EnumSet<Function::PipelineStage>(Function::PipelineStage::kFragment,
431 Function::PipelineStage::kCompute),
432 /* direction */ BuiltinChecker::IODirection::kInput,
433 /* type_check */ [](const core::type::Type* ty) -> bool { return ty->Is<core::type::U32>(); },
434 /* type_error */ "subgroup_invocation_id must be an u32",
435};
436
437constexpr BuiltinChecker kSubgroupSizeChecker{
438 /* name */ "subgroup_size",
439 /* stages */
440 EnumSet<Function::PipelineStage>(Function::PipelineStage::kFragment,
441 Function::PipelineStage::kCompute),
442 /* direction */ BuiltinChecker::IODirection::kInput,
443 /* type_check */ [](const core::type::Type* ty) -> bool { return ty->Is<core::type::U32>(); },
444 /* type_error */ "subgroup_size must be an u32",
445};
446
447constexpr BuiltinChecker kVertexIndexChecker{
448 /* name */ "vertex_index",
449 /* stages */ EnumSet<Function::PipelineStage>(Function::PipelineStage::kVertex),
450 /* direction */ BuiltinChecker::IODirection::kInput,
451 /* type_check */ [](const core::type::Type* ty) -> bool { return ty->Is<core::type::U32>(); },
452 /* type_error */ "vertex_index must be an u32",
453};
454
455constexpr BuiltinChecker kWorkgroupIdChecker{
456 /* name */ "workgroup_id",
457 /* stages */ EnumSet<Function::PipelineStage>(Function::PipelineStage::kCompute),
458 /* direction */ BuiltinChecker::IODirection::kInput,
459 /* type_check */
460 [](const core::type::Type* ty) -> bool {
461 return ty->IsUnsignedIntegerVector() && ty->Elements().count == 3;
462 },
463 /* type_error */ "workgroup_id must be an vec3<u32>",
464};
465
466/// @returns an appropriate BuiltInCheck for @p builtin, ICEs when one isn't defined
467const BuiltinChecker& BuiltinCheckerFor(BuiltinValue builtin) {
468 switch (builtin) {
469 case BuiltinValue::kPointSize:
470 return kPointSizeChecker;
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +0000471 case BuiltinValue::kFragDepth:
472 return kFragDepthChecker;
473 case BuiltinValue::kFrontFacing:
474 return kFrontFacingChecker;
475 case BuiltinValue::kGlobalInvocationId:
476 return kGlobalInvocationIdChecker;
477 case BuiltinValue::kInstanceIndex:
478 return kInstanceIndexChecker;
479 case BuiltinValue::kLocalInvocationId:
480 return kLocalInvocationIdChecker;
481 case BuiltinValue::kLocalInvocationIndex:
482 return kLocalInvocationIndexChecker;
483 case BuiltinValue::kNumWorkgroups:
484 return kNumWorkgroupsChecker;
485 case BuiltinValue::kSampleIndex:
486 return kSampleIndexChecker;
487 case BuiltinValue::kSubgroupInvocationId:
488 return kSubgroupInvocationIdChecker;
489 case BuiltinValue::kSubgroupSize:
490 return kSubgroupSizeChecker;
491 case BuiltinValue::kVertexIndex:
492 return kVertexIndexChecker;
493 case BuiltinValue::kWorkgroupId:
494 return kWorkgroupIdChecker;
495 case BuiltinValue::kPosition:
496 TINT_ICE() << "BuiltinValue::kPosition requires special handling, so does not have a "
497 "checker defined";
498 case BuiltinValue::kSampleMask:
499 TINT_ICE() << "BuiltinValue::kSampleMask requires special handling, so does not have a "
500 "checker defined";
501 default:
502 TINT_ICE() << builtin << " is does not have a checker defined for it";
503 }
504}
505
506/// Validates the basic spec rules for @builtin(position) usage
507/// @param stage the shader stage the builtin is being used
508/// @param is_input the IO direction of usage, true if input, false if output
509/// @param ty the data type being decorated by the builtin
510/// @returns Success if a valid usage, or reason for invalidity in Failure
511Result<SuccessType, std::string> ValidatePositionBuiltIn(Function::PipelineStage stage,
512 bool is_input,
513 const core::type::Type* ty) {
514 if (stage != Function::PipelineStage::kVertex && stage != Function::PipelineStage::kFragment) {
515 return std::string("position must be used in a fragment or vertex shader entry point");
516 }
517
518 if (stage == Function::PipelineStage::kVertex && is_input) {
519 return std::string("position must be an output for a vertex entry point");
520 }
521
522 if (stage == Function::PipelineStage::kFragment && !is_input) {
523 return std::string("position must be an input for a fragment entry point");
524 }
525
526 if (!ty->IsFloatVector() || ty->Elements().count != 4 ||
527 !ty->Element(0)->Is<core::type::F32>()) {
528 return std::string("position must be an vec4<f32>");
529 }
530
531 return Success;
532}
533
534/// Validates the basic spec rules for @builtin(sample_mask) usage
535/// @param stage the shader stage the builtin is being used
536/// @param ty the data type being decorated by the builtin
537/// @returns Success if a valid usage, or reason for invalidity in Failure
538Result<SuccessType, std::string> ValidateSampleMaskBuiltIn(Function::PipelineStage stage,
539 const core::type::Type* ty) {
540 if (stage != Function::PipelineStage::kFragment) {
541 return std::string("sample_mask must be used in a fragment entry point");
542 }
543
544 if (!ty->Is<core::type::U32>()) {
545 return std::string("sample_mask must be an u32");
546 }
547
548 return Success;
549}
550
Antonio Maiorano66652df2024-10-22 23:20:27 +0000551/// Validates the basic spec rules for @builtin(clip_distance) usage
552/// @param stage the shader stage the builtin is being used
553/// @param is_input the IO direction of usage, true if input, false if output
554/// @param capabilities the optional capabilities that are allowed
555/// @param ty the data type being decorated by the builtin
556/// @returns Success if a valid usage, or reason for invalidity in Failure
557Result<SuccessType, std::string> ValidateBuiltinClipDistances(Function::PipelineStage stage,
558 bool is_input,
559 const Capabilities& capabilities,
560 const core::type::Type* ty) {
561 if (stage != Function::PipelineStage::kVertex) {
562 return std::string("clip_distances must be used in a vertex shader entry point");
563 }
564
565 if (is_input) {
566 return std::string("clip_distances must be an output of a shader entry point");
567 }
568
569 auto is_valid_array = [&] {
570 const auto elems = ty->Elements();
571 return elems.type && elems.type->Is<core::type::F32>() && elems.count <= 8;
572 };
573
574 if (capabilities.Contains(Capability::kAllowClipDistancesOnF32)) {
575 if (!ty->Is<core::type::F32>() && !is_valid_array()) {
576 return std::string("clip_distances must be an f32 or an array<f32, N>, where N <= 8");
577 }
578 } else if (!is_valid_array()) {
579 return std::string("clip_distances must be an array<f32, N>, where N <= 8");
580 }
581
582 return Success;
583}
584
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +0000585/// Validates the basic spec rules for builtin usage
586/// @param builtin the builtin to test
587/// @param stage the shader stage the builtin is being used
588/// @param is_input the IO direction of usage, true if input, false if output
589/// @param ty the data type being decorated by the builtin
590/// @returns Success if a valid usage, or reason for invalidity in Failure
591Result<SuccessType, std::string> ValidateBuiltIn(BuiltinValue builtin,
592 Function::PipelineStage stage,
593 bool is_input,
Antonio Maiorano66652df2024-10-22 23:20:27 +0000594 const Capabilities& capabilities,
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +0000595 const core::type::Type* ty) {
596 // This is not an entry point function, either it is dead code and thus never called, or any
597 // issues will be detected when validating the calling entry point.
598 if (stage == Function::PipelineStage::kUndefined) {
599 return Success;
600 }
601
602 // Some builtins have multiple contexts that they are valid in, so have special handling
603 // instead of making the checker/lookup table more complex.
604 switch (builtin) {
605 case BuiltinValue::kPosition:
606 return ValidatePositionBuiltIn(stage, is_input, ty);
607 case BuiltinValue::kSampleMask:
608 return ValidateSampleMaskBuiltIn(stage, ty);
Antonio Maiorano66652df2024-10-22 23:20:27 +0000609 case BuiltinValue::kClipDistances:
610 return ValidateBuiltinClipDistances(stage, is_input, capabilities, ty);
611 default:
612 break;
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +0000613 }
614
615 const auto& checker = BuiltinCheckerFor(builtin);
616 std::stringstream msg;
617 if (!checker.stages.Contains(stage)) {
618 auto stages_size = checker.stages.Size();
619 switch (stages_size) {
620 case 1:
621 msg << checker.name << " must be used in a " << ToString(*checker.stages.begin())
622 << " shader entry point";
623 break;
624 case 2:
625 msg << checker.name << " must be used in a " << ToString(*checker.stages.begin())
626 << " or " << ToString(*(++checker.stages.begin())) << " shader entry point";
627 break;
628 default:
629 TINT_ICE() << "Unexpected number of stages set, " << stages_size;
630 }
631 return msg.str();
632 }
633
634 auto io_direction =
635 is_input ? BuiltinChecker::IODirection::kInput : BuiltinChecker::IODirection::kOutput;
636 if (io_direction != checker.direction) {
637 msg << checker.name << " must be an " << ToString(checker.direction)
638 << " of a shader entry point";
639 return msg.str();
640 }
641
642 if (!checker.type_check(ty)) {
643 return std::string(checker.type_error);
644 }
645
646 return Success;
647}
648
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000649/// The core IR validator.
650class Validator {
651 public:
652 /// Create a core validator
653 /// @param mod the module to be validated
James Price03ecbbf2024-01-17 17:01:30 +0000654 /// @param capabilities the optional capabilities that are allowed
Ben Clayton7711bfc2024-03-28 17:38:16 +0000655 explicit Validator(const Module& mod, Capabilities capabilities);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000656
657 /// Destructor
658 ~Validator();
659
660 /// Runs the validator over the module provided during construction
Ben Clayton16fb2542023-09-25 11:43:19 +0000661 /// @returns success or failure
662 Result<SuccessType> Run();
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000663
Ben Clayton35db5b52024-05-01 21:07:24 +0000664 private:
Ryan Harrison5183d1a2024-08-13 21:49:44 +0000665 /// Runs validation to confirm the structural soundness of the module.
666 /// Also runs any validation that is not dependent on the entire module being
667 /// sound and sets up data structures for later checks.
668 void RunStructuralSoundnessChecks();
669
670 /// Checks that there are no orphaned instructions
671 /// Depends on CheckStructuralSoundness() having previously been run
672 void CheckForOrphanedInstructions();
673
674 /// Checks that there are no discards called by non-fragment entrypoints
675 /// Depends on CheckStructuralSoundness() having previously been run
676 void CheckForNonFragmentDiscards();
677
Ben Clayton35db5b52024-05-01 21:07:24 +0000678 /// @returns the IR disassembly, performing a disassemble if this is the first call.
dan sinclair650bc3e2024-06-11 23:26:53 +0000679 ir::Disassembler& Disassemble();
Ben Clayton35db5b52024-05-01 21:07:24 +0000680
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000681 /// Adds an error for the @p inst and highlights the instruction in the disassembly
682 /// @param inst the instruction
Ben Claytonc27315a2024-02-26 20:24:06 +0000683 /// @returns the diagnostic
684 diag::Diagnostic& AddError(const Instruction* inst);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000685
686 /// Adds an error for the @p inst operand at @p idx and highlights the operand in the
687 /// disassembly
Ben Clayton425cb422024-05-01 15:43:55 +0000688 /// @param inst the instruction
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000689 /// @param idx the operand index
Ben Claytonc27315a2024-02-26 20:24:06 +0000690 /// @returns the diagnostic
691 diag::Diagnostic& AddError(const Instruction* inst, size_t idx);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000692
693 /// Adds an error for the @p inst result at @p idx and highlgihts the result in the disassembly
694 /// @param inst the instruction
695 /// @param idx the result index
Ben Claytonc27315a2024-02-26 20:24:06 +0000696 /// @returns the diagnostic
697 diag::Diagnostic& AddResultError(const Instruction* inst, size_t idx);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000698
James Price21517e42024-04-24 17:36:34 +0000699 /// Adds an error for the @p block and highlights the block header in the disassembly
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000700 /// @param blk the block
Ben Claytonc27315a2024-02-26 20:24:06 +0000701 /// @returns the diagnostic
702 diag::Diagnostic& AddError(const Block* blk);
703
James Price4f491bf2024-04-24 21:15:04 +0000704 /// Adds an error for the @p param and highlights the parameter in the disassembly
705 /// @param param the parameter
706 /// @returns the diagnostic
707 diag::Diagnostic& AddError(const BlockParam* param);
708
James Price21517e42024-04-24 17:36:34 +0000709 /// Adds an error for the @p func and highlights the function in the disassembly
710 /// @param func the function
711 /// @returns the diagnostic
712 diag::Diagnostic& AddError(const Function* func);
713
714 /// Adds an error for the @p param and highlights the parameter in the disassembly
715 /// @param param the parameter
716 /// @returns the diagnostic
717 diag::Diagnostic& AddError(const FunctionParam* param);
718
Ben Claytonc27315a2024-02-26 20:24:06 +0000719 /// Adds an error the @p block and highlights the block header in the disassembly
720 /// @param src the source lines to highlight
721 /// @returns the diagnostic
722 diag::Diagnostic& AddError(Source src);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000723
724 /// Adds a note to @p inst and highlights the instruction in the disassembly
725 /// @param inst the instruction
Ben Claytonc27315a2024-02-26 20:24:06 +0000726 diag::Diagnostic& AddNote(const Instruction* inst);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000727
James Pricee5380b72024-04-24 20:52:59 +0000728 /// Adds a note to @p func and highlights the function in the disassembly
729 /// @param func the function
730 diag::Diagnostic& AddNote(const Function* func);
731
Ben Clayton3aebf9e2024-05-02 10:01:02 +0000732 /// Adds a note to @p inst for operand @p idx and highlights the operand in the disassembly
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000733 /// @param inst the instruction
734 /// @param idx the operand index
Ben Clayton3aebf9e2024-05-02 10:01:02 +0000735 diag::Diagnostic& AddOperandNote(const Instruction* inst, size_t idx);
736
737 /// Adds a note to @p inst for result @p idx and highlights the result in the disassembly
738 /// @param inst the instruction
739 /// @param idx the result index
740 diag::Diagnostic& AddResultNote(const Instruction* inst, size_t idx);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000741
742 /// Adds a note to @p blk and highlights the block in the disassembly
743 /// @param blk the block
Ben Claytonc27315a2024-02-26 20:24:06 +0000744 diag::Diagnostic& AddNote(const Block* blk);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000745
746 /// Adds a note to the diagnostics
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000747 /// @param src the source lines to highlight
Ben Claytonc27315a2024-02-26 20:24:06 +0000748 diag::Diagnostic& AddNote(Source src = {});
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000749
Ben Claytonc951b862024-05-22 07:36:00 +0000750 /// Adds a note to the diagnostics highlighting where the value instruction or block is
751 /// declared, if it has a source location.
752 /// @param decl the value instruction or block
753 void AddDeclarationNote(const CastableBase* decl);
754
755 /// Adds a note to the diagnostics highlighting where the block is declared, if it has a source
Ben Clayton3aebf9e2024-05-02 10:01:02 +0000756 /// location.
Ben Claytonc951b862024-05-22 07:36:00 +0000757 /// @param block the block
758 void AddDeclarationNote(const Block* block);
759
760 /// Adds a note to the diagnostics highlighting where the block parameter is declared, if it
761 /// has a source location.
762 /// @param param the block parameter
763 void AddDeclarationNote(const BlockParam* param);
764
765 /// Adds a note to the diagnostics highlighting where the function is declared, if it has a
766 /// source location.
767 /// @param fn the function
768 void AddDeclarationNote(const Function* fn);
769
770 /// Adds a note to the diagnostics highlighting where the function parameter is declared, if it
771 /// has a source location.
772 /// @param param the function parameter
773 void AddDeclarationNote(const FunctionParam* param);
774
775 /// Adds a note to the diagnostics highlighting where the instruction is declared, if it has a
776 /// source location.
777 /// @param inst the inst
778 void AddDeclarationNote(const Instruction* inst);
779
780 /// Adds a note to the diagnostics highlighting where instruction result was declared, if it has
781 /// a source location.
782 /// @param res the res
783 void AddDeclarationNote(const InstructionResult* res);
784
Ryan Harrison8570a642024-08-15 21:33:38 +0000785 /// @param decl the type, value, instruction or block to get the name for
Ben Claytonc951b862024-05-22 07:36:00 +0000786 /// @returns the styled name for the given value, instruction or block
787 StyledText NameOf(const CastableBase* decl);
Ben Clayton3aebf9e2024-05-02 10:01:02 +0000788
Ryan Harrison8570a642024-08-15 21:33:38 +0000789 // @param ty the type to get the name for
790 /// @returns the styled name for the given type
791 StyledText NameOf(const type::Type* ty);
792
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000793 /// @param v the value to get the name for
Ben Claytonc951b862024-05-22 07:36:00 +0000794 /// @returns the styled name for the given value
Ben Clayton35db5b52024-05-01 21:07:24 +0000795 StyledText NameOf(const Value* v);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000796
Ben Claytonc951b862024-05-22 07:36:00 +0000797 /// @param inst the instruction to get the name for
798 /// @returns the styled name for the given instruction
799 StyledText NameOf(const Instruction* inst);
800
801 /// @param block the block to get the name for
802 /// @returns the styled name for the given block
803 StyledText NameOf(const Block* block);
804
Ryan Harrison54b6c1c2024-07-29 20:50:19 +0000805 /// Checks the given result is not null and its type is not null
Ryan Harrisonad423942024-07-12 00:48:07 +0000806 /// @param inst the instruction
807 /// @param idx the result index
808 /// @returns true if the result is not null
Ryan Harrison54b6c1c2024-07-29 20:50:19 +0000809 bool CheckResult(const Instruction* inst, size_t idx);
Ryan Harrisonad423942024-07-12 00:48:07 +0000810
Ryan Harrison71f1b392024-08-08 13:27:46 +0000811 /// Checks the results (and their types) for @p inst are not null. If count is specified then
812 /// number of results is checked to be exact.
Ryan Harrisonad423942024-07-12 00:48:07 +0000813 /// @param inst the instruction
814 /// @param count the number of results to check
815 /// @returns true if the results count is as expected and none are null
Ryan Harrison71f1b392024-08-08 13:27:46 +0000816 bool CheckResults(const ir::Instruction* inst, std::optional<size_t> count);
Ryan Harrisonad423942024-07-12 00:48:07 +0000817
Ryan Harrison54b6c1c2024-07-29 20:50:19 +0000818 /// Checks the given operand is not null and its type is not null
Ryan Harrisonad423942024-07-12 00:48:07 +0000819 /// @param inst the instruction
820 /// @param idx the operand index
821 /// @returns true if the operand is not null
Ryan Harrison54b6c1c2024-07-29 20:50:19 +0000822 bool CheckOperand(const Instruction* inst, size_t idx);
Ryan Harrisonad423942024-07-12 00:48:07 +0000823
Ryan Harrison54b6c1c2024-07-29 20:50:19 +0000824 /// Checks the number of operands provided to @p inst and that none of them are null. Also
825 /// checks that the types for the operands are not null
Ryan Harrison244e32b2024-07-15 23:08:14 +0000826 /// @param inst the instruction
827 /// @param min_count the minimum number of operands to expect
828 /// @param max_count the maximum number of operands to expect, if not set, than only the minimum
829 /// number is checked.
830 /// @returns true if the number of operands is in the expected range and none are null
831 bool CheckOperands(const ir::Instruction* inst,
832 size_t min_count,
833 std::optional<size_t> max_count);
834
Ryan Harrison71f1b392024-08-08 13:27:46 +0000835 /// Checks the operands (and their types) for @p inst are not null. If count is specified then
836 /// number of operands is checked to be exact.
Ryan Harrisonad423942024-07-12 00:48:07 +0000837 /// @param inst the instruction
838 /// @param count the number of operands to check
839 /// @returns true if the operands count is as expected and none are null
Ryan Harrison71f1b392024-08-08 13:27:46 +0000840 bool CheckOperands(const ir::Instruction* inst, std::optional<size_t> count);
Ryan Harrisonad423942024-07-12 00:48:07 +0000841
Ryan Harrison244e32b2024-07-15 23:08:14 +0000842 /// Checks the number of results for @p inst are exactly equal to @p num_results and the number
843 /// of operands is correctly. Both results and operands are confirmed to be non-null.
844 /// @param inst the instruction
845 /// @param num_results expected number of results for the instruction
846 /// @param min_operands the minimum number of operands to expect
847 /// @param max_operands the maximum number of operands to expect, if not set, than only the
848 /// minimum number is checked.
849 /// @returns true if the result and operand counts are as expected and none are null
850 bool CheckResultsAndOperandRange(const ir::Instruction* inst,
851 size_t num_results,
852 size_t min_operands,
853 std::optional<size_t> max_operands);
854
Ryan Harrisonad423942024-07-12 00:48:07 +0000855 /// Checks the number of results and operands for @p inst are exactly equal to num_results
856 /// and num_operands, respectively, and that none of them are null.
857 /// @param inst the instruction
858 /// @param num_results expected number of results for the instruction
859 /// @param num_operands expected number of operands for the instruction
860 /// @returns true if the result and operand counts are as expected and none are null
861 bool CheckResultsAndOperands(const ir::Instruction* inst,
862 size_t num_results,
863 size_t num_operands);
864
James Price189f0be2024-07-31 23:10:41 +0000865 /// Checks that @p type does not use any types that are prohibited by the target capabilities.
866 /// @param type the type
867 /// @param diag a function that creates an error diagnostic for the source of the type
868 /// @param ignore_caps a set of capabilities to ignore for this check
869 void CheckType(const core::type::Type* type,
870 std::function<diag::Diagnostic&()> diag,
871 Capabilities ignore_caps = {});
872
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000873 /// Validates the root block
874 /// @param blk the block
Ben Clayton1e7b3122023-11-20 12:38:31 +0000875 void CheckRootBlock(const Block* blk);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000876
877 /// Validates the given function
Ryan Harrison2cbfbd42024-09-23 16:59:01 +0000878 /// @param func the function to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +0000879 void CheckFunction(const Function* func);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000880
Ryan Harrison2cbfbd42024-09-23 16:59:01 +0000881 /// Validates the specific function as a vertex entry point
882 /// @param ep the function to validate
883 void CheckVertexEntryPoint(const Function* ep);
884
Ryan Harrison6a8b16c2024-10-09 21:49:35 +0000885 /// @returns a function that validates rules for invariant decorations
886 /// @param err error message to log when check fails
887 template <typename MSG_ANCHOR>
888 auto CheckInvariantFunc(const std::string& err) {
889 return [this, err](const MSG_ANCHOR* msg_anchor, const IOAttributes& attr) {
890 if (!InvariantOnlyIfAlsoPosition(attr)) {
891 AddError(msg_anchor) << err;
892 }
893 };
894 }
895
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +0000896 /// @returns a function that validates builtins on function params
897 auto CheckBuiltinFunctionParam(const std::string& err) {
898 return [this, err](const FunctionParam* param, const IOAttributes& attr,
899 const type::Type* ty) {
900 if (!attr.builtin.has_value()) {
901 return;
902 }
Antonio Maiorano66652df2024-10-22 23:20:27 +0000903 auto result = ValidateBuiltIn(attr.builtin.value(), param->Function()->Stage(), true,
904 capabilities_, ty);
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +0000905 if (result != Success) {
906 AddError(param) << err << result.Failure();
907 }
908 };
909 }
910
911 /// @returns a function that validates builtins on function returns
912 auto CheckBuiltinFunctionReturn(const std::string& err) {
913 return [this, err](const Function* func, const IOAttributes& attr, const type::Type* ty) {
914 if (!attr.builtin.has_value()) {
915 return;
916 }
Antonio Maiorano66652df2024-10-22 23:20:27 +0000917 auto result =
918 ValidateBuiltIn(attr.builtin.value(), func->Stage(), false, capabilities_, ty);
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +0000919 if (result != Success) {
920 AddError(func) << err << result.Failure();
921 }
922 };
923 }
924
Ryan Harrison6a8b16c2024-10-09 21:49:35 +0000925 /// @returns a function that validates that location and builtin attributes are not present at
926 /// the same time
927 /// @param err error message to log when check fails
928 template <typename MSG_ANCHOR>
929 auto CheckDoesNotHaveBothLocationAndBuiltinFunc(const std::string& err) {
930 return [this, err](const MSG_ANCHOR* msg_anchor, const IOAttributes& attr) {
931 if (HasLocationAndBuiltin(attr)) {
932 AddError(msg_anchor) << err;
933 }
934 };
935 }
936
937 /// @returns a function that validates that either a location or builtin attribute are present
938 /// @param err error message to log when check fails
939 template <typename MSG_ANCHOR>
940 auto CheckHasLocationOrBuiltinFunc(const std::string& err) {
941 return [this, err](const MSG_ANCHOR* msg_anchor, const IOAttributes& attr) {
942 if (!HasLocationOrBuiltin(attr)) {
943 AddError(msg_anchor) << err;
944 }
945 };
946 }
947
Ryan Harrison96ea4742024-10-29 18:02:45 +0000948 /// @returns a function that validates that type is bool iff decorated with
949 /// @builtin(front_facing)
950 /// @param err error message to log when check fails
951 template <typename MSG_ANCHOR>
952 auto CheckFrontFacingIfBoolFunc(const std::string& err) {
953 return [this, err](const MSG_ANCHOR* msg_anchor, const IOAttributes& attr,
954 const type::Type* ty) {
955 if (ty->Is<core::type::Bool>() && attr.builtin != BuiltinValue::kFrontFacing) {
956 AddError(msg_anchor) << err;
957 }
958 };
959 }
960
961 /// @returns a function that validates that type is not bool
962 /// @param err error message to log when check fails
963 template <typename MSG_ANCHOR>
964 auto CheckNotBool(const std::string& err) {
965 return [this, err](const MSG_ANCHOR* msg_anchor, [[maybe_unused]] const IOAttributes& attr,
966 const type::Type* ty) {
967 if (ty->Is<core::type::Bool>()) {
968 AddError(msg_anchor) << err;
969 }
970 };
971 }
972
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000973 /// Validates the given instruction
974 /// @param inst the instruction to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +0000975 void CheckInstruction(const Instruction* inst);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000976
977 /// Validates the given var
978 /// @param var the var to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +0000979 void CheckVar(const Var* var);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000980
981 /// Validates the given let
Ryan Harrisone8b48672024-09-25 17:21:11 +0000982 /// @param l the let to validate
983 void CheckLet(const Let* l);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000984
985 /// Validates the given call
986 /// @param call the call to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +0000987 void CheckCall(const Call* call);
Ben Clayton4ebed9d2023-09-05 14:39:05 +0000988
Ryan Harrisond8b74652024-08-12 23:46:57 +0000989 /// Validates the given bitcast
990 /// @param bitcast the bitcast to validate
991 void CheckBitcast(const Bitcast* bitcast);
992
Ben Clayton6a56e272023-09-06 02:01:07 +0000993 /// Validates the given builtin call
994 /// @param call the call to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +0000995 void CheckBuiltinCall(const BuiltinCall* call);
Ben Clayton6a56e272023-09-06 02:01:07 +0000996
James Pricec34ca5f2024-06-11 18:24:06 +0000997 /// Validates the given member builtin call
998 /// @param call the member call to validate
999 void CheckMemberBuiltinCall(const MemberBuiltinCall* call);
1000
James Price8527c132024-06-05 12:24:04 +00001001 /// Validates the given construct
1002 /// @param construct the construct to validate
1003 void CheckConstruct(const Construct* construct);
1004
Ryan Harrisonf9761ac2024-08-08 18:56:30 +00001005 /// Validates the given convert
1006 /// @param convert the convert to validate
1007 void CheckConvert(const Convert* convert);
1008
Ryan Harrison5183d1a2024-08-13 21:49:44 +00001009 /// Validates the given discard
1010 /// @note Does not validate that the discard is in a fragment shader, that
1011 /// needs to be handled later in the validation.
1012 /// @param discard the discard to validate
1013 void CheckDiscard(const Discard* discard);
1014
Ben Claytond0dfa742023-09-25 15:38:43 +00001015 /// Validates the given user call
1016 /// @param call the call to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001017 void CheckUserCall(const UserCall* call);
Ben Claytond0dfa742023-09-25 15:38:43 +00001018
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001019 /// Validates the given access
1020 /// @param a the access to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001021 void CheckAccess(const Access* a);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001022
1023 /// Validates the given binary
1024 /// @param b the binary to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001025 void CheckBinary(const Binary* b);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001026
1027 /// Validates the given unary
1028 /// @param u the unary to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001029 void CheckUnary(const Unary* u);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001030
1031 /// Validates the given if
1032 /// @param if_ the if to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001033 void CheckIf(const If* if_);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001034
1035 /// Validates the given loop
1036 /// @param l the loop to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001037 void CheckLoop(const Loop* l);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001038
James Pricea0025032024-05-29 15:38:19 +00001039 /// Validates the loop body block
1040 /// @param l the loop to validate
1041 void CheckLoopBody(const Loop* l);
1042
Ben Clayton7b35ff12024-05-13 16:44:35 +00001043 /// Validates the loop continuing block
1044 /// @param l the loop to validate
1045 void CheckLoopContinuing(const Loop* l);
1046
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001047 /// Validates the given switch
1048 /// @param s the switch to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001049 void CheckSwitch(const Switch* s);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001050
Ryan Harrison8b8ef022024-08-14 22:44:24 +00001051 /// Validates the given swizzle
1052 /// @param s the swizzle to validate
1053 void CheckSwizzle(const Swizzle* s);
1054
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001055 /// Validates the given terminator
1056 /// @param b the terminator to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001057 void CheckTerminator(const Terminator* b);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001058
Ben Claytonc951b862024-05-22 07:36:00 +00001059 /// Validates the break if instruction
1060 /// @param b the break if to validate
1061 void CheckBreakIf(const BreakIf* b);
1062
Ben Claytoncabf6222024-05-13 17:11:12 +00001063 /// Validates the continue instruction
1064 /// @param c the continue to validate
1065 void CheckContinue(const Continue* c);
1066
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001067 /// Validates the given exit
1068 /// @param e the exit to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001069 void CheckExit(const Exit* e);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001070
Ben Claytoncabf6222024-05-13 17:11:12 +00001071 /// Validates the next iteration instruction
1072 /// @param n the next iteration to validate
1073 void CheckNextIteration(const NextIteration* n);
Ben Clayton7b35ff12024-05-13 16:44:35 +00001074
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001075 /// Validates the given exit if
1076 /// @param e the exit if to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001077 void CheckExitIf(const ExitIf* e);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001078
1079 /// Validates the given return
1080 /// @param r the return to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001081 void CheckReturn(const Return* r);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001082
Ryan Harrisone2d11b02024-09-24 19:32:29 +00001083 /// Validates the given unreachable
1084 /// @param u the unreachable to validate
1085 void CheckUnreachable(const Unreachable* u);
1086
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001087 /// Validates the @p exit targets a valid @p control instruction where the instruction may jump
1088 /// over if control instructions.
1089 /// @param exit the exit to validate
1090 /// @param control the control instruction targeted
Ben Clayton1e7b3122023-11-20 12:38:31 +00001091 void CheckControlsAllowingIf(const Exit* exit, const Instruction* control);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001092
1093 /// Validates the given exit switch
1094 /// @param s the exit switch to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001095 void CheckExitSwitch(const ExitSwitch* s);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001096
1097 /// Validates the given exit loop
1098 /// @param l the exit loop to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001099 void CheckExitLoop(const ExitLoop* l);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001100
James Price87a211c2024-01-11 15:11:17 +00001101 /// Validates the given load
1102 /// @param l the load to validate
1103 void CheckLoad(const Load* l);
1104
James Price8fade132023-09-20 11:42:42 +00001105 /// Validates the given store
1106 /// @param s the store to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001107 void CheckStore(const Store* s);
James Price8fade132023-09-20 11:42:42 +00001108
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001109 /// Validates the given load vector element
1110 /// @param l the load vector element to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001111 void CheckLoadVectorElement(const LoadVectorElement* l);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001112
1113 /// Validates the given store vector element
1114 /// @param s the store vector element to validate
Ben Clayton1e7b3122023-11-20 12:38:31 +00001115 void CheckStoreVectorElement(const StoreVectorElement* s);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001116
Ben Claytonc951b862024-05-22 07:36:00 +00001117 /// Validates that the number and types of the source instruction operands match the target's
1118 /// values.
1119 /// @param source_inst the source instruction
1120 /// @param source_operand_offset the index of the first operand of the source instruction
1121 /// @param source_operand_count the number of operands of the source instruction
1122 /// @param target the receiver of the operand values
1123 /// @param target_values the receiver of the operand values
1124 void CheckOperandsMatchTarget(const Instruction* source_inst,
1125 size_t source_operand_offset,
1126 size_t source_operand_count,
1127 const CastableBase* target,
1128 VectorRef<const Value*> target_values);
1129
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001130 /// @param inst the instruction
1131 /// @param idx the operand index
1132 /// @returns the vector pointer type for the given instruction operand
Ben Clayton1e7b3122023-11-20 12:38:31 +00001133 const core::type::Type* GetVectorPtrElementType(const Instruction* inst, size_t idx);
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001134
Ben Clayton833b8922024-05-01 21:07:24 +00001135 /// Executes all the pending tasks
1136 void ProcessTasks();
1137
1138 /// Queues the block to be validated with ProcessTasks()
1139 /// @param blk the block to validate
1140 void QueueBlock(const Block* blk);
1141
1142 /// Queues the list of instructions starting with @p inst to be validated
1143 /// @param inst the first instruction
1144 void QueueInstructions(const Instruction* inst);
1145
1146 /// Begins validation of the block @p blk, and its instructions.
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001147 /// BeginBlock() pushes a new scope for values.
Ben Clayton833b8922024-05-01 21:07:24 +00001148 /// Must be paired with a call to EndBlock().
1149 void BeginBlock(const Block* blk);
1150
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001151 /// Ends validation of the block opened with BeginBlock() and closes the block's scope for
1152 /// values.
Ben Clayton833b8922024-05-01 21:07:24 +00001153 void EndBlock();
1154
Ryan Harrison5183d1a2024-08-13 21:49:44 +00001155 /// Get the function that contains an instruction.
1156 /// @param inst the instruction
1157 /// @returns the function
1158 const ir::Function* ContainingFunction(const ir::Instruction* inst) {
1159 return block_to_function_.GetOrAdd(inst->Block(), [&] { //
1160 return ContainingFunction(inst->Block()->Parent());
1161 });
1162 }
1163
1164 /// Get any endpoints that call a function.
1165 /// @param f the function
1166 /// @returns all end points that call the function
1167 Hashset<const ir::Function*, 4> ContainingEndPoints(const ir::Function* f) {
1168 Hashset<const ir::Function*, 4> result{};
1169 Hashset<const ir::Function*, 4> visited{f};
1170
1171 auto call_sites = user_func_calls_.GetOr(f, Hashset<const ir::UserCall*, 4>()).Vector();
1172 while (!call_sites.IsEmpty()) {
1173 auto call_site = call_sites.Pop();
1174 auto calling_function = ContainingFunction(call_site);
1175 if (visited.Contains(calling_function)) {
1176 continue;
1177 }
1178 visited.Add(calling_function);
1179
1180 if (calling_function->Stage() != Function::PipelineStage::kUndefined) {
1181 result.Add(calling_function);
1182 }
1183
1184 for (auto new_call_sites :
1185 user_func_calls_.GetOr(f, Hashset<const ir::UserCall*, 4>())) {
1186 call_sites.Push(new_call_sites);
1187 }
1188 }
1189
1190 return result;
1191 }
1192
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001193 /// ScopeStack holds a stack of values that are currently in scope
1194 struct ScopeStack {
1195 void Push() { stack_.Push({}); }
1196 void Pop() { stack_.Pop(); }
1197 void Add(const Value* value) { stack_.Back().Add(value); }
1198 bool Contains(const Value* value) {
1199 return stack_.Any([&](auto& v) { return v.Contains(value); });
1200 }
1201 bool IsEmpty() const { return stack_.IsEmpty(); }
1202
1203 private:
1204 Vector<Hashset<const Value*, 8>, 4> stack_;
1205 };
1206
Ben Clayton1e7b3122023-11-20 12:38:31 +00001207 const Module& mod_;
Ben Clayton7711bfc2024-03-28 17:38:16 +00001208 Capabilities capabilities_;
dan sinclair650bc3e2024-06-11 23:26:53 +00001209 std::optional<ir::Disassembler> disassembler_; // Use Disassemble()
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001210 diag::List diagnostics_;
Ben Clayton1e7b3122023-11-20 12:38:31 +00001211 Hashset<const Function*, 4> all_functions_;
1212 Hashset<const Instruction*, 4> visited_instructions_;
Ben Clayton7b35ff12024-05-13 16:44:35 +00001213 Hashmap<const Loop*, const Continue*, 4> first_continues_;
Ben Clayton1e7b3122023-11-20 12:38:31 +00001214 Vector<const ControlInstruction*, 8> control_stack_;
Ben Clayton833b8922024-05-01 21:07:24 +00001215 Vector<const Block*, 8> block_stack_;
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001216 ScopeStack scope_stack_;
Ben Clayton833b8922024-05-01 21:07:24 +00001217 Vector<std::function<void()>, 16> tasks_;
Ben Clayton7e9f1a62024-05-22 18:42:55 +00001218 SymbolTable symbols_ = SymbolTable::Wrap(mod_.symbols);
1219 type::Manager type_mgr_ = type::Manager::Wrap(mod_.Types());
Ryan Harrison5183d1a2024-08-13 21:49:44 +00001220 Hashmap<const ir::Block*, const ir::Function*, 64> block_to_function_{};
1221 Hashmap<const ir::Function*, Hashset<const ir::UserCall*, 4>, 4> user_func_calls_;
1222 Hashset<const ir::Discard*, 4> discards_;
Ryan Harrison2cbfbd42024-09-23 16:59:01 +00001223 core::ir::ReferencedModuleVars<const Module> referenced_module_vars_;
dan sinclairfbc4ce72024-10-21 19:25:39 +00001224
1225 Hashset<ValidatedType, 16> validated_types_{};
Ben Clayton4ebed9d2023-09-05 14:39:05 +00001226};
1227
Ben Clayton7711bfc2024-03-28 17:38:16 +00001228Validator::Validator(const Module& mod, Capabilities capabilities)
Ryan Harrison2cbfbd42024-09-23 16:59:01 +00001229 : mod_(mod), capabilities_(capabilities), referenced_module_vars_(mod) {}
dan sinclair5ccafa42023-06-03 14:57:42 +00001230
dan sinclaire4952f32023-07-14 17:47:29 +00001231Validator::~Validator() = default;
dan sinclair5ccafa42023-06-03 14:57:42 +00001232
dan sinclair650bc3e2024-06-11 23:26:53 +00001233Disassembler& Validator::Disassemble() {
1234 if (!disassembler_) {
1235 disassembler_.emplace(ir::Disassembler(mod_));
dan sinclaire4952f32023-07-14 17:47:29 +00001236 }
dan sinclair650bc3e2024-06-11 23:26:53 +00001237 return *disassembler_;
dan sinclaire4952f32023-07-14 17:47:29 +00001238}
dan sinclair5ccafa42023-06-03 14:57:42 +00001239
Ben Clayton16fb2542023-09-25 11:43:19 +00001240Result<SuccessType> Validator::Run() {
Ryan Harrison5183d1a2024-08-13 21:49:44 +00001241 RunStructuralSoundnessChecks();
1242
1243 CheckForOrphanedInstructions();
1244 CheckForNonFragmentDiscards();
1245
1246 if (diagnostics_.ContainsErrors()) {
1247 diagnostics_.AddNote(Source{}) << "# Disassembly\n" << Disassemble().Text();
1248 return Failure{std::move(diagnostics_)};
1249 }
1250 return Success;
1251}
1252
1253void Validator::CheckForOrphanedInstructions() {
1254 if (diagnostics_.ContainsErrors()) {
1255 return;
1256 }
1257
1258 // Check for orphaned instructions.
1259 for (auto* inst : mod_.Instructions()) {
1260 if (!visited_instructions_.Contains(inst)) {
1261 AddError(inst) << "orphaned instruction: " << inst->FriendlyName();
1262 }
1263 }
1264}
1265
1266void Validator::CheckForNonFragmentDiscards() {
1267 if (diagnostics_.ContainsErrors()) {
1268 return;
1269 }
1270
1271 // Check for discards in non-fragments
1272 for (const auto& d : discards_) {
1273 const auto* f = ContainingFunction(d);
1274 for (const Function* ep : ContainingEndPoints(f)) {
1275 if (ep->Stage() != Function::PipelineStage::kFragment) {
1276 AddError(d) << "cannot be called in non-fragment end point";
1277 }
1278 }
1279 }
1280}
1281
1282void Validator::RunStructuralSoundnessChecks() {
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001283 scope_stack_.Push();
Ben Clayton833b8922024-05-01 21:07:24 +00001284 TINT_DEFER({
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001285 scope_stack_.Pop();
1286 TINT_ASSERT(scope_stack_.IsEmpty());
Ben Clayton833b8922024-05-01 21:07:24 +00001287 TINT_ASSERT(tasks_.IsEmpty());
1288 TINT_ASSERT(control_stack_.IsEmpty());
1289 TINT_ASSERT(block_stack_.IsEmpty());
1290 });
dan sinclaire4952f32023-07-14 17:47:29 +00001291 CheckRootBlock(mod_.root_block);
dan sinclair5ccafa42023-06-03 14:57:42 +00001292
Ben Claytonacef3102023-11-20 17:09:35 +00001293 for (auto& func : mod_.functions) {
1294 if (!all_functions_.Add(func.Get())) {
Ben Clayton35db5b52024-05-01 21:07:24 +00001295 AddError(func) << "function " << NameOf(func.Get())
James Price21517e42024-04-24 17:36:34 +00001296 << " added to module multiple times";
Ben Claytond0dfa742023-09-25 15:38:43 +00001297 }
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001298 scope_stack_.Add(func);
Ben Claytond0dfa742023-09-25 15:38:43 +00001299 }
1300
Ben Claytonacef3102023-11-20 17:09:35 +00001301 for (auto& func : mod_.functions) {
Ryan Harrison5183d1a2024-08-13 21:49:44 +00001302 block_to_function_.Add(func->Block(), func);
dan sinclaire4952f32023-07-14 17:47:29 +00001303 CheckFunction(func);
dan sinclair5ccafa42023-06-03 14:57:42 +00001304 }
dan sinclaire4952f32023-07-14 17:47:29 +00001305}
dan sinclairb9846432023-06-07 21:52:57 +00001306
Ben Claytonc27315a2024-02-26 20:24:06 +00001307diag::Diagnostic& Validator::AddError(const Instruction* inst) {
Ben Claytonfc36dcf2024-05-09 20:36:58 +00001308 diagnostics_.ReserveAdditional(2); // Ensure diagnostics don't resize alive after AddNote()
dan sinclair650bc3e2024-06-11 23:26:53 +00001309 auto src = Disassemble().InstructionSource(inst);
Ben Claytonc27315a2024-02-26 20:24:06 +00001310 auto& diag = AddError(src) << inst->FriendlyName() << ": ";
dan sinclaire4952f32023-07-14 17:47:29 +00001311
Ben Clayton833b8922024-05-01 21:07:24 +00001312 if (!block_stack_.IsEmpty()) {
1313 AddNote(block_stack_.Back()) << "in block";
dan sinclaire4952f32023-07-14 17:47:29 +00001314 }
Ben Claytonc27315a2024-02-26 20:24:06 +00001315 return diag;
dan sinclaire4952f32023-07-14 17:47:29 +00001316}
1317
Ben Claytonc27315a2024-02-26 20:24:06 +00001318diag::Diagnostic& Validator::AddError(const Instruction* inst, size_t idx) {
Ben Claytonfc36dcf2024-05-09 20:36:58 +00001319 diagnostics_.ReserveAdditional(2); // Ensure diagnostics don't resize alive after AddNote()
Ben Claytonaaeb83a2024-05-01 21:07:24 +00001320 auto src =
dan sinclair650bc3e2024-06-11 23:26:53 +00001321 Disassemble().OperandSource(Disassembler::IndexedValue{inst, static_cast<uint32_t>(idx)});
Ben Claytonc27315a2024-02-26 20:24:06 +00001322 auto& diag = AddError(src) << inst->FriendlyName() << ": ";
dan sinclaire4952f32023-07-14 17:47:29 +00001323
Ben Clayton833b8922024-05-01 21:07:24 +00001324 if (!block_stack_.IsEmpty()) {
1325 AddNote(block_stack_.Back()) << "in block";
dan sinclaire4952f32023-07-14 17:47:29 +00001326 }
Ben Claytonc27315a2024-02-26 20:24:06 +00001327 return diag;
dan sinclaire4952f32023-07-14 17:47:29 +00001328}
1329
Ben Claytonc27315a2024-02-26 20:24:06 +00001330diag::Diagnostic& Validator::AddResultError(const Instruction* inst, size_t idx) {
Ben Claytonfc36dcf2024-05-09 20:36:58 +00001331 diagnostics_.ReserveAdditional(2); // Ensure diagnostics don't resize alive after AddNote()
Ben Claytonaaeb83a2024-05-01 21:07:24 +00001332 auto src =
dan sinclair650bc3e2024-06-11 23:26:53 +00001333 Disassemble().ResultSource(Disassembler::IndexedValue{inst, static_cast<uint32_t>(idx)});
Ben Claytonc27315a2024-02-26 20:24:06 +00001334 auto& diag = AddError(src) << inst->FriendlyName() << ": ";
dan sinclaire4952f32023-07-14 17:47:29 +00001335
Ben Clayton833b8922024-05-01 21:07:24 +00001336 if (!block_stack_.IsEmpty()) {
1337 AddNote(block_stack_.Back()) << "in block";
dan sinclaire4952f32023-07-14 17:47:29 +00001338 }
Ben Claytonc27315a2024-02-26 20:24:06 +00001339 return diag;
dan sinclaire4952f32023-07-14 17:47:29 +00001340}
1341
Ben Claytonc27315a2024-02-26 20:24:06 +00001342diag::Diagnostic& Validator::AddError(const Block* blk) {
dan sinclair650bc3e2024-06-11 23:26:53 +00001343 auto src = Disassemble().BlockSource(blk);
Ben Claytonc27315a2024-02-26 20:24:06 +00001344 return AddError(src);
dan sinclaire4952f32023-07-14 17:47:29 +00001345}
1346
James Price4f491bf2024-04-24 21:15:04 +00001347diag::Diagnostic& Validator::AddError(const BlockParam* param) {
dan sinclair650bc3e2024-06-11 23:26:53 +00001348 auto src = Disassemble().BlockParamSource(param);
James Price4f491bf2024-04-24 21:15:04 +00001349 return AddError(src);
1350}
1351
James Price21517e42024-04-24 17:36:34 +00001352diag::Diagnostic& Validator::AddError(const Function* func) {
dan sinclair650bc3e2024-06-11 23:26:53 +00001353 auto src = Disassemble().FunctionSource(func);
James Price21517e42024-04-24 17:36:34 +00001354 return AddError(src);
1355}
1356
1357diag::Diagnostic& Validator::AddError(const FunctionParam* param) {
dan sinclair650bc3e2024-06-11 23:26:53 +00001358 auto src = Disassemble().FunctionParamSource(param);
James Price21517e42024-04-24 17:36:34 +00001359 return AddError(src);
1360}
1361
Ben Claytonc27315a2024-02-26 20:24:06 +00001362diag::Diagnostic& Validator::AddNote(const Instruction* inst) {
dan sinclair650bc3e2024-06-11 23:26:53 +00001363 auto src = Disassemble().InstructionSource(inst);
Ben Claytonc27315a2024-02-26 20:24:06 +00001364 return AddNote(src);
dan sinclaire4952f32023-07-14 17:47:29 +00001365}
1366
James Pricee5380b72024-04-24 20:52:59 +00001367diag::Diagnostic& Validator::AddNote(const Function* func) {
dan sinclair650bc3e2024-06-11 23:26:53 +00001368 auto src = Disassemble().FunctionSource(func);
James Pricee5380b72024-04-24 20:52:59 +00001369 return AddNote(src);
1370}
1371
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001372diag::Diagnostic& Validator::AddOperandNote(const Instruction* inst, size_t idx) {
Ben Claytonaaeb83a2024-05-01 21:07:24 +00001373 auto src =
dan sinclair650bc3e2024-06-11 23:26:53 +00001374 Disassemble().OperandSource(Disassembler::IndexedValue{inst, static_cast<uint32_t>(idx)});
Ben Claytonc27315a2024-02-26 20:24:06 +00001375 return AddNote(src);
dan sinclaire4952f32023-07-14 17:47:29 +00001376}
1377
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001378diag::Diagnostic& Validator::AddResultNote(const Instruction* inst, size_t idx) {
1379 auto src =
dan sinclair650bc3e2024-06-11 23:26:53 +00001380 Disassemble().ResultSource(Disassembler::IndexedValue{inst, static_cast<uint32_t>(idx)});
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001381 return AddNote(src);
1382}
1383
Ben Claytonc27315a2024-02-26 20:24:06 +00001384diag::Diagnostic& Validator::AddNote(const Block* blk) {
dan sinclair650bc3e2024-06-11 23:26:53 +00001385 auto src = Disassemble().BlockSource(blk);
Ben Claytonc27315a2024-02-26 20:24:06 +00001386 return AddNote(src);
dan sinclaire4952f32023-07-14 17:47:29 +00001387}
1388
Ben Claytonc27315a2024-02-26 20:24:06 +00001389diag::Diagnostic& Validator::AddError(Source src) {
Ben Clayton415bd732024-05-02 14:36:02 +00001390 auto& diag = diagnostics_.AddError(src);
dan sinclair650bc3e2024-06-11 23:26:53 +00001391 diag.owned_file = Disassemble().File();
Ben Claytonc27315a2024-02-26 20:24:06 +00001392 return diag;
dan sinclaire4952f32023-07-14 17:47:29 +00001393}
1394
Ben Claytonc27315a2024-02-26 20:24:06 +00001395diag::Diagnostic& Validator::AddNote(Source src) {
Ben Clayton415bd732024-05-02 14:36:02 +00001396 auto& diag = diagnostics_.AddNote(src);
dan sinclair650bc3e2024-06-11 23:26:53 +00001397 diag.owned_file = Disassemble().File();
Ben Claytonc27315a2024-02-26 20:24:06 +00001398 return diag;
dan sinclaire4952f32023-07-14 17:47:29 +00001399}
1400
Ben Claytonc951b862024-05-22 07:36:00 +00001401void Validator::AddDeclarationNote(const CastableBase* decl) {
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001402 tint::Switch(
Ben Claytonc951b862024-05-22 07:36:00 +00001403 decl, //
1404 [&](const Block* block) { AddDeclarationNote(block); },
1405 [&](const BlockParam* param) { AddDeclarationNote(param); },
1406 [&](const Function* fn) { AddDeclarationNote(fn); },
1407 [&](const FunctionParam* param) { AddDeclarationNote(param); },
1408 [&](const Instruction* inst) { AddDeclarationNote(inst); },
1409 [&](const InstructionResult* res) { AddDeclarationNote(res); });
1410}
1411
1412void Validator::AddDeclarationNote(const Block* block) {
dan sinclair650bc3e2024-06-11 23:26:53 +00001413 auto src = Disassemble().BlockSource(block);
Ben Claytonc951b862024-05-22 07:36:00 +00001414 if (src.file) {
1415 AddNote(src) << NameOf(block) << " declared here";
1416 }
1417}
1418
1419void Validator::AddDeclarationNote(const BlockParam* param) {
dan sinclair650bc3e2024-06-11 23:26:53 +00001420 auto src = Disassemble().BlockParamSource(param);
Ben Claytonc951b862024-05-22 07:36:00 +00001421 if (src.file) {
1422 AddNote(src) << NameOf(param) << " declared here";
1423 }
1424}
1425
1426void Validator::AddDeclarationNote(const Function* fn) {
1427 AddNote(fn) << NameOf(fn) << " declared here";
1428}
1429
1430void Validator::AddDeclarationNote(const FunctionParam* param) {
dan sinclair650bc3e2024-06-11 23:26:53 +00001431 auto src = Disassemble().FunctionParamSource(param);
Ben Claytonc951b862024-05-22 07:36:00 +00001432 if (src.file) {
1433 AddNote(src) << NameOf(param) << " declared here";
1434 }
1435}
1436
1437void Validator::AddDeclarationNote(const Instruction* inst) {
dan sinclair650bc3e2024-06-11 23:26:53 +00001438 auto src = Disassemble().InstructionSource(inst);
Ben Claytonc951b862024-05-22 07:36:00 +00001439 if (src.file) {
1440 AddNote(src) << NameOf(inst) << " declared here";
1441 }
1442}
1443
1444void Validator::AddDeclarationNote(const InstructionResult* res) {
1445 if (auto* inst = res->Instruction()) {
1446 auto results = inst->Results();
1447 for (size_t i = 0; i < results.Length(); i++) {
1448 if (results[i] == res) {
1449 AddResultNote(res->Instruction(), i) << NameOf(res) << " declared here";
1450 return;
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001451 }
Ben Claytonc951b862024-05-22 07:36:00 +00001452 }
1453 }
1454}
1455
1456StyledText Validator::NameOf(const CastableBase* decl) {
1457 return tint::Switch(
1458 decl, //
Ryan Harrison8570a642024-08-15 21:33:38 +00001459 [&](const type::Type* ty) { return NameOf(ty); },
Ben Claytonc951b862024-05-22 07:36:00 +00001460 [&](const Value* value) { return NameOf(value); },
1461 [&](const Instruction* inst) { return NameOf(inst); },
1462 [&](const Block* block) { return NameOf(block); }, //
1463 TINT_ICE_ON_NO_MATCH);
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001464}
1465
Ryan Harrison8570a642024-08-15 21:33:38 +00001466StyledText Validator::NameOf(const type::Type* ty) {
1467 auto name = ty ? ty->FriendlyName() : "undef";
1468 return StyledText{} << style::Type(name);
1469}
1470
Ben Clayton35db5b52024-05-01 21:07:24 +00001471StyledText Validator::NameOf(const Value* value) {
dan sinclair650bc3e2024-06-11 23:26:53 +00001472 return Disassemble().NameOf(value);
dan sinclaire4952f32023-07-14 17:47:29 +00001473}
1474
Ben Claytonc951b862024-05-22 07:36:00 +00001475StyledText Validator::NameOf(const Instruction* inst) {
Ryan Harrison8570a642024-08-15 21:33:38 +00001476 auto name = inst ? inst->FriendlyName() : "undef";
1477 return StyledText{} << style::Instruction(name);
Ben Claytonc951b862024-05-22 07:36:00 +00001478}
1479
1480StyledText Validator::NameOf(const Block* block) {
Ryan Harrison8570a642024-08-15 21:33:38 +00001481 auto parent_name = block->Parent() ? block->Parent()->FriendlyName() : "undef";
1482 return StyledText{} << style::Instruction(parent_name) << " block "
dan sinclair650bc3e2024-06-11 23:26:53 +00001483 << Disassemble().NameOf(block);
Ben Claytonc951b862024-05-22 07:36:00 +00001484}
1485
Ryan Harrison54b6c1c2024-07-29 20:50:19 +00001486bool Validator::CheckResult(const Instruction* inst, size_t idx) {
Ryan Harrisonad423942024-07-12 00:48:07 +00001487 auto* result = inst->Result(idx);
dan sinclaira6941c62024-08-29 21:34:20 +00001488 if (DAWN_UNLIKELY(result == nullptr)) {
Ryan Harrisonad423942024-07-12 00:48:07 +00001489 AddResultError(inst, idx) << "result is undefined";
1490 return false;
1491 }
Ryan Harrison54b6c1c2024-07-29 20:50:19 +00001492
dan sinclaira6941c62024-08-29 21:34:20 +00001493 if (DAWN_UNLIKELY(result->Type() == nullptr)) {
Ryan Harrison54b6c1c2024-07-29 20:50:19 +00001494 AddResultError(inst, idx) << "result type is undefined";
1495 return false;
1496 }
1497
Ryan Harrisone8b48672024-09-25 17:21:11 +00001498 if (DAWN_UNLIKELY(result->Instruction() == nullptr)) {
1499 AddResultError(inst, idx) << "result instruction is undefined";
1500 return false;
1501 }
1502
Ryan Harrisonad423942024-07-12 00:48:07 +00001503 return true;
1504}
1505
Ryan Harrison71f1b392024-08-08 13:27:46 +00001506bool Validator::CheckResults(const ir::Instruction* inst, std::optional<size_t> count = {}) {
1507 if (count.has_value()) {
dan sinclaira6941c62024-08-29 21:34:20 +00001508 if (DAWN_UNLIKELY(inst->Results().Length() != count.value())) {
Ryan Harrison71f1b392024-08-08 13:27:46 +00001509 AddError(inst) << "expected exactly " << count.value() << " results, got "
1510 << inst->Results().Length();
1511 return false;
1512 }
Ryan Harrisonad423942024-07-12 00:48:07 +00001513 }
1514
1515 bool passed = true;
Ryan Harrison71f1b392024-08-08 13:27:46 +00001516 for (size_t i = 0; i < inst->Results().Length(); i++) {
dan sinclaira6941c62024-08-29 21:34:20 +00001517 if (DAWN_UNLIKELY(!CheckResult(inst, i))) {
Ryan Harrisonad423942024-07-12 00:48:07 +00001518 passed = false;
1519 }
1520 }
1521 return passed;
1522}
1523
Ryan Harrison54b6c1c2024-07-29 20:50:19 +00001524bool Validator::CheckOperand(const Instruction* inst, size_t idx) {
Ryan Harrisonad423942024-07-12 00:48:07 +00001525 auto* operand = inst->Operand(idx);
dan sinclaira6941c62024-08-29 21:34:20 +00001526 if (DAWN_UNLIKELY(operand == nullptr)) {
Ryan Harrisonad423942024-07-12 00:48:07 +00001527 AddError(inst, idx) << "operand is undefined";
1528 return false;
1529 }
Ryan Harrison54b6c1c2024-07-29 20:50:19 +00001530
Ryan Harrison71f1b392024-08-08 13:27:46 +00001531 // ir::Unused is a internal value used by some transforms to track unused entries, and is
1532 // removed as part of generating an output shader.
dan sinclaira6941c62024-08-29 21:34:20 +00001533 if (DAWN_UNLIKELY(operand->Is<ir::Unused>())) {
Ryan Harrison71f1b392024-08-08 13:27:46 +00001534 return true;
1535 }
1536
Ryan Harrison54b6c1c2024-07-29 20:50:19 +00001537 // ir::Function does not have a meaningful type, so does not override the default Type()
1538 // behaviour.
dan sinclaira6941c62024-08-29 21:34:20 +00001539 if (DAWN_UNLIKELY(!operand->Is<ir::Function>() && operand->Type() == nullptr)) {
Ryan Harrison54b6c1c2024-07-29 20:50:19 +00001540 AddError(inst, idx) << "operand type is undefined";
1541 return false;
1542 }
1543
Ryan Harrisone8b48672024-09-25 17:21:11 +00001544 if (DAWN_UNLIKELY(!operand->Alive())) {
1545 AddError(inst, idx) << "operand is not alive";
1546 return false;
1547 }
1548
1549 if (DAWN_UNLIKELY(!operand->HasUsage(inst, idx))) {
1550 AddError(inst, idx) << "operand missing usage";
1551 return false;
1552 }
1553
1554 if (auto fn = operand->As<Function>(); fn && !all_functions_.Contains(fn)) {
1555 AddError(inst, idx) << NameOf(operand) << " is not part of the module";
1556 return false;
1557 }
1558
1559 if (DAWN_UNLIKELY(!operand->Is<ir::Unused>() && !operand->Is<Constant>() &&
1560 !scope_stack_.Contains(operand))) {
1561 AddError(inst, idx) << NameOf(operand) << " is not in scope";
1562 AddDeclarationNote(operand);
1563 return false;
1564 }
1565
Ryan Harrisonad423942024-07-12 00:48:07 +00001566 return true;
1567}
1568
Ryan Harrison244e32b2024-07-15 23:08:14 +00001569bool Validator::CheckOperands(const ir::Instruction* inst,
1570 size_t min_count,
1571 std::optional<size_t> max_count) {
dan sinclaira6941c62024-08-29 21:34:20 +00001572 if (DAWN_UNLIKELY(inst->Operands().Length() < min_count)) {
Ryan Harrison244e32b2024-07-15 23:08:14 +00001573 if (max_count.has_value()) {
1574 AddError(inst) << "expected between " << min_count << " and " << max_count.value()
1575 << " operands, got " << inst->Operands().Length();
1576 } else {
1577 AddError(inst) << "expected at least " << min_count << " operands, got "
1578 << inst->Operands().Length();
1579 }
1580 return false;
1581 }
1582
dan sinclaira6941c62024-08-29 21:34:20 +00001583 if (DAWN_UNLIKELY(max_count.has_value() && inst->Operands().Length() > max_count.value())) {
Ryan Harrison244e32b2024-07-15 23:08:14 +00001584 AddError(inst) << "expected between " << min_count << " and " << max_count.value()
1585 << " operands, got " << inst->Operands().Length();
1586 return false;
1587 }
1588
1589 bool passed = true;
1590 for (size_t i = 0; i < inst->Operands().Length(); i++) {
dan sinclaira6941c62024-08-29 21:34:20 +00001591 if (DAWN_UNLIKELY(!CheckOperand(inst, i))) {
Ryan Harrison244e32b2024-07-15 23:08:14 +00001592 passed = false;
1593 }
1594 }
1595 return passed;
1596}
1597
Ryan Harrison71f1b392024-08-08 13:27:46 +00001598bool Validator::CheckOperands(const ir::Instruction* inst, std::optional<size_t> count = {}) {
1599 if (count.has_value()) {
dan sinclaira6941c62024-08-29 21:34:20 +00001600 if (DAWN_UNLIKELY(inst->Operands().Length() != count.value())) {
Ryan Harrison71f1b392024-08-08 13:27:46 +00001601 AddError(inst) << "expected exactly " << count.value() << " operands, got "
1602 << inst->Operands().Length();
1603 return false;
1604 }
Ryan Harrisonad423942024-07-12 00:48:07 +00001605 }
1606
1607 bool passed = true;
Ryan Harrison71f1b392024-08-08 13:27:46 +00001608 for (size_t i = 0; i < inst->Operands().Length(); i++) {
dan sinclaira6941c62024-08-29 21:34:20 +00001609 if (DAWN_UNLIKELY(!CheckOperand(inst, i))) {
Ryan Harrisonad423942024-07-12 00:48:07 +00001610 passed = false;
1611 }
1612 }
1613 return passed;
1614}
1615
Ryan Harrison244e32b2024-07-15 23:08:14 +00001616bool Validator::CheckResultsAndOperandRange(const ir::Instruction* inst,
1617 size_t num_results,
1618 size_t min_operands,
1619 std::optional<size_t> max_operands = {}) {
1620 // Intentionally avoiding short-circuiting here
1621 bool results_passed = CheckResults(inst, num_results);
1622 bool operands_passed = CheckOperands(inst, min_operands, max_operands);
1623 return results_passed && operands_passed;
1624}
1625
Ryan Harrisonad423942024-07-12 00:48:07 +00001626bool Validator::CheckResultsAndOperands(const ir::Instruction* inst,
1627 size_t num_results,
1628 size_t num_operands) {
1629 // Intentionally avoiding short-circuiting here
1630 bool results_passed = CheckResults(inst, num_results);
1631 bool operands_passed = CheckOperands(inst, num_operands);
1632 return results_passed && operands_passed;
1633}
1634
James Price189f0be2024-07-31 23:10:41 +00001635void Validator::CheckType(const core::type::Type* root,
1636 std::function<diag::Diagnostic&()> diag,
1637 Capabilities ignore_caps) {
dan sinclairfbc4ce72024-10-21 19:25:39 +00001638 if (root == nullptr) {
1639 return;
1640 }
1641
1642 if (!validated_types_.Add(ValidatedType{root, ignore_caps})) {
1643 return;
1644 }
1645
James Price189f0be2024-07-31 23:10:41 +00001646 auto visit = [&](const type::Type* type) {
1647 return tint::Switch(
1648 type,
1649 [&](const type::Reference*) {
1650 // Reference types are guarded by the AllowRefTypes capability.
1651 if (!capabilities_.Contains(Capability::kAllowRefTypes) ||
1652 ignore_caps.Contains(Capability::kAllowRefTypes)) {
1653 diag() << "reference types are not permitted here";
1654 return false;
James Pricefcf6cc02024-08-01 04:41:28 +00001655 } else if (type != root) {
1656 // If they are allowed, reference types still cannot be nested.
1657 diag() << "nested reference types are not permitted";
1658 return false;
1659 }
1660 return true;
1661 },
1662 [&](const type::Pointer*) {
1663 if (type != root) {
1664 // Nesting pointer types inside structures is guarded by a capability.
1665 if (!(root->Is<type::Struct>() &&
1666 capabilities_.Contains(Capability::kAllowPointersInStructures))) {
1667 diag() << "nested pointer types are not permitted";
1668 return false;
1669 }
James Price189f0be2024-07-31 23:10:41 +00001670 }
1671 return true;
1672 },
James Priced31f5e42024-08-01 04:47:58 +00001673 [&](const type::I8*) {
1674 // i8 types are guarded by the Allow8BitIntegers capability.
1675 if (!capabilities_.Contains(Capability::kAllow8BitIntegers)) {
1676 diag() << "8-bit integer types are not permitted";
1677 return false;
1678 }
1679 return true;
1680 },
1681 [&](const type::U8*) {
1682 // u8 types are guarded by the Allow8BitIntegers capability.
1683 if (!capabilities_.Contains(Capability::kAllow8BitIntegers)) {
1684 diag() << "8-bit integer types are not permitted";
1685 return false;
1686 }
1687 return true;
1688 },
James Price189f0be2024-07-31 23:10:41 +00001689 [](Default) { return true; });
1690 };
1691
1692 Vector<const type::Type*, 8> stack{root};
1693 Hashset<const type::Type*, 8> seen{};
1694 while (!stack.IsEmpty()) {
1695 auto* ty = stack.Pop();
1696 if (!ty) {
1697 continue;
1698 }
1699 if (!visit(ty)) {
1700 return;
1701 }
1702
1703 if (auto* view = ty->As<type::MemoryView>(); view && seen.Add(view)) {
James Pricefcf6cc02024-08-01 04:41:28 +00001704 stack.Push(view->StoreType());
James Price189f0be2024-07-31 23:10:41 +00001705 continue;
1706 }
1707
1708 auto type_count = ty->Elements();
1709 if (type_count.type && seen.Add(type_count.type)) {
1710 stack.Push(type_count.type);
1711 continue;
1712 }
1713
1714 for (uint32_t i = 0; i < type_count.count; i++) {
1715 if (auto* subtype = ty->Element(i); subtype && seen.Add(subtype)) {
1716 stack.Push(subtype);
1717 }
1718 }
1719 }
1720}
1721
Ben Clayton1e7b3122023-11-20 12:38:31 +00001722void Validator::CheckRootBlock(const Block* blk) {
Ben Clayton833b8922024-05-01 21:07:24 +00001723 block_stack_.Push(blk);
1724 TINT_DEFER(block_stack_.Pop());
dan sinclairb9846432023-06-07 21:52:57 +00001725
dan sinclaire4952f32023-07-14 17:47:29 +00001726 for (auto* inst : *blk) {
Ben Claytond0dfa742023-09-25 15:38:43 +00001727 if (inst->Block() != blk) {
Ben Claytonc27315a2024-02-26 20:24:06 +00001728 AddError(inst) << "instruction in root block does not have root block as parent";
Ben Claytond0dfa742023-09-25 15:38:43 +00001729 continue;
1730 }
dan sinclair9fa7c892024-06-29 21:13:48 +00001731
1732 tint::Switch(
1733 inst, //
1734 [&](const core::ir::Var* var) { CheckInstruction(var); },
1735 [&](const core::ir::Let* let) {
1736 if (capabilities_.Contains(Capability::kAllowModuleScopeLets)) {
1737 CheckInstruction(let);
1738 } else {
1739 AddError(inst) << "root block: invalid instruction: " << inst->TypeInfo().name;
1740 }
1741 },
1742 [&](const core::ir::Construct* c) {
1743 if (capabilities_.Contains(Capability::kAllowModuleScopeLets)) {
1744 CheckInstruction(c);
1745 } else {
1746 AddError(inst) << "root block: invalid instruction: " << inst->TypeInfo().name;
1747 }
1748 },
1749 [&](Default) {
1750 AddError(inst) << "root block: invalid instruction: " << inst->TypeInfo().name;
1751 });
dan sinclaire4952f32023-07-14 17:47:29 +00001752 }
1753}
1754
Ben Clayton1e7b3122023-11-20 12:38:31 +00001755void Validator::CheckFunction(const Function* func) {
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001756 // Scope holds the parameters and block
1757 scope_stack_.Push();
1758 TINT_DEFER(scope_stack_.Pop());
1759
Ryan Harrison1c613522024-10-31 16:54:33 +00001760 Hashset<const FunctionParam*, 4> param_set{};
Ben Clayton7711bfc2024-03-28 17:38:16 +00001761 for (auto* param : func->Params()) {
James Pricee5380b72024-04-24 20:52:59 +00001762 if (!param->Alive()) {
1763 AddError(param) << "destroyed parameter found in function parameter list";
1764 return;
1765 }
1766 if (!param->Function()) {
1767 AddError(param) << "function parameter has nullptr parent function";
1768 return;
1769 } else if (param->Function() != func) {
1770 AddError(param) << "function parameter has incorrect parent function";
1771 AddNote(param->Function()) << "parent function declared here";
1772 return;
1773 }
1774
James Price5fc8c9e2024-05-24 16:11:13 +00001775 if (!param->Type()) {
1776 AddError(param) << "function parameter has nullptr type";
1777 return;
1778 }
1779
Ryan Harrison1c613522024-10-31 16:54:33 +00001780 if (!param_set.Add(param)) {
1781 AddError(param) << "function parameter is not unique";
1782 continue;
1783 }
1784
Ryan Harrisone5c5c652024-10-01 22:23:03 +00001785 // References not allowed on function signatures even with Capability::kAllowRefTypes.
1786 CheckType(
1787 param->Type(), [&]() -> diag::Diagnostic& { return AddError(param); },
1788 Capabilities{Capability::kAllowRefTypes});
1789
1790 if (!IsValidFunctionParamType(param->Type())) {
1791 auto struct_ty = param->Type()->As<core::type::Struct>();
1792 if (!capabilities_.Contains(Capability::kAllowPointersInStructures) || !struct_ty ||
1793 struct_ty->Members().Any([](const core::type::StructMember* m) {
1794 return !IsValidFunctionParamType(m->Type());
1795 })) {
1796 AddError(param) << "function parameter type must be constructible, a pointer, a "
1797 "texture, or a sampler";
Ryan Harrisone5c5c652024-10-01 22:23:03 +00001798 }
1799 }
1800
Ryan Harrison96ea4742024-10-29 18:02:45 +00001801 CheckFunctionParamAttributesAndType(param, CheckBuiltinFunctionParam(""));
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +00001802
Ryan Harrison6a8b16c2024-10-09 21:49:35 +00001803 CheckFunctionParamAttributes(
1804 param,
1805 CheckInvariantFunc<FunctionParam>(
1806 "invariant can only decorate a param iff it is also decorated with position"),
1807 CheckInvariantFunc<FunctionParam>(
1808 "invariant can only decorate a param member iff it is also "
1809 "decorated with position"));
1810 CheckFunctionParamAttributes(
1811 param,
1812 CheckDoesNotHaveBothLocationAndBuiltinFunc<FunctionParam>(
1813 "a builtin and location cannot be both declared for a param"),
1814 CheckDoesNotHaveBothLocationAndBuiltinFunc<FunctionParam>(
1815 "a builtin and location cannot be both declared for a struct member"));
Ryan Harrisonceb3a5c52024-09-26 21:39:25 +00001816
Ryan Harrison96ea4742024-10-29 18:02:45 +00001817 if (func->Stage() == Function::PipelineStage::kFragment) {
1818 CheckFunctionParamAttributesAndType(
1819 param,
1820 CheckFrontFacingIfBoolFunc<FunctionParam>(
1821 "fragment entry point params can only be a bool if decorated with "
1822 "@builtin(front_facing)"),
1823 CheckFrontFacingIfBoolFunc<FunctionParam>(
1824 "fragment entry point param memebers can only be a bool if "
1825 "decorated with @builtin(front_facing)"));
1826 } else if (func->Stage() != Function::PipelineStage::kUndefined) {
1827 CheckFunctionParamAttributesAndType(
1828 param, CheckNotBool<FunctionParam>(
1829 "entry point params can only be a bool for fragment shaders"));
1830 }
1831
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001832 scope_stack_.Add(param);
Ben Clayton7711bfc2024-03-28 17:38:16 +00001833 }
Ben Claytond8bde2c2024-05-28 13:46:59 +00001834
Ryan Harrison6a8b16c2024-10-09 21:49:35 +00001835 // References not allowed on function signatures even with Capability::kAllowRefTypes.
1836 CheckType(
1837 func->ReturnType(), [&]() -> diag::Diagnostic& { return AddError(func); },
1838 Capabilities{Capability::kAllowRefTypes});
1839
Ryan Harrison96ea4742024-10-29 18:02:45 +00001840 CheckFunctionReturnAttributesAndType(func, CheckBuiltinFunctionReturn(""));
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +00001841
Ryan Harrison6a8b16c2024-10-09 21:49:35 +00001842 CheckFunctionReturnAttributes(
1843 func,
1844 CheckInvariantFunc<Function>(
1845 "invariant can only decorate outputs iff they are also position builtins"),
1846 CheckInvariantFunc<Function>(
1847 "invariant can only decorate output members iff they are also position builtins"));
1848
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +00001849 CheckFunctionReturnAttributes(
1850 func,
1851 CheckDoesNotHaveBothLocationAndBuiltinFunc<Function>(
1852 "a builtin and location cannot be both declared for a function return"),
1853 CheckDoesNotHaveBothLocationAndBuiltinFunc<Function>(
1854 "a builtin and location cannot be both declared for a struct member"));
1855
Ryan Harrison6a8b16c2024-10-09 21:49:35 +00001856 // void needs to be filtered out, since it isn't constructible, but used in the IR when no
1857 // return is specified.
1858 if (DAWN_UNLIKELY(!func->ReturnType()->Is<core::type::Void>() &&
1859 !func->ReturnType()->IsConstructible())) {
1860 AddError(func) << "function return type must be constructible";
1861 }
1862
1863 if (func->Stage() != Function::PipelineStage::kUndefined) {
1864 if (DAWN_UNLIKELY(mod_.NameOf(func).Name().empty())) {
1865 AddError(func) << "entry points must have names";
1866 }
1867 }
1868
Ben Claytond8bde2c2024-05-28 13:46:59 +00001869 if (func->Stage() == Function::PipelineStage::kCompute) {
dan sinclaira6941c62024-08-29 21:34:20 +00001870 if (DAWN_UNLIKELY(!func->WorkgroupSize().has_value())) {
Ben Claytond8bde2c2024-05-28 13:46:59 +00001871 AddError(func) << "compute entry point requires workgroup size attribute";
1872 }
Ryan Harrison10345142024-09-23 22:10:55 +00001873
1874 if (DAWN_UNLIKELY(func->ReturnType() && !func->ReturnType()->Is<core::type::Void>())) {
1875 AddError(func) << "compute entry point must not have a return type";
1876 }
Ryan Harrison38548782024-08-12 21:37:58 +00001877 }
1878
Ryan Harrisond2bb5a12024-10-22 22:27:12 +00001879 if (DAWN_UNLIKELY(func->Stage() != Function::PipelineStage::kCompute &&
1880 func->WorkgroupSize().has_value())) {
1881 AddError(func) << "workgroup size attribute only valid on compute entry point";
1882 }
1883
Ryan Harrison6a8b16c2024-10-09 21:49:35 +00001884 if (func->Stage() == Function::PipelineStage::kFragment) {
1885 if (!func->ReturnType()->Is<core::type::Void>()) {
1886 CheckFunctionReturnAttributes(
1887 func,
1888 CheckHasLocationOrBuiltinFunc<Function>(
1889 "a non-void return for an entry point must have a "
1890 "builtin or location decoration"),
1891 CheckHasLocationOrBuiltinFunc<Function>(
1892 "members of struct used for returns of entry points must have a builtin or "
1893 "location decoration"));
1894 }
1895 }
1896
Ryan Harrison96ea4742024-10-29 18:02:45 +00001897 if (func->Stage() != Function::PipelineStage::kUndefined) {
1898 CheckFunctionReturnAttributesAndType(
1899 func, CheckFrontFacingIfBoolFunc<Function>("entry point returns can not be bool"),
1900 CheckFrontFacingIfBoolFunc<Function>("entry point return members can not be bool"));
1901
1902 for (auto var : referenced_module_vars_.TransitiveReferences(func)) {
1903 const auto* mv = var->Result(0)->Type()->As<type::MemoryView>();
1904 const auto* ty = var->Result(0)->Type()->UnwrapPtrOrRef();
1905 const auto attr = var->Attributes();
1906 if (!mv || !ty) {
1907 continue;
1908 }
1909
1910 if (mv->AddressSpace() != AddressSpace::kIn &&
1911 mv->AddressSpace() != AddressSpace::kOut) {
1912 continue;
1913 }
1914
1915 if (func->Stage() == Function::PipelineStage::kFragment &&
1916 mv->AddressSpace() == AddressSpace::kIn) {
1917 CheckIOAttributesAndType(
1918 func, attr, ty,
1919 CheckFrontFacingIfBoolFunc<Function>("input address space values referenced by "
1920 "fragment shaders can only be a bool if "
1921 "decorated with @builtin(front_facing)"));
1922
1923 } else {
1924 CheckIOAttributesAndType(
1925 func, attr, ty,
1926 CheckNotBool<Function>(
1927 "IO address space values referenced by shader entry points can only be "
1928 "bool if "
1929 "in the input space, used only by fragment shaders and decorated with "
1930 "@builtin(front_facing)"));
1931 }
1932 }
1933 }
1934
Ryan Harrison2cbfbd42024-09-23 16:59:01 +00001935 if (func->Stage() == Function::PipelineStage::kVertex) {
1936 CheckVertexEntryPoint(func);
1937 }
1938
Ben Clayton833b8922024-05-01 21:07:24 +00001939 QueueBlock(func->Block());
1940 ProcessTasks();
dan sinclaire4952f32023-07-14 17:47:29 +00001941}
dan sinclair76868522023-06-28 16:50:16 +00001942
Ryan Harrison2cbfbd42024-09-23 16:59:01 +00001943void Validator::CheckVertexEntryPoint(const Function* ep) {
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +00001944 bool contains_position = IsPositionPresent(ep->ReturnAttributes(), ep->ReturnType());
Ryan Harrison2cbfbd42024-09-23 16:59:01 +00001945
1946 for (auto var : referenced_module_vars_.TransitiveReferences(ep)) {
Ryan Harrison6a8b16c2024-10-09 21:49:35 +00001947 const auto* ty = var->Result(0)->Type()->UnwrapPtrOrRef();
1948 const auto attr = var->Attributes();
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +00001949 if (!ty) {
1950 continue;
1951 }
1952
1953 if (!contains_position) {
1954 contains_position = IsPositionPresent(attr, ty);
1955 }
1956
Ryan Harrison6a8b16c2024-10-09 21:49:35 +00001957 CheckIOAttributes(
1958 ep, attr, ty,
1959 CheckInvariantFunc<Function>(
1960 "invariant can only decorate vars iff they are also position builtins"),
1961 CheckInvariantFunc<Function>(
1962 "invariant can only decorate members iff they are also position builtins"));
Ryan Harrisonfa3f89b2024-10-21 20:30:08 +00001963
1964 // Builtin rules are not checked on module-scope variables, because they are often generated
1965 // as part of the backend transforms, and have different rules for correctness.
Ryan Harrison2cbfbd42024-09-23 16:59:01 +00001966 }
1967
1968 if (DAWN_UNLIKELY(!contains_position)) {
1969 AddError(ep) << "position must be declared for vertex entry point output";
1970 }
1971}
1972
Ben Clayton833b8922024-05-01 21:07:24 +00001973void Validator::ProcessTasks() {
1974 while (!tasks_.IsEmpty()) {
1975 tasks_.Pop()();
1976 }
1977}
1978
1979void Validator::QueueBlock(const Block* blk) {
1980 tasks_.Push([this] { EndBlock(); });
1981 tasks_.Push([this, blk] { BeginBlock(blk); });
1982}
1983
1984void Validator::BeginBlock(const Block* blk) {
Ben Clayton3aebf9e2024-05-02 10:01:02 +00001985 scope_stack_.Push();
Ben Clayton833b8922024-05-01 21:07:24 +00001986 block_stack_.Push(blk);
dan sinclaire4952f32023-07-14 17:47:29 +00001987
James Price4f491bf2024-04-24 21:15:04 +00001988 if (auto* mb = blk->As<MultiInBlock>()) {
1989 for (auto* param : mb->Params()) {
1990 if (!param->Alive()) {
1991 AddError(param) << "destroyed parameter found in block parameter list";
1992 return;
1993 }
1994 if (!param->Block()) {
1995 AddError(param) << "block parameter has nullptr parent block";
1996 return;
1997 } else if (param->Block() != mb) {
1998 AddError(param) << "block parameter has incorrect parent block";
1999 AddNote(param->Block()) << "parent block declared here";
2000 return;
2001 }
James Price189f0be2024-07-31 23:10:41 +00002002
2003 // References not allowed on block parameters even with Capability::kAllowRefTypes.
2004 CheckType(
2005 param->Type(), [&]() -> diag::Diagnostic& { return AddError(param); },
2006 Capabilities{Capability::kAllowRefTypes});
2007
Ben Clayton3aebf9e2024-05-02 10:01:02 +00002008 scope_stack_.Add(param);
James Price4f491bf2024-04-24 21:15:04 +00002009 }
2010 }
2011
Ben Claytonb9d3e1c2023-11-16 21:15:09 +00002012 if (!blk->Terminator()) {
Ben Clayton833b8922024-05-01 21:07:24 +00002013 AddError(blk) << "block does not end in a terminator instruction";
dan sinclair76868522023-06-28 16:50:16 +00002014 }
2015
Ben Clayton833b8922024-05-01 21:07:24 +00002016 // Validate the instructions w.r.t. the parent block
dan sinclaire4952f32023-07-14 17:47:29 +00002017 for (auto* inst : *blk) {
Ben Claytond0dfa742023-09-25 15:38:43 +00002018 if (inst->Block() != blk) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002019 AddError(inst) << "block instruction does not have same block as parent";
Ben Clayton833b8922024-05-01 21:07:24 +00002020 AddNote(blk) << "in block";
dan sinclaire4952f32023-07-14 17:47:29 +00002021 }
dan sinclairb9846432023-06-07 21:52:57 +00002022 }
Ben Clayton833b8922024-05-01 21:07:24 +00002023
2024 // Enqueue validation of the instructions of the block
2025 if (!blk->IsEmpty()) {
2026 QueueInstructions(blk->Instructions());
2027 }
2028}
2029
2030void Validator::EndBlock() {
Ben Clayton3aebf9e2024-05-02 10:01:02 +00002031 scope_stack_.Pop();
Ben Clayton833b8922024-05-01 21:07:24 +00002032 block_stack_.Pop();
2033}
2034
2035void Validator::QueueInstructions(const Instruction* inst) {
2036 tasks_.Push([this, inst] {
2037 CheckInstruction(inst);
2038 if (inst->next) {
2039 QueueInstructions(inst->next);
2040 }
2041 });
dan sinclaire4952f32023-07-14 17:47:29 +00002042}
dan sinclairb9846432023-06-07 21:52:57 +00002043
Ben Clayton1e7b3122023-11-20 12:38:31 +00002044void Validator::CheckInstruction(const Instruction* inst) {
James Pricec00c5692023-10-03 14:39:27 +00002045 visited_instructions_.Add(inst);
dan sinclaire4952f32023-07-14 17:47:29 +00002046 if (!inst->Alive()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002047 AddError(inst) << "destroyed instruction found in instruction list";
dan sinclaire4952f32023-07-14 17:47:29 +00002048 return;
dan sinclairde3b6992023-07-12 12:51:11 +00002049 }
Ryan Harrisone8b48672024-09-25 17:21:11 +00002050
Ben Claytona89d6642023-11-17 22:41:32 +00002051 auto results = inst->Results();
2052 for (size_t i = 0; i < results.Length(); ++i) {
2053 auto* res = results[i];
2054 if (!res) {
Ben Claytona89d6642023-11-17 22:41:32 +00002055 continue;
2056 }
James Price189f0be2024-07-31 23:10:41 +00002057 CheckType(res->Type(), [&]() -> diag::Diagnostic& { return AddResultError(inst, i); });
dan sinclairde3b6992023-07-12 12:51:11 +00002058 }
dan sinclair165fdaa2023-07-12 13:03:21 +00002059
dan sinclaire4952f32023-07-14 17:47:29 +00002060 auto ops = inst->Operands();
2061 for (size_t i = 0; i < ops.Length(); ++i) {
2062 auto* op = ops[i];
2063 if (!op) {
2064 continue;
2065 }
2066
James Price189f0be2024-07-31 23:10:41 +00002067 CheckType(op->Type(), [&]() -> diag::Diagnostic& { return AddError(inst, i); });
dan sinclaire4952f32023-07-14 17:47:29 +00002068 }
2069
2070 tint::Switch(
Ben Clayton1e7b3122023-11-20 12:38:31 +00002071 inst, //
2072 [&](const Access* a) { CheckAccess(a); }, //
2073 [&](const Binary* b) { CheckBinary(b); }, //
2074 [&](const Call* c) { CheckCall(c); }, //
2075 [&](const If* if_) { CheckIf(if_); }, //
2076 [&](const Let* let) { CheckLet(let); }, //
James Price87a211c2024-01-11 15:11:17 +00002077 [&](const Load* load) { CheckLoad(load); }, //
Ben Clayton1e7b3122023-11-20 12:38:31 +00002078 [&](const LoadVectorElement* l) { CheckLoadVectorElement(l); }, //
2079 [&](const Loop* l) { CheckLoop(l); }, //
2080 [&](const Store* s) { CheckStore(s); }, //
2081 [&](const StoreVectorElement* s) { CheckStoreVectorElement(s); }, //
2082 [&](const Switch* s) { CheckSwitch(s); }, //
Ryan Harrison8b8ef022024-08-14 22:44:24 +00002083 [&](const Swizzle* s) { CheckSwizzle(s); }, //
Ben Clayton1e7b3122023-11-20 12:38:31 +00002084 [&](const Terminator* b) { CheckTerminator(b); }, //
2085 [&](const Unary* u) { CheckUnary(u); }, //
2086 [&](const Var* var) { CheckVar(var); }, //
Ben Claytonc27315a2024-02-26 20:24:06 +00002087 [&](const Default) { AddError(inst) << "missing validation"; });
Ben Clayton3aebf9e2024-05-02 10:01:02 +00002088
2089 for (auto* result : results) {
2090 scope_stack_.Add(result);
2091 }
dan sinclaire4952f32023-07-14 17:47:29 +00002092}
2093
Ben Clayton1e7b3122023-11-20 12:38:31 +00002094void Validator::CheckVar(const Var* var) {
Ryan Harrison4ce6f5a2024-07-16 19:11:16 +00002095 // Intentionally not checking operands, since Var may have a null operand
2096 if (!CheckResults(var, Var::kNumResults)) {
2097 return;
2098 }
2099
Ryan Harrison8b066742024-07-18 16:53:50 +00002100 // Check that initializer and result type match
2101 if (var->Initializer()) {
Ryan Harrison54b6c1c2024-07-29 20:50:19 +00002102 if (!CheckOperand(var, ir::Var::kInitializerOperandOffset)) {
2103 return;
2104 }
2105
Ben Clayton120ca8e2024-03-28 16:22:29 +00002106 if (var->Initializer()->Type() != var->Result(0)->Type()->UnwrapPtrOrRef()) {
Ben Clayton0a60d522024-05-09 16:42:22 +00002107 AddError(var) << "initializer type "
Ryan Harrison54b6c1c2024-07-29 20:50:19 +00002108 << style::Type(var->Initializer()->Type()->FriendlyName())
Ben Clayton0a60d522024-05-09 16:42:22 +00002109 << " does not match store type "
Ryan Harrison54b6c1c2024-07-29 20:50:19 +00002110 << style::Type(var->Result(0)->Type()->UnwrapPtrOrRef()->FriendlyName());
Ryan Harrison8b066742024-07-18 16:53:50 +00002111 return;
2112 }
2113 }
2114
Ryan Harrisonf22d8462024-07-22 19:47:16 +00002115 auto* result_type = var->Result(0)->Type();
2116 if (result_type == nullptr) {
Ryan Harrison9531e032024-08-12 14:26:29 +00002117 AddError(var) << "result type is undefined";
Ryan Harrison8b066742024-07-18 16:53:50 +00002118 return;
2119 }
2120
Ryan Harrison9531e032024-08-12 14:26:29 +00002121 auto* mv = result_type->As<type::MemoryView>();
2122 if (!mv) {
2123 AddError(var) << "result type must be a pointer or a reference";
2124 return;
2125 }
Ryan Harrisonf22d8462024-07-22 19:47:16 +00002126
Ryan Harrison9531e032024-08-12 14:26:29 +00002127 // Check that only resource variables have @group and @binding set
2128 switch (mv->AddressSpace()) {
2129 case AddressSpace::kHandle:
dan sinclair680183f2024-10-07 19:57:38 +00002130 if (!capabilities_.Contains(Capability::kAllowHandleVarsWithoutBindings)) {
2131 if (!var->BindingPoint().has_value()) {
2132 AddError(var) << "resource variable missing binding points";
2133 }
2134 }
2135 break;
Ryan Harrison9531e032024-08-12 14:26:29 +00002136 case AddressSpace::kStorage:
2137 case AddressSpace::kUniform:
2138 if (!var->BindingPoint().has_value()) {
2139 AddError(var) << "resource variable missing binding points";
2140 }
2141 break;
2142 default:
2143 break;
2144 }
2145
2146 // Check that non-handle variables don't have @input_attachment_index set
2147 if (var->InputAttachmentIndex().has_value() && mv->AddressSpace() != AddressSpace::kHandle) {
2148 AddError(var) << "'@input_attachment_index' is not valid for non-handle var";
2149 return;
dan sinclaire4952f32023-07-14 17:47:29 +00002150 }
Ryan Harrisonceb3a5c52024-09-26 21:39:25 +00002151
2152 if (HasLocationAndBuiltin(var->Attributes())) {
2153 AddError(var) << "a builtin and location cannot be both declared for a var";
2154 return;
2155 }
2156
2157 if (auto* s = var->Result(0)->Type()->UnwrapPtrOrRef()->As<core::type::Struct>()) {
2158 for (auto* mem : s->Members()) {
2159 if (HasLocationAndBuiltin(mem->Attributes())) {
2160 AddError(var)
2161 << "a builtin and location cannot be both declared for a struct member";
2162 return;
2163 }
2164 }
2165 }
dan sinclaire4952f32023-07-14 17:47:29 +00002166}
2167
Ryan Harrisone8b48672024-09-25 17:21:11 +00002168void Validator::CheckLet(const Let* l) {
2169 if (!CheckResultsAndOperands(l, Let::kNumResults, Let::kNumOperands)) {
2170 return;
2171 }
dan sinclaire4952f32023-07-14 17:47:29 +00002172
Ryan Harrisone8b48672024-09-25 17:21:11 +00002173 if (l->Result(0) && l->Value()) {
2174 if (l->Result(0)->Type() != l->Value()->Type()) {
Ryan Harrisonefed88f2024-10-31 16:55:04 +00002175 auto result_type_name =
2176 l->Result(0)->Type() ? l->Result(0)->Type()->FriendlyName() : "undef";
2177 auto value_type_name =
2178 l->Value()->Type() ? l->Value()->Type()->FriendlyName() : "undef";
2179 AddError(l) << "result type " << style::Type(result_type_name)
2180 << " does not match value type " << style::Type(value_type_name);
dan sinclaire4952f32023-07-14 17:47:29 +00002181 }
2182 }
2183}
2184
Ben Clayton1e7b3122023-11-20 12:38:31 +00002185void Validator::CheckCall(const Call* call) {
dan sinclaire4952f32023-07-14 17:47:29 +00002186 tint::Switch(
Ryan Harrison5183d1a2024-08-13 21:49:44 +00002187 call, //
2188 [&](const Bitcast* b) { CheckBitcast(b); }, //
2189 [&](const BuiltinCall* c) { CheckBuiltinCall(c); }, //
2190 [&](const MemberBuiltinCall* c) { CheckMemberBuiltinCall(c); }, //
2191 [&](const Construct* c) { CheckConstruct(c); }, //
2192 [&](const Convert* c) { CheckConvert(c); }, //
2193 [&](const Discard* d) { //
2194 discards_.Add(d); //
2195 CheckDiscard(d); //
2196 }, //
2197 [&](const UserCall* c) { //
2198 if (c->Target()) { //
2199 auto calls = //
2200 user_func_calls_.GetOr(c->Target(), //
2201 Hashset<const ir::UserCall*, 4>{}); //
2202 calls.Add(c); //
2203 user_func_calls_.Replace(c->Target(), calls); //
2204 } //
2205 CheckUserCall(c); //
2206 }, //
dan sinclair774b6a42023-09-06 21:04:30 +00002207 [&](Default) {
2208 // Validation of custom IR instructions
2209 });
dan sinclaire4952f32023-07-14 17:47:29 +00002210}
2211
Ryan Harrisond8b74652024-08-12 23:46:57 +00002212void Validator::CheckBitcast(const Bitcast* bitcast) {
2213 CheckResultsAndOperands(bitcast, Bitcast::kNumResults, Bitcast::kNumOperands);
2214}
2215
Ben Clayton1e7b3122023-11-20 12:38:31 +00002216void Validator::CheckBuiltinCall(const BuiltinCall* call) {
Ryan Harrisonc84af332024-07-30 22:39:37 +00002217 auto args =
2218 Transform<8>(call->Args(), [&](const ir::Value* v) { return v ? v->Type() : nullptr; });
2219 if (args.Any([&](const type::Type* ty) { return ty == nullptr; })) {
2220 AddError(call) << "argument to builtin has undefined type";
2221 return;
2222 }
2223
Ben Clayton1e7b3122023-11-20 12:38:31 +00002224 intrinsic::Context context{
2225 call->TableData(),
Ben Clayton7e9f1a62024-05-22 18:42:55 +00002226 type_mgr_,
2227 symbols_,
Ben Clayton1e7b3122023-11-20 12:38:31 +00002228 };
dan sinclairb88e1c72023-09-07 13:59:10 +00002229
Ryan Harrisona2d786c2024-07-23 00:26:51 +00002230 auto builtin = core::intrinsic::LookupFn(context, call->FriendlyName().c_str(), call->FuncId(),
2231 Empty, args, core::EvaluationStage::kRuntime);
2232 if (builtin != Success) {
2233 AddError(call) << builtin.Failure();
Ben Claytoncf05e922024-01-05 14:44:21 +00002234 return;
2235 }
2236
Ryan Harrisona2d786c2024-07-23 00:26:51 +00002237 TINT_ASSERT(builtin->return_type);
2238
2239 if (call->Result(0) == nullptr) {
2240 AddError(call) << "call to builtin does not have a return type";
2241 return;
2242 }
2243
2244 if (builtin->return_type != call->Result(0)->Type()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002245 AddError(call) << "call result type does not match builtin return type";
Ryan Harrisona2d786c2024-07-23 00:26:51 +00002246 return;
James Price94a6a812023-09-13 16:59:52 +00002247 }
Ben Clayton6a56e272023-09-06 02:01:07 +00002248}
2249
James Pricec34ca5f2024-06-11 18:24:06 +00002250void Validator::CheckMemberBuiltinCall(const MemberBuiltinCall* call) {
2251 auto args = Vector<const type::Type*, 8>({call->Object()->Type()});
2252 for (auto* arg : call->Args()) {
2253 args.Push(arg->Type());
2254 }
2255 intrinsic::Context context{
2256 call->TableData(),
2257 type_mgr_,
2258 symbols_,
2259 };
2260
2261 auto result =
2262 core::intrinsic::LookupMemberFn(context, call->FriendlyName().c_str(), call->FuncId(),
2263 Empty, std::move(args), core::EvaluationStage::kRuntime);
2264 if (result != Success) {
2265 AddError(call) << result.Failure();
2266 return;
2267 }
2268
2269 if (result->return_type != call->Result(0)->Type()) {
Antonio Maiorano38cce382024-10-11 20:04:59 +00002270 AddError(call) << "member call result type ("
2271 << style::Type(call->Result(0)->Type()->FriendlyName())
2272 << ") does not match builtin return type ("
2273 << style::Type(result->return_type->FriendlyName()) << ")";
James Pricec34ca5f2024-06-11 18:24:06 +00002274 }
2275}
2276
James Price8527c132024-06-05 12:24:04 +00002277void Validator::CheckConstruct(const Construct* construct) {
Ryan Harrison2c6d2ea12024-08-08 19:55:22 +00002278 if (!CheckResultsAndOperandRange(construct, Construct::kNumResults, Construct::kMinOperands)) {
James Price8527c132024-06-05 12:24:04 +00002279 return;
2280 }
2281
Ryan Harrison2c6d2ea12024-08-08 19:55:22 +00002282 auto args = construct->Args();
2283 if (args.IsEmpty()) {
2284 // Zero-value constructors are valid for all constructible types.
Ryan Harrison71f1b392024-08-08 13:27:46 +00002285 return;
2286 }
2287
dan sinclair7cd0dfe2024-07-11 04:41:29 +00002288 if (auto* str = As<type::Struct>(construct->Result(0)->Type())) {
James Price8527c132024-06-05 12:24:04 +00002289 auto members = str->Members();
2290 if (args.Length() != str->Members().Length()) {
2291 AddError(construct) << "structure has " << members.Length()
2292 << " members, but construct provides " << args.Length()
2293 << " arguments";
2294 return;
2295 }
2296 for (size_t i = 0; i < args.Length(); i++) {
Ryan Harrison71f1b392024-08-08 13:27:46 +00002297 if (args[i]->Is<ir::Unused>()) {
2298 continue;
2299 }
2300 if (args[i]->Type() != members[i]->Type()) {
James Price8527c132024-06-05 12:24:04 +00002301 AddError(construct, Construct::kArgsOperandOffset + i)
dan sinclair2791ca92024-06-26 01:16:05 +00002302 << "structure member " << i << " is of type "
James Price8527c132024-06-05 12:24:04 +00002303 << style::Type(members[i]->Type()->FriendlyName())
2304 << ", but argument is of type " << style::Type(args[i]->Type()->FriendlyName());
2305 }
2306 }
2307 }
2308}
2309
Ryan Harrisonf9761ac2024-08-08 18:56:30 +00002310void Validator::CheckConvert(const Convert* convert) {
2311 CheckResultsAndOperands(convert, Convert::kNumResults, Convert::kNumOperands);
2312}
2313
Ryan Harrison5183d1a2024-08-13 21:49:44 +00002314void Validator::CheckDiscard(const tint::core::ir::Discard* discard) {
2315 CheckResultsAndOperands(discard, Discard::kNumResults, Discard::kNumOperands);
2316}
2317
Ben Clayton1e7b3122023-11-20 12:38:31 +00002318void Validator::CheckUserCall(const UserCall* call) {
Ryan Harrisond1b935e2024-07-16 21:02:03 +00002319 if (!CheckResultsAndOperandRange(call, UserCall::kNumResults, UserCall::kMinOperands)) {
2320 return;
2321 }
2322
Ryan Harrison72ae7992024-07-18 21:01:12 +00002323 if (!call->Target()) {
2324 AddError(call, UserCall::kFunctionOperandOffset) << "target not defined or not a function";
2325 return;
2326 }
2327
James Pricefbabce42024-01-15 21:09:26 +00002328 if (call->Target()->Stage() != Function::PipelineStage::kUndefined) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002329 AddError(call, UserCall::kFunctionOperandOffset)
2330 << "call target must not have a pipeline stage";
James Pricefbabce42024-01-15 21:09:26 +00002331 }
2332
Ben Claytona3430742024-01-10 18:20:01 +00002333 auto args = call->Args();
2334 auto params = call->Target()->Params();
2335 if (args.Length() != params.Length()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002336 AddError(call, UserCall::kFunctionOperandOffset)
2337 << "function has " << params.Length() << " parameters, but call provides "
Ben Claytona3430742024-01-10 18:20:01 +00002338 << args.Length() << " arguments";
Ben Claytona3430742024-01-10 18:20:01 +00002339 return;
2340 }
2341
2342 for (size_t i = 0; i < args.Length(); i++) {
2343 if (args[i]->Type() != params[i]->Type()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002344 AddError(call, UserCall::kArgsOperandOffset + i)
Ben Clayton0a60d522024-05-09 16:42:22 +00002345 << "function parameter " << i << " is of type "
2346 << style::Type(params[i]->Type()->FriendlyName()) << ", but argument is of type "
2347 << style::Type(args[i]->Type()->FriendlyName());
Ben Claytona3430742024-01-10 18:20:01 +00002348 }
2349 }
Ben Claytond0dfa742023-09-25 15:38:43 +00002350}
2351
Ben Clayton1e7b3122023-11-20 12:38:31 +00002352void Validator::CheckAccess(const Access* a) {
Ryan Harrison244e32b2024-07-15 23:08:14 +00002353 if (!CheckResultsAndOperandRange(a, Access::kNumResults, Access::kMinNumOperands)) {
Ryan Harrisonde3007a2024-07-11 00:51:27 +00002354 return;
2355 }
2356
Ben Clayton120ca8e2024-03-28 16:22:29 +00002357 auto* obj_view = a->Object()->Type()->As<core::type::MemoryView>();
2358 auto* ty = obj_view ? obj_view->StoreType() : a->Object()->Type();
dan sinclair2bd5e042024-07-03 21:04:44 +00002359
2360 enum Kind : uint8_t { kPtr, kRef, kValue };
2361
Ben Clayton120ca8e2024-03-28 16:22:29 +00002362 auto kind_of = [&](const core::type::Type* type) {
2363 return tint::Switch(
2364 type, //
2365 [&](const core::type::Pointer*) { return kPtr; }, //
2366 [&](const core::type::Reference*) { return kRef; }, //
2367 [&](Default) { return kValue; });
2368 };
dan sinclair2bd5e042024-07-03 21:04:44 +00002369
Ben Clayton120ca8e2024-03-28 16:22:29 +00002370 const Kind in_kind = kind_of(a->Object()->Type());
2371 auto desc_of = [&](Kind kind, const core::type::Type* type) {
2372 switch (kind) {
2373 case kPtr:
Ben Clayton0a60d522024-05-09 16:42:22 +00002374 return StyledText{}
2375 << style::Type("ptr<", obj_view->AddressSpace(), ", ", type->FriendlyName(),
2376 ", ", obj_view->Access(), ">");
Ben Clayton120ca8e2024-03-28 16:22:29 +00002377 case kRef:
Ben Clayton0a60d522024-05-09 16:42:22 +00002378 return StyledText{}
2379 << style::Type("ref<", obj_view->AddressSpace(), ", ", type->FriendlyName(),
2380 ", ", obj_view->Access(), ">");
Ben Clayton120ca8e2024-03-28 16:22:29 +00002381 default:
Ben Clayton0a60d522024-05-09 16:42:22 +00002382 return StyledText{} << style::Type(type->FriendlyName());
Ben Claytonaa774842024-01-12 18:28:20 +00002383 }
2384 };
dan sinclaire4952f32023-07-14 17:47:29 +00002385
2386 for (size_t i = 0; i < a->Indices().Length(); i++) {
Ben Clayton120ca8e2024-03-28 16:22:29 +00002387 auto err = [&]() -> diag::Diagnostic& {
2388 return AddError(a, i + Access::kIndicesOperandOffset);
2389 };
2390 auto note = [&]() -> diag::Diagnostic& {
Ben Clayton3aebf9e2024-05-02 10:01:02 +00002391 return AddOperandNote(a, i + Access::kIndicesOperandOffset);
Ben Clayton120ca8e2024-03-28 16:22:29 +00002392 };
dan sinclaire4952f32023-07-14 17:47:29 +00002393
2394 auto* index = a->Indices()[i];
Ryan Harrisonebdd6ad2024-10-29 19:57:43 +00002395 if (DAWN_UNLIKELY(!index->Type() || !index->Type()->IsIntegerScalar())) {
Ryan Harrisonefed88f2024-10-31 16:55:04 +00002396 auto name = index->Type() ? index->Type()->FriendlyName() : "undef";
2397 err() << "index must be integer, got " << name;
dan sinclaire4952f32023-07-14 17:47:29 +00002398 return;
2399 }
2400
James Price03ecbbf2024-01-17 17:01:30 +00002401 if (!capabilities_.Contains(Capability::kAllowVectorElementPointer)) {
Ben Clayton120ca8e2024-03-28 16:22:29 +00002402 if (in_kind != kValue && ty->Is<core::type::Vector>()) {
2403 err() << "cannot obtain address of vector element";
James Price03ecbbf2024-01-17 17:01:30 +00002404 return;
2405 }
dan sinclaire4952f32023-07-14 17:47:29 +00002406 }
2407
2408 if (auto* const_index = index->As<ir::Constant>()) {
2409 auto* value = const_index->Value();
Ryan Harrisonebdd6ad2024-10-29 19:57:43 +00002410 if (!value->Type() || value->Type()->IsSignedIntegerScalar()) {
dan sinclaire4952f32023-07-14 17:47:29 +00002411 // index is a signed integer scalar. Check that the index isn't negative.
2412 // If the index is unsigned, we can skip this.
2413 auto idx = value->ValueAs<AInt>();
dan sinclaira6941c62024-08-29 21:34:20 +00002414 if (DAWN_UNLIKELY(idx < 0)) {
Ben Clayton120ca8e2024-03-28 16:22:29 +00002415 err() << "constant index must be positive, got " << idx;
dan sinclaire4952f32023-07-14 17:47:29 +00002416 return;
2417 }
dan sinclair165fdaa2023-07-12 13:03:21 +00002418 }
dan sinclaire4952f32023-07-14 17:47:29 +00002419
2420 auto idx = value->ValueAs<uint32_t>();
Ben Clayton120ca8e2024-03-28 16:22:29 +00002421 auto* el = ty->Element(idx);
dan sinclaira6941c62024-08-29 21:34:20 +00002422 if (DAWN_UNLIKELY(!el)) {
dan sinclaire4952f32023-07-14 17:47:29 +00002423 // Is index in bounds?
Ben Clayton120ca8e2024-03-28 16:22:29 +00002424 if (auto el_count = ty->Elements().count; el_count != 0 && idx >= el_count) {
2425 err() << "index out of bounds for type " << desc_of(in_kind, ty);
2426 note() << "acceptable range: [0.." << (el_count - 1) << "]";
dan sinclaire4952f32023-07-14 17:47:29 +00002427 return;
2428 }
Ben Clayton120ca8e2024-03-28 16:22:29 +00002429 err() << "type " << desc_of(in_kind, ty) << " cannot be indexed";
dan sinclair165fdaa2023-07-12 13:03:21 +00002430 return;
2431 }
Ben Clayton120ca8e2024-03-28 16:22:29 +00002432 ty = el;
dan sinclaire4952f32023-07-14 17:47:29 +00002433 } else {
Ben Clayton120ca8e2024-03-28 16:22:29 +00002434 auto* el = ty->Elements().type;
dan sinclaira6941c62024-08-29 21:34:20 +00002435 if (DAWN_UNLIKELY(!el)) {
Ben Clayton120ca8e2024-03-28 16:22:29 +00002436 err() << "type " << desc_of(in_kind, ty) << " cannot be dynamically indexed";
dan sinclaire4952f32023-07-14 17:47:29 +00002437 return;
dan sinclair4574f072023-07-12 17:10:21 +00002438 }
Ben Clayton120ca8e2024-03-28 16:22:29 +00002439 ty = el;
dan sinclair165fdaa2023-07-12 13:03:21 +00002440 }
2441 }
Ben Claytona8fce7c2023-07-12 18:39:00 +00002442
Ben Claytonaa774842024-01-12 18:28:20 +00002443 auto* want = a->Result(0)->Type();
Ben Clayton120ca8e2024-03-28 16:22:29 +00002444 auto* want_view = want->As<type::MemoryView>();
James Price230657c2024-05-15 18:56:08 +00002445 bool ok = true;
2446 if (obj_view) {
2447 // Pointer source always means pointer result.
2448 ok = want_view && ty == want_view->StoreType();
2449 if (ok) {
2450 // Also check that the address space and access modes match.
2451 ok = obj_view->Is<type::Pointer>() == want_view->Is<type::Pointer>() &&
2452 obj_view->AddressSpace() == want_view->AddressSpace() &&
2453 obj_view->Access() == want_view->Access();
2454 }
2455 } else {
2456 // Otherwise, result types should exactly match.
2457 ok = ty == want;
Ben Claytonaa774842024-01-12 18:28:20 +00002458 }
dan sinclaira6941c62024-08-29 21:34:20 +00002459 if (DAWN_UNLIKELY(!ok)) {
Ben Clayton120ca8e2024-03-28 16:22:29 +00002460 AddError(a) << "result of access chain is type " << desc_of(in_kind, ty)
Ben Clayton0a60d522024-05-09 16:42:22 +00002461 << " but instruction type is " << style::Type(want->FriendlyName());
dan sinclaire4952f32023-07-14 17:47:29 +00002462 }
2463}
Ben Claytona8fce7c2023-07-12 18:39:00 +00002464
Ben Clayton1e7b3122023-11-20 12:38:31 +00002465void Validator::CheckBinary(const Binary* b) {
Ryan Harrisonfb61de42024-07-23 17:09:40 +00002466 if (!CheckResultsAndOperandRange(b, Binary::kNumResults, Binary::kNumOperands)) {
2467 return;
2468 }
2469
Ben Clayton514a18a2024-01-09 10:44:27 +00002470 if (b->LHS() && b->RHS()) {
Ben Clayton7e9f1a62024-05-22 18:42:55 +00002471 intrinsic::Context context{b->TableData(), type_mgr_, symbols_};
Ben Clayton514a18a2024-01-09 10:44:27 +00002472
2473 auto overload =
2474 core::intrinsic::LookupBinary(context, b->Op(), b->LHS()->Type(), b->RHS()->Type(),
2475 core::EvaluationStage::kRuntime, /* is_compound */ false);
2476 if (overload != Success) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002477 AddError(b) << overload.Failure();
Ben Clayton514a18a2024-01-09 10:44:27 +00002478 return;
2479 }
2480
2481 if (auto* result = b->Result(0)) {
2482 if (overload->return_type != result->Type()) {
Ben Clayton0a60d522024-05-09 16:42:22 +00002483 AddError(b) << "result value type " << style::Type(result->Type()->FriendlyName())
2484 << " does not match "
dan sinclair650bc3e2024-06-11 23:26:53 +00002485 << style::Instruction(Disassemble().NameOf(b->Op())) << " result type "
Ben Clayton0a60d522024-05-09 16:42:22 +00002486 << style::Type(overload->return_type->FriendlyName());
Ben Clayton514a18a2024-01-09 10:44:27 +00002487 }
2488 }
2489 }
dan sinclaire4952f32023-07-14 17:47:29 +00002490}
2491
Ben Clayton1e7b3122023-11-20 12:38:31 +00002492void Validator::CheckUnary(const Unary* u) {
Ryan Harrison7944dde2024-07-23 19:38:41 +00002493 if (!CheckResultsAndOperandRange(u, Unary::kNumResults, Unary::kNumOperands)) {
2494 return;
2495 }
2496
Ben Claytonb865a1e2024-01-08 13:01:50 +00002497 if (u->Val()) {
Ben Clayton7e9f1a62024-05-22 18:42:55 +00002498 intrinsic::Context context{u->TableData(), type_mgr_, symbols_};
dan sinclaire4952f32023-07-14 17:47:29 +00002499
Ben Claytonb865a1e2024-01-08 13:01:50 +00002500 auto overload = core::intrinsic::LookupUnary(context, u->Op(), u->Val()->Type(),
2501 core::EvaluationStage::kRuntime);
2502 if (overload != Success) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002503 AddError(u) << overload.Failure();
Ben Claytonb865a1e2024-01-08 13:01:50 +00002504 return;
2505 }
2506
2507 if (auto* result = u->Result(0)) {
2508 if (overload->return_type != result->Type()) {
Ben Clayton0a60d522024-05-09 16:42:22 +00002509 AddError(u) << "result value type " << style::Type(result->Type()->FriendlyName())
2510 << " does not match "
dan sinclair650bc3e2024-06-11 23:26:53 +00002511 << style::Instruction(Disassemble().NameOf(u->Op())) << " result type "
Ben Clayton0a60d522024-05-09 16:42:22 +00002512 << style::Type(overload->return_type->FriendlyName());
Ben Claytonb865a1e2024-01-08 13:01:50 +00002513 }
dan sinclaire4952f32023-07-14 17:47:29 +00002514 }
2515 }
2516}
2517
Ben Clayton1e7b3122023-11-20 12:38:31 +00002518void Validator::CheckIf(const If* if_) {
Ryan Harrisone8b48672024-09-25 17:21:11 +00002519 CheckResults(if_);
2520 CheckOperand(if_, If::kConditionOperandOffset);
dan sinclaire4952f32023-07-14 17:47:29 +00002521
dan sinclaircedcdf32023-08-10 02:39:48 +00002522 if (if_->Condition() && !if_->Condition()->Type()->Is<core::type::Bool>()) {
Ben Clayton0a60d522024-05-09 16:42:22 +00002523 AddError(if_, If::kConditionOperandOffset)
2524 << "condition type must be " << style::Type("bool");
dan sinclaire4952f32023-07-14 17:47:29 +00002525 }
2526
Ben Clayton833b8922024-05-01 21:07:24 +00002527 tasks_.Push([this] { control_stack_.Pop(); });
dan sinclaire4952f32023-07-14 17:47:29 +00002528
dan sinclaire4952f32023-07-14 17:47:29 +00002529 if (!if_->False()->IsEmpty()) {
Ben Clayton833b8922024-05-01 21:07:24 +00002530 QueueBlock(if_->False());
dan sinclaire4952f32023-07-14 17:47:29 +00002531 }
Ben Clayton833b8922024-05-01 21:07:24 +00002532
2533 QueueBlock(if_->True());
2534
2535 tasks_.Push([this, if_] { control_stack_.Push(if_); });
dan sinclaire4952f32023-07-14 17:47:29 +00002536}
2537
Ben Clayton1e7b3122023-11-20 12:38:31 +00002538void Validator::CheckLoop(const Loop* l) {
Ben Clayton833b8922024-05-01 21:07:24 +00002539 // Note: Tasks are queued in reverse order of their execution
Ben Clayton7b35ff12024-05-13 16:44:35 +00002540 tasks_.Push([this, l] {
2541 first_continues_.Remove(l); // No need for this any more. Free memory.
2542 control_stack_.Pop();
2543 });
dan sinclaire4952f32023-07-14 17:47:29 +00002544 if (!l->Initializer()->IsEmpty()) {
Ben Clayton833b8922024-05-01 21:07:24 +00002545 tasks_.Push([this] { EndBlock(); });
dan sinclaire4952f32023-07-14 17:47:29 +00002546 }
Ben Clayton833b8922024-05-01 21:07:24 +00002547 tasks_.Push([this] { EndBlock(); });
2548 if (!l->Continuing()->IsEmpty()) {
2549 tasks_.Push([this] { EndBlock(); });
2550 }
2551
2552 // ⎡Initializer ⎤
2553 // ⎢ ⎡Body ⎤⎥
2554 // ⎣ ⎣ [Continuing ] ⎦⎦
dan sinclaire4952f32023-07-14 17:47:29 +00002555
2556 if (!l->Continuing()->IsEmpty()) {
Ben Clayton7b35ff12024-05-13 16:44:35 +00002557 tasks_.Push([this, l] {
2558 CheckLoopContinuing(l);
2559 BeginBlock(l->Continuing());
2560 });
dan sinclaire4952f32023-07-14 17:47:29 +00002561 }
Ben Clayton7b35ff12024-05-13 16:44:35 +00002562
James Pricea0025032024-05-29 15:38:19 +00002563 tasks_.Push([this, l] {
2564 CheckLoopBody(l);
2565 BeginBlock(l->Body());
2566 });
Ben Clayton833b8922024-05-01 21:07:24 +00002567 if (!l->Initializer()->IsEmpty()) {
2568 tasks_.Push([this, l] { BeginBlock(l->Initializer()); });
2569 }
2570 tasks_.Push([this, l] { control_stack_.Push(l); });
dan sinclaire4952f32023-07-14 17:47:29 +00002571}
2572
James Pricea0025032024-05-29 15:38:19 +00002573void Validator::CheckLoopBody(const Loop* loop) {
2574 // If the body block has parameters, there must be an initializer block.
2575 if (!loop->Body()->Params().IsEmpty()) {
2576 if (!loop->HasInitializer()) {
2577 AddError(loop) << "loop with body block parameters must have an initializer";
2578 }
2579 }
2580}
2581
Ben Clayton7b35ff12024-05-13 16:44:35 +00002582void Validator::CheckLoopContinuing(const Loop* loop) {
2583 if (!loop->HasContinuing()) {
2584 return;
2585 }
2586
2587 // Ensure that values used in the loop continuing are not from the loop body, after a
2588 // continue instruction.
2589 if (auto* first_continue = first_continues_.GetOr(loop, nullptr)) {
Ryan Harrison2cbfbd42024-09-23 16:59:01 +00002590 // Find the instruction in the body block that is or holds the first continue
2591 // instruction.
Ben Clayton7b35ff12024-05-13 16:44:35 +00002592 const Instruction* holds_continue = first_continue;
2593 while (holds_continue && holds_continue->Block() &&
2594 holds_continue->Block() != loop->Body()) {
2595 holds_continue = holds_continue->Block()->Parent();
2596 }
2597
2598 // Check that all subsequent instruction values are not used in the continuing block.
2599 for (auto* inst = holds_continue; inst; inst = inst->next) {
2600 for (auto* result : inst->Results()) {
dan sinclair3cfca9f2024-08-06 14:06:39 +00002601 result->ForEachUseUnsorted([&](Usage use) {
Ben Clayton7b35ff12024-05-13 16:44:35 +00002602 if (TransitivelyHolds(loop->Continuing(), use.instruction)) {
2603 AddError(use.instruction, use.operand_index)
2604 << NameOf(result)
Ryan Harrison2cbfbd42024-09-23 16:59:01 +00002605 << " cannot be used in continuing block as it is declared after "
2606 "the "
Ben Clayton7b35ff12024-05-13 16:44:35 +00002607 "first "
2608 << style::Instruction("continue") << " in the loop's body";
2609 AddDeclarationNote(result);
2610 AddNote(first_continue)
2611 << "loop body's first " << style::Instruction("continue");
2612 }
2613 });
2614 }
2615 }
2616 }
2617}
2618
Ben Clayton1e7b3122023-11-20 12:38:31 +00002619void Validator::CheckSwitch(const Switch* s) {
Ryan Harrisone8b48672024-09-25 17:21:11 +00002620 CheckOperand(s, Switch::kConditionOperandOffset);
dan sinclaira9c7be02024-06-21 17:35:20 +00002621
dan sinclair06b574a2024-08-26 17:19:25 +00002622 if (s->Condition() && !s->Condition()->Type()->IsIntegerScalar()) {
dan sinclaira9c7be02024-06-21 17:35:20 +00002623 AddError(s, Switch::kConditionOperandOffset) << "condition type must be an integer scalar";
2624 }
2625
Ben Clayton833b8922024-05-01 21:07:24 +00002626 tasks_.Push([this] { control_stack_.Pop(); });
dan sinclaire4952f32023-07-14 17:47:29 +00002627
dan sinclaira9c7be02024-06-21 17:35:20 +00002628 bool found_default = false;
dan sinclaire4952f32023-07-14 17:47:29 +00002629 for (auto& cse : s->Cases()) {
Ben Clayton833b8922024-05-01 21:07:24 +00002630 QueueBlock(cse.block);
dan sinclaira9c7be02024-06-21 17:35:20 +00002631
2632 for (const auto& sel : cse.selectors) {
2633 if (sel.IsDefault()) {
2634 found_default = true;
2635 }
2636 }
2637 }
2638
2639 if (!found_default) {
2640 AddError(s) << "missing default case for switch";
dan sinclaire4952f32023-07-14 17:47:29 +00002641 }
Ben Clayton833b8922024-05-01 21:07:24 +00002642
2643 tasks_.Push([this, s] { control_stack_.Push(s); });
dan sinclaire4952f32023-07-14 17:47:29 +00002644}
2645
Ryan Harrison8b8ef022024-08-14 22:44:24 +00002646void Validator::CheckSwizzle(const Swizzle* s) {
2647 if (!CheckResultsAndOperands(s, Swizzle::kNumResults, Swizzle::kNumOperands)) {
2648 return;
2649 }
2650
2651 auto indices = s->Indices();
2652 if (indices.Length() < Swizzle::kMinNumIndices) {
2653 AddError(s) << "expected at least " << Swizzle::kMinNumIndices << " indices";
2654 }
2655
2656 if (indices.Length() > Swizzle::kMaxNumIndices) {
2657 AddError(s) << "expected at most " << Swizzle::kMaxNumIndices << " indices";
2658 }
2659
2660 for (auto& idx : indices) {
2661 if (idx > Swizzle::kMaxIndexValue) {
2662 AddError(s) << "invalid index value";
2663 }
2664 }
2665}
2666
Ben Clayton1e7b3122023-11-20 12:38:31 +00002667void Validator::CheckTerminator(const Terminator* b) {
dan sinclaire4952f32023-07-14 17:47:29 +00002668 // Note, transforms create `undef` terminator arguments (this is done in MergeReturn and
2669 // DemoteToHelper) so we can't add validation.
2670
2671 tint::Switch(
Ben Claytoncabf6222024-05-13 17:11:12 +00002672 b, //
Ben Claytonc951b862024-05-22 07:36:00 +00002673 [&](const ir::BreakIf* i) { CheckBreakIf(i); }, //
Ben Claytoncabf6222024-05-13 17:11:12 +00002674 [&](const ir::Continue* c) { CheckContinue(c); }, //
2675 [&](const ir::Exit* e) { CheckExit(e); }, //
2676 [&](const ir::NextIteration* n) { CheckNextIteration(n); }, //
2677 [&](const ir::Return* ret) { CheckReturn(ret); }, //
2678 [&](const ir::TerminateInvocation*) {}, //
Ryan Harrisone2d11b02024-09-24 19:32:29 +00002679 [&](const ir::Unreachable* u) { CheckUnreachable(u); }, //
Ben Claytonc27315a2024-02-26 20:24:06 +00002680 [&](Default) { AddError(b) << "missing validation"; });
Ben Claytonf46acc52024-05-21 20:18:17 +00002681
2682 if (b->next) {
2683 AddError(b) << "must be the last instruction in the block";
2684 }
dan sinclaire4952f32023-07-14 17:47:29 +00002685}
2686
Ben Claytonc951b862024-05-22 07:36:00 +00002687void Validator::CheckBreakIf(const BreakIf* b) {
2688 auto* loop = b->Loop();
2689 if (loop == nullptr) {
2690 AddError(b) << "has no associated loop";
2691 return;
2692 }
2693
2694 if (loop->Continuing() != b->Block()) {
2695 AddError(b) << "must only be called directly from loop continuing";
2696 }
2697
2698 auto next_iter_values = b->NextIterValues();
2699 if (auto* body = loop->Body()) {
2700 CheckOperandsMatchTarget(b, b->ArgsOperandOffset(), next_iter_values.Length(), body,
2701 body->Params());
2702 }
2703
2704 auto exit_values = b->ExitValues();
2705 CheckOperandsMatchTarget(b, b->ArgsOperandOffset() + next_iter_values.Length(),
2706 exit_values.Length(), loop, loop->Results());
2707}
2708
Ben Clayton7b35ff12024-05-13 16:44:35 +00002709void Validator::CheckContinue(const Continue* c) {
2710 auto* loop = c->Loop();
2711 if (loop == nullptr) {
2712 AddError(c) << "has no associated loop";
2713 return;
2714 }
2715 if (!TransitivelyHolds(loop->Body(), c)) {
2716 if (control_stack_.Any(Eq<const ControlInstruction*>(loop))) {
2717 AddError(c) << "must only be called from loop body";
2718 } else {
2719 AddError(c) << "called outside of associated loop";
2720 }
2721 }
2722
Ben Claytonfac459a2024-05-22 07:36:00 +00002723 if (auto* cont = loop->Continuing()) {
2724 CheckOperandsMatchTarget(c, Continue::kArgsOperandOffset, c->Args().Length(), cont,
2725 cont->Params());
2726 }
2727
Ben Clayton7b35ff12024-05-13 16:44:35 +00002728 first_continues_.Add(loop, c);
2729}
2730
Ben Clayton1e7b3122023-11-20 12:38:31 +00002731void Validator::CheckExit(const Exit* e) {
dan sinclaire4952f32023-07-14 17:47:29 +00002732 if (e->ControlInstruction() == nullptr) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002733 AddError(e) << "has no parent control instruction";
dan sinclaire4952f32023-07-14 17:47:29 +00002734 return;
2735 }
2736
2737 if (control_stack_.IsEmpty()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002738 AddError(e) << "found outside all control instructions";
dan sinclaire4952f32023-07-14 17:47:29 +00002739 return;
2740 }
2741
dan sinclaire4952f32023-07-14 17:47:29 +00002742 auto args = e->Args();
Ben Clayton48360ea2024-05-22 07:36:00 +00002743 CheckOperandsMatchTarget(e, e->ArgsOperandOffset(), args.Length(), e->ControlInstruction(),
2744 e->ControlInstruction()->Results());
dan sinclaire4952f32023-07-14 17:47:29 +00002745
2746 tint::Switch(
Ben Clayton1e7b3122023-11-20 12:38:31 +00002747 e, //
2748 [&](const ir::ExitIf* i) { CheckExitIf(i); }, //
2749 [&](const ir::ExitLoop* l) { CheckExitLoop(l); }, //
2750 [&](const ir::ExitSwitch* s) { CheckExitSwitch(s); }, //
Ben Claytonc27315a2024-02-26 20:24:06 +00002751 [&](Default) { AddError(e) << "missing validation"; });
dan sinclaire4952f32023-07-14 17:47:29 +00002752}
2753
Ben Claytoncabf6222024-05-13 17:11:12 +00002754void Validator::CheckNextIteration(const NextIteration* n) {
2755 auto* loop = n->Loop();
2756 if (loop == nullptr) {
2757 AddError(n) << "has no associated loop";
2758 return;
2759 }
2760 if (!TransitivelyHolds(loop->Initializer(), n) && !TransitivelyHolds(loop->Continuing(), n)) {
2761 if (control_stack_.Any(Eq<const ControlInstruction*>(loop))) {
2762 AddError(n) << "must only be called from loop initializer or continuing";
2763 } else {
2764 AddError(n) << "called outside of associated loop";
2765 }
2766 }
Ben Clayton6654d1f2024-05-22 07:36:00 +00002767
2768 if (auto* body = loop->Body()) {
2769 CheckOperandsMatchTarget(n, NextIteration::kArgsOperandOffset, n->Args().Length(), body,
2770 body->Params());
2771 }
Ben Claytoncabf6222024-05-13 17:11:12 +00002772}
2773
Ben Clayton1e7b3122023-11-20 12:38:31 +00002774void Validator::CheckExitIf(const ExitIf* e) {
dan sinclaire4952f32023-07-14 17:47:29 +00002775 if (control_stack_.Back() != e->If()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002776 AddError(e) << "if target jumps over other control instructions";
2777 AddNote(control_stack_.Back()) << "first control instruction jumped";
dan sinclaire4952f32023-07-14 17:47:29 +00002778 }
2779}
2780
Ben Clayton1e7b3122023-11-20 12:38:31 +00002781void Validator::CheckReturn(const Return* ret) {
Ryan Harrison8570a642024-08-15 21:33:38 +00002782 if (!CheckResultsAndOperandRange(ret, Return::kNumResults, Return::kMinOperands,
2783 Return::kMaxOperands)) {
James Price17d6eda2023-07-21 15:08:02 +00002784 return;
2785 }
Ryan Harrison8570a642024-08-15 21:33:38 +00002786
2787 auto* func = ret->Func();
2788 if (func == nullptr) {
Ryan Harrison2cbfbd42024-09-23 16:59:01 +00002789 // Func() returning nullptr after CheckResultsAndOperandRange is due to the first
2790 // operand being not a function
Ryan Harrison8570a642024-08-15 21:33:38 +00002791 AddError(ret) << "expected function for first operand";
2792 return;
2793 }
2794
dan sinclaircedcdf32023-08-10 02:39:48 +00002795 if (func->ReturnType()->Is<core::type::Void>()) {
James Price17d6eda2023-07-21 15:08:02 +00002796 if (ret->Value()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002797 AddError(ret) << "unexpected return value";
James Price17d6eda2023-07-21 15:08:02 +00002798 }
2799 } else {
2800 if (!ret->Value()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002801 AddError(ret) << "expected return value";
James Price17d6eda2023-07-21 15:08:02 +00002802 } else if (ret->Value()->Type() != func->ReturnType()) {
Ryan Harrison8570a642024-08-15 21:33:38 +00002803 AddError(ret) << "return value type " << NameOf(ret->Value()->Type())
2804 << " does not match function return type " << NameOf(func->ReturnType());
James Price17d6eda2023-07-21 15:08:02 +00002805 }
2806 }
2807}
2808
Ryan Harrisone2d11b02024-09-24 19:32:29 +00002809void Validator::CheckUnreachable(const Unreachable* u) {
2810 CheckResultsAndOperands(u, Unreachable::kNumResults, Unreachable::kNumOperands);
2811}
2812
Ben Clayton1e7b3122023-11-20 12:38:31 +00002813void Validator::CheckControlsAllowingIf(const Exit* exit, const Instruction* control) {
dan sinclaire4952f32023-07-14 17:47:29 +00002814 bool found = false;
dan sinclairbae54e72023-07-28 15:01:54 +00002815 for (auto ctrl : tint::Reverse(control_stack_)) {
dan sinclaire4952f32023-07-14 17:47:29 +00002816 if (ctrl == control) {
2817 found = true;
2818 break;
2819 }
2820 // A exit switch can step over if instructions, but no others.
2821 if (!ctrl->Is<ir::If>()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002822 AddError(exit) << control->FriendlyName()
2823 << " target jumps over other control instructions";
2824 AddNote(ctrl) << "first control instruction jumped";
dan sinclaire4952f32023-07-14 17:47:29 +00002825 return;
2826 }
2827 }
2828 if (!found) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002829 AddError(exit) << control->FriendlyName() << " not found in parent control instructions";
dan sinclaire4952f32023-07-14 17:47:29 +00002830 }
2831}
2832
Ben Clayton1e7b3122023-11-20 12:38:31 +00002833void Validator::CheckExitSwitch(const ExitSwitch* s) {
dan sinclaire4952f32023-07-14 17:47:29 +00002834 CheckControlsAllowingIf(s, s->ControlInstruction());
2835}
2836
Ben Clayton1e7b3122023-11-20 12:38:31 +00002837void Validator::CheckExitLoop(const ExitLoop* l) {
dan sinclaire4952f32023-07-14 17:47:29 +00002838 CheckControlsAllowingIf(l, l->ControlInstruction());
2839
Ben Clayton1e7b3122023-11-20 12:38:31 +00002840 const Instruction* inst = l;
2841 const Loop* control = l->Loop();
dan sinclaire4952f32023-07-14 17:47:29 +00002842 while (inst) {
2843 // Found parent loop
2844 if (inst->Block()->Parent() == control) {
2845 if (inst->Block() == control->Continuing()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002846 AddError(l) << "loop exit jumps out of continuing block";
dan sinclaire4952f32023-07-14 17:47:29 +00002847 if (control->Continuing() != l->Block()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002848 AddNote(control->Continuing()) << "in continuing block";
dan sinclaire4952f32023-07-14 17:47:29 +00002849 }
2850 } else if (inst->Block() == control->Initializer()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002851 AddError(l) << "loop exit not permitted in loop initializer";
dan sinclaire4952f32023-07-14 17:47:29 +00002852 if (control->Initializer() != l->Block()) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002853 AddNote(control->Initializer()) << "in initializer block";
Ben Claytona8fce7c2023-07-12 18:39:00 +00002854 }
2855 }
dan sinclaire4952f32023-07-14 17:47:29 +00002856 break;
Ben Claytona8fce7c2023-07-12 18:39:00 +00002857 }
dan sinclaire4952f32023-07-14 17:47:29 +00002858 inst = inst->Block()->Parent();
Ben Claytona8fce7c2023-07-12 18:39:00 +00002859 }
dan sinclaire4952f32023-07-14 17:47:29 +00002860}
Ben Claytona8fce7c2023-07-12 18:39:00 +00002861
James Price87a211c2024-01-11 15:11:17 +00002862void Validator::CheckLoad(const Load* l) {
Ryan Harrison244e32b2024-07-15 23:08:14 +00002863 if (!CheckResultsAndOperands(l, Load::kNumResults, Load::kNumOperands)) {
Ryan Harrison23c42fc2024-07-12 02:32:25 +00002864 return;
2865 }
James Price87a211c2024-01-11 15:11:17 +00002866
2867 if (auto* from = l->From()) {
2868 auto* mv = from->Type()->As<core::type::MemoryView>();
2869 if (!mv) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002870 AddError(l, Load::kFromOperandOffset) << "load source operand is not a memory view";
James Price87a211c2024-01-11 15:11:17 +00002871 return;
2872 }
2873 if (l->Result(0)->Type() != mv->StoreType()) {
Ben Clayton0a60d522024-05-09 16:42:22 +00002874 AddError(l, Load::kFromOperandOffset)
2875 << "result type " << style::Type(l->Result(0)->Type()->FriendlyName())
2876 << " does not match source store type "
2877 << style::Type(mv->StoreType()->FriendlyName());
James Price87a211c2024-01-11 15:11:17 +00002878 }
2879 }
2880}
2881
Ben Clayton1e7b3122023-11-20 12:38:31 +00002882void Validator::CheckStore(const Store* s) {
Ryan Harrison244e32b2024-07-15 23:08:14 +00002883 if (!CheckResultsAndOperands(s, Store::kNumResults, Store::kNumOperands)) {
Ryan Harrisonad423942024-07-12 00:48:07 +00002884 return;
2885 }
James Price8fade132023-09-20 11:42:42 +00002886
2887 if (auto* from = s->From()) {
2888 if (auto* to = s->To()) {
dan sinclair7cd0dfe2024-07-11 04:41:29 +00002889 auto* mv = As<core::type::MemoryView>(to->Type());
Ben Clayton9887c6e2024-01-09 22:50:11 +00002890 if (!mv) {
James Price1b0c8952024-05-30 14:32:11 +00002891 AddError(s, Store::kToOperandOffset) << "store target operand is not a memory view";
Ben Clayton9887c6e2024-01-09 22:50:11 +00002892 return;
2893 }
Ben Clayton0a60d522024-05-09 16:42:22 +00002894 auto* value_type = from->Type();
2895 auto* store_type = mv->StoreType();
Ryan Harrison54b6c1c2024-07-29 20:50:19 +00002896 if (value_type != store_type) {
Ben Clayton0a60d522024-05-09 16:42:22 +00002897 AddError(s, Store::kFromOperandOffset)
2898 << "value type " << style::Type(value_type->FriendlyName())
2899 << " does not match store type " << style::Type(store_type->FriendlyName());
James Price8fade132023-09-20 11:42:42 +00002900 }
2901 }
2902 }
2903}
2904
Ben Clayton1e7b3122023-11-20 12:38:31 +00002905void Validator::CheckLoadVectorElement(const LoadVectorElement* l) {
Ryan Harrison585946e2024-07-25 19:18:49 +00002906 if (!CheckResultsAndOperands(l, LoadVectorElement::kNumResults,
2907 LoadVectorElement::kNumOperands)) {
2908 return;
2909 }
Ben Claytona8fce7c2023-07-12 18:39:00 +00002910
Ben Claytona89d6642023-11-17 22:41:32 +00002911 if (auto* res = l->Result(0)) {
dan sinclaire4952f32023-07-14 17:47:29 +00002912 if (auto* el_ty = GetVectorPtrElementType(l, LoadVectorElement::kFromOperandOffset)) {
2913 if (res->Type() != el_ty) {
Ben Clayton0a60d522024-05-09 16:42:22 +00002914 AddResultError(l, 0) << "result type " << style::Type(res->Type()->FriendlyName())
2915 << " does not match vector pointer element type "
2916 << style::Type(el_ty->FriendlyName());
Ben Claytona8fce7c2023-07-12 18:39:00 +00002917 }
2918 }
2919 }
dan sinclaire4952f32023-07-14 17:47:29 +00002920}
Ben Claytona8fce7c2023-07-12 18:39:00 +00002921
Ben Clayton1e7b3122023-11-20 12:38:31 +00002922void Validator::CheckStoreVectorElement(const StoreVectorElement* s) {
Ryan Harrison773bce42024-07-30 00:38:07 +00002923 if (!CheckResultsAndOperands(s, StoreVectorElement::kNumResults,
2924 StoreVectorElement::kNumOperands)) {
2925 return;
2926 }
Ben Claytona8fce7c2023-07-12 18:39:00 +00002927
dan sinclaire4952f32023-07-14 17:47:29 +00002928 if (auto* value = s->Value()) {
2929 if (auto* el_ty = GetVectorPtrElementType(s, StoreVectorElement::kToOperandOffset)) {
2930 if (value->Type() != el_ty) {
Ben Claytonc27315a2024-02-26 20:24:06 +00002931 AddError(s, StoreVectorElement::kValueOperandOffset)
Ben Clayton0a60d522024-05-09 16:42:22 +00002932 << "value type " << style::Type(value->Type()->FriendlyName())
2933 << " does not match vector pointer element type "
2934 << style::Type(el_ty->FriendlyName());
Ben Claytona8fce7c2023-07-12 18:39:00 +00002935 }
2936 }
dan sinclaire4952f32023-07-14 17:47:29 +00002937 }
2938}
Ben Claytona8fce7c2023-07-12 18:39:00 +00002939
Ben Claytonc951b862024-05-22 07:36:00 +00002940void Validator::CheckOperandsMatchTarget(const Instruction* source_inst,
2941 size_t source_operand_offset,
2942 size_t source_operand_count,
2943 const CastableBase* target,
2944 VectorRef<const Value*> target_values) {
2945 if (source_operand_count != target_values.Length()) {
2946 auto values = [&](size_t n) { return n == 1 ? " value" : " values"; };
2947 AddError(source_inst) << "provides " << source_operand_count << values(source_operand_count)
2948 << " but " << NameOf(target) << " expects " << target_values.Length()
2949 << values(target_values.Length());
2950 AddDeclarationNote(target);
2951 }
2952 size_t count = std::min(source_operand_count, target_values.Length());
2953 for (size_t i = 0; i < count; i++) {
2954 auto* source_value = source_inst->Operand(source_operand_offset + i);
2955 auto* target_value = target_values[i];
2956 if (!source_value || !target_value) {
2957 continue; // Caller should be checking operands are not null
2958 }
2959 auto* source_type = source_value->Type();
2960 auto* target_type = target_value->Type();
2961 if (source_type != target_type) {
2962 AddError(source_inst, source_operand_offset + i)
2963 << "operand with type " << style::Type(source_type->FriendlyName())
2964 << " does not match " << NameOf(target) << " target type "
2965 << style::Type(target_type->FriendlyName());
2966 AddDeclarationNote(target_value);
2967 }
2968 }
2969}
2970
Ben Clayton1e7b3122023-11-20 12:38:31 +00002971const core::type::Type* Validator::GetVectorPtrElementType(const Instruction* inst, size_t idx) {
dan sinclaire4952f32023-07-14 17:47:29 +00002972 auto* operand = inst->Operands()[idx];
dan sinclaira6941c62024-08-29 21:34:20 +00002973 if (DAWN_UNLIKELY(!operand)) {
Ben Claytona8fce7c2023-07-12 18:39:00 +00002974 return nullptr;
2975 }
dan sinclair5ccafa42023-06-03 14:57:42 +00002976
dan sinclaire4952f32023-07-14 17:47:29 +00002977 auto* type = operand->Type();
dan sinclaira6941c62024-08-29 21:34:20 +00002978 if (DAWN_UNLIKELY(!type)) {
dan sinclaire4952f32023-07-14 17:47:29 +00002979 return nullptr;
2980 }
2981
Ben Clayton120ca8e2024-03-28 16:22:29 +00002982 auto* memory_view_ty = type->As<core::type::MemoryView>();
dan sinclaira6941c62024-08-29 21:34:20 +00002983 if (DAWN_LIKELY(memory_view_ty)) {
Ben Clayton120ca8e2024-03-28 16:22:29 +00002984 auto* vec_ty = memory_view_ty->StoreType()->As<core::type::Vector>();
dan sinclaira6941c62024-08-29 21:34:20 +00002985 if (DAWN_LIKELY(vec_ty)) {
dan sinclaireb2da252024-08-26 16:57:23 +00002986 return vec_ty->Type();
dan sinclaire4952f32023-07-14 17:47:29 +00002987 }
2988 }
2989
Ben Clayton0a60d522024-05-09 16:42:22 +00002990 AddError(inst, idx) << "operand must be a pointer to vector, got "
2991 << style::Type(type->FriendlyName());
dan sinclaire4952f32023-07-14 17:47:29 +00002992 return nullptr;
2993}
dan sinclair5ccafa42023-06-03 14:57:42 +00002994
Ben Clayton4ebed9d2023-09-05 14:39:05 +00002995} // namespace
2996
Ben Clayton7711bfc2024-03-28 17:38:16 +00002997Result<SuccessType> Validate(const Module& mod, Capabilities capabilities) {
James Price03ecbbf2024-01-17 17:01:30 +00002998 Validator v(mod, capabilities);
Ben Clayton16fb2542023-09-25 11:43:19 +00002999 return v.Run();
dan sinclair5ccafa42023-06-03 14:57:42 +00003000}
3001
Ben Clayton1e7b3122023-11-20 12:38:31 +00003002Result<SuccessType> ValidateAndDumpIfNeeded([[maybe_unused]] const Module& ir,
James Price03ecbbf2024-01-17 17:01:30 +00003003 [[maybe_unused]] const char* msg,
Ben Clayton7711bfc2024-03-28 17:38:16 +00003004 [[maybe_unused]] Capabilities capabilities) {
James Price2eae44a2023-09-20 23:24:15 +00003005#if TINT_DUMP_IR_WHEN_VALIDATING
James Price2ee45462024-05-03 17:37:14 +00003006 auto printer = StyledTextPrinter::Create(stdout);
dan sinclair8700be52024-05-31 18:07:26 +00003007 std::cout << "=========================================================\n";
3008 std::cout << "== IR dump before " << msg << ":\n";
3009 std::cout << "=========================================================\n";
James Price57c15802024-06-13 06:23:47 +00003010 printer->Print(Disassembler(ir).Text());
James Price2eae44a2023-09-20 23:24:15 +00003011#endif
3012
James Pricedb46be12023-08-01 17:15:35 +00003013#ifndef NDEBUG
James Price03ecbbf2024-01-17 17:01:30 +00003014 auto result = Validate(ir, capabilities);
Ben Clayton89274f72024-01-03 10:53:42 +00003015 if (result != Success) {
Ben Clayton16fb2542023-09-25 11:43:19 +00003016 return result.Failure();
James Pricedb46be12023-08-01 17:15:35 +00003017 }
3018#endif
3019
James Pricedb46be12023-08-01 17:15:35 +00003020 return Success;
3021}
3022
dan sinclair6f138fe2023-08-15 21:29:34 +00003023} // namespace tint::core::ir
dan sinclairfbc4ce72024-10-21 19:25:39 +00003024
3025namespace std {
3026
3027template <>
3028struct hash<tint::core::ir::ValidatedType> {
3029 size_t operator()(const tint::core::ir::ValidatedType& v) const { return Hash(v.ty, v.caps); }
3030};
3031
3032template <>
3033struct equal_to<tint::core::ir::ValidatedType> {
3034 bool operator()(const tint::core::ir::ValidatedType& a,
3035 const tint::core::ir::ValidatedType& b) const {
3036 return a.ty->Equals(*(b.ty)) && a.caps == b.caps;
3037 }
3038};
3039
3040} // namespace std