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 "va_compiler.h"
25#include "valhall.h"
26#include "bi_builder.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 constants.
32 *   An instruction may access no more than a single special immediate (e.g. lane_id).
33 *
34 * We validate these constraints.
35 *
36 * An instruction may only access a single page of (special or uniform) FAU.
37 * This constraint does not need explicit validation: since FAU slots are
38 * naturally aligned, they never cross page boundaries, so this condition is
39 * implied by only acesssing a single 64-bit slot.
40 */
41
42struct fau_state {
43   signed uniform_slot;
44   bi_index buffer[2];
45};
46
47static bool
48fau_state_buffer(struct fau_state *fau, bi_index idx)
49{
50   for (unsigned i = 0; i < ARRAY_SIZE(fau->buffer); ++i) {
51      if (bi_is_word_equiv(fau->buffer[i], idx))
52         return true;
53      else if (bi_is_null(fau->buffer[i])) {
54         fau->buffer[i] = idx;
55         return true;
56      }
57   }
58
59   return false;
60}
61
62static bool
63fau_state_uniform(struct fau_state *fau, bi_index idx)
64{
65   /* Each slot is 64-bits. The low/high half is encoded as the offset of the
66    * bi_index, which we want to ignore.
67    */
68   unsigned slot = (idx.value & 63);
69
70   if (fau->uniform_slot < 0)
71      fau->uniform_slot = slot;
72
73   return fau->uniform_slot == slot;
74}
75
76static bool
77fau_is_special(enum bir_fau fau)
78{
79   return !(fau & (BIR_FAU_UNIFORM | BIR_FAU_IMMEDIATE));
80}
81
82static bool
83fau_state_special(struct fau_state *fau, bi_index idx)
84{
85   for (unsigned i = 0; i < ARRAY_SIZE(fau->buffer); ++i) {
86      bi_index buf = fau->buffer[i];
87      bool special = !bi_is_null(buf) && fau_is_special(buf.value);
88
89      if (special && !bi_is_equiv(buf, idx))
90         return false;
91   }
92
93   return true;
94}
95
96static bool
97valid_src(struct fau_state *fau, unsigned fau_page, bi_index src)
98{
99   if (src.type != BI_INDEX_FAU)
100      return true;
101
102   bool valid = (fau_page == va_fau_page(src.value));
103   valid &= fau_state_buffer(fau, src);
104
105   if (src.value & BIR_FAU_UNIFORM)
106      valid &= fau_state_uniform(fau, src);
107   else if (fau_is_special(src.value))
108      valid &= fau_state_special(fau, src);
109
110   return valid;
111}
112
113bool
114va_validate_fau(bi_instr *I)
115{
116   bool valid = true;
117   struct fau_state fau = { .uniform_slot = -1 };
118   unsigned fau_page = va_select_fau_page(I);
119
120   bi_foreach_src(I, s) {
121      valid &= valid_src(&fau, fau_page, I->src[s]);
122   }
123
124   return valid;
125}
126
127void
128va_repair_fau(bi_builder *b, bi_instr *I)
129{
130   struct fau_state fau = { .uniform_slot = -1 };
131   unsigned fau_page = va_select_fau_page(I);
132
133   bi_foreach_src(I, s) {
134      struct fau_state push = fau;
135      bi_index src = I->src[s];
136
137      if (!valid_src(&fau, fau_page, src)) {
138         bi_index copy = bi_mov_i32(b, bi_strip_index(src));
139         I->src[s] = bi_replace_index(src, copy);
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
149void
150va_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