1 /*
2 * Copyright (C) 2021 Collabora Ltd.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 * SOFTWARE.
22 */
23
24 #include "bi_builder.h"
25 #include "va_compiler.h"
26 #include "valhall.h"
27
28 /* Valhall has limits on access to fast-access uniforms:
29 *
30 * An instruction may access no more than a single 64-bit uniform slot.
31 * An instruction may access no more than 64-bits of combined uniforms and
32 * constants. An instruction may access no more than a single special immediate
33 * (e.g. lane_id).
34 *
35 * We validate these constraints.
36 *
37 * An instruction may only access a single page of (special or uniform) FAU.
38 * This constraint does not need explicit validation: since FAU slots are
39 * naturally aligned, they never cross page boundaries, so this condition is
40 * implied by only acesssing a single 64-bit slot.
41 */
42
43 struct fau_state {
44 signed uniform_slot;
45 bi_index buffer[2];
46 };
47
48 static bool
fau_state_buffer(struct fau_state * fau,bi_index idx)49 fau_state_buffer(struct fau_state *fau, bi_index idx)
50 {
51 for (unsigned i = 0; i < ARRAY_SIZE(fau->buffer); ++i) {
52 if (bi_is_word_equiv(fau->buffer[i], idx))
53 return true;
54 else if (bi_is_null(fau->buffer[i])) {
55 fau->buffer[i] = idx;
56 return true;
57 }
58 }
59
60 return false;
61 }
62
63 static bool
fau_state_uniform(struct fau_state * fau,bi_index idx)64 fau_state_uniform(struct fau_state *fau, bi_index idx)
65 {
66 /* Each slot is 64-bits. The low/high half is encoded as the offset of the
67 * bi_index, which we want to ignore.
68 */
69 unsigned slot = (idx.value & 63);
70
71 if (fau->uniform_slot < 0)
72 fau->uniform_slot = slot;
73
74 return fau->uniform_slot == slot;
75 }
76
77 static bool
fau_is_special(enum bir_fau fau)78 fau_is_special(enum bir_fau fau)
79 {
80 return !(fau & (BIR_FAU_UNIFORM | BIR_FAU_IMMEDIATE));
81 }
82
83 static bool
fau_state_special(struct fau_state * fau,bi_index idx)84 fau_state_special(struct fau_state *fau, bi_index idx)
85 {
86 for (unsigned i = 0; i < ARRAY_SIZE(fau->buffer); ++i) {
87 bi_index buf = fau->buffer[i];
88 bool special = !bi_is_null(buf) && fau_is_special(buf.value);
89
90 if (special && !bi_is_equiv(buf, idx))
91 return false;
92 }
93
94 return true;
95 }
96
97 static bool
valid_src(struct fau_state * fau,unsigned fau_page,bi_index src)98 valid_src(struct fau_state *fau, unsigned fau_page, bi_index src)
99 {
100 if (src.type != BI_INDEX_FAU)
101 return true;
102
103 bool valid = (fau_page == va_fau_page(src.value));
104 valid &= fau_state_buffer(fau, src);
105
106 if (src.value & BIR_FAU_UNIFORM)
107 valid &= fau_state_uniform(fau, src);
108 else if (fau_is_special(src.value))
109 valid &= fau_state_special(fau, src);
110
111 return valid;
112 }
113
114 bool
va_validate_fau(bi_instr * I)115 va_validate_fau(bi_instr *I)
116 {
117 bool valid = true;
118 struct fau_state fau = {.uniform_slot = -1};
119 unsigned fau_page = va_select_fau_page(I);
120
121 bi_foreach_src(I, s) {
122 valid &= valid_src(&fau, fau_page, I->src[s]);
123 }
124
125 return valid;
126 }
127
128 void
va_repair_fau(bi_builder * b,bi_instr * I)129 va_repair_fau(bi_builder *b, bi_instr *I)
130 {
131 struct fau_state fau = {.uniform_slot = -1};
132 unsigned fau_page = va_select_fau_page(I);
133
134 bi_foreach_src(I, s) {
135 struct fau_state push = fau;
136 bi_index src = I->src[s];
137
138 if (!valid_src(&fau, fau_page, src)) {
139 bi_replace_src(I, s, bi_mov_i32(b, bi_strip_index(src)));
140
141 /* Rollback update. Since the replacement move doesn't affect FAU
142 * state, there is no need to call valid_src again.
143 */
144 fau = push;
145 }
146 }
147 }
148
149 void
va_validate(FILE * fp,bi_context * ctx)150 va_validate(FILE *fp, bi_context *ctx)
151 {
152 bool errors = false;
153
154 bi_foreach_instr_global(ctx, I) {
155 if (!va_validate_fau(I)) {
156 if (!errors) {
157 fprintf(fp, "Validation failed, this is a bug. Shader:\n\n");
158 bi_print_shader(ctx, fp);
159 fprintf(fp, "Offending code:\n");
160 }
161
162 bi_print_instr(I, fp);
163 fprintf(fp, "\n");
164 errors = true;
165 }
166 }
167
168 if (errors)
169 exit(1);
170 }
171