blob: 0b4aa8cedf09738ab8c9d417e28a205940ee7c69 [file] [log] [blame]
Austin Engcc2516a2023-10-17 20:57:54 +00001// Copyright 2023 The Dawn & Tint Authors
dan sinclaircadd0c02023-06-26 13:50:42 +00002//
Austin Engcc2516a2023-10-17 20:57:54 +00003// Redistribution and use in source and binary forms, with or without
4// modification, are permitted provided that the following conditions are met:
dan sinclaircadd0c02023-06-26 13:50:42 +00005//
Austin Engcc2516a2023-10-17 20:57:54 +00006// 1. Redistributions of source code must retain the above copyright notice, this
7// list of conditions and the following disclaimer.
dan sinclaircadd0c02023-06-26 13:50:42 +00008//
Austin Engcc2516a2023-10-17 20:57:54 +00009// 2. Redistributions in binary form must reproduce the above copyright notice,
10// this list of conditions and the following disclaimer in the documentation
11// and/or other materials provided with the distribution.
12//
13// 3. Neither the name of the copyright holder nor the names of its
14// contributors may be used to endorse or promote products derived from
15// this software without specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
21// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
23// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
24// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
25// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
dan sinclaircadd0c02023-06-26 13:50:42 +000027
dan sinclaire7e4afc2023-07-26 02:28:30 +000028#include "src/tint/lang/msl/writer/printer/printer.h"
dan sinclaircadd0c02023-06-26 13:50:42 +000029
Ben Claytond8054e22023-10-26 14:24:30 +000030#include <unordered_map>
31#include <unordered_set>
dan sinclair155a42a2023-08-02 20:23:58 +000032#include <utility>
33
dan sinclair352f8c82023-07-21 00:40:07 +000034#include "src/tint/lang/core/constant/composite.h"
35#include "src/tint/lang/core/constant/splat.h"
dan sinclairce6dffe2023-08-14 21:01:40 +000036#include "src/tint/lang/core/fluent_types.h"
dan sinclair48318cd2023-11-23 11:27:32 +000037#include "src/tint/lang/core/ir/access.h"
dan sinclair355ee9a2023-08-03 22:50:42 +000038#include "src/tint/lang/core/ir/binary.h"
dan sinclaircea718c2023-11-22 16:08:34 +000039#include "src/tint/lang/core/ir/bitcast.h"
dan sinclair97c37272023-07-24 17:11:53 +000040#include "src/tint/lang/core/ir/constant.h"
dan sinclair7bbe4c12023-11-21 12:11:32 +000041#include "src/tint/lang/core/ir/construct.h"
dan sinclaireee3ad02023-11-20 13:22:41 +000042#include "src/tint/lang/core/ir/discard.h"
dan sinclair67d7e042023-08-02 13:45:25 +000043#include "src/tint/lang/core/ir/exit_if.h"
dan sinclair019146f2023-11-20 23:47:42 +000044#include "src/tint/lang/core/ir/ice.h"
dan sinclair67d7e042023-08-02 13:45:25 +000045#include "src/tint/lang/core/ir/if.h"
dan sinclair34e03422023-08-02 20:42:41 +000046#include "src/tint/lang/core/ir/let.h"
dan sinclairbe3b4852023-08-03 19:58:33 +000047#include "src/tint/lang/core/ir/load.h"
Ben Claytond8054e22023-10-26 14:24:30 +000048#include "src/tint/lang/core/ir/module.h"
dan sinclair3d0d6d52023-08-02 13:43:25 +000049#include "src/tint/lang/core/ir/multi_in_block.h"
50#include "src/tint/lang/core/ir/return.h"
dan sinclair67d7e042023-08-02 13:45:25 +000051#include "src/tint/lang/core/ir/unreachable.h"
dan sinclair97c37272023-07-24 17:11:53 +000052#include "src/tint/lang/core/ir/validator.h"
dan sinclairbe3b4852023-08-03 19:58:33 +000053#include "src/tint/lang/core/ir/var.h"
dan sinclair352f8c82023-07-21 00:40:07 +000054#include "src/tint/lang/core/type/array.h"
55#include "src/tint/lang/core/type/atomic.h"
56#include "src/tint/lang/core/type/bool.h"
57#include "src/tint/lang/core/type/depth_multisampled_texture.h"
58#include "src/tint/lang/core/type/depth_texture.h"
59#include "src/tint/lang/core/type/external_texture.h"
60#include "src/tint/lang/core/type/f16.h"
61#include "src/tint/lang/core/type/f32.h"
62#include "src/tint/lang/core/type/i32.h"
63#include "src/tint/lang/core/type/matrix.h"
64#include "src/tint/lang/core/type/multisampled_texture.h"
65#include "src/tint/lang/core/type/pointer.h"
66#include "src/tint/lang/core/type/sampled_texture.h"
67#include "src/tint/lang/core/type/storage_texture.h"
68#include "src/tint/lang/core/type/texture.h"
69#include "src/tint/lang/core/type/u32.h"
70#include "src/tint/lang/core/type/vector.h"
71#include "src/tint/lang/core/type/void.h"
Ben Clayton45146772023-08-01 00:37:35 +000072#include "src/tint/lang/msl/writer/common/printer_support.h"
Ben Clayton34c8e572023-08-01 00:37:35 +000073#include "src/tint/utils/containers/map.h"
Ben Claytond8054e22023-10-26 14:24:30 +000074#include "src/tint/utils/generator/text_generator.h"
dan sinclair22b4dd22023-07-21 00:40:07 +000075#include "src/tint/utils/macros/scoped_assignment.h"
76#include "src/tint/utils/rtti/switch.h"
Ben Clayton34c8e572023-08-01 00:37:35 +000077#include "src/tint/utils/text/string.h"
dan sinclaircadd0c02023-06-26 13:50:42 +000078
dan sinclairce6dffe2023-08-14 21:01:40 +000079using namespace tint::core::fluent_types; // NOLINT
80
dan sinclaire7e4afc2023-07-26 02:28:30 +000081namespace tint::msl::writer {
Ben Claytond8054e22023-10-26 14:24:30 +000082namespace {
dan sinclaircadd0c02023-06-26 13:50:42 +000083
Ben Claytond8054e22023-10-26 14:24:30 +000084/// PIMPL class for the MSL generator
85class Printer : public tint::TextGenerator {
86 public:
87 /// Constructor
88 /// @param module the Tint IR module to generate
89 explicit Printer(core::ir::Module& module) : ir_(module) {}
dan sinclaircadd0c02023-06-26 13:50:42 +000090
Ben Claytond8054e22023-10-26 14:24:30 +000091 /// @returns the generated MSL shader
92 tint::Result<std::string> Generate() {
93 auto valid = core::ir::ValidateAndDumpIfNeeded(ir_, "MSL writer");
94 if (!valid) {
95 return std::move(valid.Failure());
96 }
dan sinclaircadd0c02023-06-26 13:50:42 +000097
Ben Claytond8054e22023-10-26 14:24:30 +000098 {
99 TINT_SCOPED_ASSIGNMENT(current_buffer_, &preamble_buffer_);
100 Line() << "#include <metal_stdlib>";
101 Line() << "using namespace metal;";
102 }
103
104 // Emit module-scope declarations.
105 EmitBlockInstructions(ir_.root_block);
106
107 // Emit functions.
Ben Claytonacef3102023-11-20 17:09:35 +0000108 for (auto& func : ir_.functions) {
Ben Claytond8054e22023-10-26 14:24:30 +0000109 EmitFunction(func);
110 }
111
112 StringStream ss;
113 ss << preamble_buffer_.String() << std::endl << main_buffer_.String();
114 return ss.str();
dan sinclaircadd0c02023-06-26 13:50:42 +0000115 }
116
Ben Claytond8054e22023-10-26 14:24:30 +0000117 private:
118 /// Map of builtin structure to unique generated name
119 std::unordered_map<const core::type::Struct*, std::string> builtin_struct_names_;
120
121 core::ir::Module& ir_;
122
123 /// The buffer holding preamble text
124 TextBuffer preamble_buffer_;
125
126 /// Unique name of the 'TINT_INVARIANT' preprocessor define.
127 /// Non-empty only if an invariant attribute has been generated.
128 std::string invariant_define_name_;
129
130 std::unordered_set<const core::type::Struct*> emitted_structs_;
131
132 /// The current function being emitted
133 core::ir::Function* current_function_ = nullptr;
134 /// The current block being emitted
135 core::ir::Block* current_block_ = nullptr;
136
137 /// Unique name of the tint_array<T, N> template.
138 /// Non-empty only if the template has been generated.
139 std::string array_template_name_;
140
141 /// The representation for an IR pointer type
142 enum class PtrKind {
143 kPtr, // IR pointer is represented in a pointer
144 kRef, // IR pointer is represented in a reference
145 };
146
147 /// The structure for a value held by a 'let', 'var' or parameter.
148 struct VariableValue {
149 Symbol name; // Name of the variable
150 PtrKind ptr_kind = PtrKind::kRef;
151 };
152
153 /// The structure for an inlined value
154 struct InlinedValue {
155 std::string expr;
156 PtrKind ptr_kind = PtrKind::kRef;
157 };
158
159 /// Empty struct used as a sentinel value to indicate that an string expression has been
160 /// consumed by its single place of usage. Attempting to use this value a second time should
161 /// result in an ICE.
162 struct ConsumedValue {};
163
164 using ValueBinding = std::variant<VariableValue, InlinedValue, ConsumedValue>;
165
166 /// IR values to their representation
167 Hashmap<core::ir::Value*, ValueBinding, 32> bindings_;
168
169 /// Values that can be inlined.
170 Hashset<core::ir::Value*, 64> can_inline_;
171
172 /// @returns the name of the templated `tint_array` helper type, generating it if needed
173 const std::string& ArrayTemplateName() {
174 if (!array_template_name_.empty()) {
175 return array_template_name_;
176 }
177
178 array_template_name_ = UniqueIdentifier("tint_array");
179
dan sinclaircadd0c02023-06-26 13:50:42 +0000180 TINT_SCOPED_ASSIGNMENT(current_buffer_, &preamble_buffer_);
Ben Claytond8054e22023-10-26 14:24:30 +0000181 Line() << "template<typename T, size_t N>";
182 Line() << "struct " << array_template_name_ << " {";
dan sinclaircadd0c02023-06-26 13:50:42 +0000183
Ben Claytond8054e22023-10-26 14:24:30 +0000184 {
185 ScopedIndent si(current_buffer_);
186 Line()
187 << "const constant T& operator[](size_t i) const constant { return elements[i]; }";
188 for (auto* space : {"device", "thread", "threadgroup"}) {
189 Line() << space << " T& operator[](size_t i) " << space
190 << " { return elements[i]; }";
191 Line() << "const " << space << " T& operator[](size_t i) const " << space
192 << " { return elements[i]; }";
193 }
194 Line() << "T elements[N];";
195 }
196 Line() << "};";
197 Line();
dan sinclaircadd0c02023-06-26 13:50:42 +0000198
dan sinclair83f23362023-06-26 21:02:35 +0000199 return array_template_name_;
200 }
201
Ben Claytond8054e22023-10-26 14:24:30 +0000202 /// Emit the function
203 /// @param func the function to emit
204 void EmitFunction(core::ir::Function* func) {
205 TINT_SCOPED_ASSIGNMENT(current_function_, func);
dan sinclair83f23362023-06-26 21:02:35 +0000206
Ben Claytond8054e22023-10-26 14:24:30 +0000207 {
208 auto out = Line();
dan sinclair83f23362023-06-26 21:02:35 +0000209
dan sinclair1fa6a4b2023-11-22 13:49:42 +0000210 switch (func->Stage()) {
211 case core::ir::Function::PipelineStage::kCompute:
212 out << "kernel ";
213 break;
214 case core::ir::Function::PipelineStage::kFragment:
215 out << "fragment ";
216 break;
217 case core::ir::Function::PipelineStage::kVertex:
218 out << "vertex ";
219 break;
220 case core::ir::Function::PipelineStage::kUndefined:
221 break;
222 }
223
Ben Claytond8054e22023-10-26 14:24:30 +0000224 // TODO(dsinclair): Handle return type attributes
225
226 EmitType(out, func->ReturnType());
227 out << " " << ir_.NameOf(func).Name() << "() {";
228
229 // TODO(dsinclair): Emit Function parameters
dan sinclair83f23362023-06-26 21:02:35 +0000230 }
Ben Claytond8054e22023-10-26 14:24:30 +0000231 {
232 ScopedIndent si(current_buffer_);
233 EmitBlock(func->Block());
234 }
235
236 Line() << "}";
dan sinclair83f23362023-06-26 21:02:35 +0000237 }
dan sinclair83f23362023-06-26 21:02:35 +0000238
Ben Claytond8054e22023-10-26 14:24:30 +0000239 /// Emit a block
240 /// @param block the block to emit
dan sinclair58794b52023-11-21 14:35:44 +0000241 void EmitBlock(core::ir::Block* block) { EmitBlockInstructions(block); }
dan sinclair3d0d6d52023-08-02 13:43:25 +0000242
Ben Claytond8054e22023-10-26 14:24:30 +0000243 /// Emit the instructions in a block
244 /// @param block the block with the instructions to emit
245 void EmitBlockInstructions(core::ir::Block* block) {
246 TINT_SCOPED_ASSIGNMENT(current_block_, block);
247
248 for (auto* inst : *block) {
249 Switch(
250 inst, //
Ben Claytond8054e22023-10-26 14:24:30 +0000251 [&](core::ir::ExitIf* e) { EmitExitIf(e); }, //
252 [&](core::ir::If* if_) { EmitIf(if_); }, //
Ben Claytond8054e22023-10-26 14:24:30 +0000253 [&](core::ir::Return* r) { EmitReturn(r); }, //
254 [&](core::ir::Unreachable*) { EmitUnreachable(); }, //
255 [&](core::ir::Var* v) { EmitVar(v); }, //
dan sinclaireee3ad02023-11-20 13:22:41 +0000256 [&](core::ir::Discard*) { EmitDiscard(); }, //
dan sinclair58794b52023-11-21 14:35:44 +0000257
dan sinclaircea718c2023-11-22 16:08:34 +0000258 [&](core::ir::Bitcast*) { MaybeEmitInstruction(inst); }, //
dan sinclair55556942023-11-22 13:13:42 +0000259 [&](core::ir::Binary*) { MaybeEmitInstruction(inst); }, //
260 [&](core::ir::Let* l) { EmitLet(l); }, //
261 [&](core::ir::Load*) { MaybeEmitInstruction(inst); }, //
262 [&](core::ir::Construct*) { MaybeEmitInstruction(inst); }, //
dan sinclair48318cd2023-11-23 11:27:32 +0000263 [&](core::ir::Access*) { MaybeEmitInstruction(inst); }, //
Ben Claytond6082c52023-10-26 16:02:01 +0000264 TINT_ICE_ON_NO_MATCH);
Ben Claytond8054e22023-10-26 14:24:30 +0000265 }
266 }
267
dan sinclair55556942023-11-22 13:13:42 +0000268 // If the instruction is named, we need to emit it. If it is un-named, then we'll use it
269 // and inline it later.
270 void MaybeEmitInstruction(const core::ir::Instruction* inst) {
271 auto name = ir_.NameOf(inst->Result());
272 if (!name.IsValid()) {
273 return;
274 }
275
276 auto out = Line();
277 EmitType(out, inst->Result()->Type());
278 out << " const " << name.Name() << " = ";
279 EmitValue(out, inst->Result());
280 out << ";";
281 }
282
283 void EmitValue(StringStream& out, const core::ir::Value* v) {
dan sinclair58794b52023-11-21 14:35:44 +0000284 Switch(
dan sinclair55556942023-11-22 13:13:42 +0000285 v, //
286 [&](const core::ir::Constant* c) { EmitConstant(out, c); }, //
dan sinclair58794b52023-11-21 14:35:44 +0000287 // [&](core::ir::FunctionParam* fp) {}, //
dan sinclair55556942023-11-22 13:13:42 +0000288 [&](const core::ir::InstructionResult* r) {
dan sinclair58794b52023-11-21 14:35:44 +0000289 Switch(
dan sinclair55556942023-11-22 13:13:42 +0000290 r->Source(), //
291 [&](const core::ir::Binary* b) { EmitBinary(out, b); }, //
292 [&](const core::ir::Let* l) {
dan sinclair58794b52023-11-21 14:35:44 +0000293 auto name = ir_.NameOf(l->Result());
294 TINT_ASSERT(name.IsValid());
295 out << name.Name();
dan sinclair55556942023-11-22 13:13:42 +0000296 }, //
297 [&](const core::ir::Load* l) { EmitValue(out, l->From()); }, //
298 [&](const core::ir::Construct* c) { EmitConstruct(out, c); }, //
299 [&](const core::ir::Var* var) { EmitVarName(out, var); }, //
dan sinclaircea718c2023-11-22 16:08:34 +0000300 [&](const core::ir::Bitcast* b) { EmitBitcast(out, b); }, //
dan sinclair48318cd2023-11-23 11:27:32 +0000301 [&](const core::ir::Access* a) { EmitAccess(out, a); }, //
dan sinclair58794b52023-11-21 14:35:44 +0000302 TINT_ICE_ON_NO_MATCH);
303 }, //
304 TINT_ICE_ON_NO_MATCH);
305 }
306
Ben Claytond8054e22023-10-26 14:24:30 +0000307 /// Emit a binary instruction
308 /// @param b the binary instruction
dan sinclair55556942023-11-22 13:13:42 +0000309 void EmitBinary(StringStream& out, const core::ir::Binary* b) {
Ben Claytond8054e22023-10-26 14:24:30 +0000310 if (b->Op() == core::ir::BinaryOp::kEqual) {
311 auto* rhs = b->RHS()->As<core::ir::Constant>();
312 if (rhs && rhs->Type()->Is<core::type::Bool>() &&
313 rhs->Value()->ValueAs<bool>() == false) {
314 // expr == false
dan sinclair58794b52023-11-21 14:35:44 +0000315 out << "!(";
316 EmitValue(out, b->LHS());
317 out << ")";
Ben Claytond8054e22023-10-26 14:24:30 +0000318 return;
319 }
320 }
321
322 auto kind = [&] {
323 switch (b->Op()) {
324 case core::ir::BinaryOp::kAdd:
325 return "+";
326 case core::ir::BinaryOp::kSubtract:
327 return "-";
328 case core::ir::BinaryOp::kMultiply:
329 return "*";
330 case core::ir::BinaryOp::kDivide:
331 return "/";
332 case core::ir::BinaryOp::kModulo:
333 return "%";
334 case core::ir::BinaryOp::kAnd:
335 return "&";
336 case core::ir::BinaryOp::kOr:
337 return "|";
338 case core::ir::BinaryOp::kXor:
339 return "^";
340 case core::ir::BinaryOp::kEqual:
341 return "==";
342 case core::ir::BinaryOp::kNotEqual:
343 return "!=";
344 case core::ir::BinaryOp::kLessThan:
345 return "<";
346 case core::ir::BinaryOp::kGreaterThan:
347 return ">";
348 case core::ir::BinaryOp::kLessThanEqual:
349 return "<=";
350 case core::ir::BinaryOp::kGreaterThanEqual:
351 return ">=";
352 case core::ir::BinaryOp::kShiftLeft:
353 return "<<";
354 case core::ir::BinaryOp::kShiftRight:
355 return ">>";
356 }
357 return "<error>";
358 };
359
dan sinclair58794b52023-11-21 14:35:44 +0000360 out << "(";
361 EmitValue(out, b->LHS());
362 out << " " << kind() << " ";
363 EmitValue(out, b->RHS());
364 out << ")";
Ben Claytond8054e22023-10-26 14:24:30 +0000365 }
366
dan sinclair55556942023-11-22 13:13:42 +0000367 void EmitVarName(StringStream& out, const core::ir::Var* v) { out << ir_.NameOf(v).Name(); }
Ben Claytond8054e22023-10-26 14:24:30 +0000368
369 /// Emit a var instruction
370 /// @param v the var instruction
371 void EmitVar(core::ir::Var* v) {
dan sinclair3d0d6d52023-08-02 13:43:25 +0000372 auto out = Line();
373
Ben Claytond8054e22023-10-26 14:24:30 +0000374 auto* ptr = v->Result()->Type()->As<core::type::Pointer>();
375 TINT_ASSERT_OR_RETURN(ptr);
dan sinclair3d0d6d52023-08-02 13:43:25 +0000376
Ben Claytond8054e22023-10-26 14:24:30 +0000377 auto space = ptr->AddressSpace();
378 switch (space) {
379 case core::AddressSpace::kFunction:
380 case core::AddressSpace::kHandle:
381 break;
382 case core::AddressSpace::kPrivate:
383 out << "thread ";
384 break;
385 case core::AddressSpace::kWorkgroup:
386 out << "threadgroup ";
387 break;
388 default:
dan sinclair019146f2023-11-20 23:47:42 +0000389 TINT_IR_ICE(ir_) << "unhandled variable address space";
dan sinclair7257c562023-06-28 01:03:24 +0000390 return;
dan sinclair965321b2023-06-27 03:05:09 +0000391 }
392
Ben Claytond8054e22023-10-26 14:24:30 +0000393 auto name = ir_.NameOf(v);
dan sinclair965321b2023-06-27 03:05:09 +0000394
Ben Claytond8054e22023-10-26 14:24:30 +0000395 EmitType(out, ptr->UnwrapPtr());
396 out << " " << name.Name();
dan sinclair965321b2023-06-27 03:05:09 +0000397
Ben Claytond8054e22023-10-26 14:24:30 +0000398 if (v->Initializer()) {
dan sinclair58794b52023-11-21 14:35:44 +0000399 out << " = ";
400 EmitValue(out, v->Initializer());
Ben Claytond8054e22023-10-26 14:24:30 +0000401 } else if (space == core::AddressSpace::kPrivate ||
402 space == core::AddressSpace::kFunction ||
403 space == core::AddressSpace::kUndefined) {
404 out << " = ";
405 EmitZeroValue(out, ptr->UnwrapPtr());
dan sinclair965321b2023-06-27 03:05:09 +0000406 }
dan sinclair965321b2023-06-27 03:05:09 +0000407 out << ";";
Ben Claytond8054e22023-10-26 14:24:30 +0000408 }
409
410 /// Emit a let instruction
411 /// @param l the let instruction
412 void EmitLet(core::ir::Let* l) {
dan sinclair58794b52023-11-21 14:35:44 +0000413 auto name = ir_.NameOf(l->Result());
414 TINT_ASSERT(name.IsValid());
415
416 auto out = Line();
417 EmitType(out, l->Result()->Type());
418 out << " const " << name.Name() << " = ";
419 EmitValue(out, l->Value());
420 out << ";";
Ben Claytond8054e22023-10-26 14:24:30 +0000421 }
422
423 /// Emit an if instruction
424 /// @param if_ the if instruction
425 void EmitIf(core::ir::If* if_) {
dan sinclair58794b52023-11-21 14:35:44 +0000426 {
Ben Claytond8054e22023-10-26 14:24:30 +0000427 auto out = Line();
dan sinclair58794b52023-11-21 14:35:44 +0000428 out << "if (";
429 EmitValue(out, if_->Condition());
430 out << ") {";
Ben Claytond8054e22023-10-26 14:24:30 +0000431 }
432
Ben Claytond8054e22023-10-26 14:24:30 +0000433 {
434 ScopedIndent si(current_buffer_);
435 EmitBlockInstructions(if_->True());
436 }
437
438 if (if_->False() && !if_->False()->IsEmpty()) {
439 Line() << "} else {";
440
441 ScopedIndent si(current_buffer_);
442 EmitBlockInstructions(if_->False());
443 }
444
445 Line() << "}";
446 }
447
448 /// Emit an exit-if instruction
449 /// @param e the exit-if instruction
450 void EmitExitIf(core::ir::ExitIf* e) {
451 auto results = e->If()->Results();
452 auto args = e->Args();
453 for (size_t i = 0; i < e->Args().Length(); ++i) {
454 auto* phi = results[i];
455 auto* val = args[i];
456
dan sinclair58794b52023-11-21 14:35:44 +0000457 auto out = Line();
458 out << ir_.NameOf(phi).Name() << " = ";
459 EmitValue(out, val);
460 out << ";";
dan sinclair965321b2023-06-27 03:05:09 +0000461 }
462 }
463
Ben Claytond8054e22023-10-26 14:24:30 +0000464 /// Emit a return instruction
465 /// @param r the return instruction
466 void EmitReturn(core::ir::Return* r) {
467 // If this return has no arguments and the current block is for the function which is
468 // being returned, skip the return.
469 if (current_block_ == current_function_->Block() && r->Args().IsEmpty()) {
470 return;
471 }
472
473 auto out = Line();
474 out << "return";
475 if (!r->Args().IsEmpty()) {
dan sinclair58794b52023-11-21 14:35:44 +0000476 out << " ";
477 EmitValue(out, r->Args().Front());
Ben Claytond8054e22023-10-26 14:24:30 +0000478 }
479 out << ";";
dan sinclair965321b2023-06-27 03:05:09 +0000480 }
481
Ben Claytond8054e22023-10-26 14:24:30 +0000482 /// Emit an unreachable instruction
483 void EmitUnreachable() { Line() << "/* unreachable */"; }
dan sinclair965321b2023-06-27 03:05:09 +0000484
dan sinclaireee3ad02023-11-20 13:22:41 +0000485 /// Emit a discard instruction
486 void EmitDiscard() { Line() << "discard_fragment();"; }
487
dan sinclaircea718c2023-11-22 16:08:34 +0000488 /// Emit a bitcast instruction
489 void EmitBitcast(StringStream& out, const core::ir::Bitcast* b) {
490 out << "as_type<";
491 EmitType(out, b->Result()->Type());
492 out << ">(";
493 EmitValue(out, b->Val());
494 out << ")";
495 }
496
dan sinclair48318cd2023-11-23 11:27:32 +0000497 /// Emit an accessor
498 void EmitAccess(StringStream& out, const core::ir::Access* a) {
499 EmitValue(out, a->Object());
500
dan sinclair3505ed42023-11-23 11:45:27 +0000501 auto* current_type = a->Object()->Type();
dan sinclair48318cd2023-11-23 11:27:32 +0000502 for (auto* index : a->Indices()) {
dan sinclair3505ed42023-11-23 11:45:27 +0000503 TINT_ASSERT(current_type);
504
505 current_type = current_type->UnwrapPtr();
dan sinclair48318cd2023-11-23 11:27:32 +0000506 Switch(
507 current_type, //
508 [&](const core::type::Struct* s) {
509 auto* c = index->As<core::ir::Constant>();
510 auto* member = s->Members()[c->Value()->ValueAs<uint32_t>()];
511 out << "." << member->Name().Name();
512 current_type = member->Type();
513 },
514 [&](Default) {
515 out << "[";
516 EmitValue(out, index);
517 out << "]";
dan sinclair3505ed42023-11-23 11:45:27 +0000518 current_type = current_type->Element(0);
dan sinclair48318cd2023-11-23 11:27:32 +0000519 });
520 }
521 }
522
dan sinclair7bbe4c12023-11-21 12:11:32 +0000523 /// Emit a constructor
dan sinclair55556942023-11-22 13:13:42 +0000524 void EmitConstruct(StringStream& out, const core::ir::Construct* c) {
dan sinclair7bbe4c12023-11-21 12:11:32 +0000525 Switch(
526 c->Result()->Type(),
527 [&](const core::type::Array*) {
dan sinclair58794b52023-11-21 14:35:44 +0000528 EmitType(out, c->Result()->Type());
529 out << "{";
dan sinclair7bbe4c12023-11-21 12:11:32 +0000530 size_t i = 0;
531 for (auto* arg : c->Args()) {
532 if (i > 0) {
dan sinclair58794b52023-11-21 14:35:44 +0000533 out << ", ";
dan sinclair7bbe4c12023-11-21 12:11:32 +0000534 }
dan sinclair58794b52023-11-21 14:35:44 +0000535 EmitValue(out, arg);
dan sinclair7bbe4c12023-11-21 12:11:32 +0000536 i++;
537 }
dan sinclair58794b52023-11-21 14:35:44 +0000538 out << "}";
dan sinclair7bbe4c12023-11-21 12:11:32 +0000539 },
540 [&](const core::type::Struct* struct_ty) {
dan sinclair58794b52023-11-21 14:35:44 +0000541 out << "{";
dan sinclair7bbe4c12023-11-21 12:11:32 +0000542 size_t i = 0;
543 for (auto* arg : c->Args()) {
544 if (i > 0) {
dan sinclair58794b52023-11-21 14:35:44 +0000545 out << ", ";
dan sinclair7bbe4c12023-11-21 12:11:32 +0000546 }
547 // Emit field designators for structures to account for padding members.
548 auto name = struct_ty->Members()[i]->Name().Name();
dan sinclair58794b52023-11-21 14:35:44 +0000549 out << "." << name << "=";
550 EmitValue(out, arg);
dan sinclair7bbe4c12023-11-21 12:11:32 +0000551 i++;
552 }
dan sinclair58794b52023-11-21 14:35:44 +0000553 out << "}";
dan sinclair7bbe4c12023-11-21 12:11:32 +0000554 },
555 [&](Default) {
dan sinclair58794b52023-11-21 14:35:44 +0000556 EmitType(out, c->Result()->Type());
557 out << "(";
dan sinclair7bbe4c12023-11-21 12:11:32 +0000558 size_t i = 0;
559 for (auto* arg : c->Args()) {
560 if (i > 0) {
dan sinclair58794b52023-11-21 14:35:44 +0000561 out << ", ";
dan sinclair7bbe4c12023-11-21 12:11:32 +0000562 }
dan sinclair58794b52023-11-21 14:35:44 +0000563 EmitValue(out, arg);
dan sinclair7bbe4c12023-11-21 12:11:32 +0000564 i++;
565 }
dan sinclair58794b52023-11-21 14:35:44 +0000566 out << ")";
dan sinclair7bbe4c12023-11-21 12:11:32 +0000567 });
dan sinclair7bbe4c12023-11-21 12:11:32 +0000568 }
569
Ben Claytond8054e22023-10-26 14:24:30 +0000570 /// Handles generating a address space
571 /// @param out the output of the type stream
572 /// @param sc the address space to generate
573 void EmitAddressSpace(StringStream& out, core::AddressSpace sc) {
574 switch (sc) {
575 case core::AddressSpace::kFunction:
576 case core::AddressSpace::kPrivate:
577 case core::AddressSpace::kHandle:
578 out << "thread";
579 break;
580 case core::AddressSpace::kWorkgroup:
581 out << "threadgroup";
582 break;
583 case core::AddressSpace::kStorage:
584 out << "device";
585 break;
586 case core::AddressSpace::kUniform:
587 out << "constant";
588 break;
589 default:
dan sinclair019146f2023-11-20 23:47:42 +0000590 TINT_IR_ICE(ir_) << "unhandled address space: " << sc;
Ben Claytond8054e22023-10-26 14:24:30 +0000591 break;
dan sinclaira3565262023-06-27 13:18:13 +0000592 }
Ben Claytond8054e22023-10-26 14:24:30 +0000593 }
dan sinclaira3565262023-06-27 13:18:13 +0000594
Ben Claytond8054e22023-10-26 14:24:30 +0000595 /// Emit a type
596 /// @param out the stream to emit too
597 /// @param ty the type to emit
598 void EmitType(StringStream& out, const core::type::Type* ty) {
599 tint::Switch(
600 ty, //
601 [&](const core::type::Bool*) { out << "bool"; }, //
602 [&](const core::type::Void*) { out << "void"; }, //
603 [&](const core::type::F32*) { out << "float"; }, //
604 [&](const core::type::F16*) { out << "half"; }, //
605 [&](const core::type::I32*) { out << "int"; }, //
606 [&](const core::type::U32*) { out << "uint"; }, //
607 [&](const core::type::Array* arr) { EmitArrayType(out, arr); },
608 [&](const core::type::Vector* vec) { EmitVectorType(out, vec); },
609 [&](const core::type::Matrix* mat) { EmitMatrixType(out, mat); },
610 [&](const core::type::Atomic* atomic) { EmitAtomicType(out, atomic); },
611 [&](const core::type::Pointer* ptr) { EmitPointerType(out, ptr); },
612 [&](const core::type::Sampler*) { out << "sampler"; }, //
613 [&](const core::type::Texture* tex) { EmitTextureType(out, tex); },
614 [&](const core::type::Struct* str) {
615 out << StructName(str);
dan sinclairae33f972023-06-27 13:18:13 +0000616
Ben Claytond8054e22023-10-26 14:24:30 +0000617 TINT_SCOPED_ASSIGNMENT(current_buffer_, &preamble_buffer_);
618 EmitStructType(str);
Ben Claytond6082c52023-10-26 16:02:01 +0000619 }, //
620 TINT_ICE_ON_NO_MATCH);
Ben Claytond8054e22023-10-26 14:24:30 +0000621 }
dan sinclair67674592023-06-27 13:18:13 +0000622
Ben Claytond8054e22023-10-26 14:24:30 +0000623 /// Handles generating a pointer declaration
624 /// @param out the output stream
625 /// @param ptr the pointer to emit
626 void EmitPointerType(StringStream& out, const core::type::Pointer* ptr) {
627 if (ptr->Access() == core::Access::kRead) {
628 out << "const ";
629 }
630 EmitAddressSpace(out, ptr->AddressSpace());
631 out << " ";
632 EmitType(out, ptr->StoreType());
633 out << "*";
634 }
dan sinclair67674592023-06-27 13:18:13 +0000635
Ben Claytond8054e22023-10-26 14:24:30 +0000636 /// Handles generating an atomic declaration
637 /// @param out the output stream
638 /// @param atomic the atomic to emit
639 void EmitAtomicType(StringStream& out, const core::type::Atomic* atomic) {
640 if (atomic->Type()->Is<core::type::I32>()) {
641 out << "atomic_int";
642 return;
643 }
644 if (TINT_LIKELY(atomic->Type()->Is<core::type::U32>())) {
645 out << "atomic_uint";
646 return;
647 }
648 TINT_ICE() << "unhandled atomic type " << atomic->Type()->FriendlyName();
649 }
650
651 /// Handles generating an array declaration
652 /// @param out the output stream
653 /// @param arr the array to emit
654 void EmitArrayType(StringStream& out, const core::type::Array* arr) {
655 out << ArrayTemplateName() << "<";
656 EmitType(out, arr->ElemType());
657 out << ", ";
658 if (arr->Count()->Is<core::type::RuntimeArrayCount>()) {
659 out << "1";
660 } else {
661 auto count = arr->ConstantCount();
dan sinclair67674592023-06-27 13:18:13 +0000662 if (!count) {
dan sinclair019146f2023-11-20 23:47:42 +0000663 TINT_IR_ICE(ir_) << core::type::Array::kErrExpectedConstantCount;
dan sinclair67674592023-06-27 13:18:13 +0000664 return;
665 }
Ben Claytond8054e22023-10-26 14:24:30 +0000666 out << count.value();
667 }
668 out << ">";
669 }
dan sinclair45b59a82023-06-27 13:18:13 +0000670
Ben Claytond8054e22023-10-26 14:24:30 +0000671 /// Handles generating a vector declaration
672 /// @param out the output stream
673 /// @param vec the vector to emit
674 void EmitVectorType(StringStream& out, const core::type::Vector* vec) {
675 if (vec->Packed()) {
676 out << "packed_";
677 }
678 EmitType(out, vec->type());
679 out << vec->Width();
680 }
681
682 /// Handles generating a matrix declaration
683 /// @param out the output stream
684 /// @param mat the matrix to emit
685 void EmitMatrixType(StringStream& out, const core::type::Matrix* mat) {
686 EmitType(out, mat->type());
687 out << mat->columns() << "x" << mat->rows();
688 }
689
690 /// Handles generating a texture declaration
691 /// @param out the output stream
692 /// @param tex the texture to emit
693 void EmitTextureType(StringStream& out, const core::type::Texture* tex) {
694 if (TINT_UNLIKELY(tex->Is<core::type::ExternalTexture>())) {
dan sinclair019146f2023-11-20 23:47:42 +0000695 TINT_IR_ICE(ir_) << "Multiplanar external texture transform was not run.";
Ben Claytond8054e22023-10-26 14:24:30 +0000696 return;
697 }
698
699 if (tex->IsAnyOf<core::type::DepthTexture, core::type::DepthMultisampledTexture>()) {
700 out << "depth";
701 } else {
702 out << "texture";
703 }
704
705 switch (tex->dim()) {
706 case core::type::TextureDimension::k1d:
707 out << "1d";
708 break;
709 case core::type::TextureDimension::k2d:
710 out << "2d";
711 break;
712 case core::type::TextureDimension::k2dArray:
713 out << "2d_array";
714 break;
715 case core::type::TextureDimension::k3d:
716 out << "3d";
717 break;
718 case core::type::TextureDimension::kCube:
719 out << "cube";
720 break;
721 case core::type::TextureDimension::kCubeArray:
722 out << "cube_array";
723 break;
724 default:
dan sinclair019146f2023-11-20 23:47:42 +0000725 TINT_IR_ICE(ir_) << "invalid texture dimensions";
dan sinclair45b59a82023-06-27 13:18:13 +0000726 return;
Ben Claytond8054e22023-10-26 14:24:30 +0000727 }
728 if (tex->IsAnyOf<core::type::MultisampledTexture, core::type::DepthMultisampledTexture>()) {
729 out << "_ms";
730 }
731 out << "<";
732 TINT_DEFER(out << ">");
733
734 tint::Switch(
735 tex, //
736 [&](const core::type::DepthTexture*) { out << "float, access::sample"; },
737 [&](const core::type::DepthMultisampledTexture*) { out << "float, access::read"; },
738 [&](const core::type::StorageTexture* storage) {
739 EmitType(out, storage->type());
740 out << ", ";
741
742 std::string access_str;
743 if (storage->access() == core::Access::kRead) {
744 out << "access::read";
745 } else if (storage->access() == core::Access::kWrite) {
746 out << "access::write";
747 } else {
dan sinclair019146f2023-11-20 23:47:42 +0000748 TINT_IR_ICE(ir_) << "invalid access control for storage texture";
Ben Claytond8054e22023-10-26 14:24:30 +0000749 return;
750 }
751 },
752 [&](const core::type::MultisampledTexture* ms) {
753 EmitType(out, ms->type());
754 out << ", access::read";
755 },
756 [&](const core::type::SampledTexture* sampled) {
757 EmitType(out, sampled->type());
758 out << ", access::sample";
Ben Claytond6082c52023-10-26 16:02:01 +0000759 }, //
760 TINT_ICE_ON_NO_MATCH);
Ben Claytond8054e22023-10-26 14:24:30 +0000761 }
762
763 /// Handles generating a struct declaration. If the structure has already been emitted, then
764 /// this function will simply return without emitting anything.
765 /// @param str the struct to generate
766 void EmitStructType(const core::type::Struct* str) {
767 auto it = emitted_structs_.emplace(str);
768 if (!it.second) {
769 return;
770 }
771
772 // This does not append directly to the preamble because a struct may require other
773 // structs, or the array template, to get emitted before it. So, the struct emits into a
774 // temporary text buffer, then anything it depends on will emit to the preamble first,
775 // and then it copies the text buffer into the preamble.
776 TextBuffer str_buf;
777 Line(&str_buf) << "struct " << StructName(str) << " {";
778
779 bool is_host_shareable = str->IsHostShareable();
780
781 // Emits a `/* 0xnnnn */` byte offset comment for a struct member.
782 auto add_byte_offset_comment = [&](StringStream& out, uint32_t offset) {
783 std::ios_base::fmtflags saved_flag_state(out.flags());
784 out << "/* 0x" << std::hex << std::setfill('0') << std::setw(4) << offset << " */ ";
785 out.flags(saved_flag_state);
786 };
787
788 auto add_padding = [&](uint32_t size, uint32_t msl_offset) {
789 std::string name;
790 do {
791 name = UniqueIdentifier("tint_pad");
792 } while (str->FindMember(ir_.symbols.Get(name)));
793
794 auto out = Line(&str_buf);
795 add_byte_offset_comment(out, msl_offset);
796 out << ArrayTemplateName() << "<int8_t, " << size << "> " << name << ";";
797 };
798
799 str_buf.IncrementIndent();
800
801 uint32_t msl_offset = 0;
802 for (auto* mem : str->Members()) {
803 auto out = Line(&str_buf);
804 auto mem_name = mem->Name().Name();
805 auto ir_offset = mem->Offset();
806
807 if (is_host_shareable) {
808 if (TINT_UNLIKELY(ir_offset < msl_offset)) {
809 // Unimplementable layout
dan sinclair019146f2023-11-20 23:47:42 +0000810 TINT_IR_ICE(ir_) << "Structure member offset (" << ir_offset
811 << ") is behind MSL offset (" << msl_offset << ")";
Ben Claytond8054e22023-10-26 14:24:30 +0000812 return;
813 }
814
815 // Generate padding if required
816 if (auto padding = ir_offset - msl_offset) {
817 add_padding(padding, msl_offset);
818 msl_offset += padding;
819 }
820
821 add_byte_offset_comment(out, msl_offset);
dan sinclair45b59a82023-06-27 13:18:13 +0000822 }
823
Ben Claytond8054e22023-10-26 14:24:30 +0000824 auto* ty = mem->Type();
825
826 EmitType(out, ty);
827 out << " " << mem_name;
828
829 // Emit attributes
830 auto& attributes = mem->Attributes();
831
832 if (auto builtin = attributes.builtin) {
833 auto name = BuiltinToAttribute(builtin.value());
834 if (name.empty()) {
dan sinclair019146f2023-11-20 23:47:42 +0000835 TINT_IR_ICE(ir_) << "unknown builtin";
Ben Claytond8054e22023-10-26 14:24:30 +0000836 return;
837 }
838 out << " [[" << name << "]]";
839 }
840
841 if (auto location = attributes.location) {
842 auto& pipeline_stage_uses = str->PipelineStageUses();
843 if (TINT_UNLIKELY(pipeline_stage_uses.size() != 1)) {
dan sinclair019146f2023-11-20 23:47:42 +0000844 TINT_IR_ICE(ir_) << "invalid entry point IO struct uses";
Ben Claytond8054e22023-10-26 14:24:30 +0000845 return;
846 }
847
848 if (pipeline_stage_uses.count(core::type::PipelineStageUsage::kVertexInput)) {
849 out << " [[attribute(" + std::to_string(location.value()) + ")]]";
850 } else if (pipeline_stage_uses.count(
851 core::type::PipelineStageUsage::kVertexOutput)) {
852 out << " [[user(locn" + std::to_string(location.value()) + ")]]";
853 } else if (pipeline_stage_uses.count(
854 core::type::PipelineStageUsage::kFragmentInput)) {
855 out << " [[user(locn" + std::to_string(location.value()) + ")]]";
856 } else if (TINT_LIKELY(pipeline_stage_uses.count(
857 core::type::PipelineStageUsage::kFragmentOutput))) {
858 out << " [[color(" + std::to_string(location.value()) + ")]]";
859 } else {
dan sinclair019146f2023-11-20 23:47:42 +0000860 TINT_IR_ICE(ir_) << "invalid use of location decoration";
Ben Claytond8054e22023-10-26 14:24:30 +0000861 return;
862 }
863 }
864
865 if (auto interpolation = attributes.interpolation) {
866 auto name = InterpolationToAttribute(interpolation->type, interpolation->sampling);
867 if (name.empty()) {
dan sinclair019146f2023-11-20 23:47:42 +0000868 TINT_IR_ICE(ir_) << "unknown interpolation attribute";
Ben Claytond8054e22023-10-26 14:24:30 +0000869 return;
870 }
871 out << " [[" << name << "]]";
872 }
873
874 if (attributes.invariant) {
875 invariant_define_name_ = UniqueIdentifier("TINT_INVARIANT");
876 out << " " << invariant_define_name_;
877 }
878
879 out << ";";
880
881 if (is_host_shareable) {
882 // Calculate new MSL offset
883 auto size_align = MslPackedTypeSizeAndAlign(ty);
884 if (TINT_UNLIKELY(msl_offset % size_align.align)) {
dan sinclair019146f2023-11-20 23:47:42 +0000885 TINT_IR_ICE(ir_) << "Misaligned MSL structure member " << mem_name << " : "
886 << ty->FriendlyName() << " offset: " << msl_offset
887 << " align: " << size_align.align;
Ben Claytond8054e22023-10-26 14:24:30 +0000888 return;
889 }
890 msl_offset += size_align.size;
891 }
892 }
893
894 if (is_host_shareable && str->Size() != msl_offset) {
895 add_padding(str->Size() - msl_offset, msl_offset);
896 }
897
898 str_buf.DecrementIndent();
899 Line(&str_buf) << "};";
900
901 preamble_buffer_.Append(str_buf);
902 }
903
904 /// Handles core::ir::Constant values
905 /// @param out the stream to write the constant too
906 /// @param c the constant to emit
dan sinclair55556942023-11-22 13:13:42 +0000907 void EmitConstant(StringStream& out, const core::ir::Constant* c) {
908 EmitConstant(out, c->Value());
909 }
Ben Claytond8054e22023-10-26 14:24:30 +0000910
911 /// Handles core::constant::Value values
912 /// @param out the stream to write the constant too
913 /// @param c the constant to emit
914 void EmitConstant(StringStream& out, const core::constant::Value* c) {
915 auto emit_values = [&](uint32_t count) {
916 for (size_t i = 0; i < count; i++) {
dan sinclair45b59a82023-06-27 13:18:13 +0000917 if (i > 0) {
918 out << ", ";
919 }
dan sinclair45b59a82023-06-27 13:18:13 +0000920 EmitConstant(out, c->Index(i));
921 }
Ben Claytond8054e22023-10-26 14:24:30 +0000922 };
dan sinclaired70ac02023-06-27 03:48:03 +0000923
Ben Claytond8054e22023-10-26 14:24:30 +0000924 tint::Switch(
925 c->Type(), //
926 [&](const core::type::Bool*) { out << (c->ValueAs<bool>() ? "true" : "false"); },
927 [&](const core::type::I32*) { PrintI32(out, c->ValueAs<i32>()); },
928 [&](const core::type::U32*) { out << c->ValueAs<u32>() << "u"; },
929 [&](const core::type::F32*) { PrintF32(out, c->ValueAs<f32>()); },
930 [&](const core::type::F16*) { PrintF16(out, c->ValueAs<f16>()); },
931 [&](const core::type::Vector* v) {
932 EmitType(out, v);
dan sinclairbe3b4852023-08-03 19:58:33 +0000933
Ben Claytond8054e22023-10-26 14:24:30 +0000934 ScopedParen sp(out);
935 if (auto* splat = c->As<core::constant::Splat>()) {
936 EmitConstant(out, splat->el);
937 return;
938 }
939 emit_values(v->Width());
940 },
941 [&](const core::type::Matrix* m) {
942 EmitType(out, m);
943 ScopedParen sp(out);
944 emit_values(m->columns());
945 },
946 [&](const core::type::Array* a) {
947 EmitType(out, a);
948 out << "{";
949 TINT_DEFER(out << "}");
dan sinclairbe3b4852023-08-03 19:58:33 +0000950
Ben Claytond8054e22023-10-26 14:24:30 +0000951 if (c->AllZero()) {
952 return;
953 }
954
955 auto count = a->ConstantCount();
956 if (!count) {
dan sinclair019146f2023-11-20 23:47:42 +0000957 TINT_IR_ICE(ir_) << core::type::Array::kErrExpectedConstantCount;
Ben Claytond8054e22023-10-26 14:24:30 +0000958 return;
959 }
960 emit_values(*count);
961 },
962 [&](const core::type::Struct* s) {
963 EmitStructType(s);
964 out << StructName(s) << "{";
965 TINT_DEFER(out << "}");
966
967 if (c->AllZero()) {
968 return;
969 }
970
971 auto members = s->Members();
972 for (size_t i = 0; i < members.Length(); i++) {
973 if (i > 0) {
974 out << ", ";
975 }
976 out << "." << members[i]->Name().Name() << "=";
977 EmitConstant(out, c->Index(i));
978 }
Ben Claytond6082c52023-10-26 16:02:01 +0000979 }, //
980 TINT_ICE_ON_NO_MATCH);
Ben Clayton34c8e572023-08-01 00:37:35 +0000981 }
Ben Clayton34c8e572023-08-01 00:37:35 +0000982
Ben Claytond8054e22023-10-26 14:24:30 +0000983 /// Emits the zero value for the given type
984 /// @param out the stream to emit too
985 /// @param ty the type
986 void EmitZeroValue(StringStream& out, const core::type::Type* ty) {
987 Switch(
988 ty, [&](const core::type::Bool*) { out << "false"; }, //
989 [&](const core::type::F16*) { out << "0.0h"; }, //
990 [&](const core::type::F32*) { out << "0.0f"; }, //
991 [&](const core::type::I32*) { out << "0"; }, //
992 [&](const core::type::U32*) { out << "0u"; }, //
993 [&](const core::type::Vector* vec) { EmitZeroValue(out, vec->type()); }, //
994 [&](const core::type::Matrix* mat) {
995 EmitType(out, mat);
Ben Clayton67552302023-07-25 20:48:37 +0000996
Ben Claytond8054e22023-10-26 14:24:30 +0000997 ScopedParen sp(out);
998 EmitZeroValue(out, mat->type());
999 },
1000 [&](const core::type::Array*) { out << "{}"; }, //
1001 [&](const core::type::Struct*) { out << "{}"; }, //
Ben Claytond6082c52023-10-26 16:02:01 +00001002 TINT_ICE_ON_NO_MATCH);
Ben Claytond8054e22023-10-26 14:24:30 +00001003 }
dan sinclair155a42a2023-08-02 20:23:58 +00001004
Ben Claytond8054e22023-10-26 14:24:30 +00001005 /// @param s the structure
1006 /// @returns the name of the structure, taking special care of builtin structures that start
1007 /// with double underscores. If the structure is a builtin, then the returned name will be a
1008 /// unique name without the leading underscores.
1009 std::string StructName(const core::type::Struct* s) {
1010 auto name = s->Name().Name();
1011 if (HasPrefix(name, "__")) {
1012 name = tint::GetOrCreate(builtin_struct_names_, s,
1013 [&] { return UniqueIdentifier(name.substr(2)); });
1014 }
1015 return name;
1016 }
dan sinclair155a42a2023-08-02 20:23:58 +00001017
Ben Claytond8054e22023-10-26 14:24:30 +00001018 /// @return a new, unique identifier with the given prefix.
dan sinclair58794b52023-11-21 14:35:44 +00001019 /// @param prefix optional prefix to apply to the generated identifier. If empty
1020 /// "tint_symbol" will be used.
Ben Claytond8054e22023-10-26 14:24:30 +00001021 std::string UniqueIdentifier(const std::string& prefix /* = "" */) {
1022 return ir_.symbols.New(prefix).Name();
1023 }
Ben Claytond8054e22023-10-26 14:24:30 +00001024};
dan sinclair58794b52023-11-21 14:35:44 +00001025
Ben Claytond8054e22023-10-26 14:24:30 +00001026} // namespace
1027
1028Result<std::string> Print(core::ir::Module& module) {
1029 return Printer{module}.Generate();
dan sinclair355ee9a2023-08-03 22:50:42 +00001030}
1031
dan sinclaire7e4afc2023-07-26 02:28:30 +00001032} // namespace tint::msl::writer