Index: llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h =================================================================== --- llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h +++ llvm/include/llvm/CodeGen/GlobalISel/InstructionSelector.h @@ -374,6 +374,9 @@ public: virtual ~InstructionSelector() = default; + /// Try manual selection code common between different targets. + bool selectCommon(MachineInstr &I, CodeGenCoverage &CoverageInfo) const; + /// Select the (possibly generic) instruction \p I to only use target-specific /// opcodes. It is OK to insert multiple instructions, but they cannot be /// generic pre-isel instructions. Index: llvm/include/llvm/Support/TargetOpcodes.def =================================================================== --- llvm/include/llvm/Support/TargetOpcodes.def +++ llvm/include/llvm/Support/TargetOpcodes.def @@ -611,6 +611,9 @@ /// Generic dynamic stack allocation. HANDLE_TARGET_OPCODE(G_DYN_STACKALLOC) +HANDLE_TARGET_OPCODE(G_READ_REGISTER) +HANDLE_TARGET_OPCODE(G_WRITE_REGISTER) + // TODO: Add more generic opcodes as we move along. /// Marker for the end of the generic opcode. Index: llvm/include/llvm/Target/GenericOpcodes.td =================================================================== --- llvm/include/llvm/Target/GenericOpcodes.td +++ llvm/include/llvm/Target/GenericOpcodes.td @@ -1006,6 +1006,26 @@ let isTerminator = 1; } +def G_READ_REGISTER : GenericInstruction { + let OutOperandList = (outs type0:$dst); + let InOperandList = (ins unknown:$register); + let hasSideEffects = 1; + + // Assume convergent. It's probably not worth the effort of somehow + // modeling convergent and nonconvergent register accesses. + let isConvergent = 1; +} + +def G_WRITE_REGISTER : GenericInstruction { + let OutOperandList = (outs); + let InOperandList = (ins unknown:$register, type0:$value); + let hasSideEffects = 1; + + // Assume convergent. It's probably not worth the effort of somehow + // modeling convergent and nonconvergent register accesses. + let isConvergent = 1; +} + //------------------------------------------------------------------------------ // Vector ops //------------------------------------------------------------------------------ Index: llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp =================================================================== --- llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp +++ llvm/lib/CodeGen/GlobalISel/IRTranslator.cpp @@ -1524,6 +1524,13 @@ case Intrinsic::sideeffect: // Discard annotate attributes, assumptions, and artificial side-effects. return true; + case Intrinsic::read_register: { + Value *Arg = CI.getArgOperand(0); + MIRBuilder.buildInstr(TargetOpcode::G_READ_REGISTER) + .addDef(getOrCreateVReg(CI)) + .addMetadata(cast(cast(Arg)->getMetadata())); + return true; + } } return false; } Index: llvm/lib/CodeGen/GlobalISel/InstructionSelector.cpp =================================================================== --- llvm/lib/CodeGen/GlobalISel/InstructionSelector.cpp +++ llvm/lib/CodeGen/GlobalISel/InstructionSelector.cpp @@ -18,6 +18,8 @@ #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetLowering.h" #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/MC/MCInstrDesc.h" #include "llvm/Support/Debug.h" @@ -81,3 +83,32 @@ return !MI.mayLoadOrStore() && !MI.mayRaiseFPException() && !MI.hasUnmodeledSideEffects() && MI.implicit_operands().empty(); } + +bool InstructionSelector::selectCommon(MachineInstr &I, + CodeGenCoverage &CoverageInfo) const { + switch (I.getOpcode()) { + case TargetOpcode::G_READ_REGISTER: { + const MDString *RegStr = cast( + cast(I.getOperand(1).getMetadata())->getOperand(0)); + + const TargetSubtargetInfo &STI = MF->getSubtarget(); + const TargetInstrInfo *TII = STI.getInstrInfo(); + MachineRegisterInfo &MRI = MF->getRegInfo(); + Register Dst = I.getOperand(0).getReg(); + LLT Ty = MRI.getType(Dst); + + const TargetLowering *TLI = STI.getTargetLowering(); + Register Reg = TLI->getRegisterByName(RegStr->getString().data(), Ty, *MF); + if (!Reg.isValid()) + return false; + + MachineBasicBlock *MBB = I.getParent(); + BuildMI(*MBB, I, I.getDebugLoc(), TII->get(TargetOpcode::COPY), Dst) + .addReg(Reg); + I.eraseFromParent(); + return true; + } + default: + return false; + } +} Index: llvm/lib/Target/AMDGPU/AMDGPURegisterBankInfo.cpp =================================================================== --- llvm/lib/Target/AMDGPU/AMDGPURegisterBankInfo.cpp +++ llvm/lib/Target/AMDGPU/AMDGPURegisterBankInfo.cpp @@ -2575,6 +2575,21 @@ } break; } + case AMDGPU::G_READ_REGISTER: { + const SITargetLowering *TLI = Subtarget.getTargetLowering(); + const MDString *RegStr = cast( + cast(MI.getOperand(1).getMetadata())->getOperand(0)); + LLT Ty = MRI.getType(MI.getOperand(0).getReg()); + + Register Reg = TLI->getRegisterByName(RegStr->getString().data(), Ty, MF); + if (!Reg.isValid()) + return getInvalidInstructionMapping(); + + const TargetRegisterClass &RC = getMinimalPhysRegClass(Reg, *TRI); + const RegisterBank &RB = getRegBankFromRegClass(RC); + OpdsMapping[0] = AMDGPU::getValueMapping(RB.getID(), Ty.getSizeInBits()); + break; + } case AMDGPU::G_INTRINSIC: { switch (MI.getIntrinsicID()) { default: Index: llvm/test/CodeGen/AMDGPU/GlobalISel/read_register.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/AMDGPU/GlobalISel/read_register.ll @@ -0,0 +1,2 @@ +; Runs original SDAG test with -global-isel +; RUN: llc -global-isel -mtriple=amdgcn-amd-amdhsa -mcpu=bonaire -verify-machineinstrs < %S/../read_register.ll | FileCheck -enable-var-scope %S/../read_register.ll Index: llvm/test/CodeGen/AMDGPU/GlobalISel/regbankselect-read-register.mir =================================================================== --- /dev/null +++ llvm/test/CodeGen/AMDGPU/GlobalISel/regbankselect-read-register.mir @@ -0,0 +1,78 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -run-pass=regbankselect -regbankselect-fast -o - %s | FileCheck %s +# RUN: llc -mtriple=amdgcn-amd-amdhsa -mcpu=gfx900 -run-pass=regbankselect -regbankselect-greedy -o - %s | FileCheck %s + +--- | + define amdgpu_kernel void @test_read_m0() { + %m0 = call i32 @llvm.read_register.i32(metadata !1) + ret void + } + + define amdgpu_kernel void @test_read_exec() { + %m0 = call i64 @llvm.read_register.i64(metadata !2) + ret void + } + + define amdgpu_kernel void @test_read_flat_scratch() { + %m0 = call i64 @llvm.read_register.i64(metadata !3) + ret void + } + + declare i32 @llvm.read_register.i32(metadata) + declare i64 @llvm.read_register.i64(metadata) + + !0 = !{} + !1 = !{!"m0"} + !2 = !{!"exec"} + !3 = !{!"flat_scratch"} + !4 = !{!"flat_scratch_lo"} + !5 = !{!"flat_scratch_hi"} + !6 = !{!"exec_lo"} + !7 = !{!"exec_hi"} + +... + +--- +name: test_read_m0 +legalized: true +tracksRegLiveness: true +body: | + bb.0: + + ; CHECK-LABEL: name: test_read_m0 + ; CHECK: [[READ_REGISTER:%[0-9]+]]:sgpr(s32) = G_READ_REGISTER !0 + ; CHECK: S_ENDPGM 0, implicit [[READ_REGISTER]](s32) + %0:_(s32) = G_READ_REGISTER !1 + S_ENDPGM 0, implicit %0 + +... + +--- +name: test_read_exec +legalized: true +tracksRegLiveness: true +body: | + bb.0: + + ; CHECK-LABEL: name: test_read_exec + ; CHECK: [[READ_REGISTER:%[0-9]+]]:sgpr(s64) = G_READ_REGISTER !1 + ; CHECK: S_ENDPGM 0, implicit [[READ_REGISTER]](s64) + %0:_(s64) = G_READ_REGISTER !2 + S_ENDPGM 0, implicit %0 + +... + +--- +name: test_read_flat_scratch +legalized: true +tracksRegLiveness: true +body: | + bb.0: + + ; CHECK-LABEL: name: test_read_flat_scratch + ; CHECK: [[READ_REGISTER:%[0-9]+]]:sgpr(s64) = G_READ_REGISTER !2 + ; CHECK: S_ENDPGM 0, implicit [[READ_REGISTER]](s64) + %0:_(s64) = G_READ_REGISTER !3 + S_ENDPGM 0, implicit %0 + +... Index: llvm/utils/TableGen/GlobalISelEmitter.cpp =================================================================== --- llvm/utils/TableGen/GlobalISelEmitter.cpp +++ llvm/utils/TableGen/GlobalISelEmitter.cpp @@ -5294,7 +5294,7 @@ << ", CoverageInfo)) {\n" << " return true;\n" << " }\n\n" - << " return false;\n" + << " return selectCommon(I, CoverageInfo);\n" << "}\n\n"; const MatchTable Table =