• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2021 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/transform/binding_remapper.h"
16 
17 #include <string>
18 #include <unordered_set>
19 #include <utility>
20 
21 #include "src/ast/disable_validation_decoration.h"
22 #include "src/program_builder.h"
23 #include "src/sem/function.h"
24 #include "src/sem/variable.h"
25 
26 TINT_INSTANTIATE_TYPEINFO(tint::transform::BindingRemapper);
27 TINT_INSTANTIATE_TYPEINFO(tint::transform::BindingRemapper::Remappings);
28 
29 namespace tint {
30 namespace transform {
31 
Remappings(BindingPoints bp,AccessControls ac,bool may_collide)32 BindingRemapper::Remappings::Remappings(BindingPoints bp,
33                                         AccessControls ac,
34                                         bool may_collide)
35     : binding_points(std::move(bp)),
36       access_controls(std::move(ac)),
37       allow_collisions(may_collide) {}
38 
39 BindingRemapper::Remappings::Remappings(const Remappings&) = default;
40 BindingRemapper::Remappings::~Remappings() = default;
41 
42 BindingRemapper::BindingRemapper() = default;
43 BindingRemapper::~BindingRemapper() = default;
44 
Run(CloneContext & ctx,const DataMap & inputs,DataMap &)45 void BindingRemapper::Run(CloneContext& ctx, const DataMap& inputs, DataMap&) {
46   auto* remappings = inputs.Get<Remappings>();
47   if (!remappings) {
48     ctx.dst->Diagnostics().add_error(
49         diag::System::Transform,
50         "missing transform data for " + std::string(TypeInfo().name));
51     return;
52   }
53 
54   // A set of post-remapped binding points that need to be decorated with a
55   // DisableValidationDecoration to disable binding-point-collision validation
56   std::unordered_set<sem::BindingPoint> add_collision_deco;
57 
58   if (remappings->allow_collisions) {
59     // Scan for binding point collisions generated by this transform.
60     // Populate all collisions in the `add_collision_deco` set.
61     for (auto* func_ast : ctx.src->AST().Functions()) {
62       if (!func_ast->IsEntryPoint()) {
63         continue;
64       }
65       auto* func = ctx.src->Sem().Get(func_ast);
66       std::unordered_map<sem::BindingPoint, int> binding_point_counts;
67       for (auto* var : func->TransitivelyReferencedGlobals()) {
68         if (auto binding_point = var->Declaration()->BindingPoint()) {
69           BindingPoint from{binding_point.group->value,
70                             binding_point.binding->value};
71           auto bp_it = remappings->binding_points.find(from);
72           if (bp_it != remappings->binding_points.end()) {
73             // Remapped
74             BindingPoint to = bp_it->second;
75             if (binding_point_counts[to]++) {
76               add_collision_deco.emplace(to);
77             }
78           } else {
79             // No remapping
80             if (binding_point_counts[from]++) {
81               add_collision_deco.emplace(from);
82             }
83           }
84         }
85       }
86     }
87   }
88 
89   for (auto* var : ctx.src->AST().GlobalVariables()) {
90     if (auto binding_point = var->BindingPoint()) {
91       // The original binding point
92       BindingPoint from{binding_point.group->value,
93                         binding_point.binding->value};
94 
95       // The binding point after remapping
96       BindingPoint bp = from;
97 
98       // Replace any group or binding decorations.
99       // Note: This has to be performed *before* remapping access controls, as
100       // `ctx.Clone(var->decorations)` depend on these replacements.
101       auto bp_it = remappings->binding_points.find(from);
102       if (bp_it != remappings->binding_points.end()) {
103         BindingPoint to = bp_it->second;
104         auto* new_group = ctx.dst->create<ast::GroupDecoration>(to.group);
105         auto* new_binding = ctx.dst->create<ast::BindingDecoration>(to.binding);
106 
107         ctx.Replace(binding_point.group, new_group);
108         ctx.Replace(binding_point.binding, new_binding);
109         bp = to;
110       }
111 
112       // Replace any access controls.
113       auto ac_it = remappings->access_controls.find(from);
114       if (ac_it != remappings->access_controls.end()) {
115         ast::Access ac = ac_it->second;
116         if (ac > ast::Access::kLastValid) {
117           ctx.dst->Diagnostics().add_error(
118               diag::System::Transform,
119               "invalid access mode (" +
120                   std::to_string(static_cast<uint32_t>(ac)) + ")");
121           return;
122         }
123         auto* sem = ctx.src->Sem().Get(var);
124         if (sem->StorageClass() != ast::StorageClass::kStorage) {
125           ctx.dst->Diagnostics().add_error(
126               diag::System::Transform,
127               "cannot apply access control to variable with storage class " +
128                   std::string(ast::ToString(sem->StorageClass())));
129           return;
130         }
131         auto* ty = sem->Type()->UnwrapRef();
132         const ast::Type* inner_ty = CreateASTTypeFor(ctx, ty);
133         auto* new_var = ctx.dst->create<ast::Variable>(
134             ctx.Clone(var->source), ctx.Clone(var->symbol),
135             var->declared_storage_class, ac, inner_ty, var->is_const,
136             ctx.Clone(var->constructor), ctx.Clone(var->decorations));
137         ctx.Replace(var, new_var);
138       }
139 
140       // Add `DisableValidationDecoration`s if required
141       if (add_collision_deco.count(bp)) {
142         auto* decoration =
143             ctx.dst->Disable(ast::DisabledValidation::kBindingPointCollision);
144         ctx.InsertBefore(var->decorations, *var->decorations.begin(),
145                          decoration);
146       }
147     }
148   }
149 
150   ctx.Clone();
151 }
152 
153 }  // namespace transform
154 }  // namespace tint
155