1 // 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
15 #include "src/writer/append_vector.h"
16
17 #include <utility>
18 #include <vector>
19
20 #include "src/sem/call.h"
21 #include "src/sem/expression.h"
22 #include "src/sem/type_constructor.h"
23 #include "src/sem/type_conversion.h"
24 #include "src/utils/transform.h"
25
26 namespace tint {
27 namespace writer {
28
29 namespace {
30
31 struct VectorConstructorInfo {
32 const sem::Call* call = nullptr;
33 const sem::TypeConstructor* ctor = nullptr;
operator booltint::writer::__anon8b8ae0070111::VectorConstructorInfo34 operator bool() const { return call != nullptr; }
35 };
AsVectorConstructor(const sem::Expression * expr)36 VectorConstructorInfo AsVectorConstructor(const sem::Expression* expr) {
37 if (auto* call = expr->As<sem::Call>()) {
38 if (auto* ctor = call->Target()->As<sem::TypeConstructor>()) {
39 if (ctor->ReturnType()->Is<sem::Vector>()) {
40 return {call, ctor};
41 }
42 }
43 }
44 return {};
45 }
46
Zero(ProgramBuilder & b,const sem::Type * ty,const sem::Statement * stmt)47 const sem::Expression* Zero(ProgramBuilder& b,
48 const sem::Type* ty,
49 const sem::Statement* stmt) {
50 const ast::Expression* expr = nullptr;
51 if (ty->Is<sem::I32>()) {
52 expr = b.Expr(0);
53 } else if (ty->Is<sem::U32>()) {
54 expr = b.Expr(0u);
55 } else if (ty->Is<sem::F32>()) {
56 expr = b.Expr(0.0f);
57 } else if (ty->Is<sem::Bool>()) {
58 expr = b.Expr(false);
59 } else {
60 TINT_UNREACHABLE(Writer, b.Diagnostics())
61 << "unsupported vector element type: " << ty->TypeInfo().name;
62 return nullptr;
63 }
64 auto* sem = b.create<sem::Expression>(expr, ty, stmt, sem::Constant{});
65 b.Sem().Add(expr, sem);
66 return sem;
67 }
68
69 } // namespace
70
AppendVector(ProgramBuilder * b,const ast::Expression * vector_ast,const ast::Expression * scalar_ast)71 const sem::Call* AppendVector(ProgramBuilder* b,
72 const ast::Expression* vector_ast,
73 const ast::Expression* scalar_ast) {
74 uint32_t packed_size;
75 const sem::Type* packed_el_sem_ty;
76 auto* vector_sem = b->Sem().Get(vector_ast);
77 auto* scalar_sem = b->Sem().Get(scalar_ast);
78 auto* vector_ty = vector_sem->Type()->UnwrapRef();
79 if (auto* vec = vector_ty->As<sem::Vector>()) {
80 packed_size = vec->Width() + 1;
81 packed_el_sem_ty = vec->type();
82 } else {
83 packed_size = 2;
84 packed_el_sem_ty = vector_ty;
85 }
86
87 const ast::Type* packed_el_ast_ty = nullptr;
88 if (packed_el_sem_ty->Is<sem::I32>()) {
89 packed_el_ast_ty = b->create<ast::I32>();
90 } else if (packed_el_sem_ty->Is<sem::U32>()) {
91 packed_el_ast_ty = b->create<ast::U32>();
92 } else if (packed_el_sem_ty->Is<sem::F32>()) {
93 packed_el_ast_ty = b->create<ast::F32>();
94 } else if (packed_el_sem_ty->Is<sem::Bool>()) {
95 packed_el_ast_ty = b->create<ast::Bool>();
96 } else {
97 TINT_UNREACHABLE(Writer, b->Diagnostics())
98 << "unsupported vector element type: "
99 << packed_el_sem_ty->TypeInfo().name;
100 }
101
102 auto* statement = vector_sem->Stmt();
103
104 auto* packed_ast_ty = b->create<ast::Vector>(packed_el_ast_ty, packed_size);
105 auto* packed_sem_ty = b->create<sem::Vector>(packed_el_sem_ty, packed_size);
106
107 // If the coordinates are already passed in a vector constructor, with only
108 // scalar components supplied, extract the elements into the new vector
109 // instead of nesting a vector-in-vector.
110 // If the coordinates are a zero-constructor of the vector, then expand that
111 // to scalar zeros.
112 // The other cases for a nested vector constructor are when it is used
113 // to convert a vector of a different type, e.g. vec2<i32>(vec2<u32>()).
114 // In that case, preserve the original argument, or you'll get a type error.
115
116 std::vector<const sem::Expression*> packed;
117 if (auto vc = AsVectorConstructor(vector_sem)) {
118 const auto num_supplied = vc.call->Arguments().size();
119 if (num_supplied == 0) {
120 // Zero-value vector constructor. Populate with zeros
121 for (uint32_t i = 0; i < packed_size - 1; i++) {
122 auto* zero = Zero(*b, packed_el_sem_ty, statement);
123 packed.emplace_back(zero);
124 }
125 } else if (num_supplied + 1 == packed_size) {
126 // All vector components were supplied as scalars. Pass them through.
127 packed = vc.call->Arguments();
128 }
129 }
130 if (packed.empty()) {
131 // The special cases didn't occur. Use the vector argument as-is.
132 packed.emplace_back(vector_sem);
133 }
134
135 if (packed_el_sem_ty != scalar_sem->Type()->UnwrapRef()) {
136 // Cast scalar to the vector element type
137 auto* scalar_cast_ast = b->Construct(packed_el_ast_ty, scalar_ast);
138 auto* scalar_cast_target = b->create<sem::TypeConversion>(
139 packed_el_sem_ty,
140 b->create<sem::Parameter>(nullptr, 0, scalar_sem->Type()->UnwrapRef(),
141 ast::StorageClass::kNone,
142 ast::Access::kUndefined));
143 auto* scalar_cast_sem =
144 b->create<sem::Call>(scalar_cast_ast, scalar_cast_target,
145 std::vector<const sem::Expression*>{scalar_sem},
146 statement, sem::Constant{});
147 b->Sem().Add(scalar_cast_ast, scalar_cast_sem);
148 packed.emplace_back(scalar_cast_sem);
149 } else {
150 packed.emplace_back(scalar_sem);
151 }
152
153 auto* constructor_ast = b->Construct(
154 packed_ast_ty, utils::Transform(packed, [&](const sem::Expression* expr) {
155 return expr->Declaration();
156 }));
157 auto* constructor_target = b->create<sem::TypeConstructor>(
158 packed_sem_ty,
159 utils::Transform(packed,
160 [&](const tint::sem::Expression* arg,
161 size_t i) -> const sem::Parameter* {
162 return b->create<sem::Parameter>(
163 nullptr, i, arg->Type()->UnwrapRef(),
164 ast::StorageClass::kNone, ast::Access::kUndefined);
165 }));
166 auto* constructor_sem = b->create<sem::Call>(
167 constructor_ast, constructor_target, packed, statement, sem::Constant{});
168 b->Sem().Add(constructor_ast, constructor_sem);
169 return constructor_sem;
170 }
171
172 } // namespace writer
173 } // namespace tint
174