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