1 #include <array>
2 #include <iomanip>
3 #include "aco_ir.h"
4 #include "llvm-c/Disassembler.h"
5 #include "ac_llvm_util.h"
6
7 #include <llvm/ADT/StringRef.h>
8 #if LLVM_VERSION_MAJOR >= 11
9 #include <llvm/MC/MCDisassembler/MCDisassembler.h>
10 #endif
11
12 namespace aco {
13 namespace {
14
15 /* LLVM disassembler only supports GFX8+, try to disassemble with CLRXdisasm
16 * for GFX6-GFX7 if found on the system, this is better than nothing.
17 */
print_asm_gfx6_gfx7(Program * program,std::vector<uint32_t> & binary,FILE * output)18 bool print_asm_gfx6_gfx7(Program *program, std::vector<uint32_t>& binary,
19 FILE *output)
20 {
21 char path[] = "/tmp/fileXXXXXX";
22 char line[2048], command[128];
23 const char *gpu_type;
24 FILE *p;
25 int fd;
26
27 /* Dump the binary into a temporary file. */
28 fd = mkstemp(path);
29 if (fd < 0)
30 return true;
31
32 for (uint32_t w : binary)
33 {
34 if (write(fd, &w, sizeof(w)) == -1)
35 goto fail;
36 }
37
38 /* Determine the GPU type for CLRXdisasm. Use the family for GFX6 chips
39 * because it doesn't allow to use gfx600 directly.
40 */
41 switch (program->chip_class) {
42 case GFX6:
43 switch (program->family) {
44 case CHIP_TAHITI:
45 gpu_type = "tahiti";
46 break;
47 case CHIP_PITCAIRN:
48 gpu_type = "pitcairn";
49 break;
50 case CHIP_VERDE:
51 gpu_type = "capeverde";
52 break;
53 case CHIP_OLAND:
54 gpu_type = "oland";
55 break;
56 case CHIP_HAINAN:
57 gpu_type = "hainan";
58 break;
59 default:
60 unreachable("Invalid GFX6 family!");
61 }
62 break;
63 case GFX7:
64 gpu_type = "gfx700";
65 break;
66 default:
67 unreachable("Invalid chip class!");
68 }
69
70 sprintf(command, "clrxdisasm --gpuType=%s -r %s", gpu_type, path);
71
72 p = popen(command, "r");
73 if (p) {
74 if (!fgets(line, sizeof(line), p)) {
75 fprintf(output, "clrxdisasm not found\n");
76 pclose(p);
77 goto fail;
78 }
79
80 do {
81 fputs(line, output);
82 } while (fgets(line, sizeof(line), p));
83
84 pclose(p);
85 }
86
87 return false;
88
89 fail:
90 close(fd);
91 unlink(path);
92 return true;
93 }
94
disasm_instr(chip_class chip,LLVMDisasmContextRef disasm,uint32_t * binary,unsigned exec_size,size_t pos,char * outline,unsigned outline_size)95 std::pair<bool, size_t> disasm_instr(chip_class chip, LLVMDisasmContextRef disasm,
96 uint32_t *binary, unsigned exec_size, size_t pos,
97 char *outline, unsigned outline_size)
98 {
99 /* mask out src2 on v_writelane_b32 */
100 if (((chip == GFX8 || chip == GFX9) && (binary[pos] & 0xffff8000) == 0xd28a0000) ||
101 (chip >= GFX10 && (binary[pos] & 0xffff8000) == 0xd7610000)) {
102 binary[pos+1] = binary[pos+1] & 0xF803FFFF;
103 }
104
105 size_t l = LLVMDisasmInstruction(disasm, (uint8_t *) &binary[pos],
106 (exec_size - pos) * sizeof(uint32_t), pos * 4,
107 outline, outline_size);
108
109 if (chip >= GFX10 && l == 8 &&
110 ((binary[pos] & 0xffff0000) == 0xd7610000) &&
111 ((binary[pos + 1] & 0x1ff) == 0xff)) {
112 /* v_writelane with literal uses 3 dwords but llvm consumes only 2 */
113 l += 4;
114 }
115
116 bool invalid = false;
117 size_t size;
118 if (!l &&
119 ((chip >= GFX9 && (binary[pos] & 0xffff8000) == 0xd1348000) || /* v_add_u32_e64 + clamp */
120 (chip >= GFX10 && (binary[pos] & 0xffff8000) == 0xd7038000) || /* v_add_u16_e64 + clamp */
121 (chip <= GFX9 && (binary[pos] & 0xffff8000) == 0xd1268000) || /* v_add_u16_e64 + clamp */
122 (chip >= GFX10 && (binary[pos] & 0xffff8000) == 0xd76d8000) || /* v_add3_u32 + clamp */
123 (chip == GFX9 && (binary[pos] & 0xffff8000) == 0xd1ff8000)) /* v_add3_u32 + clamp */) {
124 strcpy(outline, "\tinteger addition + clamp");
125 bool has_literal = chip >= GFX10 &&
126 (((binary[pos+1] & 0x1ff) == 0xff) || (((binary[pos+1] >> 9) & 0x1ff) == 0xff));
127 size = 2 + has_literal;
128 } else if (chip >= GFX10 && l == 4 && ((binary[pos] & 0xfe0001ff) == 0x020000f9)) {
129 strcpy(outline, "\tv_cndmask_b32 + sdwa");
130 size = 2;
131 } else if (!l) {
132 strcpy(outline, "(invalid instruction)");
133 size = 1;
134 invalid = true;
135 } else {
136 assert(l % 4 == 0);
137 size = l / 4;
138 }
139
140 return std::make_pair(invalid, size);
141 }
142 } /* end namespace */
143
print_asm(Program * program,std::vector<uint32_t> & binary,unsigned exec_size,FILE * output)144 bool print_asm(Program *program, std::vector<uint32_t>& binary,
145 unsigned exec_size, FILE *output)
146 {
147 if (program->chip_class <= GFX7) {
148 /* Do not abort if clrxdisasm isn't found. */
149 print_asm_gfx6_gfx7(program, binary, output);
150 return false;
151 }
152
153 std::vector<bool> referenced_blocks(program->blocks.size());
154 referenced_blocks[0] = true;
155 for (Block& block : program->blocks) {
156 for (unsigned succ : block.linear_succs)
157 referenced_blocks[succ] = true;
158 }
159
160 #if LLVM_VERSION_MAJOR >= 11
161 std::vector<llvm::SymbolInfoTy> symbols;
162 #else
163 std::vector<std::tuple<uint64_t, llvm::StringRef, uint8_t>> symbols;
164 #endif
165 std::vector<std::array<char,16>> block_names;
166 block_names.reserve(program->blocks.size());
167 for (Block& block : program->blocks) {
168 if (!referenced_blocks[block.index])
169 continue;
170 std::array<char, 16> name;
171 sprintf(name.data(), "BB%u", block.index);
172 block_names.push_back(name);
173 symbols.emplace_back(block.offset * 4, llvm::StringRef(block_names[block_names.size() - 1].data()), 0);
174 }
175
176 const char *features = "";
177 if (program->chip_class >= GFX10 && program->wave_size == 64) {
178 features = "+wavefrontsize64";
179 }
180
181 LLVMDisasmContextRef disasm = LLVMCreateDisasmCPUFeatures("amdgcn-mesa-mesa3d",
182 ac_get_llvm_processor_name(program->family),
183 features,
184 &symbols, 0, NULL, NULL);
185
186 size_t pos = 0;
187 bool invalid = false;
188 unsigned next_block = 0;
189
190 unsigned prev_size = 0;
191 unsigned prev_pos = 0;
192 unsigned repeat_count = 0;
193 while (pos < exec_size) {
194 bool new_block = next_block < program->blocks.size() && pos == program->blocks[next_block].offset;
195 if (pos + prev_size <= exec_size && prev_pos != pos && !new_block &&
196 memcmp(&binary[prev_pos], &binary[pos], prev_size * 4) == 0) {
197 repeat_count++;
198 pos += prev_size;
199 continue;
200 } else {
201 if (repeat_count)
202 fprintf(output, "\t(then repeated %u times)\n", repeat_count);
203 repeat_count = 0;
204 }
205
206 while (next_block < program->blocks.size() && pos == program->blocks[next_block].offset) {
207 if (referenced_blocks[next_block])
208 fprintf(output, "BB%u:\n", next_block);
209 next_block++;
210 }
211
212 char outline[1024];
213 std::pair<bool, size_t> res = disasm_instr(
214 program->chip_class, disasm, binary.data(), exec_size, pos, outline, sizeof(outline));
215 invalid |= res.first;
216
217 fprintf(output, "%-60s ;", outline);
218
219 for (unsigned i = 0; i < res.second; i++)
220 fprintf(output, " %.8x", binary[pos + i]);
221 fputc('\n', output);
222
223 prev_size = res.second;
224 prev_pos = pos;
225 pos += res.second;
226 }
227 assert(next_block == program->blocks.size());
228
229 LLVMDisasmDispose(disasm);
230
231 if (program->constant_data.size()) {
232 fputs("\n/* constant data */\n", output);
233 for (unsigned i = 0; i < program->constant_data.size(); i += 32) {
234 fprintf(output, "[%.6u]", i);
235 unsigned line_size = std::min<size_t>(program->constant_data.size() - i, 32);
236 for (unsigned j = 0; j < line_size; j += 4) {
237 unsigned size = std::min<size_t>(program->constant_data.size() - (i + j), 4);
238 uint32_t v = 0;
239 memcpy(&v, &program->constant_data[i + j], size);
240 fprintf(output, " %.8x", v);
241 }
242 fputc('\n', output);
243 }
244 }
245
246 return invalid;
247 }
248
249 }
250