A Modern GPGPU API & wip linux RDNA2+ Driver
rdna driver linux gpu
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

amdgpu: example 07 now works!

+45 -33
+38 -33
drivers/amdgpu/compiler/compiler.cpp
··· 27 27 struct Compiler { 28 28 gir::Module& mod; 29 29 RDNA2Assembler as; 30 + 31 + uint32_t sgpr_allocator = 6; 32 + uint32_t vgpr_allocator = 3; 30 33 }; 31 34 32 35 void lower_simple(Compiler &); ··· 53 56 log("shader written to {}", filename); 54 57 } 55 58 59 + std::string_view rdna2_backend_intrinsic_to_string(uint32_t intrinsic_id) { 60 + switch(intrinsic_id) { 61 + case (uint32_t)AmdIntrinsics::GlobalLoadDwordAddTID_Scale4: return "GlobalLoadDwordAddTID_Scale4"; 62 + case (uint32_t)AmdIntrinsics::GlobalStoreDwordAddTID_Scale4: return "GlobalStoreDwordAddTID_Scale4"; 63 + default: return "?"; 64 + } 65 + } 66 + 56 67 void rdna2_compile(gir::Module &mod, void *write_ptr, uint64_t base_addr) { 57 68 Compiler compiler(mod); 58 69 59 70 gir::pass_normalize(mod); 60 - 61 71 lower_simple(compiler); 62 72 lower_memory_loads(compiler); 73 + gir::pass_eliminate_dead_code(mod); 63 74 analyze_uniformity(compiler); 64 75 65 - gir::pass_eliminate_dead_code(mod); 66 - 67 - allocate_registers(compiler); 76 + auto txt = gir::dump_module(mod, rdna2_backend_intrinsic_to_string); 77 + log("\nIntermediate Representation:\n{}", txt); 68 78 69 79 codegen(compiler); 70 80 ··· 215 225 return 0; 216 226 } 217 227 218 - void allocate_registers(Compiler &cc) { 219 - uint32_t sgpr_start = 6; 220 - uint32_t vgpr_start = 3; 221 - // @todo: improve register allocation and also reuse non-live 222 - // registers. 223 - for (auto &inst : cc.mod.insts) { 224 - if (inst.meta.phys_reg != ~0u) continue; 228 + void allocate_sgpr(Compiler &c, gir::Inst &inst) { 229 + if (inst.meta.phys_reg == ~0u) { 230 + inst.meta.phys_reg = c.sgpr_allocator++; 231 + } 232 + } 225 233 226 - // @todo: additionally, some types require a kind of align/size difference 227 - // (flat_load_dword16 needs 4 align, 16 size). 228 - // find next one / seq of regs of this kind 229 - auto count = required_regs_for_type(inst.type); 230 - 231 - if (inst.meta.is_uniform) { 232 - inst.meta.phys_reg = sgpr_start; 233 - sgpr_start += count; 234 - } else { 235 - inst.meta.phys_reg = vgpr_start; 236 - vgpr_start += count; 237 - } 234 + void allocate_vgpr(Compiler &c, gir::Inst &inst) { 235 + if (inst.meta.phys_reg == ~0u) { 236 + inst.meta.phys_reg = c.sgpr_allocator++; 238 237 } 239 238 } 240 239 ··· 248 247 if (imm == -1) return RDNA2Assembler::vsrc::int_neg_1; 249 248 if (imm >= 1 && imm <= 64) return (RDNA2Assembler::vsrc)((uint)RDNA2Assembler::vsrc::int_pos_1 + imm - 1); 250 249 if (imm < 0 && imm >= -16) return (RDNA2Assembler::vsrc)((uint)RDNA2Assembler::vsrc::int_neg_1 - imm - 1); 250 + // @todo: this needs to be additionally added after the 251 251 return RDNA2Assembler::vsrc::literal_constant; 252 252 } 253 253 254 - auto reg = inst.meta.phys_reg; 255 254 if (inst.meta.is_uniform) { 256 - return (RDNA2Assembler::vsrc)((uint)RDNA2Assembler::vsrc::sgpr0 + reg); 255 + allocate_sgpr(c, inst); 256 + return (RDNA2Assembler::vsrc)((uint)RDNA2Assembler::vsrc::sgpr0 + inst.meta.phys_reg); 257 257 } else { 258 - return (RDNA2Assembler::vsrc)((uint)RDNA2Assembler::vsrc::vgpr0 + reg); 258 + allocate_vgpr(c, inst); 259 + return (RDNA2Assembler::vsrc)((uint)RDNA2Assembler::vsrc::vgpr0 + inst.meta.phys_reg); 259 260 } 260 261 } 261 262 ··· 272 273 } 273 274 274 275 assert(inst.meta.is_uniform, "Cannot use non-uniform value as ssrc"); 276 + 277 + allocate_sgpr(c, inst); 275 278 return (RDNA2Assembler::ssrc)((uint)RDNA2Assembler::ssrc::sgpr0 + inst.meta.phys_reg); 276 279 } 277 280 ··· 283 286 case (uint32_t)AmdIntrinsics::GlobalLoadDwordAddTID_Scale4: { 284 287 auto saddr = get_ssrc(cc, inst.operands[0]); 285 288 289 + allocate_vgpr(cc, inst); 290 + 286 291 // @todo: how do we know what to do about the cache flags? 287 292 cc.as.global( 288 293 RDNA2Assembler::global_opcode::global_load_dword_addtid, 289 294 false, false, false, false, 290 - 0, // 12-bit immediate offset (0 for now) 291 - 0, // vaddr (0 = use addtid mode) 292 - (uint8_t)saddr, // saddr base pointer 293 - inst.meta.phys_reg, // vdst destination register 294 - 0 // unused in addtid mode 295 + 0, 296 + inst.meta.phys_reg, 297 + (uint8_t)saddr, 298 + 0, 299 + 0 295 300 ); 296 301 297 302 // @todo: wait for load to complete. this is very conservative ··· 351 356 case gir::Op::Add: { 352 357 if (inst.type == gir::Type::I32) { 353 358 if (inst.meta.is_uniform) { 354 - // Scalar add: s_add_u32 359 + allocate_sgpr(cc, inst); 355 360 auto src0 = get_ssrc(cc, inst.operands[0]); 356 361 auto src1 = get_ssrc(cc, inst.operands[1]); 357 362 cc.as.sop2( ··· 361 366 src1 362 367 ); 363 368 } else { 364 - // Vector add: v_add_nc_u32 (non-carry version) 369 + allocate_vgpr(cc, inst); 365 370 // vsrc1 MUST be a VGPR, src0 can be anything (SGPR, VGPR, const) 366 371 auto& op0 = cc.mod.deref(inst.operands[0]); 367 372 auto& op1 = cc.mod.deref(inst.operands[1]);
+7
test/examples/07_hello_dispatch/hello_dispatch.cpp
··· 30 30 printf("x[2]: %u\n", ((uint32_t *)x.cpu)[2]); 31 31 printf("x[3]: %u\n", ((uint32_t *)x.cpu)[3]); 32 32 33 + sleep(1); 34 + 35 + printf("x[0]: %u\n", ((uint32_t *)x.cpu)[0]); 36 + printf("x[1]: %u\n", ((uint32_t *)x.cpu)[1]); 37 + printf("x[2]: %u\n", ((uint32_t *)x.cpu)[2]); 38 + printf("x[3]: %u\n", ((uint32_t *)x.cpu)[3]); 39 + 33 40 return 0; 34 41 }