blob: acebcde7e84131d504160aa22918b224a397a379 [file] [log] [blame]
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001/// Copyright 2020 The Tint Authors.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
dan sinclair0bfeaf92023-07-26 17:39:51 +000015#include "src/tint/lang/hlsl/writer/ast_printer/ast_printer.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000016
17#include <algorithm>
18#include <cmath>
19#include <functional>
20#include <iomanip>
21#include <set>
22#include <utility>
23#include <vector>
24
dan sinclair352f8c82023-07-21 00:40:07 +000025#include "src/tint/lang/core/constant/splat.h"
26#include "src/tint/lang/core/constant/value.h"
dan sinclairce6dffe2023-08-14 21:01:40 +000027#include "src/tint/lang/core/fluent_types.h"
dan sinclair352f8c82023-07-21 00:40:07 +000028#include "src/tint/lang/core/type/array.h"
29#include "src/tint/lang/core/type/atomic.h"
30#include "src/tint/lang/core/type/depth_multisampled_texture.h"
31#include "src/tint/lang/core/type/depth_texture.h"
32#include "src/tint/lang/core/type/multisampled_texture.h"
33#include "src/tint/lang/core/type/sampled_texture.h"
34#include "src/tint/lang/core/type/storage_texture.h"
35#include "src/tint/lang/core/type/texture_dimension.h"
Ben Clayton7a5f54ea2023-09-06 16:58:22 +000036#include "src/tint/lang/hlsl/writer/ast_raise/calculate_array_length.h"
37#include "src/tint/lang/hlsl/writer/ast_raise/decompose_memory_access.h"
38#include "src/tint/lang/hlsl/writer/ast_raise/localize_struct_array_assignment.h"
39#include "src/tint/lang/hlsl/writer/ast_raise/num_workgroups_from_uniform.h"
40#include "src/tint/lang/hlsl/writer/ast_raise/remove_continue_in_switch.h"
41#include "src/tint/lang/hlsl/writer/ast_raise/truncate_interstage_variables.h"
dan sinclair99181d82023-07-20 01:14:15 +000042#include "src/tint/lang/wgsl/ast/call_statement.h"
43#include "src/tint/lang/wgsl/ast/id_attribute.h"
44#include "src/tint/lang/wgsl/ast/internal_attribute.h"
45#include "src/tint/lang/wgsl/ast/interpolate_attribute.h"
46#include "src/tint/lang/wgsl/ast/transform/add_empty_entry_point.h"
47#include "src/tint/lang/wgsl/ast/transform/array_length_from_uniform.h"
48#include "src/tint/lang/wgsl/ast/transform/binding_remapper.h"
49#include "src/tint/lang/wgsl/ast/transform/builtin_polyfill.h"
dan sinclair99181d82023-07-20 01:14:15 +000050#include "src/tint/lang/wgsl/ast/transform/canonicalize_entry_point_io.h"
dan sinclair99181d82023-07-20 01:14:15 +000051#include "src/tint/lang/wgsl/ast/transform/demote_to_helper.h"
52#include "src/tint/lang/wgsl/ast/transform/direct_variable_access.h"
53#include "src/tint/lang/wgsl/ast/transform/disable_uniformity_analysis.h"
54#include "src/tint/lang/wgsl/ast/transform/expand_compound_assignment.h"
Ben Clayton7494e832023-07-25 15:30:31 +000055#include "src/tint/lang/wgsl/ast/transform/manager.h"
dan sinclair99181d82023-07-20 01:14:15 +000056#include "src/tint/lang/wgsl/ast/transform/multiplanar_external_texture.h"
dan sinclair99181d82023-07-20 01:14:15 +000057#include "src/tint/lang/wgsl/ast/transform/promote_initializers_to_let.h"
58#include "src/tint/lang/wgsl/ast/transform/promote_side_effects_to_decl.h"
dan sinclair99181d82023-07-20 01:14:15 +000059#include "src/tint/lang/wgsl/ast/transform/remove_phonies.h"
60#include "src/tint/lang/wgsl/ast/transform/robustness.h"
61#include "src/tint/lang/wgsl/ast/transform/simplify_pointers.h"
dan sinclair99181d82023-07-20 01:14:15 +000062#include "src/tint/lang/wgsl/ast/transform/unshadow.h"
63#include "src/tint/lang/wgsl/ast/transform/vectorize_scalar_matrix_initializers.h"
64#include "src/tint/lang/wgsl/ast/transform/zero_init_workgroup_memory.h"
65#include "src/tint/lang/wgsl/ast/variable_decl_statement.h"
dan sinclaira4c17352023-07-20 09:21:10 +000066#include "src/tint/lang/wgsl/helpers/append_vector.h"
67#include "src/tint/lang/wgsl/helpers/check_supported_extensions.h"
dan sinclaird3b13692023-07-20 01:14:15 +000068#include "src/tint/lang/wgsl/sem/block_statement.h"
69#include "src/tint/lang/wgsl/sem/call.h"
70#include "src/tint/lang/wgsl/sem/function.h"
71#include "src/tint/lang/wgsl/sem/member_accessor_expression.h"
72#include "src/tint/lang/wgsl/sem/module.h"
73#include "src/tint/lang/wgsl/sem/statement.h"
74#include "src/tint/lang/wgsl/sem/struct.h"
75#include "src/tint/lang/wgsl/sem/switch_statement.h"
76#include "src/tint/lang/wgsl/sem/value_constructor.h"
77#include "src/tint/lang/wgsl/sem/value_conversion.h"
78#include "src/tint/lang/wgsl/sem/variable.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000079#include "src/tint/utils/containers/map.h"
Ben Claytonf848af22023-07-28 16:37:32 +000080#include "src/tint/utils/ice/ice.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000081#include "src/tint/utils/macros/compiler.h"
82#include "src/tint/utils/macros/defer.h"
83#include "src/tint/utils/macros/scoped_assignment.h"
84#include "src/tint/utils/rtti/switch.h"
Ben Clayton16be11d2023-08-01 00:37:35 +000085#include "src/tint/utils/strconv/float_to_string.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000086#include "src/tint/utils/text/string.h"
87#include "src/tint/utils/text/string_stream.h"
Ryan Harrisondbc13af2022-02-21 15:19:07 +000088
dan sinclairce6dffe2023-08-14 21:01:40 +000089using namespace tint::core::number_suffixes; // NOLINT
90using namespace tint::core::fluent_types; // NOLINT
Ben Clayton0ce9ab02022-05-05 20:23:40 +000091
dan sinclair0bfeaf92023-07-26 17:39:51 +000092namespace tint::hlsl::writer {
Ryan Harrisondbc13af2022-02-21 15:19:07 +000093namespace {
94
95const char kTempNamePrefix[] = "tint_tmp";
Ryan Harrisondbc13af2022-02-21 15:19:07 +000096
Ben Claytoncd52f382023-08-07 13:11:08 +000097const char* image_format_to_rwtexture_type(core::TexelFormat image_format) {
dan sinclair41e4d9a2022-05-01 14:40:55 +000098 switch (image_format) {
Ben Claytoncd52f382023-08-07 13:11:08 +000099 case core::TexelFormat::kBgra8Unorm:
100 case core::TexelFormat::kRgba8Unorm:
101 case core::TexelFormat::kRgba8Snorm:
102 case core::TexelFormat::kRgba16Float:
103 case core::TexelFormat::kR32Float:
104 case core::TexelFormat::kRg32Float:
105 case core::TexelFormat::kRgba32Float:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000106 return "float4";
Ben Claytoncd52f382023-08-07 13:11:08 +0000107 case core::TexelFormat::kRgba8Uint:
108 case core::TexelFormat::kRgba16Uint:
109 case core::TexelFormat::kR32Uint:
110 case core::TexelFormat::kRg32Uint:
111 case core::TexelFormat::kRgba32Uint:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000112 return "uint4";
Ben Claytoncd52f382023-08-07 13:11:08 +0000113 case core::TexelFormat::kRgba8Sint:
114 case core::TexelFormat::kRgba16Sint:
115 case core::TexelFormat::kR32Sint:
116 case core::TexelFormat::kRg32Sint:
117 case core::TexelFormat::kRgba32Sint:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000118 return "int4";
119 default:
120 return nullptr;
121 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000122}
123
dan sinclairbae54e72023-07-28 15:01:54 +0000124void PrintF32(StringStream& out, float value) {
Ben Claytone9f8b092022-06-01 13:14:39 +0000125 if (std::isinf(value)) {
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000126 out << "0.0f " << (value >= 0 ? "/* inf */" : "/* -inf */");
Ben Claytone9f8b092022-06-01 13:14:39 +0000127 } else if (std::isnan(value)) {
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000128 out << "0.0f /* nan */";
Ben Claytone9f8b092022-06-01 13:14:39 +0000129 } else {
dan sinclair0bfeaf92023-07-26 17:39:51 +0000130 out << tint::writer::FloatToString(value) << "f";
Ben Claytone9f8b092022-06-01 13:14:39 +0000131 }
132}
133
dan sinclairbae54e72023-07-28 15:01:54 +0000134void PrintF16(StringStream& out, float value) {
Antonio Maiorano679cf4f2022-09-03 21:43:01 +0000135 if (std::isinf(value)) {
136 out << "0.0h " << (value >= 0 ? "/* inf */" : "/* -inf */");
137 } else if (std::isnan(value)) {
138 out << "0.0h /* nan */";
Zhaoming Jianga5988a32022-07-11 15:43:38 +0000139 } else {
dan sinclair0bfeaf92023-07-26 17:39:51 +0000140 out << tint::writer::FloatToString(value) << "h";
Zhaoming Jianga5988a32022-07-11 15:43:38 +0000141 }
142}
143
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000144// Helper for writing " : register(RX, spaceY)", where R is the register, X is
145// the binding point binding value, and Y is the binding point group value.
146struct RegisterAndSpace {
Ben Clayton10252582023-07-25 20:53:25 +0000147 RegisterAndSpace(char r, BindingPoint bp) : reg(r), binding_point(bp) {}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000148
dan sinclair41e4d9a2022-05-01 14:40:55 +0000149 const char reg;
Ben Clayton10252582023-07-25 20:53:25 +0000150 BindingPoint const binding_point;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000151};
152
dan sinclairbae54e72023-07-28 15:01:54 +0000153StringStream& operator<<(StringStream& s, const RegisterAndSpace& rs) {
Peng Huangc00ff7f2023-03-31 17:55:19 +0000154 s << " : register(" << rs.reg << rs.binding_point.binding;
155 // Omit the space if it's 0, as it's the default.
156 // SM 5.0 doesn't support spaces, so we don't emit them if group is 0 for better compatibility.
157 if (rs.binding_point.group == 0) {
158 s << ")";
159 } else {
160 s << ", space" << rs.binding_point.group << ")";
161 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000162 return s;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000163}
164
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000165} // namespace
166
167SanitizedResult::SanitizedResult() = default;
168SanitizedResult::~SanitizedResult() = default;
169SanitizedResult::SanitizedResult(SanitizedResult&&) = default;
170
Ben Clayton5ed5cc42023-09-22 10:31:04 +0000171SanitizedResult Sanitize(const Program& in, const Options& options) {
Ben Clayton7494e832023-07-25 15:30:31 +0000172 ast::transform::Manager manager;
173 ast::transform::DataMap data;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000174
James Priceb4acbb82023-05-11 21:27:16 +0000175 manager.Add<ast::transform::DisableUniformityAnalysis>();
James Price791b4352022-05-11 13:50:33 +0000176
Ben Clayton46ee6392022-11-09 22:04:11 +0000177 // ExpandCompoundAssignment must come before BuiltinPolyfill
James Priceb4acbb82023-05-11 21:27:16 +0000178 manager.Add<ast::transform::ExpandCompoundAssignment>();
Ben Clayton46ee6392022-11-09 22:04:11 +0000179
James Priceb4acbb82023-05-11 21:27:16 +0000180 manager.Add<ast::transform::Unshadow>(); // Must come before DirectVariableAccess
Ben Clayton03de0e82023-03-02 20:48:48 +0000181
Ben Clayton03de0e82023-03-02 20:48:48 +0000182 // LocalizeStructArrayAssignment must come after:
183 // * SimplifyPointers, because it assumes assignment to arrays in structs are
184 // done directly, not indirectly.
185 // TODO(crbug.com/tint/1340): See if we can get rid of the duplicate
186 // SimplifyPointers transform. Can't do it right now because
187 // LocalizeStructArrayAssignment introduces pointers.
James Priceb4acbb82023-05-11 21:27:16 +0000188 manager.Add<ast::transform::SimplifyPointers>();
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000189 manager.Add<LocalizeStructArrayAssignment>();
Ben Clayton03de0e82023-03-02 20:48:48 +0000190
James Priceb4acbb82023-05-11 21:27:16 +0000191 manager.Add<ast::transform::PromoteSideEffectsToDecl>();
Ben Clayton03de0e82023-03-02 20:48:48 +0000192
193 if (!options.disable_robustness) {
Ben Clayton8525ff22023-03-06 21:05:01 +0000194 // Robustness must come after PromoteSideEffectsToDecl
195 // Robustness must come before BuiltinPolyfill and CanonicalizeEntryPointIO
James Priceb4acbb82023-05-11 21:27:16 +0000196 manager.Add<ast::transform::Robustness>();
Jiawei Shao455e4b82023-06-10 00:32:22 +0000197
198 ast::transform::Robustness::Config config = {};
Jiawei Shao2e119632023-06-21 01:43:25 +0000199
Ben Clayton10252582023-07-25 20:53:25 +0000200 config.bindings_ignored = std::unordered_set<BindingPoint>(
Jiawei Shao455e4b82023-06-10 00:32:22 +0000201 options.binding_points_ignored_in_robustness_transform.cbegin(),
202 options.binding_points_ignored_in_robustness_transform.cend());
Jiawei Shao2e119632023-06-21 01:43:25 +0000203
204 // Direct3D guarantees to return zero for any resource that is accessed out of bounds, and
205 // according to the description of the assembly store_uav_typed, out of bounds addressing
206 // means nothing gets written to memory.
207 config.texture_action = ast::transform::Robustness::Action::kIgnore;
208
Jiawei Shao455e4b82023-06-10 00:32:22 +0000209 data.Add<ast::transform::Robustness::Config>(config);
Ben Clayton03de0e82023-03-02 20:48:48 +0000210 }
211
dan sinclair04d61c82023-04-11 23:02:45 +0000212 // Note: it is more efficient for MultiplanarExternalTexture to come after Robustness
James Priceb4acbb82023-05-11 21:27:16 +0000213 data.Add<ast::transform::MultiplanarExternalTexture::NewBindingPoints>(
dan sinclair04d61c82023-04-11 23:02:45 +0000214 options.external_texture_options.bindings_map);
James Priceb4acbb82023-05-11 21:27:16 +0000215 manager.Add<ast::transform::MultiplanarExternalTexture>();
Ben Clayton03de0e82023-03-02 20:48:48 +0000216
dan sinclair39d40652023-03-15 13:41:46 +0000217 // BindingRemapper must come after MultiplanarExternalTexture
James Priceb4acbb82023-05-11 21:27:16 +0000218 manager.Add<ast::transform::BindingRemapper>();
219 data.Add<ast::transform::BindingRemapper::Remappings>(
dan sinclair39d40652023-03-15 13:41:46 +0000220 options.binding_remapper_options.binding_points,
221 options.binding_remapper_options.access_controls,
222 options.binding_remapper_options.allow_collisions);
223
dan sinclair41e4d9a2022-05-01 14:40:55 +0000224 { // Builtin polyfills
James Priceb4acbb82023-05-11 21:27:16 +0000225 ast::transform::BuiltinPolyfill::Builtins polyfills;
226 polyfills.acosh = ast::transform::BuiltinPolyfill::Level::kFull;
dan sinclaird23f2962022-06-28 15:27:44 +0000227 polyfills.asinh = true;
James Priceb4acbb82023-05-11 21:27:16 +0000228 polyfills.atanh = ast::transform::BuiltinPolyfill::Level::kFull;
Ben Clayton02f04d92022-11-03 19:15:17 +0000229 polyfills.bitshift_modulo = true;
Ben Clayton6dbb4632022-10-31 17:54:49 +0000230 polyfills.clamp_int = true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000231 // TODO(crbug.com/tint/1449): Some of these can map to HLSL's `firstbitlow`
232 // and `firstbithigh`.
Ben Claytoncc3f8512023-03-09 21:26:12 +0000233 polyfills.conv_f32_to_iu32 = true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000234 polyfills.count_leading_zeros = true;
235 polyfills.count_trailing_zeros = true;
James Priceb4acbb82023-05-11 21:27:16 +0000236 polyfills.extract_bits = ast::transform::BuiltinPolyfill::Level::kFull;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000237 polyfills.first_leading_bit = true;
238 polyfills.first_trailing_bit = true;
James Priceb4acbb82023-05-11 21:27:16 +0000239 polyfills.insert_bits = ast::transform::BuiltinPolyfill::Level::kFull;
Ben Clayton46ee6392022-11-09 22:04:11 +0000240 polyfills.int_div_mod = true;
Antonio Maioranoee665a42023-02-14 16:12:59 +0000241 polyfills.precise_float_mod = true;
Zhaoming Jiang04529be2023-02-27 02:59:50 +0000242 polyfills.reflect_vec2_f32 = options.polyfill_reflect_vec2_f32;
Ben Claytonc4ebf2c2022-09-22 22:59:16 +0000243 polyfills.texture_sample_base_clamp_to_edge_2d_f32 = true;
James Price128980f2023-01-06 02:25:06 +0000244 polyfills.workgroup_uniform_load = true;
James Priceb4acbb82023-05-11 21:27:16 +0000245 data.Add<ast::transform::BuiltinPolyfill::Config>(polyfills);
246 manager.Add<ast::transform::BuiltinPolyfill>(); // Must come before DirectVariableAccess
dan sinclair41e4d9a2022-05-01 14:40:55 +0000247 }
Ben Clayton27aa57c2022-02-22 23:13:39 +0000248
James Priceb4acbb82023-05-11 21:27:16 +0000249 manager.Add<ast::transform::DirectVariableAccess>();
Antonio Maioranofa00fe92023-05-03 15:30:54 +0000250
dan sinclair41e4d9a2022-05-01 14:40:55 +0000251 if (!options.disable_workgroup_init) {
252 // ZeroInitWorkgroupMemory must come before CanonicalizeEntryPointIO as
253 // ZeroInitWorkgroupMemory may inject new builtin parameters.
James Priceb4acbb82023-05-11 21:27:16 +0000254 manager.Add<ast::transform::ZeroInitWorkgroupMemory>();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000255 }
Ben Clayton03de0e82023-03-02 20:48:48 +0000256
257 // CanonicalizeEntryPointIO must come after Robustness
James Priceb4acbb82023-05-11 21:27:16 +0000258 manager.Add<ast::transform::CanonicalizeEntryPointIO>();
shrekshaof9c66332022-11-22 21:36:27 +0000259
shrekshao8f0607a2023-04-18 01:34:41 +0000260 if (options.truncate_interstage_variables) {
shrekshaof9c66332022-11-22 21:36:27 +0000261 // When interstage_locations is empty, it means there's no user-defined interstage variables
shrekshao8f0607a2023-04-18 01:34:41 +0000262 // being used in the next stage. Still, HLSL compiler register mismatch could happen, if
263 // there's builtin inputs used in the next stage. So we still run
264 // TruncateInterstageVariables transform.
shrekshaof9c66332022-11-22 21:36:27 +0000265
266 // TruncateInterstageVariables itself will skip when interstage_locations matches exactly
267 // with the current stage output.
268
269 // Build the config for internal TruncateInterstageVariables transform.
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000270 TruncateInterstageVariables::Config truncate_interstage_variables_cfg;
shrekshaof9c66332022-11-22 21:36:27 +0000271 truncate_interstage_variables_cfg.interstage_locations =
272 std::move(options.interstage_locations);
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000273 manager.Add<TruncateInterstageVariables>();
274 data.Add<TruncateInterstageVariables::Config>(std::move(truncate_interstage_variables_cfg));
shrekshaof9c66332022-11-22 21:36:27 +0000275 }
276
dan sinclair41e4d9a2022-05-01 14:40:55 +0000277 // NumWorkgroupsFromUniform must come after CanonicalizeEntryPointIO, as it
278 // assumes that num_workgroups builtins only appear as struct members and are
279 // only accessed directly via member accessors.
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000280 manager.Add<NumWorkgroupsFromUniform>();
James Priceb4acbb82023-05-11 21:27:16 +0000281 manager.Add<ast::transform::VectorizeScalarMatrixInitializers>();
282 manager.Add<ast::transform::SimplifyPointers>();
283 manager.Add<ast::transform::RemovePhonies>();
James Price744d0eb2022-11-09 19:58:59 +0000284
Ben Clayton03de0e82023-03-02 20:48:48 +0000285 // Build the config for the internal ArrayLengthFromUniform transform.
286 auto& array_length_from_uniform = options.array_length_from_uniform;
James Priceb4acbb82023-05-11 21:27:16 +0000287 ast::transform::ArrayLengthFromUniform::Config array_length_from_uniform_cfg(
Ben Clayton03de0e82023-03-02 20:48:48 +0000288 array_length_from_uniform.ubo_binding);
289 array_length_from_uniform_cfg.bindpoint_to_size_index =
290 array_length_from_uniform.bindpoint_to_size_index;
291
James Price744d0eb2022-11-09 19:58:59 +0000292 // DemoteToHelper must come after CanonicalizeEntryPointIO, PromoteSideEffectsToDecl, and
293 // ExpandCompoundAssignment.
294 // TODO(crbug.com/tint/1752): This is only necessary when FXC is being used.
James Priceb4acbb82023-05-11 21:27:16 +0000295 manager.Add<ast::transform::DemoteToHelper>();
James Price744d0eb2022-11-09 19:58:59 +0000296
Ben Clayton559e5a22023-04-17 14:24:58 +0000297 // ArrayLengthFromUniform must come after SimplifyPointers as it assumes that the form of the
298 // array length argument is &var.array.
James Priceb4acbb82023-05-11 21:27:16 +0000299 manager.Add<ast::transform::ArrayLengthFromUniform>();
300 data.Add<ast::transform::ArrayLengthFromUniform::Config>(
301 std::move(array_length_from_uniform_cfg));
dan sinclair41e4d9a2022-05-01 14:40:55 +0000302 // DecomposeMemoryAccess must come after:
Ben Clayton559e5a22023-04-17 14:24:58 +0000303 // * SimplifyPointers, as we cannot take the address of calls to
304 // DecomposeMemoryAccess::Intrinsic and we need to fold away the address-of and dereferences
305 // of `*(&(intrinsic_load()))` expressions.
dan sinclair41e4d9a2022-05-01 14:40:55 +0000306 // * RemovePhonies, as phonies can be assigned a pointer to a
307 // non-constructible buffer, or dynamic array, which DMA cannot cope with.
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000308 manager.Add<DecomposeMemoryAccess>();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000309 // CalculateArrayLength must come after DecomposeMemoryAccess, as
310 // DecomposeMemoryAccess special-cases the arrayLength() intrinsic, which
311 // will be transformed by CalculateArrayLength
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000312 manager.Add<CalculateArrayLength>();
James Priceb4acbb82023-05-11 21:27:16 +0000313 manager.Add<ast::transform::PromoteInitializersToLet>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000314
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000315 manager.Add<RemoveContinueInSwitch>();
Antonio Maioranob3497102022-03-31 15:02:25 +0000316
James Priceb4acbb82023-05-11 21:27:16 +0000317 manager.Add<ast::transform::AddEmptyEntryPoint>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000318
James Priceb4acbb82023-05-11 21:27:16 +0000319 data.Add<ast::transform::CanonicalizeEntryPointIO::Config>(
320 ast::transform::CanonicalizeEntryPointIO::ShaderStyle::kHlsl);
Ben Clayton7a5f54ea2023-09-06 16:58:22 +0000321 data.Add<NumWorkgroupsFromUniform::Config>(options.root_constant_binding_point);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000322
dan sinclair41e4d9a2022-05-01 14:40:55 +0000323 SanitizedResult result;
Ben Clayton7494e832023-07-25 15:30:31 +0000324 ast::transform::DataMap outputs;
James Price0e6534e2023-05-17 01:21:45 +0000325 result.program = manager.Run(in, data, outputs);
326 if (auto* res = outputs.Get<ast::transform::ArrayLengthFromUniform::Result>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000327 result.used_array_length_from_uniform_indices = std::move(res->used_size_indices);
328 }
329 return result;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000330}
331
Ben Clayton5ed5cc42023-09-22 10:31:04 +0000332ASTPrinter::ASTPrinter(const Program& program) : builder_(ProgramBuilder::Wrap(program)) {}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000333
dan sinclair0bfeaf92023-07-26 17:39:51 +0000334ASTPrinter::~ASTPrinter() = default;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000335
dan sinclair0bfeaf92023-07-26 17:39:51 +0000336bool ASTPrinter::Generate() {
337 if (!tint::writer::CheckSupportedExtensions(
338 "HLSL", builder_.AST(), diagnostics_,
dan sinclairbae54e72023-07-28 15:01:54 +0000339 Vector{
Ben Clayton11653892023-09-19 19:15:59 +0000340 wgsl::Extension::kChromiumDisableUniformityAnalysis,
341 wgsl::Extension::kChromiumExperimentalDp4A,
342 wgsl::Extension::kChromiumExperimentalFullPtrParameters,
343 wgsl::Extension::kChromiumExperimentalPushConstant,
344 wgsl::Extension::kChromiumExperimentalReadWriteStorageTexture,
345 wgsl::Extension::kChromiumExperimentalSubgroups,
346 wgsl::Extension::kF16,
347 wgsl::Extension::kChromiumInternalDualSourceBlending,
dan sinclair0bfeaf92023-07-26 17:39:51 +0000348 })) {
Ben Clayton1a567782022-10-14 13:38:27 +0000349 return false;
350 }
351
dan sinclairbae54e72023-07-28 15:01:54 +0000352 const tint::TypeInfo* last_kind = nullptr;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000353 size_t last_padding_line = 0;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000354
dan sinclair41e4d9a2022-05-01 14:40:55 +0000355 auto* mod = builder_.Sem().Module();
356 for (auto* decl : mod->DependencyOrderedDeclarations()) {
James Price98dc5a82023-02-04 12:20:55 +0000357 if (decl->IsAnyOf<ast::Alias, ast::DiagnosticDirective, ast::Enable, ast::ConstAssert>()) {
Ben Claytonb4744ac2022-08-03 07:01:08 +0000358 continue; // These are not emitted.
James Price791b4352022-05-11 13:50:33 +0000359 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000360
dan sinclair41e4d9a2022-05-01 14:40:55 +0000361 // Emit a new line between declarations if the type of declaration has
362 // changed, or we're about to emit a function
363 auto* kind = &decl->TypeInfo();
364 if (current_buffer_->lines.size() != last_padding_line) {
365 if (last_kind && (last_kind != kind || decl->Is<ast::Function>())) {
dan sinclair67a18932023-06-26 19:54:49 +0000366 Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000367 last_padding_line = current_buffer_->lines.size();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000368 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000369 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000370 last_kind = kind;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000371
dan sinclair41e4d9a2022-05-01 14:40:55 +0000372 bool ok = Switch(
373 decl,
374 [&](const ast::Variable* global) { //
375 return EmitGlobalVariable(global);
376 },
377 [&](const ast::Struct* str) {
378 auto* ty = builder_.Sem().Get(str);
dan sinclairff7cf212022-10-03 14:05:23 +0000379 auto address_space_uses = ty->AddressSpaceUsage();
380 if (address_space_uses.size() !=
Ben Claytoncd52f382023-08-07 13:11:08 +0000381 (address_space_uses.count(core::AddressSpace::kStorage) +
382 address_space_uses.count(core::AddressSpace::kUniform))) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000383 // The structure is used as something other than a storage buffer or
384 // uniform buffer, so it needs to be emitted.
385 // Storage buffer are read and written to via a ByteAddressBuffer
386 // instead of true structure.
387 // Structures used as uniform buffer are read from an array of
388 // vectors instead of true structure.
389 return EmitStructType(current_buffer_, ty);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000390 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000391 return true;
392 },
393 [&](const ast::Function* func) {
394 if (func->IsEntryPoint()) {
395 return EmitEntryPointFunction(func);
396 }
397 return EmitFunction(func);
398 },
dan sinclair41e4d9a2022-05-01 14:40:55 +0000399 [&](Default) {
Ben Claytonf848af22023-07-28 16:37:32 +0000400 TINT_ICE() << "unhandled module-scope declaration: " << decl->TypeInfo().name;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000401 return false;
402 });
403
404 if (!ok) {
405 return false;
406 }
407 }
408
409 if (!helpers_.lines.empty()) {
410 current_buffer_->Insert(helpers_, 0, 0);
411 }
412
413 return true;
414}
415
dan sinclair0bfeaf92023-07-26 17:39:51 +0000416bool ASTPrinter::EmitDynamicVectorAssignment(const ast::AssignmentStatement* stmt,
dan sinclaircedcdf32023-08-10 02:39:48 +0000417 const core::type::Vector* vec) {
dan sinclairbae54e72023-07-28 15:01:54 +0000418 auto name = tint::GetOrCreate(dynamic_vector_write_, vec, [&]() -> std::string {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000419 std::string fn;
420 {
dan sinclairbae54e72023-07-28 15:01:54 +0000421 StringStream ss;
Ben Claytoncd52f382023-08-07 13:11:08 +0000422 if (!EmitType(ss, vec, tint::core::AddressSpace::kUndefined, core::Access::kUndefined,
423 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000424 return "";
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000425 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000426 fn = UniqueIdentifier("set_" + ss.str());
427 }
428 {
dan sinclair67a18932023-06-26 19:54:49 +0000429 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000430 out << "void " << fn << "(inout ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000431 if (!EmitTypeAndName(out, vec, core::AddressSpace::kUndefined, core::Access::kUndefined,
432 "vec")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000433 return "";
434 }
435 out << ", int idx, ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000436 if (!EmitTypeAndName(out, vec->type(), core::AddressSpace::kUndefined,
437 core::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000438 return "";
439 }
440 out << ") {";
441 }
442 {
443 ScopedIndent si(&helpers_);
dan sinclair67a18932023-06-26 19:54:49 +0000444 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000445 switch (vec->Width()) {
446 case 2:
447 out << "vec = (idx.xx == int2(0, 1)) ? val.xx : vec;";
448 break;
449 case 3:
450 out << "vec = (idx.xxx == int3(0, 1, 2)) ? val.xxx : vec;";
451 break;
452 case 4:
453 out << "vec = (idx.xxxx == int4(0, 1, 2, 3)) ? val.xxxx : vec;";
454 break;
455 default:
Ben Claytonf848af22023-07-28 16:37:32 +0000456 TINT_UNREACHABLE() << "invalid vector size " << vec->Width();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000457 break;
458 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000459 }
dan sinclair67a18932023-06-26 19:54:49 +0000460 Line(&helpers_) << "}";
461 Line(&helpers_);
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000462 return fn;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000463 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000464
dan sinclair41e4d9a2022-05-01 14:40:55 +0000465 if (name.empty()) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000466 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000467 }
468
dan sinclair41e4d9a2022-05-01 14:40:55 +0000469 auto* ast_access_expr = stmt->lhs->As<ast::IndexAccessorExpression>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000470
dan sinclair67a18932023-06-26 19:54:49 +0000471 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000472 out << name << "(";
473 if (!EmitExpression(out, ast_access_expr->object)) {
474 return false;
475 }
476 out << ", ";
477 if (!EmitExpression(out, ast_access_expr->index)) {
478 return false;
479 }
480 out << ", ";
481 if (!EmitExpression(out, stmt->rhs)) {
482 return false;
483 }
484 out << ");";
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000485
dan sinclair41e4d9a2022-05-01 14:40:55 +0000486 return true;
487}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000488
dan sinclair0bfeaf92023-07-26 17:39:51 +0000489bool ASTPrinter::EmitDynamicMatrixVectorAssignment(const ast::AssignmentStatement* stmt,
dan sinclaircedcdf32023-08-10 02:39:48 +0000490 const core::type::Matrix* mat) {
dan sinclairbae54e72023-07-28 15:01:54 +0000491 auto name = tint::GetOrCreate(dynamic_matrix_vector_write_, mat, [&]() -> std::string {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000492 std::string fn;
493 {
dan sinclairbae54e72023-07-28 15:01:54 +0000494 StringStream ss;
Ben Claytoncd52f382023-08-07 13:11:08 +0000495 if (!EmitType(ss, mat, tint::core::AddressSpace::kUndefined, core::Access::kUndefined,
496 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000497 return "";
498 }
499 fn = UniqueIdentifier("set_vector_" + ss.str());
500 }
501 {
dan sinclair67a18932023-06-26 19:54:49 +0000502 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000503 out << "void " << fn << "(inout ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000504 if (!EmitTypeAndName(out, mat, core::AddressSpace::kUndefined, core::Access::kUndefined,
505 "mat")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000506 return "";
507 }
508 out << ", int col, ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000509 if (!EmitTypeAndName(out, mat->ColumnType(), core::AddressSpace::kUndefined,
510 core::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000511 return "";
512 }
513 out << ") {";
514 }
515 {
516 ScopedIndent si(&helpers_);
dan sinclair67a18932023-06-26 19:54:49 +0000517 Line(&helpers_) << "switch (col) {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000518 {
519 ScopedIndent si2(&helpers_);
520 for (uint32_t i = 0; i < mat->columns(); ++i) {
dan sinclair67a18932023-06-26 19:54:49 +0000521 Line(&helpers_) << "case " << i << ": mat[" << i << "] = val; break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000522 }
523 }
dan sinclair67a18932023-06-26 19:54:49 +0000524 Line(&helpers_) << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000525 }
dan sinclair67a18932023-06-26 19:54:49 +0000526 Line(&helpers_) << "}";
527 Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000528 return fn;
529 });
530
531 if (name.empty()) {
532 return false;
533 }
534
535 auto* ast_access_expr = stmt->lhs->As<ast::IndexAccessorExpression>();
536
dan sinclair67a18932023-06-26 19:54:49 +0000537 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000538 out << name << "(";
539 if (!EmitExpression(out, ast_access_expr->object)) {
540 return false;
541 }
542 out << ", ";
543 if (!EmitExpression(out, ast_access_expr->index)) {
544 return false;
545 }
546 out << ", ";
547 if (!EmitExpression(out, stmt->rhs)) {
548 return false;
549 }
550 out << ");";
551
552 return true;
553}
554
dan sinclair0bfeaf92023-07-26 17:39:51 +0000555bool ASTPrinter::EmitDynamicMatrixScalarAssignment(const ast::AssignmentStatement* stmt,
dan sinclaircedcdf32023-08-10 02:39:48 +0000556 const core::type::Matrix* mat) {
Antonio Maioranof031ca22023-02-02 22:16:42 +0000557 auto* lhs_row_access = stmt->lhs->As<ast::IndexAccessorExpression>();
558 auto* lhs_col_access = lhs_row_access->object->As<ast::IndexAccessorExpression>();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000559
dan sinclairbae54e72023-07-28 15:01:54 +0000560 auto name = tint::GetOrCreate(dynamic_matrix_scalar_write_, mat, [&]() -> std::string {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000561 std::string fn;
562 {
dan sinclairbae54e72023-07-28 15:01:54 +0000563 StringStream ss;
Ben Claytoncd52f382023-08-07 13:11:08 +0000564 if (!EmitType(ss, mat, tint::core::AddressSpace::kUndefined, core::Access::kUndefined,
565 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000566 return "";
567 }
568 fn = UniqueIdentifier("set_scalar_" + ss.str());
569 }
570 {
dan sinclair67a18932023-06-26 19:54:49 +0000571 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000572 out << "void " << fn << "(inout ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000573 if (!EmitTypeAndName(out, mat, core::AddressSpace::kUndefined, core::Access::kUndefined,
574 "mat")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000575 return "";
576 }
577 out << ", int col, int row, ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000578 if (!EmitTypeAndName(out, mat->type(), core::AddressSpace::kUndefined,
579 core::Access::kUndefined, "val")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000580 return "";
581 }
582 out << ") {";
583 }
584 {
585 ScopedIndent si(&helpers_);
dan sinclair67a18932023-06-26 19:54:49 +0000586 Line(&helpers_) << "switch (col) {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000587 {
588 ScopedIndent si2(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000589 for (uint32_t i = 0; i < mat->columns(); ++i) {
dan sinclair67a18932023-06-26 19:54:49 +0000590 Line(&helpers_) << "case " << i << ":";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000591 {
592 auto vec_name = "mat[" + std::to_string(i) + "]";
593 ScopedIndent si3(&helpers_);
594 {
dan sinclair67a18932023-06-26 19:54:49 +0000595 auto out = Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000596 switch (mat->rows()) {
597 case 2:
598 out << vec_name
599 << " = (row.xx == int2(0, 1)) ? val.xx : " << vec_name
600 << ";";
601 break;
602 case 3:
603 out << vec_name
604 << " = (row.xxx == int3(0, 1, 2)) ? val.xxx : " << vec_name
605 << ";";
606 break;
607 case 4:
608 out << vec_name
609 << " = (row.xxxx == int4(0, 1, 2, 3)) ? val.xxxx : "
610 << vec_name << ";";
611 break;
Antonio Maioranof031ca22023-02-02 22:16:42 +0000612 default: {
613 auto* vec = TypeOf(lhs_row_access->object)
614 ->UnwrapRef()
dan sinclaircedcdf32023-08-10 02:39:48 +0000615 ->As<core::type::Vector>();
Ben Claytonf848af22023-07-28 16:37:32 +0000616 TINT_UNREACHABLE() << "invalid vector size " << vec->Width();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000617 break;
Antonio Maioranof031ca22023-02-02 22:16:42 +0000618 }
dan sinclair41e4d9a2022-05-01 14:40:55 +0000619 }
620 }
dan sinclair67a18932023-06-26 19:54:49 +0000621 Line(&helpers_) << "break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000622 }
623 }
624 }
dan sinclair67a18932023-06-26 19:54:49 +0000625 Line(&helpers_) << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000626 }
dan sinclair67a18932023-06-26 19:54:49 +0000627 Line(&helpers_) << "}";
628 Line(&helpers_);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000629 return fn;
630 });
631
632 if (name.empty()) {
633 return false;
634 }
635
dan sinclair67a18932023-06-26 19:54:49 +0000636 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000637 out << name << "(";
Antonio Maioranof031ca22023-02-02 22:16:42 +0000638 if (!EmitExpression(out, lhs_col_access->object)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000639 return false;
640 }
641 out << ", ";
642 if (!EmitExpression(out, lhs_col_access->index)) {
643 return false;
644 }
645 out << ", ";
646 if (!EmitExpression(out, lhs_row_access->index)) {
647 return false;
648 }
649 out << ", ";
650 if (!EmitExpression(out, stmt->rhs)) {
651 return false;
652 }
653 out << ");";
654
655 return true;
656}
657
dan sinclairbae54e72023-07-28 15:01:54 +0000658bool ASTPrinter::EmitIndexAccessor(StringStream& out, const ast::IndexAccessorExpression* expr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000659 if (!EmitExpression(out, expr->object)) {
660 return false;
661 }
662 out << "[";
663
664 if (!EmitExpression(out, expr->index)) {
665 return false;
666 }
667 out << "]";
668
669 return true;
670}
671
dan sinclairbae54e72023-07-28 15:01:54 +0000672bool ASTPrinter::EmitBitcast(StringStream& out, const ast::BitcastExpression* expr) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000673 auto* dst_type = TypeOf(expr)->UnwrapRef();
674 auto* src_type = TypeOf(expr->expr)->UnwrapRef();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000675
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000676 auto* src_el_type = src_type->DeepestElement();
677 auto* dst_el_type = dst_type->DeepestElement();
678
679 if (!dst_el_type->is_integer_scalar() && !dst_el_type->is_float_scalar()) {
dan sinclaird026e132023-04-18 19:38:25 +0000680 diagnostics_.add_error(diag::System::Writer,
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000681 "Unable to do bitcast to type " + dst_el_type->FriendlyName());
dan sinclair41e4d9a2022-05-01 14:40:55 +0000682 return false;
683 }
684
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000685 // Handle identity bitcast.
686 if (src_type == dst_type) {
687 return EmitExpression(out, expr->expr);
688 }
689
690 // Handle the f16 types using polyfill functions
dan sinclaircedcdf32023-08-10 02:39:48 +0000691 if (src_el_type->Is<core::type::F16>() || dst_el_type->Is<core::type::F16>()) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000692 auto f16_bitcast_polyfill = [&]() {
dan sinclaircedcdf32023-08-10 02:39:48 +0000693 if (src_el_type->Is<core::type::F16>()) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000694 // Source type must be vec2<f16> or vec4<f16>, since type f16 and vec3<f16> can only
695 // have identity bitcast.
dan sinclaircedcdf32023-08-10 02:39:48 +0000696 auto* src_vec = src_type->As<core::type::Vector>();
Ben Claytonf848af22023-07-28 16:37:32 +0000697 TINT_ASSERT(src_vec);
698 TINT_ASSERT(((src_vec->Width() == 2u) || (src_vec->Width() == 4u)));
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000699
700 // Bitcast f16 types to others by converting the given f16 value to f32 and call
701 // f32tof16 to get the bits. This should be safe, because the convertion is precise
702 // for finite and infinite f16 value as they are exactly representable by f32, and
703 // WGSL spec allow any result if f16 value is NaN.
dan sinclairbae54e72023-07-28 15:01:54 +0000704 return tint::GetOrCreate(
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000705 bitcast_funcs_, BinaryType{{src_type, dst_type}}, [&]() -> std::string {
706 TextBuffer b;
707 TINT_DEFER(helpers_.Append(b));
708
709 auto fn_name = UniqueIdentifier(std::string("tint_bitcast_from_f16"));
710 {
711 auto decl = Line(&b);
Ben Claytoncd52f382023-08-07 13:11:08 +0000712 if (!EmitTypeAndName(decl, dst_type, core::AddressSpace::kUndefined,
713 core::Access::kUndefined, fn_name)) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000714 return "";
715 }
716 {
717 ScopedParen sp(decl);
Ben Claytoncd52f382023-08-07 13:11:08 +0000718 if (!EmitTypeAndName(decl, src_type, core::AddressSpace::kUndefined,
719 core::Access::kUndefined, "src")) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000720 return "";
721 }
722 }
723 decl << " {";
724 }
725 {
726 ScopedIndent si(&b);
727 {
728 Line(&b) << "uint" << src_vec->Width() << " r = f32tof16(float"
729 << src_vec->Width() << "(src));";
730
731 {
732 auto s = Line(&b);
733 s << "return as";
Ben Claytoncd52f382023-08-07 13:11:08 +0000734 if (!EmitType(s, dst_el_type, core::AddressSpace::kUndefined,
735 core::Access::kReadWrite, "")) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000736 return "";
737 }
738 s << "(";
739 switch (src_vec->Width()) {
740 case 2: {
741 s << "uint((r.x & 0xffff) | ((r.y & 0xffff) << 16))";
742 break;
743 }
744 case 4: {
745 s << "uint2((r.x & 0xffff) | ((r.y & 0xffff) << 16), "
746 "(r.z & 0xffff) | ((r.w & 0xffff) << 16))";
747 break;
748 }
749 }
750 s << ");";
751 }
752 }
753 }
754 Line(&b) << "}";
755 Line(&b);
756 return fn_name;
757 });
758 } else {
759 // Destination type must be vec2<f16> or vec4<f16>.
dan sinclaircedcdf32023-08-10 02:39:48 +0000760 auto* dst_vec = dst_type->As<core::type::Vector>();
Ben Claytonf848af22023-07-28 16:37:32 +0000761 TINT_ASSERT((dst_vec && ((dst_vec->Width() == 2u) || (dst_vec->Width() == 4u)) &&
dan sinclaircedcdf32023-08-10 02:39:48 +0000762 dst_el_type->Is<core::type::F16>()));
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000763 // Source type must be f32/i32/u32 or vec2<f32/i32/u32>.
dan sinclaircedcdf32023-08-10 02:39:48 +0000764 auto* src_vec = src_type->As<core::type::Vector>();
765 TINT_ASSERT(
766 (src_type->IsAnyOf<core::type::I32, core::type::U32, core::type::F32>() ||
767 (src_vec && src_vec->Width() == 2u &&
768 src_el_type->IsAnyOf<core::type::I32, core::type::U32, core::type::F32>())));
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000769 std::string src_type_suffix = (src_vec ? "2" : "");
770
771 // Bitcast other types to f16 types by reinterpreting their bits as f16 using
772 // f16tof32, and convert the result f32 to f16. This should be safe, because the
773 // convertion is precise for finite and infinite f16 result value as they are
774 // exactly representable by f32, and WGSL spec allow any result if f16 result value
775 // would be NaN.
dan sinclairbae54e72023-07-28 15:01:54 +0000776 return tint::GetOrCreate(
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000777 bitcast_funcs_, BinaryType{{src_type, dst_type}}, [&]() -> std::string {
778 TextBuffer b;
779 TINT_DEFER(helpers_.Append(b));
780
781 auto fn_name = UniqueIdentifier(std::string("tint_bitcast_to_f16"));
782 {
783 auto decl = Line(&b);
Ben Claytoncd52f382023-08-07 13:11:08 +0000784 if (!EmitTypeAndName(decl, dst_type, core::AddressSpace::kUndefined,
785 core::Access::kUndefined, fn_name)) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000786 return "";
787 }
788 {
789 ScopedParen sp(decl);
Ben Claytoncd52f382023-08-07 13:11:08 +0000790 if (!EmitTypeAndName(decl, src_type, core::AddressSpace::kUndefined,
791 core::Access::kUndefined, "src")) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000792 return "";
793 }
794 }
795 decl << " {";
796 }
797 {
798 ScopedIndent si(&b);
799 {
800 // Convert the source to uint for f16tof32.
801 Line(&b) << "uint" << src_type_suffix << " v = asuint(src);";
802 // Reinterprete the low 16 bits and high 16 bits
803 Line(&b) << "float" << src_type_suffix
804 << " t_low = f16tof32(v & 0xffff);";
805 Line(&b) << "float" << src_type_suffix
806 << " t_high = f16tof32((v >> 16) & 0xffff);";
807 // Construct the result f16 vector
808 {
809 auto s = Line(&b);
810 s << "return ";
Ben Claytoncd52f382023-08-07 13:11:08 +0000811 if (!EmitType(s, dst_type, core::AddressSpace::kUndefined,
812 core::Access::kReadWrite, "")) {
Zhaoming Jiang6c4de8b2023-07-17 09:37:19 +0000813 return "";
814 }
815 s << "(";
816 switch (dst_vec->Width()) {
817 case 2: {
818 s << "t_low.x, t_high.x";
819 break;
820 }
821 case 4: {
822 s << "t_low.x, t_high.x, t_low.y, t_high.y";
823 break;
824 }
825 }
826 s << ");";
827 }
828 }
829 }
830 Line(&b) << "}";
831 Line(&b);
832 return fn_name;
833 });
834 }
835 };
836
837 // Get or create the polyfill
838 auto fn = f16_bitcast_polyfill();
839 if (fn.empty()) {
840 return false;
841 }
842 // Call the polyfill
843 out << fn;
844 {
845 ScopedParen sp(out);
846 if (!EmitExpression(out, expr->expr)) {
847 return false;
848 }
849 }
850
851 return true;
852 }
853
854 // Otherwise, bitcasting between non-f16 types.
dan sinclaircedcdf32023-08-10 02:39:48 +0000855 TINT_ASSERT((!src_el_type->Is<core::type::F16>() && !dst_el_type->Is<core::type::F16>()));
dan sinclair41e4d9a2022-05-01 14:40:55 +0000856 out << "as";
Ben Claytoncd52f382023-08-07 13:11:08 +0000857 if (!EmitType(out, dst_el_type, core::AddressSpace::kUndefined, core::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000858 return false;
859 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000860 out << "(";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000861 if (!EmitExpression(out, expr->expr)) {
862 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000863 }
864 out << ")";
865 return true;
dan sinclair41e4d9a2022-05-01 14:40:55 +0000866}
Ryan Harrisondbc13af2022-02-21 15:19:07 +0000867
dan sinclair0bfeaf92023-07-26 17:39:51 +0000868bool ASTPrinter::EmitAssign(const ast::AssignmentStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000869 if (auto* lhs_access = stmt->lhs->As<ast::IndexAccessorExpression>()) {
870 // BUG(crbug.com/tint/1333): work around assignment of scalar to matrices
871 // with at least one dynamic index
872 if (auto* lhs_sub_access = lhs_access->object->As<ast::IndexAccessorExpression>()) {
dan sinclaircedcdf32023-08-10 02:39:48 +0000873 if (auto* mat = TypeOf(lhs_sub_access->object)->UnwrapRef()->As<core::type::Matrix>()) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +0000874 auto* rhs_row_idx_sem = builder_.Sem().GetVal(lhs_access->index);
875 auto* rhs_col_idx_sem = builder_.Sem().GetVal(lhs_sub_access->index);
Antonio Maioranof031ca22023-02-02 22:16:42 +0000876 if (!rhs_row_idx_sem->ConstantValue() || !rhs_col_idx_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000877 return EmitDynamicMatrixScalarAssignment(stmt, mat);
878 }
879 }
880 }
881 // BUG(crbug.com/tint/1333): work around assignment of vector to matrices
882 // with dynamic indices
883 const auto* lhs_access_type = TypeOf(lhs_access->object)->UnwrapRef();
dan sinclaircedcdf32023-08-10 02:39:48 +0000884 if (auto* mat = lhs_access_type->As<core::type::Matrix>()) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +0000885 auto* lhs_index_sem = builder_.Sem().GetVal(lhs_access->index);
Ben Claytonaa037ac2022-06-29 19:07:30 +0000886 if (!lhs_index_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000887 return EmitDynamicMatrixVectorAssignment(stmt, mat);
888 }
889 }
890 // BUG(crbug.com/tint/534): work around assignment to vectors with dynamic
891 // indices
dan sinclaircedcdf32023-08-10 02:39:48 +0000892 if (auto* vec = lhs_access_type->As<core::type::Vector>()) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +0000893 auto* rhs_sem = builder_.Sem().GetVal(lhs_access->index);
Ben Claytonaa037ac2022-06-29 19:07:30 +0000894 if (!rhs_sem->ConstantValue()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000895 return EmitDynamicVectorAssignment(stmt, vec);
896 }
897 }
898 }
899
dan sinclair67a18932023-06-26 19:54:49 +0000900 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000901 if (!EmitExpression(out, stmt->lhs)) {
902 return false;
903 }
904 out << " = ";
905 if (!EmitExpression(out, stmt->rhs)) {
906 return false;
907 }
908 out << ";";
909 return true;
910}
911
dan sinclairbae54e72023-07-28 15:01:54 +0000912bool ASTPrinter::EmitBinary(StringStream& out, const ast::BinaryExpression* expr) {
Ben Claytonedc51ab2023-08-08 07:58:19 +0000913 if (expr->op == core::BinaryOp::kLogicalAnd || expr->op == core::BinaryOp::kLogicalOr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000914 auto name = UniqueIdentifier(kTempNamePrefix);
915
916 {
dan sinclair67a18932023-06-26 19:54:49 +0000917 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000918 pre << "bool " << name << " = ";
919 if (!EmitExpression(pre, expr->lhs)) {
920 return false;
921 }
922 pre << ";";
923 }
924
Ben Claytonedc51ab2023-08-08 07:58:19 +0000925 if (expr->op == core::BinaryOp::kLogicalOr) {
dan sinclair67a18932023-06-26 19:54:49 +0000926 Line() << "if (!" << name << ") {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000927 } else {
dan sinclair67a18932023-06-26 19:54:49 +0000928 Line() << "if (" << name << ") {";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000929 }
930
931 {
932 ScopedIndent si(this);
dan sinclair67a18932023-06-26 19:54:49 +0000933 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000934 pre << name << " = ";
935 if (!EmitExpression(pre, expr->rhs)) {
936 return false;
937 }
938 pre << ";";
939 }
940
dan sinclair67a18932023-06-26 19:54:49 +0000941 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +0000942
943 out << "(" << name << ")";
944 return true;
945 }
946
947 auto* lhs_type = TypeOf(expr->lhs)->UnwrapRef();
948 auto* rhs_type = TypeOf(expr->rhs)->UnwrapRef();
949 // Multiplying by a matrix requires the use of `mul` in order to get the
950 // type of multiply we desire.
Ben Claytonedc51ab2023-08-08 07:58:19 +0000951 if (expr->op == core::BinaryOp::kMultiply &&
dan sinclaircedcdf32023-08-10 02:39:48 +0000952 ((lhs_type->Is<core::type::Vector>() && rhs_type->Is<core::type::Matrix>()) ||
953 (lhs_type->Is<core::type::Matrix>() && rhs_type->Is<core::type::Vector>()) ||
954 (lhs_type->Is<core::type::Matrix>() && rhs_type->Is<core::type::Matrix>()))) {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000955 // Matrices are transposed, so swap LHS and RHS.
956 out << "mul(";
957 if (!EmitExpression(out, expr->rhs)) {
958 return false;
959 }
960 out << ", ";
961 if (!EmitExpression(out, expr->lhs)) {
962 return false;
963 }
964 out << ")";
965
966 return true;
967 }
968
dan sinclairb2ba57b2023-02-28 15:14:09 +0000969 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +0000970
971 if (!EmitExpression(out, expr->lhs)) {
972 return false;
973 }
974 out << " ";
975
976 switch (expr->op) {
Ben Claytonedc51ab2023-08-08 07:58:19 +0000977 case core::BinaryOp::kAnd:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000978 out << "&";
979 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +0000980 case core::BinaryOp::kOr:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000981 out << "|";
982 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +0000983 case core::BinaryOp::kXor:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000984 out << "^";
985 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +0000986 case core::BinaryOp::kLogicalAnd:
987 case core::BinaryOp::kLogicalOr: {
dan sinclair41e4d9a2022-05-01 14:40:55 +0000988 // These are both handled above.
Ben Claytonf848af22023-07-28 16:37:32 +0000989 TINT_UNREACHABLE();
dan sinclair41e4d9a2022-05-01 14:40:55 +0000990 return false;
991 }
Ben Claytonedc51ab2023-08-08 07:58:19 +0000992 case core::BinaryOp::kEqual:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000993 out << "==";
994 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +0000995 case core::BinaryOp::kNotEqual:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000996 out << "!=";
997 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +0000998 case core::BinaryOp::kLessThan:
dan sinclair41e4d9a2022-05-01 14:40:55 +0000999 out << "<";
1000 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001001 case core::BinaryOp::kGreaterThan:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001002 out << ">";
1003 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001004 case core::BinaryOp::kLessThanEqual:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001005 out << "<=";
1006 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001007 case core::BinaryOp::kGreaterThanEqual:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001008 out << ">=";
1009 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001010 case core::BinaryOp::kShiftLeft:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001011 out << "<<";
1012 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001013 case core::BinaryOp::kShiftRight:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001014 // TODO(dsinclair): MSL is based on C++14, and >> in C++14 has
1015 // implementation-defined behaviour for negative LHS. We may have to
1016 // generate extra code to implement WGSL-specified behaviour for negative
1017 // LHS.
1018 out << R"(>>)";
1019 break;
1020
Ben Claytonedc51ab2023-08-08 07:58:19 +00001021 case core::BinaryOp::kAdd:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001022 out << "+";
1023 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001024 case core::BinaryOp::kSubtract:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001025 out << "-";
1026 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001027 case core::BinaryOp::kMultiply:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001028 out << "*";
1029 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001030 case core::BinaryOp::kDivide:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001031 out << "/";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001032 break;
Ben Claytonedc51ab2023-08-08 07:58:19 +00001033 case core::BinaryOp::kModulo:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001034 out << "%";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001035 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001036 }
1037 out << " ";
1038
1039 if (!EmitExpression(out, expr->rhs)) {
1040 return false;
1041 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001042
1043 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001044}
1045
dan sinclairbae54e72023-07-28 15:01:54 +00001046bool ASTPrinter::EmitStatements(VectorRef<const ast::Statement*> stmts) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001047 for (auto* s : stmts) {
1048 if (!EmitStatement(s)) {
1049 return false;
1050 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001051 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001052 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001053}
1054
dan sinclairbae54e72023-07-28 15:01:54 +00001055bool ASTPrinter::EmitStatementsWithIndent(VectorRef<const ast::Statement*> stmts) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001056 ScopedIndent si(this);
1057 return EmitStatements(stmts);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001058}
1059
dan sinclair0bfeaf92023-07-26 17:39:51 +00001060bool ASTPrinter::EmitBlock(const ast::BlockStatement* stmt) {
dan sinclair67a18932023-06-26 19:54:49 +00001061 Line() << "{";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001062 if (!EmitStatementsWithIndent(stmt->statements)) {
1063 return false;
1064 }
dan sinclair67a18932023-06-26 19:54:49 +00001065 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001066 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001067}
1068
dan sinclair0bfeaf92023-07-26 17:39:51 +00001069bool ASTPrinter::EmitBreak(const ast::BreakStatement*) {
dan sinclair67a18932023-06-26 19:54:49 +00001070 Line() << "break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001071 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001072}
1073
dan sinclair0bfeaf92023-07-26 17:39:51 +00001074bool ASTPrinter::EmitBreakIf(const ast::BreakIfStatement* b) {
dan sinclair67a18932023-06-26 19:54:49 +00001075 auto out = Line();
dan sinclairb8b0c212022-10-20 22:45:50 +00001076 out << "if (";
1077 if (!EmitExpression(out, b->condition)) {
1078 return false;
1079 }
1080 out << ") { break; }";
1081 return true;
1082}
1083
dan sinclairbae54e72023-07-28 15:01:54 +00001084bool ASTPrinter::EmitCall(StringStream& out, const ast::CallExpression* expr) {
Ben Claytone9f8b092022-06-01 13:14:39 +00001085 auto* call = builder_.Sem().Get<sem::Call>(expr);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001086 auto* target = call->Target();
1087 return Switch(
Ben Clayton54a104e2023-02-22 20:04:40 +00001088 target, //
1089 [&](const sem::Function* func) { return EmitFunctionCall(out, call, func); },
Ben Claytond9766dc2023-09-21 12:41:20 +00001090 [&](const sem::BuiltinFn* builtin) { return EmitBuiltinCall(out, call, builtin); },
Ben Clayton54a104e2023-02-22 20:04:40 +00001091 [&](const sem::ValueConversion* conv) { return EmitValueConversion(out, call, conv); },
1092 [&](const sem::ValueConstructor* ctor) { return EmitValueConstructor(out, call, ctor); },
dan sinclair41e4d9a2022-05-01 14:40:55 +00001093 [&](Default) {
Ben Claytonf848af22023-07-28 16:37:32 +00001094 TINT_ICE() << "unhandled call target: " << target->TypeInfo().name;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001095 return false;
1096 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001097}
1098
dan sinclairbae54e72023-07-28 15:01:54 +00001099bool ASTPrinter::EmitFunctionCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001100 const sem::Call* call,
1101 const sem::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001102 auto* expr = call->Declaration();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001103
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001104 if (ast::HasAttribute<CalculateArrayLength::BufferSizeIntrinsic>(
dan sinclair41e4d9a2022-05-01 14:40:55 +00001105 func->Declaration()->attributes)) {
1106 // Special function generated by the CalculateArrayLength transform for
1107 // calling X.GetDimensions(Y)
1108 if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
1109 return false;
1110 }
1111 out << ".GetDimensions(";
1112 if (!EmitExpression(out, call->Arguments()[1]->Declaration())) {
1113 return false;
1114 }
1115 out << ")";
1116 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001117 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001118
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001119 if (auto* intrinsic =
1120 ast::GetAttribute<DecomposeMemoryAccess::Intrinsic>(func->Declaration()->attributes)) {
dan sinclairff7cf212022-10-03 14:05:23 +00001121 switch (intrinsic->address_space) {
Ben Claytoncd52f382023-08-07 13:11:08 +00001122 case core::AddressSpace::kUniform:
dan sinclair41e4d9a2022-05-01 14:40:55 +00001123 return EmitUniformBufferAccess(out, expr, intrinsic);
Ben Claytoncd52f382023-08-07 13:11:08 +00001124 case core::AddressSpace::kStorage:
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001125 if (!intrinsic->IsAtomic()) {
1126 return EmitStorageBufferAccess(out, expr, intrinsic);
1127 }
1128 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001129 default:
Ben Claytonf848af22023-07-28 16:37:32 +00001130 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic address space:"
1131 << intrinsic->address_space;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001132 return false;
1133 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001134 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001135
James Pricefe24b4a2023-08-08 01:40:43 +00001136 if (auto* wave_intrinsic =
1137 ast::GetAttribute<ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic>(
1138 func->Declaration()->attributes)) {
1139 switch (wave_intrinsic->op) {
1140 case ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic::Op::kWaveGetLaneCount:
1141 out << "WaveGetLaneCount()";
1142 return true;
1143 case ast::transform::CanonicalizeEntryPointIO::HLSLWaveIntrinsic::Op::kWaveGetLaneIndex:
1144 out << "WaveGetLaneIndex()";
1145 return true;
1146 }
1147 }
1148
dan sinclaird026e132023-04-18 19:38:25 +00001149 out << func->Declaration()->name->symbol.Name() << "(";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001150
1151 bool first = true;
1152 for (auto* arg : call->Arguments()) {
1153 if (!first) {
1154 out << ", ";
1155 }
1156 first = false;
1157
1158 if (!EmitExpression(out, arg->Declaration())) {
1159 return false;
1160 }
1161 }
1162
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001163 out << ")";
1164 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001165}
1166
dan sinclairbae54e72023-07-28 15:01:54 +00001167bool ASTPrinter::EmitBuiltinCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001168 const sem::Call* call,
Ben Claytond9766dc2023-09-21 12:41:20 +00001169 const sem::BuiltinFn* builtin) {
1170 const auto type = builtin->Fn();
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001171
dan sinclair41e4d9a2022-05-01 14:40:55 +00001172 auto* expr = call->Declaration();
1173 if (builtin->IsTexture()) {
1174 return EmitTextureCall(out, call, builtin);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001175 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001176 if (type == wgsl::BuiltinFn::kSelect) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001177 return EmitSelectCall(out, expr);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001178 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001179 if (type == wgsl::BuiltinFn::kModf) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001180 return EmitModfCall(out, expr, builtin);
1181 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001182 if (type == wgsl::BuiltinFn::kFrexp) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001183 return EmitFrexpCall(out, expr, builtin);
1184 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001185 if (type == wgsl::BuiltinFn::kDegrees) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001186 return EmitDegreesCall(out, expr, builtin);
1187 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001188 if (type == wgsl::BuiltinFn::kRadians) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001189 return EmitRadiansCall(out, expr, builtin);
1190 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001191 if (type == wgsl::BuiltinFn::kSign) {
dan sinclair70927862023-01-11 13:18:29 +00001192 return EmitSignCall(out, call, builtin);
1193 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001194 if (type == wgsl::BuiltinFn::kQuantizeToF16) {
Ben Clayton2bea9052022-11-02 00:09:50 +00001195 return EmitQuantizeToF16Call(out, expr, builtin);
1196 }
Ben Claytondfc815c2023-09-25 15:38:43 +00001197 if (type == wgsl::BuiltinFn::kTrunc) {
Antonio Maiorano5cf943e2023-03-27 18:55:25 +00001198 return EmitTruncCall(out, expr, builtin);
1199 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001200 if (builtin->IsDataPacking()) {
1201 return EmitDataPackingCall(out, expr, builtin);
1202 }
1203 if (builtin->IsDataUnpacking()) {
1204 return EmitDataUnpackingCall(out, expr, builtin);
1205 }
1206 if (builtin->IsBarrier()) {
1207 return EmitBarrierCall(out, builtin);
1208 }
1209 if (builtin->IsAtomic()) {
1210 return EmitWorkgroupAtomicCall(out, expr, builtin);
1211 }
Jiawei Shaoab975702022-05-13 00:09:56 +00001212 if (builtin->IsDP4a()) {
1213 return EmitDP4aCall(out, expr, builtin);
1214 }
James Price501983f2023-08-08 01:37:22 +00001215 if (builtin->IsSubgroup()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00001216 if (builtin->Fn() == wgsl::BuiltinFn::kSubgroupBroadcast) {
David Netoeea786f2023-09-21 05:32:53 +00001217 // Fall through the regular path.
1218 } else {
1219 return EmitSubgroupCall(out, expr, builtin);
1220 }
James Price501983f2023-08-08 01:37:22 +00001221 }
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001222
dan sinclair41e4d9a2022-05-01 14:40:55 +00001223 auto name = generate_builtin_name(builtin);
1224 if (name.empty()) {
1225 return false;
1226 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001227
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001228 // Handle single argument builtins that only accept and return uint (not int overload). We need
1229 // to explicitly cast the return value (we also cast the arg for good measure). See
1230 // crbug.com/tint/1550
Ben Claytondfc815c2023-09-25 15:38:43 +00001231 if (type == wgsl::BuiltinFn::kCountOneBits || type == wgsl::BuiltinFn::kReverseBits) {
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001232 auto* arg = call->Arguments()[0];
Ben Clayton6c337aa2022-12-07 19:25:17 +00001233 if (arg->Type()->UnwrapRef()->is_signed_integer_scalar_or_vector()) {
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001234 out << "asint(" << name << "(asuint(";
1235 if (!EmitExpression(out, arg->Declaration())) {
1236 return false;
1237 }
1238 out << ")))";
1239 return true;
1240 }
1241 }
1242
dan sinclair41e4d9a2022-05-01 14:40:55 +00001243 out << name << "(";
1244
1245 bool first = true;
1246 for (auto* arg : call->Arguments()) {
1247 if (!first) {
1248 out << ", ";
1249 }
1250 first = false;
1251
1252 if (!EmitExpression(out, arg->Declaration())) {
1253 return false;
1254 }
1255 }
1256
1257 out << ")";
Antonio Maioranoab4c0352022-05-20 01:58:40 +00001258
dan sinclair41e4d9a2022-05-01 14:40:55 +00001259 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001260}
1261
dan sinclairbae54e72023-07-28 15:01:54 +00001262bool ASTPrinter::EmitValueConversion(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001263 const sem::Call* call,
1264 const sem::ValueConversion* conv) {
Ben Claytoncd52f382023-08-07 13:11:08 +00001265 if (!EmitType(out, conv->Target(), core::AddressSpace::kUndefined, core::Access::kReadWrite,
1266 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001267 return false;
1268 }
1269 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001270
dan sinclair41e4d9a2022-05-01 14:40:55 +00001271 if (!EmitExpression(out, call->Arguments()[0]->Declaration())) {
1272 return false;
1273 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001274
dan sinclair41e4d9a2022-05-01 14:40:55 +00001275 out << ")";
1276 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001277}
1278
dan sinclairbae54e72023-07-28 15:01:54 +00001279bool ASTPrinter::EmitValueConstructor(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001280 const sem::Call* call,
1281 const sem::ValueConstructor* ctor) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001282 auto* type = call->Type();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001283
Ben Clayton54a104e2023-02-22 20:04:40 +00001284 // If the value constructor arguments are empty then we need to construct with the zero value
1285 // for all components.
Ben Clayton958a4642022-07-26 07:55:24 +00001286 if (call->Arguments().IsEmpty()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001287 return EmitZeroValue(out, type);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001288 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001289
dan sinclair6e77b472022-10-20 13:38:28 +00001290 // Single parameter matrix initializers must be identity initializer.
Zhaoming Jiangc5f7e8f2022-06-24 17:21:59 +00001291 // It could also be conversions between f16 and f32 matrix when f16 is properly supported.
dan sinclaircedcdf32023-08-10 02:39:48 +00001292 if (type->Is<core::type::Matrix>() && call->Arguments().Length() == 1) {
Zhaoming Jiangc5f7e8f2022-06-24 17:21:59 +00001293 if (!ctor->Parameters()[0]->Type()->UnwrapRef()->is_float_matrix()) {
Ben Claytonf848af22023-07-28 16:37:32 +00001294 TINT_UNREACHABLE()
dan sinclair6e77b472022-10-20 13:38:28 +00001295 << "found a single-parameter matrix initializer that is not identity initializer";
Zhaoming Jiangc5f7e8f2022-06-24 17:21:59 +00001296 return false;
Ben Clayton3b5edf12022-05-16 21:14:11 +00001297 }
1298 }
1299
dan sinclaircedcdf32023-08-10 02:39:48 +00001300 bool brackets = type->IsAnyOf<core::type::Array, core::type::Struct>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001301
dan sinclair41e4d9a2022-05-01 14:40:55 +00001302 // For single-value vector initializers, swizzle the scalar to the right
1303 // vector dimension using .x
dan sinclaircedcdf32023-08-10 02:39:48 +00001304 const bool is_single_value_vector_init =
1305 type->is_scalar_vector() && call->Arguments().Length() == 1 &&
1306 ctor->Parameters()[0]->Type()->Is<core::type::Scalar>();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001307
Ben Clayton6c098ba2022-07-14 20:46:39 +00001308 if (brackets) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001309 out << "{";
1310 } else {
Ben Claytoncd52f382023-08-07 13:11:08 +00001311 if (!EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001312 return false;
1313 }
1314 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001315 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001316
dan sinclair41e4d9a2022-05-01 14:40:55 +00001317 if (is_single_value_vector_init) {
1318 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001319 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001320
dan sinclair41e4d9a2022-05-01 14:40:55 +00001321 bool first = true;
1322 for (auto* e : call->Arguments()) {
1323 if (!first) {
1324 out << ", ";
1325 }
1326 first = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001327
dan sinclair41e4d9a2022-05-01 14:40:55 +00001328 if (!EmitExpression(out, e->Declaration())) {
1329 return false;
1330 }
1331 }
1332
1333 if (is_single_value_vector_init) {
dan sinclaircedcdf32023-08-10 02:39:48 +00001334 out << ")." << std::string(type->As<core::type::Vector>()->Width(), 'x');
dan sinclair41e4d9a2022-05-01 14:40:55 +00001335 }
1336
1337 out << (brackets ? "}" : ")");
1338 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001339}
1340
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001341bool ASTPrinter::EmitUniformBufferAccess(StringStream& out,
1342 const ast::CallExpression* expr,
1343 const DecomposeMemoryAccess::Intrinsic* intrinsic) {
dan sinclaird026e132023-04-18 19:38:25 +00001344 auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001345 auto* const offset = expr->args[0];
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001346
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001347 // offset in bytes
1348 uint32_t scalar_offset_bytes = 0;
1349 // offset in uint (4 bytes)
1350 uint32_t scalar_offset_index = 0;
1351 // expression to calculate offset in bytes
1352 std::string scalar_offset_bytes_expr;
1353 // expression to calculate offset in uint, by dividing scalar_offset_bytes_expr by 4
1354 std::string scalar_offset_index_expr;
1355 // expression to calculate offset in uint, independently
1356 std::string scalar_offset_index_unified_expr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001357
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001358 // If true, use scalar_offset_index, otherwise use scalar_offset_index_expr
dan sinclair41e4d9a2022-05-01 14:40:55 +00001359 bool scalar_offset_constant = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001360
Ben Clayton1a1b5272023-02-24 17:16:55 +00001361 if (auto* val = builder_.Sem().GetVal(offset)->ConstantValue()) {
dan sinclaircedcdf32023-08-10 02:39:48 +00001362 TINT_ASSERT(val->Type()->Is<core::type::U32>());
dan sinclairb53b8cf2022-12-15 16:25:31 +00001363 scalar_offset_bytes = static_cast<uint32_t>(val->ValueAs<AInt>());
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001364 scalar_offset_index = scalar_offset_bytes / 4; // bytes -> scalar index
dan sinclair41e4d9a2022-05-01 14:40:55 +00001365 scalar_offset_constant = true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001366 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001367
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001368 // If true, scalar_offset_bytes or scalar_offset_bytes_expr should be used, otherwise only use
1369 // scalar_offset_index or scalar_offset_index_unified_expr. Currently only loading f16 scalar
1370 // require using offset in bytes.
1371 const bool need_offset_in_bytes =
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001372 intrinsic->type == DecomposeMemoryAccess::Intrinsic::DataType::kF16;
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001373
dan sinclair41e4d9a2022-05-01 14:40:55 +00001374 if (!scalar_offset_constant) {
1375 // UBO offset not compile-time known.
1376 // Calculate the scalar offset into a temporary.
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001377 if (need_offset_in_bytes) {
1378 scalar_offset_bytes_expr = UniqueIdentifier("scalar_offset_bytes");
1379 scalar_offset_index_expr = UniqueIdentifier("scalar_offset_index");
1380 {
dan sinclair67a18932023-06-26 19:54:49 +00001381 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001382 pre << "const uint " << scalar_offset_bytes_expr << " = (";
Ben Clayton1a1b5272023-02-24 17:16:55 +00001383 if (!EmitExpression(pre, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001384 return false;
1385 }
1386 pre << ");";
1387 }
dan sinclair67a18932023-06-26 19:54:49 +00001388 Line() << "const uint " << scalar_offset_index_expr << " = " << scalar_offset_bytes_expr
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001389 << " / 4;";
1390 } else {
1391 scalar_offset_index_unified_expr = UniqueIdentifier("scalar_offset");
dan sinclair67a18932023-06-26 19:54:49 +00001392 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001393 pre << "const uint " << scalar_offset_index_unified_expr << " = (";
Ben Clayton1a1b5272023-02-24 17:16:55 +00001394 if (!EmitExpression(pre, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001395 return false;
1396 }
1397 pre << ") / 4;";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001398 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001399 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001400
Austin Enga0e96b52023-02-18 00:39:01 +00001401 const char swizzle[] = {'x', 'y', 'z', 'w'};
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001402
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001403 using Op = DecomposeMemoryAccess::Intrinsic::Op;
1404 using DataType = DecomposeMemoryAccess::Intrinsic::DataType;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001405 switch (intrinsic->op) {
1406 case Op::kLoad: {
1407 auto cast = [&](const char* to, auto&& load) {
1408 out << to << "(";
1409 auto result = load();
1410 out << ")";
1411 return result;
1412 };
dan sinclairbae54e72023-07-28 15:01:54 +00001413 auto load_u32_to = [&](StringStream& target) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001414 target << buffer;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001415 if (scalar_offset_constant) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001416 target << "[" << (scalar_offset_index / 4) << "]."
1417 << swizzle[scalar_offset_index & 3];
dan sinclair41e4d9a2022-05-01 14:40:55 +00001418 } else {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001419 target << "[" << scalar_offset_index_unified_expr << " / 4]["
1420 << scalar_offset_index_unified_expr << " % 4]";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001421 }
1422 return true;
1423 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001424 auto load_u32 = [&] { return load_u32_to(out); };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001425 // Has a minimum alignment of 8 bytes, so is either .xy or .zw
dan sinclairbae54e72023-07-28 15:01:54 +00001426 auto load_vec2_u32_to = [&](StringStream& target) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001427 if (scalar_offset_constant) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001428 target << buffer << "[" << (scalar_offset_index / 4) << "]"
1429 << ((scalar_offset_index & 2) == 0 ? ".xy" : ".zw");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001430 } else {
1431 std::string ubo_load = UniqueIdentifier("ubo_load");
1432 {
dan sinclair67a18932023-06-26 19:54:49 +00001433 auto pre = Line();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001434 pre << "uint4 " << ubo_load << " = " << buffer << "["
1435 << scalar_offset_index_unified_expr << " / 4];";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001436 }
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001437 target << "((" << scalar_offset_index_unified_expr << " & 2) ? " << ubo_load
1438 << ".zw : " << ubo_load << ".xy)";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001439 }
1440 return true;
1441 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001442 auto load_vec2_u32 = [&] { return load_vec2_u32_to(out); };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001443 // vec4 has a minimum alignment of 16 bytes, easiest case
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001444 auto load_vec4_u32 = [&] {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001445 out << buffer;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001446 if (scalar_offset_constant) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001447 out << "[" << (scalar_offset_index / 4) << "]";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001448 } else {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001449 out << "[" << scalar_offset_index_unified_expr << " / 4]";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001450 }
1451 return true;
1452 };
1453 // vec3 has a minimum alignment of 16 bytes, so is just a .xyz swizzle
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001454 auto load_vec3_u32 = [&] {
1455 if (!load_vec4_u32()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001456 return false;
1457 }
1458 out << ".xyz";
1459 return true;
1460 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001461 auto load_scalar_f16 = [&] {
1462 // offset bytes = 4k, ((buffer[index].x) & 0xFFFF)
1463 // offset bytes = 4k+2, ((buffer[index].x >> 16) & 0xFFFF)
Ben Clayton1a1b5272023-02-24 17:16:55 +00001464 out << "float16_t(f16tof32(((" << buffer;
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001465 if (scalar_offset_constant) {
1466 out << "[" << (scalar_offset_index / 4) << "]."
1467 << swizzle[scalar_offset_index & 3];
1468 // WGSL spec ensure little endian memory layout.
1469 if (scalar_offset_bytes % 4 == 0) {
1470 out << ") & 0xFFFF)";
1471 } else {
1472 out << " >> 16) & 0xFFFF)";
1473 }
1474 } else {
1475 out << "[" << scalar_offset_index_expr << " / 4][" << scalar_offset_index_expr
1476 << " % 4] >> (" << scalar_offset_bytes_expr
1477 << " % 4 == 0 ? 0 : 16)) & 0xFFFF)";
1478 }
1479 out << "))";
1480 return true;
1481 };
1482 auto load_vec2_f16 = [&] {
1483 // vec2<f16> is aligned to 4 bytes
1484 // Preclude code load the vec2<f16> data as a uint:
1485 // uint ubo_load = buffer[id0][id1];
1486 // Loading code convert it to vec2<f16>:
1487 // vector<float16_t, 2>(float16_t(f16tof32(ubo_load & 0xFFFF)),
1488 // float16_t(f16tof32(ubo_load >> 16)))
1489 std::string ubo_load = UniqueIdentifier("ubo_load");
1490 {
dan sinclair67a18932023-06-26 19:54:49 +00001491 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001492 // Load the 4 bytes f16 vector as an uint
1493 pre << "uint " << ubo_load << " = ";
1494 if (!load_u32_to(pre)) {
1495 return false;
1496 }
1497 pre << ";";
1498 }
1499 out << "vector<float16_t, 2>(float16_t(f16tof32(" << ubo_load
1500 << " & 0xFFFF)), float16_t(f16tof32(" << ubo_load << " >> 16)))";
1501 return true;
1502 };
1503 auto load_vec3_f16 = [&] {
1504 // vec3<f16> is aligned to 8 bytes
1505 // Preclude code load the vec3<f16> data as uint2 and convert its elements to
1506 // float16_t:
1507 // uint2 ubo_load = buffer[id0].xy;
1508 // /* The low 8 bits of two uint are the x and z elements of vec3<f16> */
1509 // vector<float16_t> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load &
1510 // 0xFFFF));
1511 // /* The high 8 bits of first uint is the y element of vec3<f16> */
1512 // float16_t ubo_load_y = f16tof32(ubo_load[0] >> 16);
1513 // Loading code convert it to vec3<f16>:
1514 // vector<float16_t, 3>(ubo_load_xz[0], ubo_load_y, ubo_load_xz[1])
1515 std::string ubo_load = UniqueIdentifier("ubo_load");
1516 std::string ubo_load_xz = UniqueIdentifier(ubo_load + "_xz");
1517 std::string ubo_load_y = UniqueIdentifier(ubo_load + "_y");
1518 {
dan sinclair67a18932023-06-26 19:54:49 +00001519 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001520 // Load the 8 bytes uint2 with the f16 vector at lower 6 bytes
1521 pre << "uint2 " << ubo_load << " = ";
1522 if (!load_vec2_u32_to(pre)) {
1523 return false;
1524 }
1525 pre << ";";
1526 }
1527 {
dan sinclair67a18932023-06-26 19:54:49 +00001528 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001529 pre << "vector<float16_t, 2> " << ubo_load_xz
1530 << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " & 0xFFFF));";
1531 }
1532 {
dan sinclair67a18932023-06-26 19:54:49 +00001533 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001534 pre << "float16_t " << ubo_load_y << " = f16tof32(" << ubo_load
1535 << "[0] >> 16);";
1536 }
1537 out << "vector<float16_t, 3>(" << ubo_load_xz << "[0], " << ubo_load_y << ", "
1538 << ubo_load_xz << "[1])";
1539 return true;
1540 };
1541 auto load_vec4_f16 = [&] {
1542 // vec4<f16> is aligned to 8 bytes
1543 // Preclude code load the vec4<f16> data as uint2 and convert its elements to
1544 // float16_t:
1545 // uint2 ubo_load = buffer[id0].xy;
1546 // /* The low 8 bits of two uint are the x and z elements of vec4<f16> */
1547 // vector<float16_t> ubo_load_xz = vector<float16_t, 2>(f16tof32(ubo_load &
1548 // 0xFFFF));
1549 // /* The high 8 bits of two uint are the y and w elements of vec4<f16> */
1550 // vector<float16_t, 2> ubo_load_yw = vector<float16_t, 2>(f16tof32(ubo_load >>
1551 // 16));
1552 // Loading code convert it to vec4<f16>:
1553 // vector<float16_t, 4>(ubo_load_xz[0], ubo_load_yw[0], ubo_load_xz[1],
1554 // ubo_load_yw[1])
1555 std::string ubo_load = UniqueIdentifier("ubo_load");
1556 std::string ubo_load_xz = UniqueIdentifier(ubo_load + "_xz");
1557 std::string ubo_load_yw = UniqueIdentifier(ubo_load + "_yw");
1558 {
dan sinclair67a18932023-06-26 19:54:49 +00001559 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001560 // Load the 8 bytes f16 vector as an uint2
1561 pre << "uint2 " << ubo_load << " = ";
1562 if (!load_vec2_u32_to(pre)) {
1563 return false;
1564 }
1565 pre << ";";
1566 }
1567 {
dan sinclair67a18932023-06-26 19:54:49 +00001568 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001569 pre << "vector<float16_t, 2> " << ubo_load_xz
1570 << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " & 0xFFFF));";
1571 }
1572 {
dan sinclair67a18932023-06-26 19:54:49 +00001573 auto pre = Line();
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001574 pre << "vector<float16_t, 2> " << ubo_load_yw
1575 << " = vector<float16_t, 2>(f16tof32(" << ubo_load << " >> 16));";
1576 }
1577 out << "vector<float16_t, 4>(" << ubo_load_xz << "[0], " << ubo_load_yw << "[0], "
1578 << ubo_load_xz << "[1], " << ubo_load_yw << "[1])";
1579 return true;
1580 };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001581 switch (intrinsic->type) {
1582 case DataType::kU32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001583 return load_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001584 case DataType::kF32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001585 return cast("asfloat", load_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001586 case DataType::kI32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001587 return cast("asint", load_u32);
1588 case DataType::kF16:
1589 return load_scalar_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001590 case DataType::kVec2U32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001591 return load_vec2_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001592 case DataType::kVec2F32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001593 return cast("asfloat", load_vec2_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001594 case DataType::kVec2I32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001595 return cast("asint", load_vec2_u32);
1596 case DataType::kVec2F16:
1597 return load_vec2_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001598 case DataType::kVec3U32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001599 return load_vec3_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001600 case DataType::kVec3F32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001601 return cast("asfloat", load_vec3_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001602 case DataType::kVec3I32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001603 return cast("asint", load_vec3_u32);
1604 case DataType::kVec3F16:
1605 return load_vec3_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001606 case DataType::kVec4U32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001607 return load_vec4_u32();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001608 case DataType::kVec4F32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001609 return cast("asfloat", load_vec4_u32);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001610 case DataType::kVec4I32:
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001611 return cast("asint", load_vec4_u32);
1612 case DataType::kVec4F16:
1613 return load_vec4_f16();
dan sinclair41e4d9a2022-05-01 14:40:55 +00001614 }
Ben Claytonf848af22023-07-28 16:37:32 +00001615 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1616 << static_cast<int>(intrinsic->type);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001617 return false;
1618 }
1619 default:
1620 break;
1621 }
Ben Claytonf848af22023-07-28 16:37:32 +00001622 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::Op: "
1623 << static_cast<int>(intrinsic->op);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001624 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001625}
1626
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001627bool ASTPrinter::EmitStorageBufferAccess(StringStream& out,
1628 const ast::CallExpression* expr,
1629 const DecomposeMemoryAccess::Intrinsic* intrinsic) {
dan sinclaird026e132023-04-18 19:38:25 +00001630 auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001631 auto* const offset = expr->args[0];
Ben Clayton407137a2023-06-28 21:02:32 +00001632 auto* const value = expr->args.Length() > 1 ? expr->args[1] : nullptr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001633
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001634 using Op = DecomposeMemoryAccess::Intrinsic::Op;
1635 using DataType = DecomposeMemoryAccess::Intrinsic::DataType;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001636 switch (intrinsic->op) {
1637 case Op::kLoad: {
1638 auto load = [&](const char* cast, int n) {
1639 if (cast) {
1640 out << cast << "(";
1641 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001642 out << buffer << ".Load";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001643 if (n > 1) {
1644 out << n;
1645 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00001646 ScopedParen sp(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001647 if (!EmitExpression(out, offset)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001648 return false;
1649 }
1650 if (cast) {
1651 out << ")";
1652 }
1653 return true;
1654 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001655 // Templated load used for f16 types, requires SM6.2 or higher and DXC
1656 // Used by loading f16 types, e.g. for f16 type, set type parameter to "float16_t"
1657 // to emit `buffer.Load<float16_t>(offset)`.
1658 auto templated_load = [&](const char* type) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001659 out << buffer << ".Load<" << type << ">"; // templated load
dan sinclairb2ba57b2023-02-28 15:14:09 +00001660 ScopedParen sp(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001661 if (!EmitExpression(out, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001662 return false;
1663 }
1664 return true;
1665 };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001666 switch (intrinsic->type) {
1667 case DataType::kU32:
1668 return load(nullptr, 1);
1669 case DataType::kF32:
1670 return load("asfloat", 1);
1671 case DataType::kI32:
1672 return load("asint", 1);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001673 case DataType::kF16:
1674 return templated_load("float16_t");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001675 case DataType::kVec2U32:
1676 return load(nullptr, 2);
1677 case DataType::kVec2F32:
1678 return load("asfloat", 2);
1679 case DataType::kVec2I32:
1680 return load("asint", 2);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001681 case DataType::kVec2F16:
1682 return templated_load("vector<float16_t, 2> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001683 case DataType::kVec3U32:
1684 return load(nullptr, 3);
1685 case DataType::kVec3F32:
1686 return load("asfloat", 3);
1687 case DataType::kVec3I32:
1688 return load("asint", 3);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001689 case DataType::kVec3F16:
1690 return templated_load("vector<float16_t, 3> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001691 case DataType::kVec4U32:
1692 return load(nullptr, 4);
1693 case DataType::kVec4F32:
1694 return load("asfloat", 4);
1695 case DataType::kVec4I32:
1696 return load("asint", 4);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001697 case DataType::kVec4F16:
1698 return templated_load("vector<float16_t, 4> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001699 }
Ben Claytonf848af22023-07-28 16:37:32 +00001700 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1701 << static_cast<int>(intrinsic->type);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001702 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001703 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001704
1705 case Op::kStore: {
1706 auto store = [&](int n) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001707 out << buffer << ".Store";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001708 if (n > 1) {
1709 out << n;
1710 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00001711 ScopedParen sp1(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001712 if (!EmitExpression(out, offset)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001713 return false;
1714 }
1715 out << ", asuint";
dan sinclairb2ba57b2023-02-28 15:14:09 +00001716 ScopedParen sp2(out);
Antonio Maioranod1368d72023-09-05 20:29:56 +00001717 if (!EmitTextureOrStorageBufferCallArgExpression(out, value)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001718 return false;
1719 }
1720 return true;
1721 };
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001722 // Templated stored used for f16 types, requires SM6.2 or higher and DXC
1723 // Used by storing f16 types, e.g. for f16 type, set type parameter to "float16_t"
1724 // to emit `buffer.Store<float16_t>(offset)`.
1725 auto templated_store = [&](const char* type) {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001726 out << buffer << ".Store<" << type << ">"; // templated store
dan sinclairb2ba57b2023-02-28 15:14:09 +00001727 ScopedParen sp1(out);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001728 if (!EmitExpression(out, offset)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001729 return false;
1730 }
1731 out << ", ";
Ben Clayton1a1b5272023-02-24 17:16:55 +00001732 if (!EmitExpression(out, value)) {
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001733 return false;
1734 }
1735 return true;
1736 };
dan sinclair41e4d9a2022-05-01 14:40:55 +00001737 switch (intrinsic->type) {
1738 case DataType::kU32:
1739 return store(1);
1740 case DataType::kF32:
1741 return store(1);
1742 case DataType::kI32:
1743 return store(1);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001744 case DataType::kF16:
1745 return templated_store("float16_t");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001746 case DataType::kVec2U32:
1747 return store(2);
1748 case DataType::kVec2F32:
1749 return store(2);
1750 case DataType::kVec2I32:
1751 return store(2);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001752 case DataType::kVec2F16:
1753 return templated_store("vector<float16_t, 2> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001754 case DataType::kVec3U32:
1755 return store(3);
1756 case DataType::kVec3F32:
1757 return store(3);
1758 case DataType::kVec3I32:
1759 return store(3);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001760 case DataType::kVec3F16:
1761 return templated_store("vector<float16_t, 3> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001762 case DataType::kVec4U32:
1763 return store(4);
1764 case DataType::kVec4F32:
1765 return store(4);
1766 case DataType::kVec4I32:
1767 return store(4);
Zhaoming Jiangab9b5f32022-11-24 05:25:35 +00001768 case DataType::kVec4F16:
1769 return templated_store("vector<float16_t, 4> ");
dan sinclair41e4d9a2022-05-01 14:40:55 +00001770 }
Ben Claytonf848af22023-07-28 16:37:32 +00001771 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::DataType: "
1772 << static_cast<int>(intrinsic->type);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001773 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001774 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001775 default:
Ben Clayton1a1b5272023-02-24 17:16:55 +00001776 // Break out to error case below
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001777 // Note that atomic intrinsics are generated as functions.
1778 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001779 }
1780
Ben Claytonf848af22023-07-28 16:37:32 +00001781 TINT_UNREACHABLE() << "unsupported DecomposeMemoryAccess::Intrinsic::Op: "
1782 << static_cast<int>(intrinsic->op);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001783 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001784}
1785
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00001786bool ASTPrinter::EmitStorageAtomicIntrinsic(const ast::Function* func,
1787 const DecomposeMemoryAccess::Intrinsic* intrinsic) {
1788 using Op = DecomposeMemoryAccess::Intrinsic::Op;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001789
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001790 const sem::Function* sem_func = builder_.Sem().Get(func);
1791 auto* result_ty = sem_func->ReturnType();
dan sinclaird026e132023-04-18 19:38:25 +00001792 const auto name = func->name->symbol.Name();
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001793 auto& buf = *current_buffer_;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001794
dan sinclaird026e132023-04-18 19:38:25 +00001795 auto const buffer = intrinsic->Buffer()->identifier->symbol.Name();
Ben Clayton1a1b5272023-02-24 17:16:55 +00001796
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001797 auto rmw = [&](const char* hlsl) -> bool {
1798 {
dan sinclair67a18932023-06-26 19:54:49 +00001799 auto fn = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001800 if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
1801 core::Access::kUndefined, name)) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001802 return false;
1803 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001804 fn << "(uint offset, ";
Ben Claytoncd52f382023-08-07 13:11:08 +00001805 if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
1806 core::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001807 return false;
1808 }
1809 fn << ") {";
1810 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001811
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001812 buf.IncrementIndent();
1813 TINT_DEFER({
1814 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00001815 Line(&buf) << "}";
1816 Line(&buf);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001817 });
1818
1819 {
dan sinclair67a18932023-06-26 19:54:49 +00001820 auto l = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001821 if (!EmitTypeAndName(l, result_ty, core::AddressSpace::kUndefined,
1822 core::Access::kUndefined, "original_value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001823 return false;
1824 }
1825 l << " = 0;";
1826 }
1827 {
dan sinclair67a18932023-06-26 19:54:49 +00001828 auto l = Line(&buf);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001829 l << buffer << "." << hlsl << "(offset, ";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001830 if (intrinsic->op == Op::kAtomicSub) {
1831 l << "-";
1832 }
1833 l << "value, original_value);";
1834 }
dan sinclair67a18932023-06-26 19:54:49 +00001835 Line(&buf) << "return original_value;";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001836 return true;
1837 };
1838
1839 switch (intrinsic->op) {
1840 case Op::kAtomicAdd:
1841 return rmw("InterlockedAdd");
1842
1843 case Op::kAtomicSub:
1844 // Use add with the operand negated.
1845 return rmw("InterlockedAdd");
1846
1847 case Op::kAtomicMax:
1848 return rmw("InterlockedMax");
1849
1850 case Op::kAtomicMin:
1851 return rmw("InterlockedMin");
1852
1853 case Op::kAtomicAnd:
1854 return rmw("InterlockedAnd");
1855
1856 case Op::kAtomicOr:
1857 return rmw("InterlockedOr");
1858
1859 case Op::kAtomicXor:
1860 return rmw("InterlockedXor");
1861
1862 case Op::kAtomicExchange:
1863 return rmw("InterlockedExchange");
1864
1865 case Op::kAtomicLoad: {
1866 // HLSL does not have an InterlockedLoad, so we emulate it with
1867 // InterlockedOr using 0 as the OR value
dan sinclair41e4d9a2022-05-01 14:40:55 +00001868 {
dan sinclair67a18932023-06-26 19:54:49 +00001869 auto fn = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001870 if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
1871 core::Access::kUndefined, name)) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001872 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001873 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001874 fn << "(uint offset) {";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001875 }
1876
1877 buf.IncrementIndent();
1878 TINT_DEFER({
1879 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00001880 Line(&buf) << "}";
1881 Line(&buf);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001882 });
1883
1884 {
dan sinclair67a18932023-06-26 19:54:49 +00001885 auto l = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001886 if (!EmitTypeAndName(l, result_ty, core::AddressSpace::kUndefined,
1887 core::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001888 return false;
1889 }
1890 l << " = 0;";
1891 }
1892
dan sinclair67a18932023-06-26 19:54:49 +00001893 Line(&buf) << buffer << ".InterlockedOr(offset, 0, value);";
1894 Line(&buf) << "return value;";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001895 return true;
1896 }
1897 case Op::kAtomicStore: {
Ben Clayton1a1b5272023-02-24 17:16:55 +00001898 auto* const value_ty = sem_func->Parameters()[1]->Type()->UnwrapRef();
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001899 // HLSL does not have an InterlockedStore, so we emulate it with
1900 // InterlockedExchange and discard the returned value
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001901 {
dan sinclair67a18932023-06-26 19:54:49 +00001902 auto fn = Line(&buf);
Ben Clayton1a1b5272023-02-24 17:16:55 +00001903 fn << "void " << name << "(uint offset, ";
Ben Claytoncd52f382023-08-07 13:11:08 +00001904 if (!EmitTypeAndName(fn, value_ty, core::AddressSpace::kUndefined,
1905 core::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001906 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001907 }
1908 fn << ") {";
1909 }
1910
1911 buf.IncrementIndent();
1912 TINT_DEFER({
1913 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00001914 Line(&buf) << "}";
1915 Line(&buf);
dan sinclair41e4d9a2022-05-01 14:40:55 +00001916 });
1917
1918 {
dan sinclair67a18932023-06-26 19:54:49 +00001919 auto l = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001920 if (!EmitTypeAndName(l, value_ty, core::AddressSpace::kUndefined,
1921 core::Access::kUndefined, "ignored")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001922 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001923 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001924 l << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001925 }
dan sinclair67a18932023-06-26 19:54:49 +00001926 Line(&buf) << buffer << ".InterlockedExchange(offset, value, ignored);";
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001927 return true;
1928 }
1929 case Op::kAtomicCompareExchangeWeak: {
dan sinclaircedcdf32023-08-10 02:39:48 +00001930 if (!EmitStructType(&helpers_, result_ty->As<core::type::Struct>())) {
Ben Clayton4b8a3d32023-06-13 16:55:57 +00001931 return false;
1932 }
1933
Ben Clayton1a1b5272023-02-24 17:16:55 +00001934 auto* const value_ty = sem_func->Parameters()[1]->Type()->UnwrapRef();
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001935 // NOTE: We don't need to emit the return type struct here as DecomposeMemoryAccess
1936 // already added it to the AST, and it should have already been emitted by now.
dan sinclair41e4d9a2022-05-01 14:40:55 +00001937 {
dan sinclair67a18932023-06-26 19:54:49 +00001938 auto fn = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001939 if (!EmitTypeAndName(fn, result_ty, core::AddressSpace::kUndefined,
1940 core::Access::kUndefined, name)) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001941 return false;
1942 }
Ben Clayton1a1b5272023-02-24 17:16:55 +00001943 fn << "(uint offset, ";
Ben Claytoncd52f382023-08-07 13:11:08 +00001944 if (!EmitTypeAndName(fn, value_ty, core::AddressSpace::kUndefined,
1945 core::Access::kUndefined, "compare")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001946 return false;
1947 }
1948 fn << ", ";
Ben Claytoncd52f382023-08-07 13:11:08 +00001949 if (!EmitTypeAndName(fn, value_ty, core::AddressSpace::kUndefined,
1950 core::Access::kUndefined, "value")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001951 return false;
1952 }
1953 fn << ") {";
1954 }
1955
1956 buf.IncrementIndent();
1957 TINT_DEFER({
1958 buf.DecrementIndent();
dan sinclair67a18932023-06-26 19:54:49 +00001959 Line(&buf) << "}";
1960 Line(&buf);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001961 });
1962
1963 { // T result = {0};
dan sinclair67a18932023-06-26 19:54:49 +00001964 auto l = Line(&buf);
Ben Claytoncd52f382023-08-07 13:11:08 +00001965 if (!EmitTypeAndName(l, result_ty, core::AddressSpace::kUndefined,
1966 core::Access::kUndefined, "result")) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001967 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00001968 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001969 l << "=";
1970 if (!EmitZeroValue(l, result_ty)) {
1971 return false;
1972 }
1973 l << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001974 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00001975
dan sinclair67a18932023-06-26 19:54:49 +00001976 Line(&buf) << buffer
Ben Clayton1a1b5272023-02-24 17:16:55 +00001977 << ".InterlockedCompareExchange(offset, compare, value, result.old_value);";
dan sinclair67a18932023-06-26 19:54:49 +00001978 Line(&buf) << "result.exchanged = result.old_value == compare;";
1979 Line(&buf) << "return result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00001980
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001981 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001982 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001983 default:
1984 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001985 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001986
Ben Claytonf848af22023-07-28 16:37:32 +00001987 TINT_UNREACHABLE() << "unsupported atomic DecomposeMemoryAccess::Intrinsic::Op: "
1988 << static_cast<int>(intrinsic->op);
Antonio Maiorano08f4b552022-05-31 13:20:28 +00001989 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001990}
1991
dan sinclairbae54e72023-07-28 15:01:54 +00001992bool ASTPrinter::EmitWorkgroupAtomicCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00001993 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00001994 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00001995 std::string result = UniqueIdentifier("atomic_result");
Ryan Harrisondbc13af2022-02-21 15:19:07 +00001996
dan sinclaircedcdf32023-08-10 02:39:48 +00001997 if (!builtin->ReturnType()->Is<core::type::Void>()) {
dan sinclair67a18932023-06-26 19:54:49 +00001998 auto pre = Line();
Ben Claytoncd52f382023-08-07 13:11:08 +00001999 if (!EmitTypeAndName(pre, builtin->ReturnType(), core::AddressSpace::kUndefined,
2000 core::Access::kUndefined, result)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002001 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002002 }
2003 pre << " = ";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002004 if (!EmitZeroValue(pre, builtin->ReturnType())) {
2005 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002006 }
2007 pre << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002008 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002009
dan sinclair41e4d9a2022-05-01 14:40:55 +00002010 auto call = [&](const char* name) {
dan sinclair67a18932023-06-26 19:54:49 +00002011 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002012 pre << name;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002013
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002014 {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002015 ScopedParen sp(pre);
Ben Clayton783b1692022-08-02 17:03:35 +00002016 for (size_t i = 0; i < expr->args.Length(); i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002017 auto* arg = expr->args[i];
2018 if (i > 0) {
2019 pre << ", ";
2020 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002021 if (i == 1 && builtin->Fn() == wgsl::BuiltinFn::kAtomicSub) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002022 // Sub uses InterlockedAdd with the operand negated.
2023 pre << "-";
2024 }
2025 if (!EmitExpression(pre, arg)) {
2026 return false;
2027 }
2028 }
2029
2030 pre << ", " << result;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002031 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002032
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002033 pre << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002034
dan sinclair41e4d9a2022-05-01 14:40:55 +00002035 out << result;
2036 return true;
2037 };
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002038
Ben Claytond9766dc2023-09-21 12:41:20 +00002039 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002040 case wgsl::BuiltinFn::kAtomicLoad: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002041 // HLSL does not have an InterlockedLoad, so we emulate it with
2042 // InterlockedOr using 0 as the OR value
dan sinclair67a18932023-06-26 19:54:49 +00002043 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002044 pre << "InterlockedOr";
2045 {
2046 ScopedParen sp(pre);
2047 if (!EmitExpression(pre, expr->args[0])) {
2048 return false;
2049 }
2050 pre << ", 0, " << result;
2051 }
2052 pre << ";";
2053
2054 out << result;
2055 return true;
2056 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002057 case wgsl::BuiltinFn::kAtomicStore: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002058 // HLSL does not have an InterlockedStore, so we emulate it with
2059 // InterlockedExchange and discard the returned value
2060 { // T result = 0;
dan sinclair67a18932023-06-26 19:54:49 +00002061 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002062 auto* value_ty = builtin->Parameters()[1]->Type()->UnwrapRef();
Ben Claytoncd52f382023-08-07 13:11:08 +00002063 if (!EmitTypeAndName(pre, value_ty, core::AddressSpace::kUndefined,
2064 core::Access::kUndefined, result)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002065 return false;
2066 }
2067 pre << " = ";
2068 if (!EmitZeroValue(pre, value_ty)) {
2069 return false;
2070 }
2071 pre << ";";
2072 }
2073
2074 out << "InterlockedExchange";
2075 {
dan sinclairb2ba57b2023-02-28 15:14:09 +00002076 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002077 if (!EmitExpression(out, expr->args[0])) {
2078 return false;
2079 }
2080 out << ", ";
2081 if (!EmitExpression(out, expr->args[1])) {
2082 return false;
2083 }
2084 out << ", " << result;
2085 }
2086 return true;
2087 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002088 case wgsl::BuiltinFn::kAtomicCompareExchangeWeak: {
dan sinclaircedcdf32023-08-10 02:39:48 +00002089 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<core::type::Struct>())) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002090 return false;
2091 }
2092
dan sinclair41e4d9a2022-05-01 14:40:55 +00002093 auto* dest = expr->args[0];
2094 auto* compare_value = expr->args[1];
2095 auto* value = expr->args[2];
2096
2097 std::string compare = UniqueIdentifier("atomic_compare_value");
2098
2099 { // T compare_value = <compare_value>;
dan sinclair67a18932023-06-26 19:54:49 +00002100 auto pre = Line();
Antonio Maioranof99671b2022-06-23 13:14:54 +00002101 if (!EmitTypeAndName(pre, TypeOf(compare_value)->UnwrapRef(),
Ben Claytoncd52f382023-08-07 13:11:08 +00002102 core::AddressSpace::kUndefined, core::Access::kUndefined,
dan sinclair61c16eb2023-01-21 23:44:38 +00002103 compare)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002104 return false;
2105 }
2106 pre << " = ";
2107 if (!EmitExpression(pre, compare_value)) {
2108 return false;
2109 }
2110 pre << ";";
2111 }
2112
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002113 { // InterlockedCompareExchange(dst, compare, value, result.old_value);
dan sinclair67a18932023-06-26 19:54:49 +00002114 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002115 pre << "InterlockedCompareExchange";
2116 {
2117 ScopedParen sp(pre);
2118 if (!EmitExpression(pre, dest)) {
2119 return false;
2120 }
2121 pre << ", " << compare << ", ";
2122 if (!EmitExpression(pre, value)) {
2123 return false;
2124 }
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002125 pre << ", " << result << ".old_value";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002126 }
2127 pre << ";";
2128 }
2129
Antonio Maiorano08f4b552022-05-31 13:20:28 +00002130 // result.exchanged = result.old_value == compare;
dan sinclair67a18932023-06-26 19:54:49 +00002131 Line() << result << ".exchanged = " << result << ".old_value == " << compare << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002132
2133 out << result;
2134 return true;
2135 }
2136
Ben Claytondfc815c2023-09-25 15:38:43 +00002137 case wgsl::BuiltinFn::kAtomicAdd:
2138 case wgsl::BuiltinFn::kAtomicSub:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002139 return call("InterlockedAdd");
2140
Ben Claytondfc815c2023-09-25 15:38:43 +00002141 case wgsl::BuiltinFn::kAtomicMax:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002142 return call("InterlockedMax");
2143
Ben Claytondfc815c2023-09-25 15:38:43 +00002144 case wgsl::BuiltinFn::kAtomicMin:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002145 return call("InterlockedMin");
2146
Ben Claytondfc815c2023-09-25 15:38:43 +00002147 case wgsl::BuiltinFn::kAtomicAnd:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002148 return call("InterlockedAnd");
2149
Ben Claytondfc815c2023-09-25 15:38:43 +00002150 case wgsl::BuiltinFn::kAtomicOr:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002151 return call("InterlockedOr");
2152
Ben Claytondfc815c2023-09-25 15:38:43 +00002153 case wgsl::BuiltinFn::kAtomicXor:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002154 return call("InterlockedXor");
2155
Ben Claytondfc815c2023-09-25 15:38:43 +00002156 case wgsl::BuiltinFn::kAtomicExchange:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002157 return call("InterlockedExchange");
2158
2159 default:
2160 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002161 }
2162
Ben Claytond9766dc2023-09-21 12:41:20 +00002163 TINT_UNREACHABLE() << "unsupported atomic builtin: " << builtin->Fn();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002164 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002165}
2166
dan sinclairbae54e72023-07-28 15:01:54 +00002167bool ASTPrinter::EmitSelectCall(StringStream& out, const ast::CallExpression* expr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002168 auto* expr_false = expr->args[0];
2169 auto* expr_true = expr->args[1];
2170 auto* expr_cond = expr->args[2];
dan sinclairb2ba57b2023-02-28 15:14:09 +00002171 ScopedParen paren(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002172 if (!EmitExpression(out, expr_cond)) {
2173 return false;
2174 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002175
dan sinclair41e4d9a2022-05-01 14:40:55 +00002176 out << " ? ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002177
dan sinclair41e4d9a2022-05-01 14:40:55 +00002178 if (!EmitExpression(out, expr_true)) {
2179 return false;
2180 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002181
dan sinclair41e4d9a2022-05-01 14:40:55 +00002182 out << " : ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002183
dan sinclair41e4d9a2022-05-01 14:40:55 +00002184 if (!EmitExpression(out, expr_false)) {
2185 return false;
2186 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002187
dan sinclair41e4d9a2022-05-01 14:40:55 +00002188 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002189}
2190
dan sinclairbae54e72023-07-28 15:01:54 +00002191bool ASTPrinter::EmitModfCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002192 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002193 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002194 return CallBuiltinHelper(
2195 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2196 auto* ty = builtin->Parameters()[0]->Type();
2197 auto in = params[0];
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002198
dan sinclair41e4d9a2022-05-01 14:40:55 +00002199 std::string width;
dan sinclaircedcdf32023-08-10 02:39:48 +00002200 if (auto* vec = ty->As<core::type::Vector>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002201 width = std::to_string(vec->Width());
2202 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002203
dan sinclair41e4d9a2022-05-01 14:40:55 +00002204 // Emit the builtin return type unique to this overload. This does not
2205 // exist in the AST, so it will not be generated in Generate().
dan sinclaircedcdf32023-08-10 02:39:48 +00002206 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<core::type::Struct>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002207 return false;
2208 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002209
dan sinclair41e4d9a2022-05-01 14:40:55 +00002210 {
dan sinclair67a18932023-06-26 19:54:49 +00002211 auto l = Line(b);
Ben Claytoncd52f382023-08-07 13:11:08 +00002212 if (!EmitType(l, builtin->ReturnType(), core::AddressSpace::kUndefined,
2213 core::Access::kUndefined, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002214 return false;
2215 }
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00002216 l << " result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002217 }
dan sinclair67a18932023-06-26 19:54:49 +00002218 Line(b) << "result.fract = modf(" << params[0] << ", result.whole);";
2219 Line(b) << "return result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002220 return true;
2221 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002222}
2223
dan sinclairbae54e72023-07-28 15:01:54 +00002224bool ASTPrinter::EmitFrexpCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002225 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002226 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002227 return CallBuiltinHelper(
2228 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2229 auto* ty = builtin->Parameters()[0]->Type();
2230 auto in = params[0];
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002231
dan sinclair41e4d9a2022-05-01 14:40:55 +00002232 std::string width;
dan sinclaircedcdf32023-08-10 02:39:48 +00002233 if (auto* vec = ty->As<core::type::Vector>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002234 width = std::to_string(vec->Width());
2235 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002236
dan sinclair41e4d9a2022-05-01 14:40:55 +00002237 // Emit the builtin return type unique to this overload. This does not
2238 // exist in the AST, so it will not be generated in Generate().
dan sinclaircedcdf32023-08-10 02:39:48 +00002239 if (!EmitStructType(&helpers_, builtin->ReturnType()->As<core::type::Struct>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002240 return false;
2241 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002242
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00002243 std::string member_type;
dan sinclaircedcdf32023-08-10 02:39:48 +00002244 if (Is<core::type::F16>(ty->DeepestElement())) {
Zhaoming Jiang20cddbf2022-08-05 15:11:44 +00002245 member_type = width.empty() ? "float16_t" : ("vector<float16_t, " + width + ">");
2246 } else {
2247 member_type = "float" + width;
2248 }
2249
dan sinclair67a18932023-06-26 19:54:49 +00002250 Line(b) << member_type << " exp;";
2251 Line(b) << member_type << " fract = sign(" << in << ") * frexp(" << in << ", exp);";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002252 {
dan sinclair67a18932023-06-26 19:54:49 +00002253 auto l = Line(b);
Ben Claytoncd52f382023-08-07 13:11:08 +00002254 if (!EmitType(l, builtin->ReturnType(), core::AddressSpace::kUndefined,
2255 core::Access::kUndefined, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002256 return false;
2257 }
Ben Clayton10fae7a2022-11-14 15:29:29 +00002258 l << " result = {fract, int" << width << "(exp)};";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002259 }
dan sinclair67a18932023-06-26 19:54:49 +00002260 Line(b) << "return result;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002261 return true;
2262 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002263}
2264
dan sinclairbae54e72023-07-28 15:01:54 +00002265bool ASTPrinter::EmitDegreesCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002266 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002267 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002268 return CallBuiltinHelper(out, expr, builtin,
2269 [&](TextBuffer* b, const std::vector<std::string>& params) {
dan sinclair67a18932023-06-26 19:54:49 +00002270 Line(b) << "return " << params[0] << " * " << std::setprecision(20)
dan sinclair41e4d9a2022-05-01 14:40:55 +00002271 << sem::kRadToDeg << ";";
2272 return true;
2273 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002274}
2275
dan sinclairbae54e72023-07-28 15:01:54 +00002276bool ASTPrinter::EmitRadiansCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002277 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002278 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002279 return CallBuiltinHelper(out, expr, builtin,
2280 [&](TextBuffer* b, const std::vector<std::string>& params) {
dan sinclair67a18932023-06-26 19:54:49 +00002281 Line(b) << "return " << params[0] << " * " << std::setprecision(20)
dan sinclair41e4d9a2022-05-01 14:40:55 +00002282 << sem::kDegToRad << ";";
2283 return true;
2284 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002285}
2286
dan sinclair70927862023-01-11 13:18:29 +00002287// The HLSL `sign` method always returns an `int` result (scalar or vector). In WGSL the result is
2288// expected to be the same type as the argument. This injects a cast to the expected WGSL result
2289// type after the call to `sign`.
Ben Claytond9766dc2023-09-21 12:41:20 +00002290bool ASTPrinter::EmitSignCall(StringStream& out, const sem::Call* call, const sem::BuiltinFn*) {
dan sinclair70927862023-01-11 13:18:29 +00002291 auto* arg = call->Arguments()[0];
Ben Claytoncd52f382023-08-07 13:11:08 +00002292 if (!EmitType(out, arg->Type(), core::AddressSpace::kUndefined, core::Access::kReadWrite, "")) {
dan sinclair70927862023-01-11 13:18:29 +00002293 return false;
2294 }
2295 out << "(sign(";
2296 if (!EmitExpression(out, arg->Declaration())) {
2297 return false;
2298 }
2299 out << "))";
2300 return true;
2301}
2302
dan sinclairbae54e72023-07-28 15:01:54 +00002303bool ASTPrinter::EmitQuantizeToF16Call(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002304 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002305 const sem::BuiltinFn* builtin) {
Antonio Maiorano51249b82023-03-31 08:00:33 +00002306 // Cast to f16 and back
Ben Clayton2bea9052022-11-02 00:09:50 +00002307 std::string width;
dan sinclaircedcdf32023-08-10 02:39:48 +00002308 if (auto* vec = builtin->ReturnType()->As<core::type::Vector>()) {
Ben Clayton2bea9052022-11-02 00:09:50 +00002309 width = std::to_string(vec->Width());
2310 }
Antonio Maiorano51249b82023-03-31 08:00:33 +00002311 out << "f16tof32(f32tof16"
2312 << "(";
Ben Clayton2bea9052022-11-02 00:09:50 +00002313 if (!EmitExpression(out, expr->args[0])) {
2314 return false;
2315 }
2316 out << "))";
2317 return true;
2318}
2319
dan sinclairbae54e72023-07-28 15:01:54 +00002320bool ASTPrinter::EmitTruncCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002321 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002322 const sem::BuiltinFn* builtin) {
Antonio Maiorano5cf943e2023-03-27 18:55:25 +00002323 // HLSL's trunc is broken for very large/small float values.
2324 // See crbug.com/tint/1883
2325 return CallBuiltinHelper( //
2326 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2327 // value < 0 ? ceil(value) : floor(value)
dan sinclair67a18932023-06-26 19:54:49 +00002328 Line(b) << "return " << params[0] << " < 0 ? ceil(" << params[0] << ") : floor("
Antonio Maiorano5cf943e2023-03-27 18:55:25 +00002329 << params[0] << ");";
2330 return true;
2331 });
2332}
2333
dan sinclairbae54e72023-07-28 15:01:54 +00002334bool ASTPrinter::EmitDataPackingCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002335 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002336 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002337 return CallBuiltinHelper(
2338 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2339 uint32_t dims = 2;
2340 bool is_signed = false;
2341 uint32_t scale = 65535;
Ben Claytondfc815c2023-09-25 15:38:43 +00002342 if (builtin->Fn() == wgsl::BuiltinFn::kPack4X8Snorm ||
2343 builtin->Fn() == wgsl::BuiltinFn::kPack4X8Unorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002344 dims = 4;
2345 scale = 255;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002346 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002347 if (builtin->Fn() == wgsl::BuiltinFn::kPack4X8Snorm ||
2348 builtin->Fn() == wgsl::BuiltinFn::kPack2X16Snorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002349 is_signed = true;
2350 scale = (scale - 1) / 2;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002351 }
Ben Claytond9766dc2023-09-21 12:41:20 +00002352 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002353 case wgsl::BuiltinFn::kPack4X8Snorm:
2354 case wgsl::BuiltinFn::kPack4X8Unorm:
2355 case wgsl::BuiltinFn::kPack2X16Snorm:
2356 case wgsl::BuiltinFn::kPack2X16Unorm: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002357 {
dan sinclair67a18932023-06-26 19:54:49 +00002358 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002359 l << (is_signed ? "" : "u") << "int" << dims
2360 << " i = " << (is_signed ? "" : "u") << "int" << dims << "(round(clamp("
2361 << params[0] << ", " << (is_signed ? "-1.0" : "0.0") << ", 1.0) * "
2362 << scale << ".0))";
2363 if (is_signed) {
2364 l << " & " << (dims == 4 ? "0xff" : "0xffff");
2365 }
2366 l << ";";
2367 }
2368 {
dan sinclair67a18932023-06-26 19:54:49 +00002369 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002370 l << "return ";
2371 if (is_signed) {
2372 l << "asuint";
2373 }
2374 l << "(i.x | i.y << " << (32 / dims);
2375 if (dims == 4) {
2376 l << " | i.z << 16 | i.w << 24";
2377 }
2378 l << ");";
2379 }
2380 break;
2381 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002382 case wgsl::BuiltinFn::kPack2X16Float: {
dan sinclair67a18932023-06-26 19:54:49 +00002383 Line(b) << "uint2 i = f32tof16(" << params[0] << ");";
2384 Line(b) << "return i.x | (i.y << 16);";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002385 break;
2386 }
2387 default:
2388 diagnostics_.add_error(diag::System::Writer,
2389 "Internal error: unhandled data packing builtin");
2390 return false;
2391 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002392
dan sinclair41e4d9a2022-05-01 14:40:55 +00002393 return true;
2394 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002395}
2396
dan sinclairbae54e72023-07-28 15:01:54 +00002397bool ASTPrinter::EmitDataUnpackingCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002398 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002399 const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002400 return CallBuiltinHelper(
2401 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2402 uint32_t dims = 2;
2403 bool is_signed = false;
2404 uint32_t scale = 65535;
Ben Claytondfc815c2023-09-25 15:38:43 +00002405 if (builtin->Fn() == wgsl::BuiltinFn::kUnpack4X8Snorm ||
2406 builtin->Fn() == wgsl::BuiltinFn::kUnpack4X8Unorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002407 dims = 4;
2408 scale = 255;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002409 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002410 if (builtin->Fn() == wgsl::BuiltinFn::kUnpack4X8Snorm ||
2411 builtin->Fn() == wgsl::BuiltinFn::kUnpack2X16Snorm) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002412 is_signed = true;
2413 scale = (scale - 1) / 2;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002414 }
Ben Claytond9766dc2023-09-21 12:41:20 +00002415 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002416 case wgsl::BuiltinFn::kUnpack4X8Snorm:
2417 case wgsl::BuiltinFn::kUnpack2X16Snorm: {
dan sinclair67a18932023-06-26 19:54:49 +00002418 Line(b) << "int j = int(" << params[0] << ");";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002419 { // Perform sign extension on the converted values.
dan sinclair67a18932023-06-26 19:54:49 +00002420 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002421 l << "int" << dims << " i = int" << dims << "(";
2422 if (dims == 2) {
2423 l << "j << 16, j) >> 16";
2424 } else {
2425 l << "j << 24, j << 16, j << 8, j) >> 24";
2426 }
2427 l << ";";
2428 }
dan sinclair67a18932023-06-26 19:54:49 +00002429 Line(b) << "return clamp(float" << dims << "(i) / " << scale << ".0, "
dan sinclair41e4d9a2022-05-01 14:40:55 +00002430 << (is_signed ? "-1.0" : "0.0") << ", 1.0);";
2431 break;
2432 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002433 case wgsl::BuiltinFn::kUnpack4X8Unorm:
2434 case wgsl::BuiltinFn::kUnpack2X16Unorm: {
dan sinclair67a18932023-06-26 19:54:49 +00002435 Line(b) << "uint j = " << params[0] << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002436 {
dan sinclair67a18932023-06-26 19:54:49 +00002437 auto l = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002438 l << "uint" << dims << " i = uint" << dims << "(";
2439 l << "j & " << (dims == 2 ? "0xffff" : "0xff") << ", ";
2440 if (dims == 4) {
2441 l << "(j >> " << (32 / dims) << ") & 0xff, (j >> 16) & 0xff, j >> 24";
2442 } else {
2443 l << "j >> " << (32 / dims);
2444 }
2445 l << ");";
2446 }
dan sinclair67a18932023-06-26 19:54:49 +00002447 Line(b) << "return float" << dims << "(i) / " << scale << ".0;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002448 break;
2449 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002450 case wgsl::BuiltinFn::kUnpack2X16Float:
dan sinclair67a18932023-06-26 19:54:49 +00002451 Line(b) << "uint i = " << params[0] << ";";
2452 Line(b) << "return f16tof32(uint2(i & 0xffff, i >> 16));";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002453 break;
2454 default:
2455 diagnostics_.add_error(diag::System::Writer,
2456 "Internal error: unhandled data packing builtin");
2457 return false;
2458 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002459
dan sinclair41e4d9a2022-05-01 14:40:55 +00002460 return true;
2461 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002462}
2463
dan sinclairbae54e72023-07-28 15:01:54 +00002464bool ASTPrinter::EmitDP4aCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002465 const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002466 const sem::BuiltinFn* builtin) {
Jiawei Shaoab975702022-05-13 00:09:56 +00002467 // TODO(crbug.com/tint/1497): support the polyfill version of DP4a functions.
2468 return CallBuiltinHelper(
2469 out, expr, builtin, [&](TextBuffer* b, const std::vector<std::string>& params) {
2470 std::string functionName;
Ben Claytond9766dc2023-09-21 12:41:20 +00002471 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002472 case wgsl::BuiltinFn::kDot4I8Packed:
dan sinclair67a18932023-06-26 19:54:49 +00002473 Line(b) << "int accumulator = 0;";
Jiawei Shaoab975702022-05-13 00:09:56 +00002474 functionName = "dot4add_i8packed";
2475 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002476 case wgsl::BuiltinFn::kDot4U8Packed:
dan sinclair67a18932023-06-26 19:54:49 +00002477 Line(b) << "uint accumulator = 0u;";
Jiawei Shaoab975702022-05-13 00:09:56 +00002478 functionName = "dot4add_u8packed";
2479 break;
2480 default:
2481 diagnostics_.add_error(diag::System::Writer,
2482 "Internal error: unhandled DP4a builtin");
2483 return false;
2484 }
dan sinclair67a18932023-06-26 19:54:49 +00002485 Line(b) << "return " << functionName << "(" << params[0] << ", " << params[1]
Jiawei Shao1c759212022-05-15 13:53:21 +00002486 << ", accumulator);";
Jiawei Shaoab975702022-05-13 00:09:56 +00002487
2488 return true;
2489 });
2490}
2491
Ben Claytond9766dc2023-09-21 12:41:20 +00002492bool ASTPrinter::EmitBarrierCall(StringStream& out, const sem::BuiltinFn* builtin) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002493 // TODO(crbug.com/tint/661): Combine sequential barriers to a single
2494 // instruction.
Ben Claytondfc815c2023-09-25 15:38:43 +00002495 if (builtin->Fn() == wgsl::BuiltinFn::kWorkgroupBarrier) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002496 out << "GroupMemoryBarrierWithGroupSync()";
Ben Claytondfc815c2023-09-25 15:38:43 +00002497 } else if (builtin->Fn() == wgsl::BuiltinFn::kStorageBarrier) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002498 out << "DeviceMemoryBarrierWithGroupSync()";
Ben Claytondfc815c2023-09-25 15:38:43 +00002499 } else if (builtin->Fn() == wgsl::BuiltinFn::kTextureBarrier) {
James Pricea6287df2023-08-11 00:45:54 +00002500 out << "DeviceMemoryBarrierWithGroupSync()";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002501 } else {
Ben Claytondfc815c2023-09-25 15:38:43 +00002502 TINT_UNREACHABLE() << "unexpected barrier builtin type " << builtin->Fn();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002503 return false;
2504 }
2505 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002506}
2507
James Price501983f2023-08-08 01:37:22 +00002508bool ASTPrinter::EmitSubgroupCall(StringStream& out,
2509 [[maybe_unused]] const ast::CallExpression* expr,
Ben Claytond9766dc2023-09-21 12:41:20 +00002510 const sem::BuiltinFn* builtin) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002511 if (builtin->Fn() == wgsl::BuiltinFn::kSubgroupBallot) {
James Price501983f2023-08-08 01:37:22 +00002512 out << "WaveActiveBallot(true)";
2513 } else {
David Netoeea786f2023-09-21 05:32:53 +00002514 // subgroupBroadcast is already handled in the regular builtin flow.
Ben Claytondfc815c2023-09-25 15:38:43 +00002515 TINT_UNREACHABLE() << "unexpected subgroup builtin type " << builtin->Fn();
James Price501983f2023-08-08 01:37:22 +00002516 return false;
2517 }
2518 return true;
2519}
2520
Antonio Maioranod1368d72023-09-05 20:29:56 +00002521bool ASTPrinter::EmitTextureOrStorageBufferCallArgExpression(StringStream& out,
2522 const ast::Expression* expr) {
2523 // TODO(crbug.com/tint/1976): Workaround DXC bug that fails to compile texture/storage function
2524 // calls with signed integer splatted constants. DXC fails to convert the coord arg, for e.g.
2525 // `0.xxx`, from a vector of 64-bit ints to a vector of 32-bit ints to match the texture load
2526 // parameter type. We work around this for now by explicitly casting the splatted constant to
2527 // the right type, for e.g. `int3(0.xxx)`.
2528 bool emitted_cast = false;
2529 if (auto* sem = builder_.Sem().GetVal(expr)) {
2530 if (auto* constant = sem->ConstantValue()) {
2531 if (auto* splat = constant->As<core::constant::Splat>()) {
2532 if (splat->Type()->is_signed_integer_vector()) {
2533 if (!EmitType(out, constant->Type(), core::AddressSpace::kUndefined,
2534 core::Access::kUndefined, "")) {
2535 return false;
2536 }
2537 out << "(";
2538 emitted_cast = true;
2539 }
2540 }
2541 }
2542 }
2543 if (!EmitExpression(out, expr)) {
2544 return false;
2545 }
2546 if (emitted_cast) {
2547 out << ")";
2548 }
2549 return true;
2550}
2551
dan sinclairbae54e72023-07-28 15:01:54 +00002552bool ASTPrinter::EmitTextureCall(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00002553 const sem::Call* call,
Ben Claytond9766dc2023-09-21 12:41:20 +00002554 const sem::BuiltinFn* builtin) {
Ben Clayton015fbe72023-08-09 07:46:44 +00002555 using Usage = core::ParameterUsage;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002556
dan sinclair41e4d9a2022-05-01 14:40:55 +00002557 auto& signature = builtin->Signature();
2558 auto* expr = call->Declaration();
2559 auto arguments = expr->args;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002560
dan sinclair41e4d9a2022-05-01 14:40:55 +00002561 // Returns the argument with the given usage
2562 auto arg = [&](Usage usage) {
2563 int idx = signature.IndexOf(usage);
dan sinclair3a2a2792022-06-29 14:38:15 +00002564 return (idx >= 0) ? arguments[static_cast<size_t>(idx)] : nullptr;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002565 };
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002566
dan sinclair41e4d9a2022-05-01 14:40:55 +00002567 auto* texture = arg(Usage::kTexture);
Ben Clayton884f9522023-01-12 22:52:57 +00002568 if (TINT_UNLIKELY(!texture)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002569 TINT_ICE() << "missing texture argument";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002570 return false;
2571 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002572
dan sinclaircedcdf32023-08-10 02:39:48 +00002573 auto* texture_type = TypeOf(texture)->UnwrapRef()->As<core::type::Texture>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002574
Ben Claytond9766dc2023-09-21 12:41:20 +00002575 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002576 case wgsl::BuiltinFn::kTextureDimensions:
2577 case wgsl::BuiltinFn::kTextureNumLayers:
2578 case wgsl::BuiltinFn::kTextureNumLevels:
2579 case wgsl::BuiltinFn::kTextureNumSamples: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002580 // All of these builtins use the GetDimensions() method on the texture
dan sinclaircedcdf32023-08-10 02:39:48 +00002581 bool is_ms = texture_type->IsAnyOf<core::type::MultisampledTexture,
2582 core::type::DepthMultisampledTexture>();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002583 int num_dimensions = 0;
2584 std::string swizzle;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002585
Ben Claytond9766dc2023-09-21 12:41:20 +00002586 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002587 case wgsl::BuiltinFn::kTextureDimensions:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002588 switch (texture_type->dim()) {
dan sinclaircedcdf32023-08-10 02:39:48 +00002589 case core::type::TextureDimension::kNone:
Ben Claytonf848af22023-07-28 16:37:32 +00002590 TINT_ICE() << "texture dimension is kNone";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002591 return false;
dan sinclaircedcdf32023-08-10 02:39:48 +00002592 case core::type::TextureDimension::k1d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002593 num_dimensions = 1;
2594 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002595 case core::type::TextureDimension::k2d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002596 num_dimensions = is_ms ? 3 : 2;
2597 swizzle = is_ms ? ".xy" : "";
2598 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002599 case core::type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002600 num_dimensions = is_ms ? 4 : 3;
2601 swizzle = ".xy";
2602 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002603 case core::type::TextureDimension::k3d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002604 num_dimensions = 3;
2605 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002606 case core::type::TextureDimension::kCube:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002607 num_dimensions = 2;
2608 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002609 case core::type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002610 num_dimensions = 3;
2611 swizzle = ".xy";
2612 break;
2613 }
2614 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002615 case wgsl::BuiltinFn::kTextureNumLayers:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002616 switch (texture_type->dim()) {
2617 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002618 TINT_ICE() << "texture dimension is not arrayed";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002619 return false;
dan sinclaircedcdf32023-08-10 02:39:48 +00002620 case core::type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002621 num_dimensions = is_ms ? 4 : 3;
2622 swizzle = ".z";
2623 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002624 case core::type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002625 num_dimensions = 3;
2626 swizzle = ".z";
2627 break;
2628 }
2629 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002630 case wgsl::BuiltinFn::kTextureNumLevels:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002631 switch (texture_type->dim()) {
2632 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002633 TINT_ICE() << "texture dimension does not support mips";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002634 return false;
dan sinclaircedcdf32023-08-10 02:39:48 +00002635 case core::type::TextureDimension::k1d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002636 num_dimensions = 2;
2637 swizzle = ".y";
2638 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002639 case core::type::TextureDimension::k2d:
2640 case core::type::TextureDimension::kCube:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002641 num_dimensions = 3;
2642 swizzle = ".z";
2643 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002644 case core::type::TextureDimension::k2dArray:
2645 case core::type::TextureDimension::k3d:
2646 case core::type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002647 num_dimensions = 4;
2648 swizzle = ".w";
2649 break;
2650 }
2651 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002652 case wgsl::BuiltinFn::kTextureNumSamples:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002653 switch (texture_type->dim()) {
2654 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002655 TINT_ICE() << "texture dimension does not support multisampling";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002656 return false;
dan sinclaircedcdf32023-08-10 02:39:48 +00002657 case core::type::TextureDimension::k2d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002658 num_dimensions = 3;
2659 swizzle = ".z";
2660 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00002661 case core::type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002662 num_dimensions = 4;
2663 swizzle = ".w";
2664 break;
2665 }
2666 break;
2667 default:
Ben Claytonf848af22023-07-28 16:37:32 +00002668 TINT_ICE() << "unexpected builtin";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002669 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002670 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002671
2672 auto* level_arg = arg(Usage::kLevel);
2673
2674 if (level_arg) {
2675 // `NumberOfLevels` is a non-optional argument if `MipLevel` was passed.
2676 // Increment the number of dimensions for the temporary vector to
2677 // accommodate this.
2678 num_dimensions++;
2679
2680 // If the swizzle was empty, the expression will evaluate to the whole
2681 // vector. As we've grown the vector by one element, we now need to
2682 // swizzle to keep the result expression equivalent.
2683 if (swizzle.empty()) {
2684 static constexpr const char* swizzles[] = {"", ".x", ".xy", ".xyz"};
2685 swizzle = swizzles[num_dimensions - 1];
2686 }
2687 }
2688
Ben Clayton884f9522023-01-12 22:52:57 +00002689 if (TINT_UNLIKELY(num_dimensions > 4)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002690 TINT_ICE() << "Texture query builtin temporary vector has " << num_dimensions
2691 << " dimensions";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002692 return false;
2693 }
2694
2695 // Declare a variable to hold the queried texture info
2696 auto dims = UniqueIdentifier(kTempNamePrefix);
2697 if (num_dimensions == 1) {
dan sinclair67a18932023-06-26 19:54:49 +00002698 Line() << "uint " << dims << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002699 } else {
dan sinclair67a18932023-06-26 19:54:49 +00002700 Line() << "uint" << num_dimensions << " " << dims << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002701 }
2702
2703 { // texture.GetDimensions(...)
dan sinclair67a18932023-06-26 19:54:49 +00002704 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002705 if (!EmitExpression(pre, texture)) {
2706 return false;
2707 }
2708 pre << ".GetDimensions(";
2709
2710 if (level_arg) {
2711 if (!EmitExpression(pre, level_arg)) {
2712 return false;
2713 }
2714 pre << ", ";
Ben Claytondfc815c2023-09-25 15:38:43 +00002715 } else if (builtin->Fn() == wgsl::BuiltinFn::kTextureNumLevels) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002716 pre << "0, ";
2717 }
2718
2719 if (num_dimensions == 1) {
2720 pre << dims;
2721 } else {
2722 static constexpr char xyzw[] = {'x', 'y', 'z', 'w'};
Ben Clayton884f9522023-01-12 22:52:57 +00002723 if (TINT_UNLIKELY(num_dimensions < 0 || num_dimensions > 4)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002724 TINT_ICE() << "vector dimensions are " << num_dimensions;
dan sinclair41e4d9a2022-05-01 14:40:55 +00002725 return false;
2726 }
2727 for (int i = 0; i < num_dimensions; i++) {
2728 if (i > 0) {
2729 pre << ", ";
2730 }
2731 pre << dims << "." << xyzw[i];
2732 }
2733 }
2734
2735 pre << ");";
2736 }
2737
2738 // The out parameters of the GetDimensions() call is now in temporary
2739 // `dims` variable. This may be packed with other data, so the final
2740 // expression may require a swizzle.
2741 out << dims << swizzle;
2742 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002743 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002744 default:
2745 break;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002746 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002747
Austin Eng86a617f2022-05-19 20:08:19 +00002748 if (!EmitExpression(out, texture)) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002749 return false;
Austin Eng86a617f2022-05-19 20:08:19 +00002750 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002751
2752 // If pack_level_in_coords is true, then the mip level will be appended as the
2753 // last value of the coordinates argument. If the WGSL builtin overload does
2754 // not have a level parameter and pack_level_in_coords is true, then a zero
2755 // mip level will be inserted.
2756 bool pack_level_in_coords = false;
2757
2758 uint32_t hlsl_ret_width = 4u;
2759
Ben Claytond9766dc2023-09-21 12:41:20 +00002760 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002761 case wgsl::BuiltinFn::kTextureSample:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002762 out << ".Sample(";
2763 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002764 case wgsl::BuiltinFn::kTextureSampleBias:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002765 out << ".SampleBias(";
2766 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002767 case wgsl::BuiltinFn::kTextureSampleLevel:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002768 out << ".SampleLevel(";
2769 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002770 case wgsl::BuiltinFn::kTextureSampleGrad:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002771 out << ".SampleGrad(";
2772 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002773 case wgsl::BuiltinFn::kTextureSampleCompare:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002774 out << ".SampleCmp(";
2775 hlsl_ret_width = 1;
2776 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002777 case wgsl::BuiltinFn::kTextureSampleCompareLevel:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002778 out << ".SampleCmpLevelZero(";
2779 hlsl_ret_width = 1;
2780 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002781 case wgsl::BuiltinFn::kTextureLoad:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002782 out << ".Load(";
2783 // Multisampled textures do not support mip-levels.
dan sinclaircedcdf32023-08-10 02:39:48 +00002784 if (!texture_type->Is<core::type::MultisampledTexture>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002785 pack_level_in_coords = true;
2786 }
2787 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002788 case wgsl::BuiltinFn::kTextureGather:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002789 out << ".Gather";
Ben Clayton015fbe72023-08-09 07:46:44 +00002790 if (builtin->Parameters()[0]->Usage() == core::ParameterUsage::kComponent) {
dan sinclair5addefb2022-12-14 20:46:32 +00002791 switch (call->Arguments()[0]->ConstantValue()->ValueAs<AInt>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002792 case 0:
2793 out << "Red";
2794 break;
2795 case 1:
2796 out << "Green";
2797 break;
2798 case 2:
2799 out << "Blue";
2800 break;
2801 case 3:
2802 out << "Alpha";
2803 break;
2804 }
2805 }
2806 out << "(";
2807 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002808 case wgsl::BuiltinFn::kTextureGatherCompare:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002809 out << ".GatherCmp(";
2810 break;
Ben Claytondfc815c2023-09-25 15:38:43 +00002811 case wgsl::BuiltinFn::kTextureStore:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002812 out << "[";
2813 break;
2814 default:
2815 diagnostics_.add_error(diag::System::Writer,
2816 "Internal compiler error: Unhandled texture builtin '" +
2817 std::string(builtin->str()) + "'");
2818 return false;
2819 }
2820
2821 if (auto* sampler = arg(Usage::kSampler)) {
Austin Eng86a617f2022-05-19 20:08:19 +00002822 if (!EmitExpression(out, sampler)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002823 return false;
Austin Eng86a617f2022-05-19 20:08:19 +00002824 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00002825 out << ", ";
2826 }
2827
2828 auto* param_coords = arg(Usage::kCoords);
Ben Clayton884f9522023-01-12 22:52:57 +00002829 if (TINT_UNLIKELY(!param_coords)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002830 TINT_ICE() << "missing coords argument";
dan sinclair41e4d9a2022-05-01 14:40:55 +00002831 return false;
2832 }
2833
2834 auto emit_vector_appended_with_i32_zero = [&](const ast::Expression* vector) {
dan sinclaircedcdf32023-08-10 02:39:48 +00002835 auto* i32 = builder_.create<core::type::I32>();
Ben Clayton0ce9ab02022-05-05 20:23:40 +00002836 auto* zero = builder_.Expr(0_i);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002837 auto* stmt = builder_.Sem().Get(vector)->Stmt();
Ben Clayton3fb9a3f2023-02-04 21:20:26 +00002838 builder_.Sem().Add(zero, builder_.create<sem::ValueExpression>(
Ben Clayton36c61552023-08-08 07:58:19 +00002839 zero, i32, core::EvaluationStage::kRuntime, stmt,
Ben Clayton3fb9a3f2023-02-04 21:20:26 +00002840 /* constant_value */ nullptr,
2841 /* has_side_effects */ false));
dan sinclair0bfeaf92023-07-26 17:39:51 +00002842 auto* packed = tint::writer::AppendVector(&builder_, vector, zero);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002843 return EmitExpression(out, packed->Declaration());
2844 };
2845
2846 auto emit_vector_appended_with_level = [&](const ast::Expression* vector) {
2847 if (auto* level = arg(Usage::kLevel)) {
dan sinclair0bfeaf92023-07-26 17:39:51 +00002848 auto* packed = tint::writer::AppendVector(&builder_, vector, level);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002849 return EmitExpression(out, packed->Declaration());
2850 }
2851 return emit_vector_appended_with_i32_zero(vector);
2852 };
2853
2854 if (auto* array_index = arg(Usage::kArrayIndex)) {
2855 // Array index needs to be appended to the coordinates.
dan sinclair0bfeaf92023-07-26 17:39:51 +00002856 auto* packed = tint::writer::AppendVector(&builder_, param_coords, array_index);
dan sinclair41e4d9a2022-05-01 14:40:55 +00002857 if (pack_level_in_coords) {
2858 // Then mip level needs to be appended to the coordinates.
2859 if (!emit_vector_appended_with_level(packed->Declaration())) {
2860 return false;
2861 }
2862 } else {
2863 if (!EmitExpression(out, packed->Declaration())) {
2864 return false;
2865 }
2866 }
2867 } else if (pack_level_in_coords) {
2868 // Mip level needs to be appended to the coordinates.
2869 if (!emit_vector_appended_with_level(param_coords)) {
2870 return false;
2871 }
Ben Claytondfc815c2023-09-25 15:38:43 +00002872 } else if (builtin->Fn() == wgsl::BuiltinFn::kTextureStore) {
Antonio Maioranob0dfccd2023-08-30 14:49:42 +00002873 // param_coords is an index expression, not a function arg
dan sinclair41e4d9a2022-05-01 14:40:55 +00002874 if (!EmitExpression(out, param_coords)) {
2875 return false;
2876 }
Antonio Maioranod1368d72023-09-05 20:29:56 +00002877 } else if (!EmitTextureOrStorageBufferCallArgExpression(out, param_coords)) {
Antonio Maioranob0dfccd2023-08-30 14:49:42 +00002878 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002879 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002880
dan sinclair41e4d9a2022-05-01 14:40:55 +00002881 for (auto usage : {Usage::kDepthRef, Usage::kBias, Usage::kLevel, Usage::kDdx, Usage::kDdy,
2882 Usage::kSampleIndex, Usage::kOffset}) {
2883 if (usage == Usage::kLevel && pack_level_in_coords) {
2884 continue; // mip level already packed in coordinates.
2885 }
2886 if (auto* e = arg(usage)) {
2887 out << ", ";
Antonio Maioranod1368d72023-09-05 20:29:56 +00002888 if (!EmitTextureOrStorageBufferCallArgExpression(out, e)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002889 return false;
2890 }
2891 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002892 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002893
Ben Claytondfc815c2023-09-25 15:38:43 +00002894 if (builtin->Fn() == wgsl::BuiltinFn::kTextureStore) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002895 out << "] = ";
2896 if (!EmitExpression(out, arg(Usage::kValue))) {
2897 return false;
2898 }
2899 } else {
2900 out << ")";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002901
dan sinclair41e4d9a2022-05-01 14:40:55 +00002902 // If the builtin return type does not match the number of elements of the
2903 // HLSL builtin, we need to swizzle the expression to generate the correct
2904 // number of components.
2905 uint32_t wgsl_ret_width = 1;
dan sinclaircedcdf32023-08-10 02:39:48 +00002906 if (auto* vec = builtin->ReturnType()->As<core::type::Vector>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00002907 wgsl_ret_width = vec->Width();
2908 }
2909 if (wgsl_ret_width < hlsl_ret_width) {
2910 out << ".";
2911 for (uint32_t i = 0; i < wgsl_ret_width; i++) {
2912 out << "xyz"[i];
2913 }
2914 }
Ben Clayton884f9522023-01-12 22:52:57 +00002915 if (TINT_UNLIKELY(wgsl_ret_width > hlsl_ret_width)) {
Ben Claytonf848af22023-07-28 16:37:32 +00002916 TINT_ICE() << "WGSL return width (" << wgsl_ret_width
2917 << ") is wider than HLSL return width (" << hlsl_ret_width << ") for "
Ben Claytond9766dc2023-09-21 12:41:20 +00002918 << builtin->Fn();
dan sinclair41e4d9a2022-05-01 14:40:55 +00002919 return false;
2920 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002921 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002922
dan sinclair41e4d9a2022-05-01 14:40:55 +00002923 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00002924}
2925
Ben Claytond9766dc2023-09-21 12:41:20 +00002926std::string ASTPrinter::generate_builtin_name(const sem::BuiltinFn* builtin) {
2927 switch (builtin->Fn()) {
Ben Claytondfc815c2023-09-25 15:38:43 +00002928 case wgsl::BuiltinFn::kAbs:
2929 case wgsl::BuiltinFn::kAcos:
2930 case wgsl::BuiltinFn::kAll:
2931 case wgsl::BuiltinFn::kAny:
2932 case wgsl::BuiltinFn::kAsin:
2933 case wgsl::BuiltinFn::kAtan:
2934 case wgsl::BuiltinFn::kAtan2:
2935 case wgsl::BuiltinFn::kCeil:
2936 case wgsl::BuiltinFn::kClamp:
2937 case wgsl::BuiltinFn::kCos:
2938 case wgsl::BuiltinFn::kCosh:
2939 case wgsl::BuiltinFn::kCross:
2940 case wgsl::BuiltinFn::kDeterminant:
2941 case wgsl::BuiltinFn::kDistance:
2942 case wgsl::BuiltinFn::kDot:
2943 case wgsl::BuiltinFn::kExp:
2944 case wgsl::BuiltinFn::kExp2:
2945 case wgsl::BuiltinFn::kFloor:
2946 case wgsl::BuiltinFn::kFrexp:
2947 case wgsl::BuiltinFn::kLdexp:
2948 case wgsl::BuiltinFn::kLength:
2949 case wgsl::BuiltinFn::kLog:
2950 case wgsl::BuiltinFn::kLog2:
2951 case wgsl::BuiltinFn::kMax:
2952 case wgsl::BuiltinFn::kMin:
2953 case wgsl::BuiltinFn::kModf:
2954 case wgsl::BuiltinFn::kNormalize:
2955 case wgsl::BuiltinFn::kPow:
2956 case wgsl::BuiltinFn::kReflect:
2957 case wgsl::BuiltinFn::kRefract:
2958 case wgsl::BuiltinFn::kRound:
2959 case wgsl::BuiltinFn::kSaturate:
2960 case wgsl::BuiltinFn::kSin:
2961 case wgsl::BuiltinFn::kSinh:
2962 case wgsl::BuiltinFn::kSqrt:
2963 case wgsl::BuiltinFn::kStep:
2964 case wgsl::BuiltinFn::kTan:
2965 case wgsl::BuiltinFn::kTanh:
2966 case wgsl::BuiltinFn::kTranspose:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002967 return builtin->str();
Ben Claytondfc815c2023-09-25 15:38:43 +00002968 case wgsl::BuiltinFn::kCountOneBits: // uint
dan sinclair41e4d9a2022-05-01 14:40:55 +00002969 return "countbits";
Ben Claytondfc815c2023-09-25 15:38:43 +00002970 case wgsl::BuiltinFn::kDpdx:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002971 return "ddx";
Ben Claytondfc815c2023-09-25 15:38:43 +00002972 case wgsl::BuiltinFn::kDpdxCoarse:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002973 return "ddx_coarse";
Ben Claytondfc815c2023-09-25 15:38:43 +00002974 case wgsl::BuiltinFn::kDpdxFine:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002975 return "ddx_fine";
Ben Claytondfc815c2023-09-25 15:38:43 +00002976 case wgsl::BuiltinFn::kDpdy:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002977 return "ddy";
Ben Claytondfc815c2023-09-25 15:38:43 +00002978 case wgsl::BuiltinFn::kDpdyCoarse:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002979 return "ddy_coarse";
Ben Claytondfc815c2023-09-25 15:38:43 +00002980 case wgsl::BuiltinFn::kDpdyFine:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002981 return "ddy_fine";
Ben Claytondfc815c2023-09-25 15:38:43 +00002982 case wgsl::BuiltinFn::kFaceForward:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002983 return "faceforward";
Ben Claytondfc815c2023-09-25 15:38:43 +00002984 case wgsl::BuiltinFn::kFract:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002985 return "frac";
Ben Claytondfc815c2023-09-25 15:38:43 +00002986 case wgsl::BuiltinFn::kFma:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002987 return "mad";
Ben Claytondfc815c2023-09-25 15:38:43 +00002988 case wgsl::BuiltinFn::kFwidth:
2989 case wgsl::BuiltinFn::kFwidthCoarse:
2990 case wgsl::BuiltinFn::kFwidthFine:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002991 return "fwidth";
Ben Claytondfc815c2023-09-25 15:38:43 +00002992 case wgsl::BuiltinFn::kInverseSqrt:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002993 return "rsqrt";
Ben Claytondfc815c2023-09-25 15:38:43 +00002994 case wgsl::BuiltinFn::kMix:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002995 return "lerp";
Ben Claytondfc815c2023-09-25 15:38:43 +00002996 case wgsl::BuiltinFn::kReverseBits: // uint
dan sinclair41e4d9a2022-05-01 14:40:55 +00002997 return "reversebits";
Ben Claytondfc815c2023-09-25 15:38:43 +00002998 case wgsl::BuiltinFn::kSmoothstep:
dan sinclair41e4d9a2022-05-01 14:40:55 +00002999 return "smoothstep";
Ben Claytondfc815c2023-09-25 15:38:43 +00003000 case wgsl::BuiltinFn::kSubgroupBroadcast:
David Netoeea786f2023-09-21 05:32:53 +00003001 return "WaveReadLaneAt";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003002 default:
3003 diagnostics_.add_error(diag::System::Writer,
3004 "Unknown builtin method: " + std::string(builtin->str()));
3005 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003006
dan sinclair41e4d9a2022-05-01 14:40:55 +00003007 return "";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003008}
3009
dan sinclair0bfeaf92023-07-26 17:39:51 +00003010bool ASTPrinter::EmitCase(const ast::SwitchStatement* s, size_t case_idx) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003011 auto* stmt = s->body[case_idx];
dan sinclairf148f082022-10-19 15:55:02 +00003012 auto* sem = builder_.Sem().Get<sem::CaseStatement>(stmt);
3013 for (auto* selector : sem->Selectors()) {
dan sinclair67a18932023-06-26 19:54:49 +00003014 auto out = Line();
dan sinclairf148f082022-10-19 15:55:02 +00003015 if (selector->IsDefault()) {
3016 out << "default";
3017 } else {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003018 out << "case ";
Ben Clayton329dfd72022-11-23 00:05:05 +00003019 if (!EmitConstant(out, selector->Value(), /* is_variable_initializer */ false)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003020 return false;
3021 }
dan sinclairf148f082022-10-19 15:55:02 +00003022 }
3023 out << ":";
3024 if (selector == sem->Selectors().back()) {
3025 out << " {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003026 }
3027 }
3028
dan sinclair67a18932023-06-26 19:54:49 +00003029 IncrementIndent();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003030 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00003031 DecrementIndent();
3032 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003033 });
3034
3035 // Emit the case statement
3036 if (!EmitStatements(stmt->body->statements)) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003037 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003038 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003039
dan sinclairbae54e72023-07-28 15:01:54 +00003040 if (!tint::IsAnyOf<ast::BreakStatement>(stmt->body->Last())) {
dan sinclair67a18932023-06-26 19:54:49 +00003041 Line() << "break;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003042 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003043
dan sinclair41e4d9a2022-05-01 14:40:55 +00003044 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003045}
3046
dan sinclair0bfeaf92023-07-26 17:39:51 +00003047bool ASTPrinter::EmitContinue(const ast::ContinueStatement*) {
dan sinclair4b88dbc2022-06-16 15:27:38 +00003048 if (!emit_continuing_ || !emit_continuing_()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003049 return false;
3050 }
dan sinclair67a18932023-06-26 19:54:49 +00003051 Line() << "continue;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003052 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003053}
3054
dan sinclair0bfeaf92023-07-26 17:39:51 +00003055bool ASTPrinter::EmitDiscard(const ast::DiscardStatement*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003056 // TODO(dsinclair): Verify this is correct when the discard semantics are
3057 // defined for WGSL (https://github.com/gpuweb/gpuweb/issues/361)
dan sinclair67a18932023-06-26 19:54:49 +00003058 Line() << "discard;";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003059 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003060}
3061
dan sinclairbae54e72023-07-28 15:01:54 +00003062bool ASTPrinter::EmitExpression(StringStream& out, const ast::Expression* expr) {
Ben Clayton0b4a2f12023-02-05 22:59:40 +00003063 if (auto* sem = builder_.Sem().GetVal(expr)) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003064 if (auto* constant = sem->ConstantValue()) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003065 bool is_variable_initializer = false;
3066 if (auto* stmt = sem->Stmt()) {
3067 if (auto* decl = As<ast::VariableDeclStatement>(stmt->Declaration())) {
3068 is_variable_initializer = decl->variable->initializer == expr;
3069 }
3070 }
3071 return EmitConstant(out, constant, is_variable_initializer);
Ben Claytone9f8b092022-06-01 13:14:39 +00003072 }
3073 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003074 return Switch(
Ben Claytonb90b6bf2022-08-23 16:23:05 +00003075 expr, //
3076 [&](const ast::IndexAccessorExpression* a) { return EmitIndexAccessor(out, a); },
3077 [&](const ast::BinaryExpression* b) { return EmitBinary(out, b); },
3078 [&](const ast::BitcastExpression* b) { return EmitBitcast(out, b); },
3079 [&](const ast::CallExpression* c) { return EmitCall(out, c); },
3080 [&](const ast::IdentifierExpression* i) { return EmitIdentifier(out, i); },
3081 [&](const ast::LiteralExpression* l) { return EmitLiteral(out, l); },
3082 [&](const ast::MemberAccessorExpression* m) { return EmitMemberAccessor(out, m); },
3083 [&](const ast::UnaryOpExpression* u) { return EmitUnaryOp(out, u); },
3084 [&](Default) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003085 diagnostics_.add_error(diag::System::Writer, "unknown expression type: " +
3086 std::string(expr->TypeInfo().name));
3087 return false;
3088 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003089}
3090
dan sinclairbae54e72023-07-28 15:01:54 +00003091bool ASTPrinter::EmitIdentifier(StringStream& out, const ast::IdentifierExpression* expr) {
dan sinclaird026e132023-04-18 19:38:25 +00003092 out << expr->identifier->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003093 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003094}
3095
dan sinclair0bfeaf92023-07-26 17:39:51 +00003096bool ASTPrinter::EmitIf(const ast::IfStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003097 {
dan sinclair67a18932023-06-26 19:54:49 +00003098 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003099 out << "if (";
3100 if (!EmitExpression(out, stmt->condition)) {
3101 return false;
3102 }
3103 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003104 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003105
dan sinclair41e4d9a2022-05-01 14:40:55 +00003106 if (!EmitStatementsWithIndent(stmt->body->statements)) {
James Price26ebe5e2022-04-29 00:14:53 +00003107 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003108 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003109
dan sinclair41e4d9a2022-05-01 14:40:55 +00003110 if (stmt->else_statement) {
dan sinclair67a18932023-06-26 19:54:49 +00003111 Line() << "} else {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003112 if (auto* block = stmt->else_statement->As<ast::BlockStatement>()) {
3113 if (!EmitStatementsWithIndent(block->statements)) {
3114 return false;
3115 }
3116 } else {
dan sinclairbae54e72023-07-28 15:01:54 +00003117 if (!EmitStatementsWithIndent(Vector{stmt->else_statement})) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003118 return false;
3119 }
3120 }
3121 }
dan sinclair67a18932023-06-26 19:54:49 +00003122 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003123
3124 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003125}
3126
dan sinclair0bfeaf92023-07-26 17:39:51 +00003127bool ASTPrinter::EmitFunction(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003128 auto* sem = builder_.Sem().Get(func);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003129
Antonio Maiorano08f4b552022-05-31 13:20:28 +00003130 // Emit storage atomic helpers
Ben Clayton7a5f54ea2023-09-06 16:58:22 +00003131 if (auto* intrinsic = ast::GetAttribute<DecomposeMemoryAccess::Intrinsic>(func->attributes)) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003132 if (intrinsic->address_space == core::AddressSpace::kStorage && intrinsic->IsAtomic()) {
Antonio Maiorano08f4b552022-05-31 13:20:28 +00003133 if (!EmitStorageAtomicIntrinsic(func, intrinsic)) {
3134 return false;
3135 }
3136 }
3137 return true;
3138 }
3139
dan sinclair41e4d9a2022-05-01 14:40:55 +00003140 if (ast::HasAttribute<ast::InternalAttribute>(func->attributes)) {
3141 // An internal function. Do not emit.
3142 return true;
3143 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003144
dan sinclair41e4d9a2022-05-01 14:40:55 +00003145 {
dan sinclair67a18932023-06-26 19:54:49 +00003146 auto out = Line();
dan sinclaird026e132023-04-18 19:38:25 +00003147 auto name = func->name->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003148 // If the function returns an array, then we need to declare a typedef for
3149 // this.
dan sinclaircedcdf32023-08-10 02:39:48 +00003150 if (sem->ReturnType()->Is<core::type::Array>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003151 auto typedef_name = UniqueIdentifier(name + "_ret");
dan sinclair67a18932023-06-26 19:54:49 +00003152 auto pre = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003153 pre << "typedef ";
Ben Claytoncd52f382023-08-07 13:11:08 +00003154 if (!EmitTypeAndName(pre, sem->ReturnType(), core::AddressSpace::kUndefined,
3155 core::Access::kReadWrite, typedef_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003156 return false;
3157 }
3158 pre << ";";
3159 out << typedef_name;
3160 } else {
Ben Claytoncd52f382023-08-07 13:11:08 +00003161 if (!EmitType(out, sem->ReturnType(), core::AddressSpace::kUndefined,
3162 core::Access::kReadWrite, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003163 return false;
3164 }
3165 }
3166
3167 out << " " << name << "(";
3168
3169 bool first = true;
3170
3171 for (auto* v : sem->Parameters()) {
3172 if (!first) {
3173 out << ", ";
3174 }
3175 first = false;
3176
3177 auto const* type = v->Type();
Ben Claytoncd52f382023-08-07 13:11:08 +00003178 auto address_space = core::AddressSpace::kUndefined;
3179 auto access = core::Access::kUndefined;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003180
dan sinclaircedcdf32023-08-10 02:39:48 +00003181 if (auto* ptr = type->As<core::type::Pointer>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003182 type = ptr->StoreType();
dan sinclairff7cf212022-10-03 14:05:23 +00003183 switch (ptr->AddressSpace()) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003184 case core::AddressSpace::kStorage:
3185 case core::AddressSpace::kUniform:
Ben Clayton2032d032022-06-15 19:32:37 +00003186 // Not allowed by WGSL, but is used by certain transforms (e.g. DMA) to pass
3187 // storage buffers and uniform buffers down into transform-generated
3188 // functions. In this situation we want to generate the parameter without an
dan sinclairff7cf212022-10-03 14:05:23 +00003189 // 'inout', using the address space and access from the pointer.
3190 address_space = ptr->AddressSpace();
Ben Clayton2032d032022-06-15 19:32:37 +00003191 access = ptr->Access();
3192 break;
3193 default:
3194 // Transform regular WGSL pointer parameters in to `inout` parameters.
3195 out << "inout ";
3196 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003197 }
3198
Ben Clayton1b90f932023-02-18 12:37:34 +00003199 // Note: WGSL only allows for AddressSpace::kUndefined on parameters, however
dan sinclair41e4d9a2022-05-01 14:40:55 +00003200 // the sanitizer transforms generates load / store functions for storage
3201 // or uniform buffers. These functions have a buffer parameter with
dan sinclairff7cf212022-10-03 14:05:23 +00003202 // AddressSpace::kStorage or AddressSpace::kUniform. This is required to
dan sinclair41e4d9a2022-05-01 14:40:55 +00003203 // correctly translate the parameter to a [RW]ByteAddressBuffer for
3204 // storage buffers and a uint4[N] for uniform buffers.
dan sinclairff7cf212022-10-03 14:05:23 +00003205 if (!EmitTypeAndName(out, type, address_space, access,
dan sinclaird026e132023-04-18 19:38:25 +00003206 v->Declaration()->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003207 return false;
3208 }
3209 }
3210 out << ") {";
3211 }
3212
dan sinclaircedcdf32023-08-10 02:39:48 +00003213 if (sem->DiscardStatement() && !sem->ReturnType()->Is<core::type::Void>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003214 // BUG(crbug.com/tint/1081): work around non-void functions with discard
3215 // failing compilation sometimes
3216 if (!EmitFunctionBodyWithDiscard(func)) {
3217 return false;
3218 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003219 } else {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003220 if (!EmitStatementsWithIndent(func->body->statements)) {
3221 return false;
3222 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003223 }
3224
dan sinclair67a18932023-06-26 19:54:49 +00003225 Line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003226
dan sinclair41e4d9a2022-05-01 14:40:55 +00003227 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003228}
3229
dan sinclair0bfeaf92023-07-26 17:39:51 +00003230bool ASTPrinter::EmitFunctionBodyWithDiscard(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003231 // FXC sometimes fails to compile functions that discard with 'Not all control
3232 // paths return a value'. We work around this by wrapping the function body
3233 // within an "if (true) { <body> } return <default return type obj>;" so that
3234 // there is always an (unused) return statement.
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003235
dan sinclair41e4d9a2022-05-01 14:40:55 +00003236 auto* sem = builder_.Sem().Get(func);
dan sinclaircedcdf32023-08-10 02:39:48 +00003237 TINT_ASSERT(sem->DiscardStatement() && !sem->ReturnType()->Is<core::type::Void>());
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003238
dan sinclair41e4d9a2022-05-01 14:40:55 +00003239 ScopedIndent si(this);
dan sinclair67a18932023-06-26 19:54:49 +00003240 Line() << "if (true) {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003241
dan sinclair41e4d9a2022-05-01 14:40:55 +00003242 if (!EmitStatementsWithIndent(func->body->statements)) {
3243 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003244 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003245
dan sinclair67a18932023-06-26 19:54:49 +00003246 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003247
3248 // Return an unused result that matches the type of the return value
dan sinclaird026e132023-04-18 19:38:25 +00003249 auto name = builder_.Symbols().New("unused").Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003250 {
dan sinclair67a18932023-06-26 19:54:49 +00003251 auto out = Line();
Ben Claytoncd52f382023-08-07 13:11:08 +00003252 if (!EmitTypeAndName(out, sem->ReturnType(), core::AddressSpace::kUndefined,
3253 core::Access::kReadWrite, name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003254 return false;
3255 }
3256 out << ";";
3257 }
dan sinclair67a18932023-06-26 19:54:49 +00003258 Line() << "return " << name << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003259
3260 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003261}
3262
dan sinclair0bfeaf92023-07-26 17:39:51 +00003263bool ASTPrinter::EmitGlobalVariable(const ast::Variable* global) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00003264 return Switch(
3265 global, //
3266 [&](const ast::Var* var) {
3267 auto* sem = builder_.Sem().Get(global);
dan sinclairff7cf212022-10-03 14:05:23 +00003268 switch (sem->AddressSpace()) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003269 case core::AddressSpace::kUniform:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003270 return EmitUniformVariable(var, sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003271 case core::AddressSpace::kStorage:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003272 return EmitStorageVariable(var, sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003273 case core::AddressSpace::kHandle:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003274 return EmitHandleVariable(var, sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003275 case core::AddressSpace::kPrivate:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003276 return EmitPrivateVariable(sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003277 case core::AddressSpace::kWorkgroup:
Ben Claytondcdf66e2022-06-17 12:48:51 +00003278 return EmitWorkgroupVariable(sem);
Ben Claytoncd52f382023-08-07 13:11:08 +00003279 case core::AddressSpace::kPushConstant:
dan sinclair4abf28e2022-08-02 15:55:35 +00003280 diagnostics_.add_error(
3281 diag::System::Writer,
dan sinclairbae54e72023-07-28 15:01:54 +00003282 "unhandled address space " + tint::ToString(sem->AddressSpace()));
dan sinclair4abf28e2022-08-02 15:55:35 +00003283 return false;
dan sinclair8dbd4d02022-07-27 18:54:05 +00003284 default: {
Ben Claytonf848af22023-07-28 16:37:32 +00003285 TINT_ICE() << "unhandled address space " << sem->AddressSpace();
Ben Claytondcdf66e2022-06-17 12:48:51 +00003286 return false;
dan sinclair8dbd4d02022-07-27 18:54:05 +00003287 }
Ben Claytondcdf66e2022-06-17 12:48:51 +00003288 }
3289 },
dan sinclairf6a94042022-09-09 16:16:19 +00003290 [&](const ast::Override*) {
3291 // Override is removed with SubstituteOverride
Ben Clayton490d9882022-09-21 21:05:45 +00003292 diagnostics_.add_error(diag::System::Writer,
Ben Claytonf10a5792022-10-13 13:47:39 +00003293 "override-expressions should have been removed with the "
Ben Clayton490d9882022-09-21 21:05:45 +00003294 "SubstituteOverride transform");
dan sinclairf6a94042022-09-09 16:16:19 +00003295 return false;
3296 },
Ben Clayton19576e92022-06-28 12:44:16 +00003297 [&](const ast::Const*) {
3298 return true; // Constants are embedded at their use
3299 },
Ben Claytondcdf66e2022-06-17 12:48:51 +00003300 [&](Default) {
Ben Claytonf848af22023-07-28 16:37:32 +00003301 TINT_ICE() << "unhandled global variable type " << global->TypeInfo().name;
dan sinclair4abf28e2022-08-02 15:55:35 +00003302
Ben Claytondcdf66e2022-06-17 12:48:51 +00003303 return false;
3304 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003305}
3306
dan sinclair0bfeaf92023-07-26 17:39:51 +00003307bool ASTPrinter::EmitUniformVariable(const ast::Var* var, const sem::Variable* sem) {
Ben Clayton5f4847c2023-04-19 13:24:27 +00003308 auto binding_point = *sem->As<sem::GlobalVariable>()->BindingPoint();
Ben Claytondcdf66e2022-06-17 12:48:51 +00003309 auto* type = sem->Type()->UnwrapRef();
dan sinclaird026e132023-04-18 19:38:25 +00003310 auto name = var->name->symbol.Name();
dan sinclair67a18932023-06-26 19:54:49 +00003311 Line() << "cbuffer cbuffer_" << name << RegisterAndSpace('b', binding_point) << " {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003312
dan sinclair41e4d9a2022-05-01 14:40:55 +00003313 {
3314 ScopedIndent si(this);
dan sinclair67a18932023-06-26 19:54:49 +00003315 auto out = Line();
Ben Claytoncd52f382023-08-07 13:11:08 +00003316 if (!EmitTypeAndName(out, type, core::AddressSpace::kUniform, sem->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003317 return false;
3318 }
3319 out << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003320 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003321
dan sinclair67a18932023-06-26 19:54:49 +00003322 Line() << "};";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003323
dan sinclair41e4d9a2022-05-01 14:40:55 +00003324 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003325}
3326
dan sinclair0bfeaf92023-07-26 17:39:51 +00003327bool ASTPrinter::EmitStorageVariable(const ast::Var* var, const sem::Variable* sem) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00003328 auto* type = sem->Type()->UnwrapRef();
dan sinclair67a18932023-06-26 19:54:49 +00003329 auto out = Line();
Ben Claytoncd52f382023-08-07 13:11:08 +00003330 if (!EmitTypeAndName(out, type, core::AddressSpace::kStorage, sem->Access(),
dan sinclaird026e132023-04-18 19:38:25 +00003331 var->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003332 return false;
3333 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003334
dan sinclairacdf6e12022-08-24 15:47:25 +00003335 auto* global_sem = sem->As<sem::GlobalVariable>();
Ben Claytoncd52f382023-08-07 13:11:08 +00003336 out << RegisterAndSpace(sem->Access() == core::Access::kRead ? 't' : 'u',
Ben Clayton5f4847c2023-04-19 13:24:27 +00003337 *global_sem->BindingPoint())
dan sinclair41e4d9a2022-05-01 14:40:55 +00003338 << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003339
dan sinclair41e4d9a2022-05-01 14:40:55 +00003340 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003341}
3342
dan sinclair0bfeaf92023-07-26 17:39:51 +00003343bool ASTPrinter::EmitHandleVariable(const ast::Var* var, const sem::Variable* sem) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00003344 auto* unwrapped_type = sem->Type()->UnwrapRef();
dan sinclair67a18932023-06-26 19:54:49 +00003345 auto out = Line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003346
dan sinclaird026e132023-04-18 19:38:25 +00003347 auto name = var->name->symbol.Name();
Ben Claytondcdf66e2022-06-17 12:48:51 +00003348 auto* type = sem->Type()->UnwrapRef();
dan sinclairff7cf212022-10-03 14:05:23 +00003349 if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003350 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003351 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003352
dan sinclair41e4d9a2022-05-01 14:40:55 +00003353 const char* register_space = nullptr;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003354
dan sinclaircedcdf32023-08-10 02:39:48 +00003355 if (unwrapped_type->Is<core::type::Texture>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003356 register_space = "t";
James Price88c231b2023-08-15 12:47:28 +00003357 if (auto* st = unwrapped_type->As<core::type::StorageTexture>();
3358 st && st->access() != core::Access::kRead) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003359 register_space = "u";
3360 }
dan sinclaircedcdf32023-08-10 02:39:48 +00003361 } else if (unwrapped_type->Is<core::type::Sampler>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003362 register_space = "s";
3363 }
3364
3365 if (register_space) {
dan sinclairacdf6e12022-08-24 15:47:25 +00003366 auto bp = sem->As<sem::GlobalVariable>()->BindingPoint();
Ben Clayton5f4847c2023-04-19 13:24:27 +00003367 out << " : register(" << register_space << bp->binding;
Peng Huangc00ff7f2023-03-31 17:55:19 +00003368 // Omit the space if it's 0, as it's the default.
3369 // SM 5.0 doesn't support spaces, so we don't emit them if group is 0 for better
3370 // compatibility.
Ben Clayton5f4847c2023-04-19 13:24:27 +00003371 if (bp->group == 0) {
Peng Huangc00ff7f2023-03-31 17:55:19 +00003372 out << ")";
3373 } else {
Ben Clayton5f4847c2023-04-19 13:24:27 +00003374 out << ", space" << bp->group << ")";
Peng Huangc00ff7f2023-03-31 17:55:19 +00003375 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003376 }
3377
3378 out << ";";
3379 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003380}
3381
dan sinclair0bfeaf92023-07-26 17:39:51 +00003382bool ASTPrinter::EmitPrivateVariable(const sem::Variable* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003383 auto* decl = var->Declaration();
dan sinclair67a18932023-06-26 19:54:49 +00003384 auto out = Line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003385
dan sinclair41e4d9a2022-05-01 14:40:55 +00003386 out << "static ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003387
dan sinclaird026e132023-04-18 19:38:25 +00003388 auto name = decl->name->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003389 auto* type = var->Type()->UnwrapRef();
dan sinclairff7cf212022-10-03 14:05:23 +00003390 if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003391 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003392 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003393
dan sinclair41e4d9a2022-05-01 14:40:55 +00003394 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00003395 if (auto* initializer = decl->initializer) {
3396 if (!EmitExpression(out, initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003397 return false;
3398 }
3399 } else {
3400 if (!EmitZeroValue(out, var->Type()->UnwrapRef())) {
3401 return false;
3402 }
3403 }
3404
3405 out << ";";
3406 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003407}
3408
dan sinclair0bfeaf92023-07-26 17:39:51 +00003409bool ASTPrinter::EmitWorkgroupVariable(const sem::Variable* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003410 auto* decl = var->Declaration();
dan sinclair67a18932023-06-26 19:54:49 +00003411 auto out = Line();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003412
dan sinclair41e4d9a2022-05-01 14:40:55 +00003413 out << "groupshared ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003414
dan sinclaird026e132023-04-18 19:38:25 +00003415 auto name = decl->name->symbol.Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003416 auto* type = var->Type()->UnwrapRef();
dan sinclairff7cf212022-10-03 14:05:23 +00003417 if (!EmitTypeAndName(out, type, var->AddressSpace(), var->Access(), name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003418 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003419 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003420
dan sinclair6e77b472022-10-20 13:38:28 +00003421 if (auto* initializer = decl->initializer) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003422 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00003423 if (!EmitExpression(out, initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003424 return false;
3425 }
3426 }
3427
3428 out << ";";
3429 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003430}
3431
Ben Claytoncd52f382023-08-07 13:11:08 +00003432std::string ASTPrinter::builtin_to_attribute(core::BuiltinValue builtin) const {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003433 switch (builtin) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003434 case core::BuiltinValue::kPosition:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003435 return "SV_Position";
Ben Claytoncd52f382023-08-07 13:11:08 +00003436 case core::BuiltinValue::kVertexIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003437 return "SV_VertexID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003438 case core::BuiltinValue::kInstanceIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003439 return "SV_InstanceID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003440 case core::BuiltinValue::kFrontFacing:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003441 return "SV_IsFrontFace";
Ben Claytoncd52f382023-08-07 13:11:08 +00003442 case core::BuiltinValue::kFragDepth:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003443 return "SV_Depth";
Ben Claytoncd52f382023-08-07 13:11:08 +00003444 case core::BuiltinValue::kLocalInvocationId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003445 return "SV_GroupThreadID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003446 case core::BuiltinValue::kLocalInvocationIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003447 return "SV_GroupIndex";
Ben Claytoncd52f382023-08-07 13:11:08 +00003448 case core::BuiltinValue::kGlobalInvocationId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003449 return "SV_DispatchThreadID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003450 case core::BuiltinValue::kWorkgroupId:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003451 return "SV_GroupID";
Ben Claytoncd52f382023-08-07 13:11:08 +00003452 case core::BuiltinValue::kSampleIndex:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003453 return "SV_SampleIndex";
Ben Claytoncd52f382023-08-07 13:11:08 +00003454 case core::BuiltinValue::kSampleMask:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003455 return "SV_Coverage";
3456 default:
3457 break;
3458 }
3459 return "";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003460}
3461
Ben Claytoncd52f382023-08-07 13:11:08 +00003462std::string ASTPrinter::interpolation_to_modifiers(core::InterpolationType type,
3463 core::InterpolationSampling sampling) const {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003464 std::string modifiers;
3465 switch (type) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003466 case core::InterpolationType::kPerspective:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003467 modifiers += "linear ";
3468 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003469 case core::InterpolationType::kLinear:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003470 modifiers += "noperspective ";
3471 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003472 case core::InterpolationType::kFlat:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003473 modifiers += "nointerpolation ";
3474 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003475 case core::InterpolationType::kUndefined:
Ben Claytonf9ed9d32022-10-11 19:49:17 +00003476 break;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003477 }
3478 switch (sampling) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003479 case core::InterpolationSampling::kCentroid:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003480 modifiers += "centroid ";
3481 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003482 case core::InterpolationSampling::kSample:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003483 modifiers += "sample ";
3484 break;
Ben Claytoncd52f382023-08-07 13:11:08 +00003485 case core::InterpolationSampling::kCenter:
3486 case core::InterpolationSampling::kUndefined:
dan sinclair41e4d9a2022-05-01 14:40:55 +00003487 break;
3488 }
3489 return modifiers;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003490}
3491
dan sinclair0bfeaf92023-07-26 17:39:51 +00003492bool ASTPrinter::EmitEntryPointFunction(const ast::Function* func) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003493 auto* func_sem = builder_.Sem().Get(func);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003494
dan sinclair41e4d9a2022-05-01 14:40:55 +00003495 {
dan sinclair67a18932023-06-26 19:54:49 +00003496 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003497 if (func->PipelineStage() == ast::PipelineStage::kCompute) {
3498 // Emit the workgroup_size attribute.
3499 auto wgsize = func_sem->WorkgroupSize();
3500 out << "[numthreads(";
dan sinclair3a2a2792022-06-29 14:38:15 +00003501 for (size_t i = 0; i < 3; i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003502 if (i > 0) {
3503 out << ", ";
3504 }
Ben Clayton490d9882022-09-21 21:05:45 +00003505 if (!wgsize[i].has_value()) {
3506 diagnostics_.add_error(
3507 diag::System::Writer,
Ben Claytonf10a5792022-10-13 13:47:39 +00003508 "override-expressions should have been removed with the SubstituteOverride "
Ben Clayton490d9882022-09-21 21:05:45 +00003509 "transform");
3510 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003511 }
Ben Clayton490d9882022-09-21 21:05:45 +00003512 out << std::to_string(wgsize[i].value());
dan sinclair41e4d9a2022-05-01 14:40:55 +00003513 }
3514 out << ")]" << std::endl;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003515 }
3516
Ben Claytoncd52f382023-08-07 13:11:08 +00003517 if (!EmitTypeAndName(out, func_sem->ReturnType(), core::AddressSpace::kUndefined,
3518 core::Access::kUndefined, func->name->symbol.Name())) {
Ben Clayton19068572023-02-07 21:28:09 +00003519 return false;
3520 }
3521 out << "(";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003522
3523 bool first = true;
3524
3525 // Emit entry point parameters.
3526 for (auto* var : func->params) {
3527 auto* sem = builder_.Sem().Get(var);
3528 auto* type = sem->Type();
dan sinclaircedcdf32023-08-10 02:39:48 +00003529 if (TINT_UNLIKELY(!type->Is<core::type::Struct>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003530 // ICE likely indicates that the CanonicalizeEntryPointIO transform was
3531 // not run, or a builtin parameter was added after it was run.
Ben Claytonf848af22023-07-28 16:37:32 +00003532 TINT_ICE() << "Unsupported non-struct entry point parameter";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003533 }
3534
3535 if (!first) {
3536 out << ", ";
3537 }
3538 first = false;
3539
dan sinclairff7cf212022-10-03 14:05:23 +00003540 if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(),
dan sinclaird026e132023-04-18 19:38:25 +00003541 var->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003542 return false;
3543 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003544 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003545
3546 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003547 }
3548
dan sinclair41e4d9a2022-05-01 14:40:55 +00003549 {
3550 ScopedIndent si(this);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003551
dan sinclair41e4d9a2022-05-01 14:40:55 +00003552 if (!EmitStatements(func->body->statements)) {
3553 return false;
3554 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003555
dan sinclair41e4d9a2022-05-01 14:40:55 +00003556 if (!Is<ast::ReturnStatement>(func->body->Last())) {
dan sinclair637a2fe2023-07-24 21:11:41 +00003557 ast::ReturnStatement ret(GenerationID(), ast::NodeID{}, Source{});
dan sinclair41e4d9a2022-05-01 14:40:55 +00003558 if (!EmitStatement(&ret)) {
3559 return false;
3560 }
3561 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003562 }
3563
dan sinclair67a18932023-06-26 19:54:49 +00003564 Line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003565
dan sinclair41e4d9a2022-05-01 14:40:55 +00003566 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003567}
3568
dan sinclairbae54e72023-07-28 15:01:54 +00003569bool ASTPrinter::EmitConstant(StringStream& out,
dan sinclair464b3b82023-08-09 14:14:28 +00003570 const core::constant::Value* constant,
dan sinclair0bfeaf92023-07-26 17:39:51 +00003571 bool is_variable_initializer) {
Ben Clayton50414802022-06-24 08:06:19 +00003572 return Switch(
Ben Claytonaa037ac2022-06-29 19:07:30 +00003573 constant->Type(), //
dan sinclaircedcdf32023-08-10 02:39:48 +00003574 [&](const core::type::Bool*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003575 out << (constant->ValueAs<AInt>() ? "true" : "false");
Ben Claytone9f8b092022-06-01 13:14:39 +00003576 return true;
Ben Clayton50414802022-06-24 08:06:19 +00003577 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003578 [&](const core::type::F32*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003579 PrintF32(out, constant->ValueAs<f32>());
Ben Clayton50414802022-06-24 08:06:19 +00003580 return true;
3581 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003582 [&](const core::type::F16*) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003583 // emit a f16 scalar with explicit float16_t type declaration.
3584 out << "float16_t(";
dan sinclair5addefb2022-12-14 20:46:32 +00003585 PrintF16(out, constant->ValueAs<f16>());
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003586 out << ")";
Antonio Maiorano679cf4f2022-09-03 21:43:01 +00003587 return true;
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003588 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003589 [&](const core::type::I32*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003590 out << constant->ValueAs<AInt>();
Ben Clayton50414802022-06-24 08:06:19 +00003591 return true;
3592 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003593 [&](const core::type::U32*) {
dan sinclair5addefb2022-12-14 20:46:32 +00003594 out << constant->ValueAs<AInt>() << "u";
Ben Clayton50414802022-06-24 08:06:19 +00003595 return true;
3596 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003597 [&](const core::type::Vector* v) {
dan sinclair464b3b82023-08-09 14:14:28 +00003598 if (auto* splat = constant->As<core::constant::Splat>()) {
Ben Clayton50414802022-06-24 08:06:19 +00003599 {
dan sinclairb2ba57b2023-02-28 15:14:09 +00003600 ScopedParen sp(out);
James Price7bca4d72023-03-13 19:05:16 +00003601 if (!EmitConstant(out, splat->el, is_variable_initializer)) {
Ben Clayton50414802022-06-24 08:06:19 +00003602 return false;
3603 }
3604 }
3605 out << ".";
Ben Claytonaa037ac2022-06-29 19:07:30 +00003606 for (size_t i = 0; i < v->Width(); i++) {
Ben Clayton50414802022-06-24 08:06:19 +00003607 out << "x";
3608 }
3609 return true;
3610 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003611
Ben Claytoncd52f382023-08-07 13:11:08 +00003612 if (!EmitType(out, v, core::AddressSpace::kUndefined, core::Access::kUndefined, "")) {
Ben Clayton50414802022-06-24 08:06:19 +00003613 return false;
3614 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003615
dan sinclairb2ba57b2023-02-28 15:14:09 +00003616 ScopedParen sp(out);
Ben Claytone9f8b092022-06-01 13:14:39 +00003617
Ben Claytonaa037ac2022-06-29 19:07:30 +00003618 for (size_t i = 0; i < v->Width(); i++) {
3619 if (i > 0) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003620 out << ", ";
3621 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003622 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003623 return false;
3624 }
3625 }
3626 return true;
Ben Clayton50414802022-06-24 08:06:19 +00003627 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003628 [&](const core::type::Matrix* m) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003629 if (!EmitType(out, m, core::AddressSpace::kUndefined, core::Access::kUndefined, "")) {
Ben Claytone9f8b092022-06-01 13:14:39 +00003630 return false;
3631 }
Ben Clayton50414802022-06-24 08:06:19 +00003632
dan sinclairb2ba57b2023-02-28 15:14:09 +00003633 ScopedParen sp(out);
Ben Clayton50414802022-06-24 08:06:19 +00003634
Ben Claytonaa037ac2022-06-29 19:07:30 +00003635 for (size_t i = 0; i < m->columns(); i++) {
3636 if (i > 0) {
Ben Clayton50414802022-06-24 08:06:19 +00003637 out << ", ";
3638 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003639 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Clayton50414802022-06-24 08:06:19 +00003640 return false;
3641 }
3642 }
3643 return true;
3644 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003645 [&](const core::type::Array* a) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003646 if (constant->AllZero()) {
Ben Clayton19576e92022-06-28 12:44:16 +00003647 out << "(";
Ben Claytoncd52f382023-08-07 13:11:08 +00003648 if (!EmitType(out, a, core::AddressSpace::kUndefined, core::Access::kUndefined,
3649 "")) {
Ben Clayton19576e92022-06-28 12:44:16 +00003650 return false;
3651 }
3652 out << ")0";
3653 return true;
3654 }
3655
3656 out << "{";
3657 TINT_DEFER(out << "}");
3658
dan sinclair78f80672022-09-22 22:28:21 +00003659 auto count = a->ConstantCount();
3660 if (!count) {
dan sinclair946858a2022-12-08 22:21:24 +00003661 diagnostics_.add_error(diag::System::Writer,
dan sinclaircedcdf32023-08-10 02:39:48 +00003662 core::type::Array::kErrExpectedConstantCount);
dan sinclair78f80672022-09-22 22:28:21 +00003663 return false;
3664 }
3665
3666 for (size_t i = 0; i < count; i++) {
Ben Claytonaa037ac2022-06-29 19:07:30 +00003667 if (i > 0) {
Ben Clayton19576e92022-06-28 12:44:16 +00003668 out << ", ";
3669 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003670 if (!EmitConstant(out, constant->Index(i), is_variable_initializer)) {
Ben Clayton19576e92022-06-28 12:44:16 +00003671 return false;
3672 }
3673 }
3674
3675 return true;
3676 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003677 [&](const core::type::Struct* s) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003678 if (!EmitStructType(&helpers_, s)) {
3679 return false;
3680 }
3681
Ben Clayton6c098ba2022-07-14 20:46:39 +00003682 if (constant->AllZero()) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003683 out << "(" << StructName(s) << ")0";
Ben Clayton6c098ba2022-07-14 20:46:39 +00003684 return true;
3685 }
3686
dan sinclairbae54e72023-07-28 15:01:54 +00003687 auto emit_member_values = [&](StringStream& o) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003688 o << "{";
dan sinclairad9cd0a2022-12-06 20:01:54 +00003689 for (size_t i = 0; i < s->Members().Length(); i++) {
Ben Clayton329dfd72022-11-23 00:05:05 +00003690 if (i > 0) {
3691 o << ", ";
3692 }
3693 if (!EmitConstant(o, constant->Index(i), is_variable_initializer)) {
3694 return false;
3695 }
Ben Clayton6c098ba2022-07-14 20:46:39 +00003696 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003697 o << "}";
3698 return true;
3699 };
3700
3701 if (is_variable_initializer) {
3702 if (!emit_member_values(out)) {
Ben Clayton6c098ba2022-07-14 20:46:39 +00003703 return false;
3704 }
Ben Clayton329dfd72022-11-23 00:05:05 +00003705 } else {
3706 // HLSL requires structure initializers to be assigned directly to a variable.
3707 auto name = UniqueIdentifier("c");
3708 {
dan sinclair67a18932023-06-26 19:54:49 +00003709 auto decl = Line();
Ben Clayton329dfd72022-11-23 00:05:05 +00003710 decl << "const " << StructName(s) << " " << name << " = ";
3711 if (!emit_member_values(decl)) {
3712 return false;
3713 }
3714 decl << ";";
3715 }
3716 out << name;
Ben Clayton6c098ba2022-07-14 20:46:39 +00003717 }
3718
3719 return true;
3720 },
Ben Claytone9f8b092022-06-01 13:14:39 +00003721 [&](Default) {
Ben Clayton1545ca12023-05-03 16:26:40 +00003722 diagnostics_.add_error(diag::System::Writer,
3723 "unhandled constant type: " + constant->Type()->FriendlyName());
Ben Claytone9f8b092022-06-01 13:14:39 +00003724 return false;
3725 });
3726}
3727
dan sinclairbae54e72023-07-28 15:01:54 +00003728bool ASTPrinter::EmitLiteral(StringStream& out, const ast::LiteralExpression* lit) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003729 return Switch(
3730 lit,
3731 [&](const ast::BoolLiteralExpression* l) {
3732 out << (l->value ? "true" : "false");
3733 return true;
3734 },
Ben Clayton3ad927c2022-05-25 23:12:14 +00003735 [&](const ast::FloatLiteralExpression* l) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003736 if (l->suffix == ast::FloatLiteralExpression::Suffix::kH) {
3737 // Emit f16 literal with explicit float16_t type declaration.
3738 out << "float16_t(";
Antonio Maiorano679cf4f2022-09-03 21:43:01 +00003739 PrintF16(out, static_cast<float>(l->value));
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003740 out << ")";
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003741 }
Ben Claytone9f8b092022-06-01 13:14:39 +00003742 PrintF32(out, static_cast<float>(l->value));
dan sinclair41e4d9a2022-05-01 14:40:55 +00003743 return true;
3744 },
Ben Clayton8822e292022-05-04 22:18:49 +00003745 [&](const ast::IntLiteralExpression* i) {
3746 out << i->value;
3747 switch (i->suffix) {
3748 case ast::IntLiteralExpression::Suffix::kNone:
3749 case ast::IntLiteralExpression::Suffix::kI:
3750 return true;
3751 case ast::IntLiteralExpression::Suffix::kU:
3752 out << "u";
3753 return true;
3754 }
3755 diagnostics_.add_error(diag::System::Writer, "unknown integer literal suffix type");
3756 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003757 },
3758 [&](Default) {
3759 diagnostics_.add_error(diag::System::Writer, "unknown literal type");
3760 return false;
3761 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003762}
3763
dan sinclaircedcdf32023-08-10 02:39:48 +00003764bool ASTPrinter::EmitValue(StringStream& out, const core::type::Type* type, int value) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003765 return Switch(
3766 type,
dan sinclaircedcdf32023-08-10 02:39:48 +00003767 [&](const core::type::Bool*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003768 out << (value == 0 ? "false" : "true");
3769 return true;
3770 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003771 [&](const core::type::F32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003772 out << value << ".0f";
3773 return true;
3774 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003775 [&](const core::type::F16*) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00003776 out << "float16_t(" << value << ".0h)";
3777 return true;
3778 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003779 [&](const core::type::I32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003780 out << value;
3781 return true;
3782 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003783 [&](const core::type::U32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003784 out << value << "u";
3785 return true;
3786 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003787 [&](const core::type::Vector* vec) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003788 if (!EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kReadWrite,
Ben Clayton1b90f932023-02-18 12:37:34 +00003789 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003790 return false;
3791 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00003792 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003793 for (uint32_t i = 0; i < vec->Width(); i++) {
3794 if (i != 0) {
3795 out << ", ";
3796 }
3797 if (!EmitValue(out, vec->type(), value)) {
3798 return false;
3799 }
3800 }
3801 return true;
3802 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003803 [&](const core::type::Matrix* mat) {
Ben Claytoncd52f382023-08-07 13:11:08 +00003804 if (!EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kReadWrite,
Ben Clayton1b90f932023-02-18 12:37:34 +00003805 "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003806 return false;
3807 }
dan sinclairb2ba57b2023-02-28 15:14:09 +00003808 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00003809 for (uint32_t i = 0; i < (mat->rows() * mat->columns()); i++) {
3810 if (i != 0) {
3811 out << ", ";
3812 }
3813 if (!EmitValue(out, mat->type(), value)) {
3814 return false;
3815 }
3816 }
3817 return true;
3818 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003819 [&](const core::type::Struct*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003820 out << "(";
3821 TINT_DEFER(out << ")" << value);
Ben Claytoncd52f382023-08-07 13:11:08 +00003822 return EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kUndefined,
3823 "");
dan sinclair41e4d9a2022-05-01 14:40:55 +00003824 },
dan sinclaircedcdf32023-08-10 02:39:48 +00003825 [&](const core::type::Array*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003826 out << "(";
3827 TINT_DEFER(out << ")" << value);
Ben Claytoncd52f382023-08-07 13:11:08 +00003828 return EmitType(out, type, core::AddressSpace::kUndefined, core::Access::kUndefined,
3829 "");
dan sinclair41e4d9a2022-05-01 14:40:55 +00003830 },
3831 [&](Default) {
dan sinclaird026e132023-04-18 19:38:25 +00003832 diagnostics_.add_error(diag::System::Writer,
3833 "Invalid type for value emission: " + type->FriendlyName());
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003834 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003835 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003836}
3837
dan sinclaircedcdf32023-08-10 02:39:48 +00003838bool ASTPrinter::EmitZeroValue(StringStream& out, const core::type::Type* type) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003839 return EmitValue(out, type, 0);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003840}
3841
dan sinclair0bfeaf92023-07-26 17:39:51 +00003842bool ASTPrinter::EmitLoop(const ast::LoopStatement* stmt) {
Ben Claytona0aabaf2023-06-22 23:53:09 +00003843 auto emit_continuing = [this, stmt] {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003844 if (stmt->continuing && !stmt->continuing->Empty()) {
3845 if (!EmitBlock(stmt->continuing)) {
3846 return false;
3847 }
3848 }
3849 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003850 };
3851
3852 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
dan sinclair67a18932023-06-26 19:54:49 +00003853 Line() << "while (true) {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003854 {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003855 ScopedIndent si(this);
3856 if (!EmitStatements(stmt->body->statements)) {
3857 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003858 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00003859 if (!emit_continuing_()) {
3860 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003861 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003862 }
dan sinclair67a18932023-06-26 19:54:49 +00003863 Line() << "}";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003864
dan sinclair41e4d9a2022-05-01 14:40:55 +00003865 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003866}
3867
dan sinclair0bfeaf92023-07-26 17:39:51 +00003868bool ASTPrinter::EmitForLoop(const ast::ForLoopStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003869 // Nest a for loop with a new block. In HLSL the initializer scope is not
3870 // nested by the for-loop, so we may get variable redefinitions.
dan sinclair67a18932023-06-26 19:54:49 +00003871 Line() << "{";
3872 IncrementIndent();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003873 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00003874 DecrementIndent();
3875 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003876 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003877
dan sinclair41e4d9a2022-05-01 14:40:55 +00003878 TextBuffer init_buf;
3879 if (auto* init = stmt->initializer) {
3880 TINT_SCOPED_ASSIGNMENT(current_buffer_, &init_buf);
3881 if (!EmitStatement(init)) {
3882 return false;
3883 }
3884 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00003885
dan sinclair41e4d9a2022-05-01 14:40:55 +00003886 TextBuffer cond_pre;
dan sinclairbae54e72023-07-28 15:01:54 +00003887 StringStream cond_buf;
dan sinclair41e4d9a2022-05-01 14:40:55 +00003888 if (auto* cond = stmt->condition) {
3889 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
3890 if (!EmitExpression(cond_buf, cond)) {
3891 return false;
3892 }
3893 }
3894
3895 TextBuffer cont_buf;
3896 if (auto* cont = stmt->continuing) {
3897 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cont_buf);
3898 if (!EmitStatement(cont)) {
3899 return false;
3900 }
3901 }
3902
3903 // If the for-loop has a multi-statement conditional and / or continuing, then
3904 // we cannot emit this as a regular for-loop in HLSL. Instead we need to
3905 // generate a `while(true)` loop.
3906 bool emit_as_loop = cond_pre.lines.size() > 0 || cont_buf.lines.size() > 1;
3907
3908 // If the for-loop has multi-statement initializer, or is going to be emitted
3909 // as a `while(true)` loop, then declare the initializer statement(s) before
3910 // the loop.
3911 if (init_buf.lines.size() > 1 || (stmt->initializer && emit_as_loop)) {
3912 current_buffer_->Append(init_buf);
3913 init_buf.lines.clear(); // Don't emit the initializer again in the 'for'
3914 }
3915
3916 if (emit_as_loop) {
Ben Claytona0aabaf2023-06-22 23:53:09 +00003917 auto emit_continuing = [&] {
dan sinclair41e4d9a2022-05-01 14:40:55 +00003918 current_buffer_->Append(cont_buf);
3919 return true;
3920 };
3921
3922 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
dan sinclair67a18932023-06-26 19:54:49 +00003923 Line() << "while (true) {";
3924 IncrementIndent();
dan sinclair41e4d9a2022-05-01 14:40:55 +00003925 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00003926 DecrementIndent();
3927 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003928 });
3929
3930 if (stmt->condition) {
3931 current_buffer_->Append(cond_pre);
dan sinclair67a18932023-06-26 19:54:49 +00003932 Line() << "if (!(" << cond_buf.str() << ")) { break; }";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003933 }
3934
3935 if (!EmitStatements(stmt->body->statements)) {
3936 return false;
3937 }
3938
3939 if (!emit_continuing_()) {
3940 return false;
3941 }
3942 } else {
3943 // For-loop can be generated.
3944 {
dan sinclair67a18932023-06-26 19:54:49 +00003945 auto out = Line();
Antonio Maiorano06844a52022-09-29 16:53:58 +00003946 out << "for";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003947 {
3948 ScopedParen sp(out);
3949
3950 if (!init_buf.lines.empty()) {
3951 out << init_buf.lines[0].content << " ";
3952 } else {
3953 out << "; ";
3954 }
3955
3956 out << cond_buf.str() << "; ";
3957
3958 if (!cont_buf.lines.empty()) {
dan sinclairbae54e72023-07-28 15:01:54 +00003959 out << tint::TrimSuffix(cont_buf.lines[0].content, ";");
dan sinclair41e4d9a2022-05-01 14:40:55 +00003960 }
3961 }
3962 out << " {";
3963 }
3964 {
3965 auto emit_continuing = [] { return true; };
3966 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
3967 if (!EmitStatementsWithIndent(stmt->body->statements)) {
3968 return false;
3969 }
3970 }
dan sinclair67a18932023-06-26 19:54:49 +00003971 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00003972 }
3973
3974 return true;
3975}
3976
dan sinclair0bfeaf92023-07-26 17:39:51 +00003977bool ASTPrinter::EmitWhile(const ast::WhileStatement* stmt) {
dan sinclair49d1a2d2022-06-16 12:01:27 +00003978 TextBuffer cond_pre;
dan sinclairbae54e72023-07-28 15:01:54 +00003979 StringStream cond_buf;
dan sinclair49d1a2d2022-06-16 12:01:27 +00003980 {
3981 auto* cond = stmt->condition;
3982 TINT_SCOPED_ASSIGNMENT(current_buffer_, &cond_pre);
3983 if (!EmitExpression(cond_buf, cond)) {
3984 return false;
3985 }
3986 }
3987
Ben Claytona0aabaf2023-06-22 23:53:09 +00003988 auto emit_continuing = [&] { return true; };
dan sinclair4b88dbc2022-06-16 15:27:38 +00003989 TINT_SCOPED_ASSIGNMENT(emit_continuing_, emit_continuing);
3990
dan sinclair49d1a2d2022-06-16 12:01:27 +00003991 // If the while has a multi-statement conditional, then we cannot emit this
3992 // as a regular while in HLSL. Instead we need to generate a `while(true)` loop.
3993 bool emit_as_loop = cond_pre.lines.size() > 0;
3994 if (emit_as_loop) {
dan sinclair67a18932023-06-26 19:54:49 +00003995 Line() << "while (true) {";
3996 IncrementIndent();
dan sinclair49d1a2d2022-06-16 12:01:27 +00003997 TINT_DEFER({
dan sinclair67a18932023-06-26 19:54:49 +00003998 DecrementIndent();
3999 Line() << "}";
dan sinclair49d1a2d2022-06-16 12:01:27 +00004000 });
4001
4002 current_buffer_->Append(cond_pre);
dan sinclair67a18932023-06-26 19:54:49 +00004003 Line() << "if (!(" << cond_buf.str() << ")) { break; }";
dan sinclair49d1a2d2022-06-16 12:01:27 +00004004 if (!EmitStatements(stmt->body->statements)) {
4005 return false;
4006 }
4007 } else {
4008 // While can be generated.
4009 {
dan sinclair67a18932023-06-26 19:54:49 +00004010 auto out = Line();
Antonio Maiorano06844a52022-09-29 16:53:58 +00004011 out << "while";
dan sinclair49d1a2d2022-06-16 12:01:27 +00004012 {
4013 ScopedParen sp(out);
4014 out << cond_buf.str();
4015 }
4016 out << " {";
4017 }
4018 if (!EmitStatementsWithIndent(stmt->body->statements)) {
4019 return false;
4020 }
dan sinclair67a18932023-06-26 19:54:49 +00004021 Line() << "}";
dan sinclair49d1a2d2022-06-16 12:01:27 +00004022 }
4023
4024 return true;
4025}
4026
dan sinclairbae54e72023-07-28 15:01:54 +00004027bool ASTPrinter::EmitMemberAccessor(StringStream& out, const ast::MemberAccessorExpression* expr) {
Ben Claytonad315652023-02-05 12:36:50 +00004028 if (!EmitExpression(out, expr->object)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004029 return false;
4030 }
4031 out << ".";
4032
Ben Clayton2f9a9882022-12-17 02:20:04 +00004033 auto* sem = builder_.Sem().Get(expr)->UnwrapLoad();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004034
Ben Clayton10fae7a2022-11-14 15:29:29 +00004035 return Switch(
4036 sem,
4037 [&](const sem::Swizzle*) {
4038 // Swizzles output the name directly
dan sinclaird026e132023-04-18 19:38:25 +00004039 out << expr->member->symbol.Name();
Ben Clayton10fae7a2022-11-14 15:29:29 +00004040 return true;
4041 },
4042 [&](const sem::StructMemberAccess* member_access) {
dan sinclaird026e132023-04-18 19:38:25 +00004043 out << member_access->Member()->Name().Name();
Ben Clayton10fae7a2022-11-14 15:29:29 +00004044 return true;
4045 },
4046 [&](Default) {
Ben Claytonf848af22023-07-28 16:37:32 +00004047 TINT_ICE() << "unknown member access type: " << sem->TypeInfo().name;
Ben Clayton10fae7a2022-11-14 15:29:29 +00004048 return false;
4049 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004050}
4051
dan sinclair0bfeaf92023-07-26 17:39:51 +00004052bool ASTPrinter::EmitReturn(const ast::ReturnStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004053 if (stmt->value) {
dan sinclair67a18932023-06-26 19:54:49 +00004054 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004055 out << "return ";
4056 if (!EmitExpression(out, stmt->value)) {
4057 return false;
4058 }
4059 out << ";";
4060 } else {
dan sinclair67a18932023-06-26 19:54:49 +00004061 Line() << "return;";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004062 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004063 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004064}
4065
dan sinclair0bfeaf92023-07-26 17:39:51 +00004066bool ASTPrinter::EmitStatement(const ast::Statement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004067 return Switch(
4068 stmt,
4069 [&](const ast::AssignmentStatement* a) { //
4070 return EmitAssign(a);
4071 },
4072 [&](const ast::BlockStatement* b) { //
4073 return EmitBlock(b);
4074 },
4075 [&](const ast::BreakStatement* b) { //
4076 return EmitBreak(b);
4077 },
dan sinclairb8b0c212022-10-20 22:45:50 +00004078 [&](const ast::BreakIfStatement* b) { //
4079 return EmitBreakIf(b);
4080 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00004081 [&](const ast::CallStatement* c) { //
dan sinclair67a18932023-06-26 19:54:49 +00004082 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004083 if (!EmitCall(out, c->expr)) {
4084 return false;
4085 }
4086 out << ";";
4087 return true;
4088 },
4089 [&](const ast::ContinueStatement* c) { //
4090 return EmitContinue(c);
4091 },
4092 [&](const ast::DiscardStatement* d) { //
4093 return EmitDiscard(d);
4094 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00004095 [&](const ast::IfStatement* i) { //
4096 return EmitIf(i);
4097 },
4098 [&](const ast::LoopStatement* l) { //
4099 return EmitLoop(l);
4100 },
4101 [&](const ast::ForLoopStatement* l) { //
4102 return EmitForLoop(l);
4103 },
dan sinclair49d1a2d2022-06-16 12:01:27 +00004104 [&](const ast::WhileStatement* l) { //
4105 return EmitWhile(l);
4106 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00004107 [&](const ast::ReturnStatement* r) { //
4108 return EmitReturn(r);
4109 },
4110 [&](const ast::SwitchStatement* s) { //
4111 return EmitSwitch(s);
4112 },
4113 [&](const ast::VariableDeclStatement* v) { //
Ben Claytondcdf66e2022-06-17 12:48:51 +00004114 return Switch(
4115 v->variable, //
4116 [&](const ast::Var* var) { return EmitVar(var); },
4117 [&](const ast::Let* let) { return EmitLet(let); },
Ben Clayton19576e92022-06-28 12:44:16 +00004118 [&](const ast::Const*) {
4119 return true; // Constants are embedded at their use
4120 },
Ben Claytondcdf66e2022-06-17 12:48:51 +00004121 [&](Default) { //
Ben Claytonf848af22023-07-28 16:37:32 +00004122 TINT_ICE() << "unknown variable type: " << v->variable->TypeInfo().name;
Ben Claytondcdf66e2022-06-17 12:48:51 +00004123 return false;
4124 });
dan sinclair41e4d9a2022-05-01 14:40:55 +00004125 },
Ben Claytonc98d57d2023-01-24 14:59:43 +00004126 [&](const ast::ConstAssert*) {
Ben Claytonb4744ac2022-08-03 07:01:08 +00004127 return true; // Not emitted
4128 },
dan sinclair41e4d9a2022-05-01 14:40:55 +00004129 [&](Default) { //
4130 diagnostics_.add_error(diag::System::Writer,
4131 "unknown statement type: " + std::string(stmt->TypeInfo().name));
4132 return false;
4133 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004134}
4135
dan sinclair0bfeaf92023-07-26 17:39:51 +00004136bool ASTPrinter::EmitDefaultOnlySwitch(const ast::SwitchStatement* stmt) {
Ben Claytonf848af22023-07-28 16:37:32 +00004137 TINT_ASSERT(stmt->body.Length() == 1 && stmt->body[0]->ContainsDefault());
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004138
dan sinclair41e4d9a2022-05-01 14:40:55 +00004139 // FXC fails to compile a switch with just a default case, ignoring the
4140 // default case body. We work around this here by emitting the default case
4141 // without the switch.
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004142
Antonio Maioranoeab1f622023-02-01 15:46:34 +00004143 // Emit the switch condition as-is if it has side-effects (e.g.
4144 // function call). Note that we can ignore the result of the expression (if any).
Ben Clayton0b4a2f12023-02-05 22:59:40 +00004145 if (auto* sem_cond = builder_.Sem().GetVal(stmt->condition); sem_cond->HasSideEffects()) {
dan sinclair67a18932023-06-26 19:54:49 +00004146 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004147 if (!EmitExpression(out, stmt->condition)) {
4148 return false;
4149 }
4150 out << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004151 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004152
dan sinclair41e4d9a2022-05-01 14:40:55 +00004153 // Emit "do { <default case body> } while(false);". We use a 'do' loop so
4154 // that break statements work as expected, and make it 'while (false)' in
4155 // case there isn't a break statement.
dan sinclair67a18932023-06-26 19:54:49 +00004156 Line() << "do {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004157 {
4158 ScopedIndent si(this);
4159 if (!EmitStatements(stmt->body[0]->body->statements)) {
4160 return false;
4161 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004162 }
dan sinclair67a18932023-06-26 19:54:49 +00004163 Line() << "} while (false);";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004164 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004165}
4166
dan sinclair0bfeaf92023-07-26 17:39:51 +00004167bool ASTPrinter::EmitSwitch(const ast::SwitchStatement* stmt) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004168 // BUG(crbug.com/tint/1188): work around default-only switches
dan sinclairf148f082022-10-19 15:55:02 +00004169 if (stmt->body.Length() == 1 && stmt->body[0]->selectors.Length() == 1 &&
4170 stmt->body[0]->ContainsDefault()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004171 return EmitDefaultOnlySwitch(stmt);
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004172 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004173
dan sinclair41e4d9a2022-05-01 14:40:55 +00004174 { // switch(expr) {
dan sinclair67a18932023-06-26 19:54:49 +00004175 auto out = Line();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004176 out << "switch(";
4177 if (!EmitExpression(out, stmt->condition)) {
4178 return false;
4179 }
4180 out << ") {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004181 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004182
dan sinclair41e4d9a2022-05-01 14:40:55 +00004183 {
4184 ScopedIndent si(this);
Ben Clayton783b1692022-08-02 17:03:35 +00004185 for (size_t i = 0; i < stmt->body.Length(); i++) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004186 if (!EmitCase(stmt, i)) {
4187 return false;
4188 }
4189 }
4190 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004191
dan sinclair67a18932023-06-26 19:54:49 +00004192 Line() << "}";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004193
4194 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004195}
4196
dan sinclairbae54e72023-07-28 15:01:54 +00004197bool ASTPrinter::EmitType(StringStream& out,
dan sinclaircedcdf32023-08-10 02:39:48 +00004198 const core::type::Type* type,
Ben Claytoncd52f382023-08-07 13:11:08 +00004199 core::AddressSpace address_space,
4200 core::Access access,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004201 const std::string& name,
4202 bool* name_printed /* = nullptr */) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004203 if (name_printed) {
4204 *name_printed = false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004205 }
dan sinclairff7cf212022-10-03 14:05:23 +00004206 switch (address_space) {
Ben Claytoncd52f382023-08-07 13:11:08 +00004207 case core::AddressSpace::kStorage:
4208 if (access != core::Access::kRead) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004209 out << "RW";
4210 }
4211 out << "ByteAddressBuffer";
4212 return true;
Ben Claytoncd52f382023-08-07 13:11:08 +00004213 case core::AddressSpace::kUniform: {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004214 auto array_length = (type->Size() + 15) / 16;
4215 out << "uint4 " << name << "[" << array_length << "]";
4216 if (name_printed) {
4217 *name_printed = true;
4218 }
4219 return true;
4220 }
4221 default:
4222 break;
4223 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004224
dan sinclair41e4d9a2022-05-01 14:40:55 +00004225 return Switch(
4226 type,
dan sinclaircedcdf32023-08-10 02:39:48 +00004227 [&](const core::type::Array* ary) {
4228 const core::type::Type* base_type = ary;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004229 std::vector<uint32_t> sizes;
dan sinclaircedcdf32023-08-10 02:39:48 +00004230 while (auto* arr = base_type->As<core::type::Array>()) {
4231 if (TINT_UNLIKELY(arr->Count()->Is<core::type::RuntimeArrayCount>())) {
Ben Claytonf848af22023-07-28 16:37:32 +00004232 TINT_ICE()
dan sinclair78f80672022-09-22 22:28:21 +00004233 << "runtime arrays may only exist in storage buffers, which should have "
Ben Clayton3a68ab42022-06-24 08:30:28 +00004234 "been transformed into a ByteAddressBuffer";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004235 return false;
4236 }
dan sinclair78f80672022-09-22 22:28:21 +00004237 const auto count = arr->ConstantCount();
4238 if (!count) {
4239 diagnostics_.add_error(diag::System::Writer,
dan sinclaircedcdf32023-08-10 02:39:48 +00004240 core::type::Array::kErrExpectedConstantCount);
dan sinclair78f80672022-09-22 22:28:21 +00004241 return false;
4242 }
4243
4244 sizes.push_back(count.value());
dan sinclair41e4d9a2022-05-01 14:40:55 +00004245 base_type = arr->ElemType();
4246 }
dan sinclairff7cf212022-10-03 14:05:23 +00004247 if (!EmitType(out, base_type, address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004248 return false;
4249 }
4250 if (!name.empty()) {
4251 out << " " << name;
4252 if (name_printed) {
4253 *name_printed = true;
4254 }
4255 }
4256 for (uint32_t size : sizes) {
4257 out << "[" << size << "]";
4258 }
4259 return true;
4260 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004261 [&](const core::type::Bool*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004262 out << "bool";
4263 return true;
4264 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004265 [&](const core::type::F32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004266 out << "float";
4267 return true;
4268 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004269 [&](const core::type::F16*) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004270 out << "float16_t";
4271 return true;
Zhaoming Jiang62bfd312022-05-13 12:01:11 +00004272 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004273 [&](const core::type::I32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004274 out << "int";
4275 return true;
4276 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004277 [&](const core::type::Matrix* mat) {
4278 if (mat->type()->Is<core::type::F16>()) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004279 // Use matrix<type, N, M> for f16 matrix
4280 out << "matrix<";
dan sinclairff7cf212022-10-03 14:05:23 +00004281 if (!EmitType(out, mat->type(), address_space, access, "")) {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004282 return false;
4283 }
4284 out << ", " << mat->columns() << ", " << mat->rows() << ">";
4285 return true;
4286 }
dan sinclairff7cf212022-10-03 14:05:23 +00004287 if (!EmitType(out, mat->type(), address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004288 return false;
4289 }
4290 // Note: HLSL's matrices are declared as <type>NxM, where N is the
4291 // number of rows and M is the number of columns. Despite HLSL's
4292 // matrices being column-major by default, the index operator and
dan sinclair6e77b472022-10-20 13:38:28 +00004293 // initializers actually operate on row-vectors, where as WGSL operates
dan sinclair41e4d9a2022-05-01 14:40:55 +00004294 // on column vectors. To simplify everything we use the transpose of the
4295 // matrices. See:
4296 // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-per-component-math#matrix-ordering
4297 out << mat->columns() << "x" << mat->rows();
4298 return true;
4299 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004300 [&](const core::type::Pointer*) {
Ben Claytonf848af22023-07-28 16:37:32 +00004301 TINT_ICE() << "Attempting to emit pointer type. These should have "
4302 "been removed with the SimplifyPointers transform";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004303 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004304 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004305 [&](const core::type::Sampler* sampler) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004306 out << "Sampler";
4307 if (sampler->IsComparison()) {
4308 out << "Comparison";
4309 }
4310 out << "State";
4311 return true;
4312 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004313 [&](const core::type::Struct* str) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004314 out << StructName(str);
4315 return true;
4316 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004317 [&](const core::type::Texture* tex) {
4318 if (TINT_UNLIKELY(tex->Is<core::type::ExternalTexture>())) {
Ben Claytonf848af22023-07-28 16:37:32 +00004319 TINT_ICE() << "Multiplanar external texture transform was not run.";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004320 return false;
4321 }
Brandon Jones6661b282022-02-25 20:14:52 +00004322
dan sinclaircedcdf32023-08-10 02:39:48 +00004323 auto* storage = tex->As<core::type::StorageTexture>();
4324 auto* ms = tex->As<core::type::MultisampledTexture>();
4325 auto* depth_ms = tex->As<core::type::DepthMultisampledTexture>();
4326 auto* sampled = tex->As<core::type::SampledTexture>();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004327
Ben Claytoncd52f382023-08-07 13:11:08 +00004328 if (storage && storage->access() != core::Access::kRead) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004329 out << "RW";
4330 }
4331 out << "Texture";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004332
dan sinclair41e4d9a2022-05-01 14:40:55 +00004333 switch (tex->dim()) {
dan sinclaircedcdf32023-08-10 02:39:48 +00004334 case core::type::TextureDimension::k1d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004335 out << "1D";
4336 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004337 case core::type::TextureDimension::k2d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004338 out << ((ms || depth_ms) ? "2DMS" : "2D");
4339 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004340 case core::type::TextureDimension::k2dArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004341 out << ((ms || depth_ms) ? "2DMSArray" : "2DArray");
4342 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004343 case core::type::TextureDimension::k3d:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004344 out << "3D";
4345 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004346 case core::type::TextureDimension::kCube:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004347 out << "Cube";
4348 break;
dan sinclaircedcdf32023-08-10 02:39:48 +00004349 case core::type::TextureDimension::kCubeArray:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004350 out << "CubeArray";
4351 break;
4352 default:
Ben Claytonf848af22023-07-28 16:37:32 +00004353 TINT_UNREACHABLE() << "unexpected TextureDimension " << tex->dim();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004354 return false;
4355 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004356
dan sinclair41e4d9a2022-05-01 14:40:55 +00004357 if (storage) {
4358 auto* component = image_format_to_rwtexture_type(storage->texel_format());
Ben Clayton884f9522023-01-12 22:52:57 +00004359 if (TINT_UNLIKELY(!component)) {
Ben Claytonf848af22023-07-28 16:37:32 +00004360 TINT_ICE() << "Unsupported StorageTexture TexelFormat: "
4361 << static_cast<int>(storage->texel_format());
dan sinclair41e4d9a2022-05-01 14:40:55 +00004362 return false;
4363 }
4364 out << "<" << component << ">";
4365 } else if (depth_ms) {
4366 out << "<float4>";
4367 } else if (sampled || ms) {
4368 auto* subtype = sampled ? sampled->type() : ms->type();
4369 out << "<";
dan sinclaircedcdf32023-08-10 02:39:48 +00004370 if (subtype->Is<core::type::F32>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004371 out << "float4";
dan sinclaircedcdf32023-08-10 02:39:48 +00004372 } else if (subtype->Is<core::type::I32>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004373 out << "int4";
dan sinclaircedcdf32023-08-10 02:39:48 +00004374 } else if (TINT_LIKELY(subtype->Is<core::type::U32>())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004375 out << "uint4";
4376 } else {
Ben Claytonf848af22023-07-28 16:37:32 +00004377 TINT_ICE() << "Unsupported multisampled texture type";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004378 return false;
4379 }
4380 out << ">";
4381 }
4382 return true;
4383 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004384 [&](const core::type::U32*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004385 out << "uint";
4386 return true;
4387 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004388 [&](const core::type::Vector* vec) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004389 auto width = vec->Width();
dan sinclaircedcdf32023-08-10 02:39:48 +00004390 if (vec->type()->Is<core::type::F32>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004391 out << "float" << width;
dan sinclaircedcdf32023-08-10 02:39:48 +00004392 } else if (vec->type()->Is<core::type::I32>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004393 out << "int" << width;
dan sinclaircedcdf32023-08-10 02:39:48 +00004394 } else if (vec->type()->Is<core::type::U32>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004395 out << "uint" << width;
dan sinclaircedcdf32023-08-10 02:39:48 +00004396 } else if (vec->type()->Is<core::type::Bool>() && width >= 1 && width <= 4) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004397 out << "bool" << width;
4398 } else {
Zhaoming Jianga5988a32022-07-11 15:43:38 +00004399 // For example, use "vector<float16_t, N>" for f16 vector.
dan sinclair41e4d9a2022-05-01 14:40:55 +00004400 out << "vector<";
dan sinclairff7cf212022-10-03 14:05:23 +00004401 if (!EmitType(out, vec->type(), address_space, access, "")) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004402 return false;
4403 }
4404 out << ", " << width << ">";
4405 }
4406 return true;
4407 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004408 [&](const core::type::Atomic* atomic) {
dan sinclairff7cf212022-10-03 14:05:23 +00004409 return EmitType(out, atomic->Type(), address_space, access, name);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004410 },
dan sinclaircedcdf32023-08-10 02:39:48 +00004411 [&](const core::type::Void*) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004412 out << "void";
4413 return true;
4414 },
4415 [&](Default) {
4416 diagnostics_.add_error(diag::System::Writer, "unknown type in EmitType");
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004417 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004418 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004419}
4420
dan sinclairbae54e72023-07-28 15:01:54 +00004421bool ASTPrinter::EmitTypeAndName(StringStream& out,
dan sinclaircedcdf32023-08-10 02:39:48 +00004422 const core::type::Type* type,
Ben Claytoncd52f382023-08-07 13:11:08 +00004423 core::AddressSpace address_space,
4424 core::Access access,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004425 const std::string& name) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004426 bool name_printed = false;
dan sinclairff7cf212022-10-03 14:05:23 +00004427 if (!EmitType(out, type, address_space, access, name, &name_printed)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004428 return false;
4429 }
4430 if (!name.empty() && !name_printed) {
4431 out << " " << name;
4432 }
4433 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004434}
4435
dan sinclaircedcdf32023-08-10 02:39:48 +00004436bool ASTPrinter::EmitStructType(TextBuffer* b, const core::type::Struct* str) {
Ben Clayton329dfd72022-11-23 00:05:05 +00004437 auto it = emitted_structs_.emplace(str);
4438 if (!it.second) {
4439 return true;
4440 }
4441
dan sinclair67a18932023-06-26 19:54:49 +00004442 Line(b) << "struct " << StructName(str) << " {";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004443 {
4444 ScopedIndent si(b);
4445 for (auto* mem : str->Members()) {
dan sinclaird026e132023-04-18 19:38:25 +00004446 auto mem_name = mem->Name().Name();
dan sinclair41e4d9a2022-05-01 14:40:55 +00004447 auto* ty = mem->Type();
dan sinclair67a18932023-06-26 19:54:49 +00004448 auto out = Line(b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004449 std::string pre, post;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004450
Ben Clayton576ba1c2023-04-27 17:58:25 +00004451 auto& attributes = mem->Attributes();
Ben Claytonf0b4dbb2023-02-21 21:05:28 +00004452
Ben Clayton576ba1c2023-04-27 17:58:25 +00004453 if (auto location = attributes.location) {
4454 auto& pipeline_stage_uses = str->PipelineStageUses();
4455 if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) {
Ben Claytonf848af22023-07-28 16:37:32 +00004456 TINT_ICE() << "invalid entry point IO struct uses";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004457 }
dan sinclaircedcdf32023-08-10 02:39:48 +00004458 if (pipeline_stage_uses.count(core::type::PipelineStageUsage::kVertexInput)) {
Ben Clayton576ba1c2023-04-27 17:58:25 +00004459 post += " : TEXCOORD" + std::to_string(location.value());
dan sinclaircedcdf32023-08-10 02:39:48 +00004460 } else if (pipeline_stage_uses.count(
4461 core::type::PipelineStageUsage::kVertexOutput)) {
Ben Clayton576ba1c2023-04-27 17:58:25 +00004462 post += " : TEXCOORD" + std::to_string(location.value());
dan sinclaircedcdf32023-08-10 02:39:48 +00004463 } else if (pipeline_stage_uses.count(
4464 core::type::PipelineStageUsage::kFragmentInput)) {
Ben Clayton576ba1c2023-04-27 17:58:25 +00004465 post += " : TEXCOORD" + std::to_string(location.value());
4466 } else if (TINT_LIKELY(pipeline_stage_uses.count(
dan sinclaircedcdf32023-08-10 02:39:48 +00004467 core::type::PipelineStageUsage::kFragmentOutput))) {
Brandon Jones07500d32023-07-21 22:07:03 +00004468 if (auto index = attributes.index) {
4469 post += " : SV_Target" + std::to_string(location.value() + index.value());
4470 } else {
4471 post += " : SV_Target" + std::to_string(location.value());
4472 }
4473
Ben Clayton576ba1c2023-04-27 17:58:25 +00004474 } else {
Ben Claytonf848af22023-07-28 16:37:32 +00004475 TINT_ICE() << "invalid use of location attribute";
Ben Clayton576ba1c2023-04-27 17:58:25 +00004476 }
4477 }
4478 if (auto builtin = attributes.builtin) {
4479 auto name = builtin_to_attribute(builtin.value());
4480 if (name.empty()) {
4481 diagnostics_.add_error(diag::System::Writer, "unsupported builtin");
4482 return false;
4483 }
4484 post += " : " + name;
4485 }
4486 if (auto interpolation = attributes.interpolation) {
4487 auto mod = interpolation_to_modifiers(interpolation->type, interpolation->sampling);
4488 if (mod.empty()) {
4489 diagnostics_.add_error(diag::System::Writer, "unsupported interpolation");
4490 return false;
4491 }
4492 pre += mod;
4493 }
4494 if (attributes.invariant) {
4495 // Note: `precise` is not exactly the same as `invariant`, but is
4496 // stricter and therefore provides the necessary guarantees.
4497 // See discussion here: https://github.com/gpuweb/gpuweb/issues/893
4498 pre += "precise ";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004499 }
4500
dan sinclair41e4d9a2022-05-01 14:40:55 +00004501 out << pre;
Ben Claytoncd52f382023-08-07 13:11:08 +00004502 if (!EmitTypeAndName(out, ty, core::AddressSpace::kUndefined, core::Access::kReadWrite,
4503 mem_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004504 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004505 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004506 out << post << ";";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004507 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004508 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004509
dan sinclair67a18932023-06-26 19:54:49 +00004510 Line(b) << "};";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004511 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004512}
4513
dan sinclairbae54e72023-07-28 15:01:54 +00004514bool ASTPrinter::EmitUnaryOp(StringStream& out, const ast::UnaryOpExpression* expr) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004515 switch (expr->op) {
Ben Clayton67b461b2023-08-08 07:58:19 +00004516 case core::UnaryOp::kIndirection:
4517 case core::UnaryOp::kAddressOf:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004518 return EmitExpression(out, expr->expr);
Ben Clayton67b461b2023-08-08 07:58:19 +00004519 case core::UnaryOp::kComplement:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004520 out << "~";
4521 break;
Ben Clayton67b461b2023-08-08 07:58:19 +00004522 case core::UnaryOp::kNot:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004523 out << "!";
4524 break;
Ben Clayton67b461b2023-08-08 07:58:19 +00004525 case core::UnaryOp::kNegation:
dan sinclair41e4d9a2022-05-01 14:40:55 +00004526 out << "-";
4527 break;
4528 }
4529 out << "(";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004530
dan sinclair41e4d9a2022-05-01 14:40:55 +00004531 if (!EmitExpression(out, expr->expr)) {
4532 return false;
4533 }
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004534
dan sinclair41e4d9a2022-05-01 14:40:55 +00004535 out << ")";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004536
dan sinclair41e4d9a2022-05-01 14:40:55 +00004537 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004538}
4539
dan sinclair0bfeaf92023-07-26 17:39:51 +00004540bool ASTPrinter::EmitVar(const ast::Var* var) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004541 auto* sem = builder_.Sem().Get(var);
4542 auto* type = sem->Type()->UnwrapRef();
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004543
dan sinclair67a18932023-06-26 19:54:49 +00004544 auto out = Line();
dan sinclaird026e132023-04-18 19:38:25 +00004545 if (!EmitTypeAndName(out, type, sem->AddressSpace(), sem->Access(), var->name->symbol.Name())) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004546 return false;
4547 }
4548
4549 out << " = ";
4550
dan sinclair6e77b472022-10-20 13:38:28 +00004551 if (var->initializer) {
4552 if (!EmitExpression(out, var->initializer)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004553 return false;
4554 }
4555 } else {
4556 if (!EmitZeroValue(out, type)) {
4557 return false;
4558 }
4559 }
4560 out << ";";
4561
4562 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004563}
4564
dan sinclair0bfeaf92023-07-26 17:39:51 +00004565bool ASTPrinter::EmitLet(const ast::Let* let) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00004566 auto* sem = builder_.Sem().Get(let);
4567 auto* type = sem->Type()->UnwrapRef();
4568
dan sinclair67a18932023-06-26 19:54:49 +00004569 auto out = Line();
Ben Claytondcdf66e2022-06-17 12:48:51 +00004570 out << "const ";
Ben Claytoncd52f382023-08-07 13:11:08 +00004571 if (!EmitTypeAndName(out, type, core::AddressSpace::kUndefined, core::Access::kUndefined,
dan sinclaird026e132023-04-18 19:38:25 +00004572 let->name->symbol.Name())) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004573 return false;
dan sinclair41e4d9a2022-05-01 14:40:55 +00004574 }
Ben Claytondcdf66e2022-06-17 12:48:51 +00004575 out << " = ";
dan sinclair6e77b472022-10-20 13:38:28 +00004576 if (!EmitExpression(out, let->initializer)) {
Ben Claytondcdf66e2022-06-17 12:48:51 +00004577 return false;
4578 }
4579 out << ";";
dan sinclair41e4d9a2022-05-01 14:40:55 +00004580
Ben Claytondcdf66e2022-06-17 12:48:51 +00004581 return true;
4582}
4583
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004584template <typename F>
dan sinclairbae54e72023-07-28 15:01:54 +00004585bool ASTPrinter::CallBuiltinHelper(StringStream& out,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004586 const ast::CallExpression* call,
Ben Claytond9766dc2023-09-21 12:41:20 +00004587 const sem::BuiltinFn* builtin,
dan sinclair0bfeaf92023-07-26 17:39:51 +00004588 F&& build) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004589 // Generate the helper function if it hasn't been created already
dan sinclairbae54e72023-07-28 15:01:54 +00004590 auto fn = tint::GetOrCreate(builtins_, builtin, [&]() -> std::string {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004591 TextBuffer b;
4592 TINT_DEFER(helpers_.Append(b));
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004593
Ben Claytondfc815c2023-09-25 15:38:43 +00004594 auto fn_name = UniqueIdentifier(std::string("tint_") + wgsl::str(builtin->Fn()));
dan sinclair41e4d9a2022-05-01 14:40:55 +00004595 std::vector<std::string> parameter_names;
4596 {
dan sinclair67a18932023-06-26 19:54:49 +00004597 auto decl = Line(&b);
Ben Claytoncd52f382023-08-07 13:11:08 +00004598 if (!EmitTypeAndName(decl, builtin->ReturnType(), core::AddressSpace::kUndefined,
4599 core::Access::kUndefined, fn_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004600 return "";
4601 }
4602 {
4603 ScopedParen sp(decl);
4604 for (auto* param : builtin->Parameters()) {
4605 if (!parameter_names.empty()) {
4606 decl << ", ";
4607 }
4608 auto param_name = "param_" + std::to_string(parameter_names.size());
4609 const auto* ty = param->Type();
dan sinclaircedcdf32023-08-10 02:39:48 +00004610 if (auto* ptr = ty->As<core::type::Pointer>()) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004611 decl << "inout ";
4612 ty = ptr->StoreType();
4613 }
Ben Claytoncd52f382023-08-07 13:11:08 +00004614 if (!EmitTypeAndName(decl, ty, core::AddressSpace::kUndefined,
4615 core::Access::kUndefined, param_name)) {
dan sinclair41e4d9a2022-05-01 14:40:55 +00004616 return "";
4617 }
4618 parameter_names.emplace_back(std::move(param_name));
4619 }
4620 }
4621 decl << " {";
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004622 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004623 {
4624 ScopedIndent si(&b);
4625 if (!build(&b, parameter_names)) {
4626 return "";
4627 }
4628 }
dan sinclair67a18932023-06-26 19:54:49 +00004629 Line(&b) << "}";
4630 Line(&b);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004631 return fn_name;
4632 });
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004633
dan sinclair41e4d9a2022-05-01 14:40:55 +00004634 if (fn.empty()) {
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004635 return false;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004636 }
dan sinclair41e4d9a2022-05-01 14:40:55 +00004637
4638 // Call the helper
4639 out << fn;
4640 {
dan sinclairb2ba57b2023-02-28 15:14:09 +00004641 ScopedParen sp(out);
dan sinclair41e4d9a2022-05-01 14:40:55 +00004642 bool first = true;
4643 for (auto* arg : call->args) {
4644 if (!first) {
4645 out << ", ";
4646 }
4647 first = false;
4648 if (!EmitExpression(out, arg)) {
4649 return false;
4650 }
4651 }
4652 }
4653 return true;
Ryan Harrisondbc13af2022-02-21 15:19:07 +00004654}
4655
dan sinclaircedcdf32023-08-10 02:39:48 +00004656std::string ASTPrinter::StructName(const core::type::Struct* s) {
Ben Clayton34c8e572023-08-01 00:37:35 +00004657 auto name = s->Name().Name();
4658 if (HasPrefix(name, "__")) {
4659 name = tint::GetOrCreate(builtin_struct_names_, s,
4660 [&] { return UniqueIdentifier(name.substr(2)); });
4661 }
4662 return name;
4663}
4664
dan sinclair0bfeaf92023-07-26 17:39:51 +00004665std::string ASTPrinter::UniqueIdentifier(const std::string& prefix /* = "" */) {
Ben Clayton5ed099a2023-07-25 18:52:03 +00004666 return builder_.Symbols().New(prefix).Name();
4667}
4668
dan sinclair0bfeaf92023-07-26 17:39:51 +00004669} // namespace tint::hlsl::writer