• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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