Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mesa
Path: blob/21.2-virgl/src/freedreno/afuc/asm.c
4564 views
1
/*
2
* Copyright (c) 2017 Rob Clark <[email protected]>
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 <assert.h>
25
#include <err.h>
26
#include <fcntl.h>
27
#include <getopt.h>
28
#include <stdarg.h>
29
#include <stdbool.h>
30
#include <stdint.h>
31
#include <stdio.h>
32
#include <stdlib.h>
33
#include <string.h>
34
#include <unistd.h>
35
36
#include "util/macros.h"
37
#include "afuc.h"
38
#include "asm.h"
39
#include "parser.h"
40
#include "util.h"
41
42
int gpuver;
43
44
/* bit lame to hard-code max but fw sizes are small */
45
static struct asm_instruction instructions[0x2000];
46
static unsigned num_instructions;
47
48
static struct asm_label labels[0x512];
49
static unsigned num_labels;
50
51
struct asm_instruction *
52
next_instr(int tok)
53
{
54
struct asm_instruction *ai = &instructions[num_instructions++];
55
assert(num_instructions < ARRAY_SIZE(instructions));
56
ai->tok = tok;
57
return ai;
58
}
59
60
void
61
decl_label(const char *str)
62
{
63
struct asm_label *label = &labels[num_labels++];
64
65
assert(num_labels < ARRAY_SIZE(labels));
66
67
label->offset = num_instructions;
68
label->label = str;
69
}
70
71
static int
72
resolve_label(const char *str)
73
{
74
int i;
75
76
for (i = 0; i < num_labels; i++) {
77
struct asm_label *label = &labels[i];
78
79
if (!strcmp(str, label->label)) {
80
return label->offset;
81
}
82
}
83
84
fprintf(stderr, "Undeclared label: %s\n", str);
85
exit(2);
86
}
87
88
static afuc_opc
89
tok2alu(int tok)
90
{
91
switch (tok) {
92
case T_OP_ADD:
93
return OPC_ADD;
94
case T_OP_ADDHI:
95
return OPC_ADDHI;
96
case T_OP_SUB:
97
return OPC_SUB;
98
case T_OP_SUBHI:
99
return OPC_SUBHI;
100
case T_OP_AND:
101
return OPC_AND;
102
case T_OP_OR:
103
return OPC_OR;
104
case T_OP_XOR:
105
return OPC_XOR;
106
case T_OP_NOT:
107
return OPC_NOT;
108
case T_OP_SHL:
109
return OPC_SHL;
110
case T_OP_USHR:
111
return OPC_USHR;
112
case T_OP_ISHR:
113
return OPC_ISHR;
114
case T_OP_ROT:
115
return OPC_ROT;
116
case T_OP_MUL8:
117
return OPC_MUL8;
118
case T_OP_MIN:
119
return OPC_MIN;
120
case T_OP_MAX:
121
return OPC_MAX;
122
case T_OP_CMP:
123
return OPC_CMP;
124
case T_OP_MSB:
125
return OPC_MSB;
126
default:
127
assert(0);
128
return -1;
129
}
130
}
131
132
static void
133
emit_instructions(int outfd)
134
{
135
int i;
136
137
/* there is an extra 0x00000000 which kernel strips off.. we could
138
* perhaps use it for versioning.
139
*/
140
i = 0;
141
write(outfd, &i, 4);
142
143
for (i = 0; i < num_instructions; i++) {
144
struct asm_instruction *ai = &instructions[i];
145
afuc_instr instr = {0};
146
afuc_opc opc;
147
148
/* special case, 2nd dword is patched up w/ # of instructions
149
* (ie. offset of jmptbl)
150
*/
151
if (i == 1) {
152
assert(ai->is_literal);
153
ai->literal &= ~0xffff;
154
ai->literal |= num_instructions;
155
}
156
157
if (ai->is_literal) {
158
write(outfd, &ai->literal, 4);
159
continue;
160
}
161
162
switch (ai->tok) {
163
case T_OP_NOP:
164
opc = OPC_NOP;
165
if (gpuver >= 6)
166
instr.pad = 0x1000000;
167
break;
168
case T_OP_ADD:
169
case T_OP_ADDHI:
170
case T_OP_SUB:
171
case T_OP_SUBHI:
172
case T_OP_AND:
173
case T_OP_OR:
174
case T_OP_XOR:
175
case T_OP_NOT:
176
case T_OP_SHL:
177
case T_OP_USHR:
178
case T_OP_ISHR:
179
case T_OP_ROT:
180
case T_OP_MUL8:
181
case T_OP_MIN:
182
case T_OP_MAX:
183
case T_OP_CMP:
184
case T_OP_MSB:
185
if (ai->has_immed) {
186
/* MSB overlaps with STORE */
187
assert(ai->tok != T_OP_MSB);
188
if (ai->xmov) {
189
fprintf(stderr,
190
"ALU instruction cannot have immediate and xmov\n");
191
exit(1);
192
}
193
opc = tok2alu(ai->tok);
194
instr.alui.dst = ai->dst;
195
instr.alui.src = ai->src1;
196
instr.alui.uimm = ai->immed;
197
} else {
198
opc = OPC_ALU;
199
instr.alu.dst = ai->dst;
200
instr.alu.src1 = ai->src1;
201
instr.alu.src2 = ai->src2;
202
instr.alu.xmov = ai->xmov;
203
instr.alu.alu = tok2alu(ai->tok);
204
}
205
break;
206
case T_OP_MOV:
207
/* move can either be encoded as movi (ie. move w/ immed) or
208
* an alu instruction
209
*/
210
if ((ai->has_immed || ai->label) && ai->xmov) {
211
fprintf(stderr, "ALU instruction cannot have immediate and xmov\n");
212
exit(1);
213
}
214
if (ai->has_immed) {
215
opc = OPC_MOVI;
216
instr.movi.dst = ai->dst;
217
instr.movi.uimm = ai->immed;
218
instr.movi.shift = ai->shift;
219
} else if (ai->label) {
220
/* mov w/ a label is just an alias for an immediate, this
221
* is useful to load the address of a constant table into
222
* a register:
223
*/
224
opc = OPC_MOVI;
225
instr.movi.dst = ai->dst;
226
instr.movi.uimm = resolve_label(ai->label);
227
instr.movi.shift = ai->shift;
228
} else {
229
/* encode as: or $dst, $00, $src */
230
opc = OPC_ALU;
231
instr.alu.dst = ai->dst;
232
instr.alu.src1 = 0x00; /* $00 reads-back 0 */
233
instr.alu.src2 = ai->src1;
234
instr.alu.xmov = ai->xmov;
235
instr.alu.alu = OPC_OR;
236
}
237
break;
238
case T_OP_CWRITE:
239
case T_OP_CREAD:
240
case T_OP_STORE:
241
case T_OP_LOAD:
242
if (gpuver >= 6) {
243
if (ai->tok == T_OP_CWRITE) {
244
opc = OPC_CWRITE6;
245
} else if (ai->tok == T_OP_CREAD) {
246
opc = OPC_CREAD6;
247
} else if (ai->tok == T_OP_STORE) {
248
opc = OPC_STORE6;
249
} else if (ai->tok == T_OP_LOAD) {
250
opc = OPC_LOAD6;
251
}
252
} else {
253
if (ai->tok == T_OP_CWRITE) {
254
opc = OPC_CWRITE5;
255
} else if (ai->tok == T_OP_CREAD) {
256
opc = OPC_CREAD5;
257
} else if (ai->tok == T_OP_STORE || ai->tok == T_OP_LOAD) {
258
fprintf(stderr, "load and store do not exist on a5xx\n");
259
exit(1);
260
}
261
}
262
instr.control.src1 = ai->src1;
263
instr.control.src2 = ai->src2;
264
instr.control.flags = ai->bit;
265
instr.control.uimm = ai->immed;
266
break;
267
case T_OP_BRNE:
268
case T_OP_BREQ:
269
if (ai->has_immed) {
270
opc = (ai->tok == T_OP_BRNE) ? OPC_BRNEI : OPC_BREQI;
271
instr.br.bit_or_imm = ai->immed;
272
} else {
273
opc = (ai->tok == T_OP_BRNE) ? OPC_BRNEB : OPC_BREQB;
274
instr.br.bit_or_imm = ai->bit;
275
}
276
instr.br.src = ai->src1;
277
instr.br.ioff = resolve_label(ai->label) - i;
278
break;
279
case T_OP_RET:
280
opc = OPC_RET;
281
break;
282
case T_OP_IRET:
283
opc = OPC_RET;
284
instr.ret.interrupt = 1;
285
break;
286
case T_OP_CALL:
287
opc = OPC_CALL;
288
instr.call.uoff = resolve_label(ai->label);
289
break;
290
case T_OP_PREEMPTLEAVE:
291
opc = OPC_PREEMPTLEAVE6;
292
instr.call.uoff = resolve_label(ai->label);
293
break;
294
case T_OP_SETSECURE:
295
opc = OPC_SETSECURE;
296
if (resolve_label(ai->label) != i + 3) {
297
fprintf(stderr, "jump label %s is incorrect for setsecure\n",
298
ai->label);
299
exit(1);
300
}
301
if (ai->src1 != 0x2) {
302
fprintf(stderr, "source for setsecure must be $02\n");
303
exit(1);
304
}
305
break;
306
case T_OP_JUMP:
307
/* encode jump as: brne $00, b0, #label */
308
opc = OPC_BRNEB;
309
instr.br.bit_or_imm = 0;
310
instr.br.src = 0x00; /* $00 reads-back 0.. compare to 0 */
311
instr.br.ioff = resolve_label(ai->label) - i;
312
break;
313
case T_OP_WAITIN:
314
opc = OPC_WIN;
315
break;
316
default:
317
unreachable("");
318
}
319
320
afuc_set_opc(&instr, opc, ai->rep);
321
322
write(outfd, &instr, 4);
323
}
324
}
325
326
unsigned
327
parse_control_reg(const char *name)
328
{
329
/* skip leading "@" */
330
return afuc_control_reg(name + 1);
331
}
332
333
static void
334
emit_jumptable(int outfd)
335
{
336
uint32_t jmptable[0x80] = {0};
337
int i;
338
339
for (i = 0; i < num_labels; i++) {
340
struct asm_label *label = &labels[i];
341
int id = afuc_pm4_id(label->label);
342
343
/* if it doesn't match a known PM4 packet-id, try to match UNKN%d: */
344
if (id < 0) {
345
if (sscanf(label->label, "UNKN%d", &id) != 1) {
346
/* if still not found, must not belong in jump-table: */
347
continue;
348
}
349
}
350
351
jmptable[id] = label->offset;
352
}
353
354
write(outfd, jmptable, sizeof(jmptable));
355
}
356
357
static void
358
usage(void)
359
{
360
fprintf(stderr, "Usage:\n"
361
"\tasm [-g GPUVER] filename.asm filename.fw\n"
362
"\t\t-g - specify GPU version (5, etc)\n");
363
exit(2);
364
}
365
366
int
367
main(int argc, char **argv)
368
{
369
FILE *in;
370
char *file, *outfile;
371
int c, ret, outfd;
372
373
/* Argument parsing: */
374
while ((c = getopt(argc, argv, "g:")) != -1) {
375
switch (c) {
376
case 'g':
377
gpuver = atoi(optarg);
378
break;
379
default:
380
usage();
381
}
382
}
383
384
if (optind >= (argc + 1)) {
385
fprintf(stderr, "no file specified!\n");
386
usage();
387
}
388
389
file = argv[optind];
390
outfile = argv[optind + 1];
391
392
outfd = open(outfile, O_WRONLY | O_CREAT | O_TRUNC, 0644);
393
if (outfd < 0) {
394
fprintf(stderr, "could not open \"%s\"\n", outfile);
395
usage();
396
}
397
398
in = fopen(file, "r");
399
if (!in) {
400
fprintf(stderr, "could not open \"%s\"\n", file);
401
usage();
402
}
403
404
yyset_in(in);
405
406
/* if gpu version not specified, infer from filename: */
407
if (!gpuver) {
408
if (strstr(file, "a5")) {
409
gpuver = 5;
410
} else if (strstr(file, "a6")) {
411
gpuver = 6;
412
}
413
}
414
415
ret = afuc_util_init(gpuver, false);
416
if (ret < 0) {
417
usage();
418
}
419
420
ret = yyparse();
421
if (ret) {
422
fprintf(stderr, "parse failed: %d\n", ret);
423
return ret;
424
}
425
426
emit_instructions(outfd);
427
emit_jumptable(outfd);
428
429
close(outfd);
430
431
return 0;
432
}
433
434