1/*
2 * Copyright © 2020 Google, Inc.
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 "util/log.h"
25
26#include "ir3/ir3.h"
27#include "ir3/ir3_shader.h"
28#include "ir3/instr-a3xx.h"  // TODO move opc's and other useful things to ir3-instr.h or so
29
30#include "isa.h"
31
32struct bitset_params;
33
34struct encode_state {
35	struct ir3_compiler *compiler;
36
37	/**
38	 * The instruction which is currently being encoded
39	 */
40	struct ir3_instruction *instr;
41};
42
43/*
44 * Helpers defining how to map from ir3_instruction/ir3_register/etc to fields
45 * to be encoded:
46 */
47
48static inline bool
49extract_SRC1_R(struct ir3_instruction *instr)
50{
51	if (instr->nop) {
52		assert(!instr->repeat);
53		return instr->nop & 0x1;
54	}
55	return !!(instr->srcs[0]->flags & IR3_REG_R);
56}
57
58static inline bool
59extract_SRC2_R(struct ir3_instruction *instr)
60{
61	if (instr->nop) {
62		assert(!instr->repeat);
63		return (instr->nop >> 1) & 0x1;
64	}
65	/* src2 does not appear in all cat2, but SRC2_R does (for nop encoding) */
66	if (instr->srcs_count > 1)
67		return !!(instr->srcs[1]->flags & IR3_REG_R);
68	return 0;
69}
70
71static inline opc_t
72__instruction_case(struct encode_state *s, struct ir3_instruction *instr)
73{
74	/*
75	 * Temporary hack.. the new world doesn't map opcodes directly to hw
76	 * encoding, so there are some cases where we need to fixup the opc
77	 * to match what the encoder expects.  Eventually this will go away
78	 * once we completely transition away from the packed-struct encoding/
79	 * decoding and split up things which are logically different
80	 * instructions
81	 */
82	if (instr->opc == OPC_B) {
83		switch (instr->cat0.brtype) {
84		case BRANCH_PLAIN:
85			return OPC_BR;
86		case BRANCH_OR:
87			return OPC_BRAO;
88		case BRANCH_AND:
89			return OPC_BRAA;
90		case BRANCH_CONST:
91			return OPC_BRAC;
92		case BRANCH_ANY:
93			return OPC_BANY;
94		case BRANCH_ALL:
95			return OPC_BALL;
96		case BRANCH_X:
97			return OPC_BRAX;
98		}
99	} else if (instr->opc == OPC_MOV) {
100		struct ir3_register *src = instr->srcs[0];
101		if (src->flags & IR3_REG_IMMED) {
102			return OPC_MOV_IMMED;
103		} if (src->flags & IR3_REG_RELATIV) {
104			if (src->flags & IR3_REG_CONST) {
105				return OPC_MOV_RELCONST;
106			} else {
107				return OPC_MOV_RELGPR;
108			}
109		} else if (src->flags & IR3_REG_CONST) {
110			return OPC_MOV_CONST;
111		} else {
112			return OPC_MOV_GPR;
113		}
114	} else if (instr->opc == OPC_DEMOTE) {
115		return OPC_KILL;
116	} else if ((instr->block->shader->compiler->gen >= 6) &&
117			is_atomic(instr->opc) && (instr->flags & IR3_INSTR_G)) {
118		return instr->opc - OPC_ATOMIC_ADD + OPC_ATOMIC_B_ADD;
119	} else if (s->compiler->gen >= 6) {
120		if (instr->opc == OPC_RESINFO) {
121			return OPC_RESINFO_B;
122		} else if (instr->opc == OPC_LDIB) {
123			return OPC_LDIB_B;
124		} else if (instr->opc == OPC_STIB) {
125			return OPC_STIB_B;
126		}
127	}
128	return instr->opc;
129}
130
131static inline unsigned
132extract_ABSNEG(struct ir3_register *reg)
133{
134	// TODO generate enums for this:
135	if (reg->flags & (IR3_REG_FNEG | IR3_REG_SNEG | IR3_REG_BNOT)) {
136		if (reg->flags & (IR3_REG_FABS | IR3_REG_SABS)) {
137			return 3; // ABSNEG
138		} else {
139			return 1; // NEG
140		}
141	} else if (reg->flags & (IR3_REG_FABS | IR3_REG_SABS)) {
142		return 2; // ABS
143	} else {
144		return 0;
145	}
146}
147
148/**
149 * This is a bit messy, to deal with the fact that the optional "s2en"
150 * src is the first src, shifting everything else up by one.
151 *
152 * TODO revisit this once legacy 'packed struct' encoding is gone
153 */
154static inline struct ir3_register *
155extract_cat5_SRC(struct ir3_instruction *instr, unsigned n)
156{
157	if (instr->flags & IR3_INSTR_S2EN) {
158		n++;
159	}
160	if (n < instr->srcs_count)
161		return instr->srcs[n];
162	return NULL;
163}
164
165static inline bool
166extract_cat5_FULL(struct ir3_instruction *instr)
167{
168	struct ir3_register *reg = extract_cat5_SRC(instr, 0);
169	/* some cat5 have zero src regs, in which case 'FULL' is false */
170	if (!reg)
171		return false;
172	return !(reg->flags & IR3_REG_HALF);
173}
174
175static inline cat5_desc_mode_t
176extract_cat5_DESC_MODE(struct ir3_instruction *instr)
177{
178	assert(instr->flags & (IR3_INSTR_S2EN | IR3_INSTR_B));
179	if (instr->flags & IR3_INSTR_S2EN) {
180		if (instr->flags & IR3_INSTR_B) {
181			if (instr->flags & IR3_INSTR_A1EN) {
182				if (instr->flags & IR3_INSTR_NONUNIF) {
183					return CAT5_BINDLESS_A1_NONUNIFORM;
184				} else {
185					return CAT5_BINDLESS_A1_UNIFORM;
186				}
187			} else if (instr->flags & IR3_INSTR_NONUNIF) {
188				return CAT5_BINDLESS_NONUNIFORM;
189			} else {
190				return CAT5_BINDLESS_UNIFORM;
191			}
192		} else {
193			/* TODO: This should probably be CAT5_UNIFORM, at least on a6xx,
194			 * as this is what the blob does and it is presumably faster, but
195			 * first we should confirm it is actually nonuniform and figure
196			 * out when the whole descriptor mode mechanism was introduced.
197			 */
198			return CAT5_NONUNIFORM;
199		}
200		assert(!(instr->cat5.samp | instr->cat5.tex));
201	} else if (instr->flags & IR3_INSTR_B) {
202		if (instr->flags & IR3_INSTR_A1EN) {
203			return CAT5_BINDLESS_A1_IMM;
204		} else {
205			return CAT5_BINDLESS_IMM;
206		}
207	}
208	return 0;
209}
210
211static inline unsigned
212extract_cat6_DESC_MODE(struct ir3_instruction *instr)
213{
214	struct ir3_register *ssbo = instr->srcs[0];
215	if (ssbo->flags & IR3_REG_IMMED) {
216		return 0; // todo enum
217	} else if (instr->flags & IR3_INSTR_NONUNIF) {
218		return 2; // todo enum
219	} else {
220		return 1; // todo enum
221	}
222}
223
224/**
225 * This is a bit messy, for legacy (pre-bindless) atomic instructions,
226 * the .g (global) variety have SSBO as first src and everything else
227 * shifted up by one.
228 *
229 * TODO revisit this once legacy 'packed struct' encoding is gone
230 */
231static inline struct ir3_register *
232extract_cat6_SRC(struct ir3_instruction *instr, unsigned n)
233{
234	if (instr->flags & IR3_INSTR_G) {
235		n++;
236	}
237	assert(n < instr->srcs_count);
238	return instr->srcs[n];
239}
240
241typedef enum {
242	REG_MULITSRC_IMMED,
243	REG_MULTISRC_IMMED_FLUT_FULL,
244	REG_MULTISRC_IMMED_FLUT_HALF,
245	REG_MULTISRC_GPR,
246	REG_MULTISRC_CONST,
247	REG_MULTISRC_RELATIVE_GPR,
248	REG_MULTISRC_RELATIVE_CONST,
249} reg_multisrc_t;
250
251static inline reg_multisrc_t
252__multisrc_case(struct encode_state *s, struct ir3_register *reg)
253{
254	if (reg->flags & IR3_REG_IMMED) {
255		assert(opc_cat(s->instr->opc) == 2);
256		if (ir3_cat2_int(s->instr->opc)) {
257			return REG_MULITSRC_IMMED;
258		} else if (reg->flags & IR3_REG_HALF) {
259			return REG_MULTISRC_IMMED_FLUT_HALF;
260		} else {
261			return REG_MULTISRC_IMMED_FLUT_FULL;
262		}
263	} else if (reg->flags & IR3_REG_RELATIV) {
264		if (reg->flags & IR3_REG_CONST) {
265			return REG_MULTISRC_RELATIVE_CONST;
266		} else {
267			return REG_MULTISRC_RELATIVE_GPR;
268		}
269	} else if (reg->flags & IR3_REG_CONST) {
270		return REG_MULTISRC_CONST;
271	} else {
272		return REG_MULTISRC_GPR;
273	}
274}
275
276typedef enum {
277	REG_CAT3_SRC_GPR,
278	REG_CAT3_SRC_CONST_OR_IMMED,
279	REG_CAT3_SRC_RELATIVE_GPR,
280	REG_CAT3_SRC_RELATIVE_CONST,
281} reg_cat3_src_t;
282
283static inline reg_cat3_src_t
284__cat3_src_case(struct encode_state *s, struct ir3_register *reg)
285{
286	if (reg->flags & IR3_REG_RELATIV) {
287		if (reg->flags & IR3_REG_CONST) {
288			return REG_CAT3_SRC_RELATIVE_CONST;
289		} else {
290			return REG_CAT3_SRC_RELATIVE_GPR;
291		}
292	} else if (reg->flags & (IR3_REG_CONST | IR3_REG_IMMED)) {
293		return REG_CAT3_SRC_CONST_OR_IMMED;
294	} else {
295		return REG_CAT3_SRC_GPR;
296	}
297}
298
299#include "encode.h"
300
301
302void *
303isa_assemble(struct ir3_shader_variant *v)
304{
305	BITSET_WORD *ptr, *instrs;
306	const struct ir3_info *info = &v->info;
307	struct ir3 *shader = v->ir;
308
309	ptr = instrs = rzalloc_size(v, info->size);
310
311	foreach_block (block, &shader->block_list) {
312		foreach_instr (instr, &block->instr_list) {
313			struct encode_state s = {
314				.compiler = shader->compiler,
315				.instr = instr,
316			};
317
318			const bitmask_t encoded = encode__instruction(&s, NULL, instr);
319			store_instruction(instrs, encoded);
320			instrs += BITMASK_WORDS;
321		}
322	}
323
324	return ptr;
325}
326