mirror of
https://github.com/RPCS3/llvm.git
synced 2025-01-19 16:35:10 +00:00
[GISEl]: Translate phi into G_PHI
G_PHI has the same semantics as PHI but also has types. This lets us verify that the types in the G_PHI are consistent. This also allows specifying legalization actions for G_PHIs. https://reviews.llvm.org/D36990 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@311596 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
parent
5417eb62a0
commit
5f06407357
@ -792,7 +792,10 @@ public:
|
||||
&& getOperand(1).isImm();
|
||||
}
|
||||
|
||||
bool isPHI() const { return getOpcode() == TargetOpcode::PHI; }
|
||||
bool isPHI() const {
|
||||
return getOpcode() == TargetOpcode::PHI ||
|
||||
getOpcode() == TargetOpcode::G_PHI;
|
||||
}
|
||||
bool isKill() const { return getOpcode() == TargetOpcode::KILL; }
|
||||
bool isImplicitDef() const { return getOpcode()==TargetOpcode::IMPLICIT_DEF; }
|
||||
bool isInlineAsm() const { return getOpcode() == TargetOpcode::INLINEASM; }
|
||||
@ -869,6 +872,7 @@ public:
|
||||
return isMetaInstruction();
|
||||
// Copy-like instructions are usually eliminated during register allocation.
|
||||
case TargetOpcode::PHI:
|
||||
case TargetOpcode::G_PHI:
|
||||
case TargetOpcode::COPY:
|
||||
case TargetOpcode::INSERT_SUBREG:
|
||||
case TargetOpcode::SUBREG_TO_REG:
|
||||
|
@ -55,6 +55,12 @@ def G_IMPLICIT_DEF : Instruction {
|
||||
let hasSideEffects = 0;
|
||||
}
|
||||
|
||||
def G_PHI : Instruction {
|
||||
let OutOperandList = (outs type0:$dst);
|
||||
let InOperandList = (ins variable_ops);
|
||||
let hasSideEffects = 0;
|
||||
}
|
||||
|
||||
def G_FRAME_INDEX : Instruction {
|
||||
let OutOperandList = (outs type0:$dst);
|
||||
let InOperandList = (ins unknown:$src2);
|
||||
|
@ -224,6 +224,9 @@ HANDLE_TARGET_OPCODE(G_XOR)
|
||||
|
||||
HANDLE_TARGET_OPCODE(G_IMPLICIT_DEF)
|
||||
|
||||
/// Generic PHI instruction with types.
|
||||
HANDLE_TARGET_OPCODE(G_PHI)
|
||||
|
||||
/// Generic instruction to materialize the address of an alloca or other
|
||||
/// stack-based object.
|
||||
HANDLE_TARGET_OPCODE(G_FRAME_INDEX)
|
||||
|
@ -1098,7 +1098,7 @@ bool IRTranslator::translateShuffleVector(const User &U,
|
||||
|
||||
bool IRTranslator::translatePHI(const User &U, MachineIRBuilder &MIRBuilder) {
|
||||
const PHINode &PI = cast<PHINode>(U);
|
||||
auto MIB = MIRBuilder.buildInstr(TargetOpcode::PHI);
|
||||
auto MIB = MIRBuilder.buildInstr(TargetOpcode::G_PHI);
|
||||
MIB.addDef(getOrCreateVReg(PI));
|
||||
|
||||
PendingPHIs.emplace_back(&PI, MIB.getInstr());
|
||||
|
@ -926,6 +926,23 @@ void MachineVerifier::visitMachineInstrBefore(const MachineInstr *MI) {
|
||||
report("Generic instruction accessing memory must have one mem operand",
|
||||
MI);
|
||||
break;
|
||||
case TargetOpcode::G_PHI: {
|
||||
LLT DstTy = MRI->getType(MI->getOperand(0).getReg());
|
||||
if (!DstTy.isValid() ||
|
||||
!std::all_of(MI->operands_begin() + 1, MI->operands_end(),
|
||||
[this, &DstTy](const MachineOperand &MO) {
|
||||
if (!MO.isReg())
|
||||
return true;
|
||||
LLT Ty = MRI->getType(MO.getReg());
|
||||
if (!Ty.isValid() || (Ty != DstTy))
|
||||
return false;
|
||||
return true;
|
||||
}))
|
||||
report("Generic Instruction G_PHI has operands with incompatible/missing "
|
||||
"types",
|
||||
MI);
|
||||
break;
|
||||
}
|
||||
case TargetOpcode::STATEPOINT:
|
||||
if (!MI->getOperand(StatepointOpers::IDPos).isImm() ||
|
||||
!MI->getOperand(StatepointOpers::NBytesPos).isImm() ||
|
||||
|
@ -590,13 +590,14 @@ bool AArch64InstructionSelector::select(MachineInstr &I) const {
|
||||
MachineRegisterInfo &MRI = MF.getRegInfo();
|
||||
|
||||
unsigned Opcode = I.getOpcode();
|
||||
if (!isPreISelGenericOpcode(I.getOpcode())) {
|
||||
// G_PHI requires same handling as PHI
|
||||
if (!isPreISelGenericOpcode(Opcode) || Opcode == TargetOpcode::G_PHI) {
|
||||
// Certain non-generic instructions also need some special handling.
|
||||
|
||||
if (Opcode == TargetOpcode::LOAD_STACK_GUARD)
|
||||
return constrainSelectedInstRegOperands(I, TII, TRI, RBI);
|
||||
|
||||
if (Opcode == TargetOpcode::PHI) {
|
||||
if (Opcode == TargetOpcode::PHI || Opcode == TargetOpcode::G_PHI) {
|
||||
const unsigned DefReg = I.getOperand(0).getReg();
|
||||
const LLT DefTy = MRI.getType(DefReg);
|
||||
|
||||
@ -621,6 +622,7 @@ bool AArch64InstructionSelector::select(MachineInstr &I) const {
|
||||
}
|
||||
}
|
||||
}
|
||||
I.setDesc(TII.get(TargetOpcode::PHI));
|
||||
|
||||
return RBI.constrainGenericRegister(DefReg, *DefRC, MRI);
|
||||
}
|
||||
|
@ -38,6 +38,9 @@ AArch64LegalizerInfo::AArch64LegalizerInfo() {
|
||||
for (auto Ty : {p0, s1, s8, s16, s32, s64})
|
||||
setAction({G_IMPLICIT_DEF, Ty}, Legal);
|
||||
|
||||
for (auto Ty : {s16, s32, s64})
|
||||
setAction({G_PHI, Ty}, Legal);
|
||||
|
||||
for (unsigned BinOp : {G_ADD, G_SUB, G_MUL, G_AND, G_OR, G_XOR, G_SHL}) {
|
||||
// These operations naturally get the right answer when used on
|
||||
// GPR32, even if the actual type is narrower.
|
||||
|
@ -420,7 +420,8 @@ AArch64RegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
|
||||
|
||||
// Try the default logic for non-generic instructions that are either copies
|
||||
// or already have some operands assigned to banks.
|
||||
if (!isPreISelGenericOpcode(Opc)) {
|
||||
if (!isPreISelGenericOpcode(Opc) ||
|
||||
Opc == TargetOpcode::G_PHI) {
|
||||
const RegisterBankInfo::InstructionMapping &Mapping =
|
||||
getInstrMappingImpl(MI);
|
||||
if (Mapping.isValid())
|
||||
|
@ -198,7 +198,7 @@ ARMRegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
|
||||
|
||||
// Try the default logic for non-generic instructions that are either copies
|
||||
// or already have some operands assigned to banks.
|
||||
if (!isPreISelGenericOpcode(Opc)) {
|
||||
if (!isPreISelGenericOpcode(Opc) || Opc == TargetOpcode::G_PHI) {
|
||||
const InstructionMapping &Mapping = getInstrMappingImpl(MI);
|
||||
if (Mapping.isValid())
|
||||
return Mapping;
|
||||
|
@ -160,7 +160,7 @@ X86RegisterBankInfo::getInstrMapping(const MachineInstr &MI) const {
|
||||
|
||||
// Try the default logic for non-generic instructions that are either copies
|
||||
// or already have some operands assigned to banks.
|
||||
if (!isPreISelGenericOpcode(Opc)) {
|
||||
if (!isPreISelGenericOpcode(Opc) || Opc == TargetOpcode::G_PHI) {
|
||||
const InstructionMapping &Mapping = getInstrMappingImpl(MI);
|
||||
if (Mapping.isValid())
|
||||
return Mapping;
|
||||
|
@ -170,7 +170,7 @@ false:
|
||||
; CHECK: %[[regretc200:[0-9]+]](s32) = G_ADD %0, %[[reg2]]
|
||||
;
|
||||
; CHECK: [[BB_RET]]:
|
||||
; CHECK-NEXT: %[[regret:[0-9]+]](s32) = PHI %[[regretdefault]](s32), %[[BB_DEFAULT]], %[[regretc100]](s32), %[[BB_CASE100]]
|
||||
; CHECK-NEXT: %[[regret:[0-9]+]](s32) = G_PHI %[[regretdefault]](s32), %[[BB_DEFAULT]], %[[regretc100]](s32), %[[BB_CASE100]]
|
||||
; CHECK: %w0 = COPY %[[regret]](s32)
|
||||
; CHECK: RET_ReallyLR implicit %w0
|
||||
;
|
||||
@ -211,7 +211,7 @@ return:
|
||||
; CHECK: G_BR %[[PHI_BLOCK]]
|
||||
;
|
||||
; CHECK: [[PHI_BLOCK]]:
|
||||
; CHECK-NEXT: PHI %{{.*}}(s32), %[[NOTCASE57_BLOCK:bb.[0-9]+.entry]], %{{.*}}(s32),
|
||||
; CHECK-NEXT: G_PHI %{{.*}}(s32), %[[NOTCASE57_BLOCK:bb.[0-9]+.entry]], %{{.*}}(s32),
|
||||
;
|
||||
define i32 @test_cfg_remap(i32 %in) {
|
||||
entry:
|
||||
@ -230,7 +230,7 @@ phi.block:
|
||||
}
|
||||
|
||||
; CHECK-LABEL: name: test_cfg_remap_multiple_preds
|
||||
; CHECK: PHI [[ENTRY:%.*]](s32), %bb.{{[0-9]+}}.entry, [[ENTRY]](s32), %bb.{{[0-9]+}}.entry
|
||||
; CHECK: G_PHI [[ENTRY:%.*]](s32), %bb.{{[0-9]+}}.entry, [[ENTRY]](s32), %bb.{{[0-9]+}}.entry
|
||||
define i32 @test_cfg_remap_multiple_preds(i32 %in) {
|
||||
entry:
|
||||
switch i32 %in, label %odd [i32 1, label %next
|
||||
@ -521,7 +521,7 @@ define void @intrinsics(i32 %cur, i32 %bits) {
|
||||
; CHECK: [[FALSE]]:
|
||||
; CHECK: [[RES2:%[0-9]+]](s32) = G_LOAD
|
||||
|
||||
; CHECK: [[RES:%[0-9]+]](s32) = PHI [[RES1]](s32), %[[TRUE]], [[RES2]](s32), %[[FALSE]]
|
||||
; CHECK: [[RES:%[0-9]+]](s32) = G_PHI [[RES1]](s32), %[[TRUE]], [[RES2]](s32), %[[FALSE]]
|
||||
; CHECK: %w0 = COPY [[RES]]
|
||||
define i32 @test_phi(i32* %addr1, i32* %addr2, i1 %tst) {
|
||||
br i1 %tst, label %true, label %false
|
||||
|
78
test/CodeGen/AArch64/GlobalISel/select-phi.mir
Normal file
78
test/CodeGen/AArch64/GlobalISel/select-phi.mir
Normal file
@ -0,0 +1,78 @@
|
||||
# RUN: llc -mtriple=aarch64-unknown-unknown -o - -global-isel -verify-machineinstrs -run-pass=instruction-select %s | FileCheck %s
|
||||
--- |
|
||||
; ModuleID = '/tmp/test.ll'
|
||||
source_filename = "/tmp/test.ll"
|
||||
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
|
||||
target triple = "aarch64-unknown-unknown"
|
||||
|
||||
define i32 @test_phi(i32 %argc) {
|
||||
entry:
|
||||
%cmp = icmp ugt i32 %argc, 0
|
||||
br i1 %cmp, label %case1, label %case2
|
||||
|
||||
case1: ; preds = %entry
|
||||
%tmp1 = add i32 %argc, 1
|
||||
br label %return
|
||||
|
||||
case2: ; preds = %entry
|
||||
%tmp2 = add i32 %argc, 2
|
||||
br label %return
|
||||
|
||||
return: ; preds = %case2, %case1
|
||||
%res = phi i32 [ %tmp1, %case1 ], [ %tmp2, %case2 ]
|
||||
ret i32 %res
|
||||
}
|
||||
|
||||
...
|
||||
---
|
||||
name: test_phi
|
||||
alignment: 2
|
||||
exposesReturnsTwice: false
|
||||
legalized: true
|
||||
regBankSelected: true
|
||||
selected: false
|
||||
tracksRegLiveness: true
|
||||
registers:
|
||||
- { id: 0, class: gpr, preferred-register: '' }
|
||||
- { id: 1, class: gpr, preferred-register: '' }
|
||||
- { id: 2, class: gpr, preferred-register: '' }
|
||||
- { id: 3, class: gpr, preferred-register: '' }
|
||||
- { id: 4, class: gpr, preferred-register: '' }
|
||||
- { id: 5, class: gpr, preferred-register: '' }
|
||||
- { id: 6, class: gpr, preferred-register: '' }
|
||||
- { id: 7, class: gpr, preferred-register: '' }
|
||||
- { id: 8, class: gpr, preferred-register: '' }
|
||||
liveins:
|
||||
body: |
|
||||
bb.1.entry:
|
||||
successors: %bb.2.case1(0x40000000), %bb.3.case2(0x40000000)
|
||||
liveins: %w0
|
||||
; CHECK-LABEL: name: test_phi
|
||||
; CHECK: [[RES:%.*]] = PHI
|
||||
|
||||
%0(s32) = COPY %w0
|
||||
%1(s32) = G_CONSTANT i32 0
|
||||
%3(s32) = G_CONSTANT i32 1
|
||||
%5(s32) = G_CONSTANT i32 2
|
||||
%8(s32) = G_ICMP intpred(ugt), %0(s32), %1
|
||||
%2(s1) = G_TRUNC %8(s32)
|
||||
G_BRCOND %2(s1), %bb.2.case1
|
||||
G_BR %bb.3.case2
|
||||
|
||||
bb.2.case1:
|
||||
successors: %bb.4.return(0x80000000)
|
||||
|
||||
%4(s32) = G_ADD %0, %3
|
||||
G_BR %bb.4.return
|
||||
|
||||
bb.3.case2:
|
||||
successors: %bb.4.return(0x80000000)
|
||||
|
||||
%6(s32) = G_ADD %0, %5
|
||||
|
||||
bb.4.return:
|
||||
%7(s32) = G_PHI %4(s32), %bb.2.case1, %6(s32), %bb.3.case2
|
||||
%w0 = COPY %7(s32)
|
||||
RET_ReallyLR implicit %w0
|
||||
|
||||
...
|
78
test/Verifier/test_g_phi.mir
Normal file
78
test/Verifier/test_g_phi.mir
Normal file
@ -0,0 +1,78 @@
|
||||
#RUN: not llc -mtriple=aarch64-unknown-unknown -o - -global-isel -run-pass=legalizer -verify-machineinstrs %s 2>&1 | FileCheck %s
|
||||
--- |
|
||||
; ModuleID = 'test.ll'
|
||||
source_filename = "test.ll"
|
||||
target datalayout = "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128"
|
||||
target triple = "aarch64-unknown-unknown"
|
||||
|
||||
define i32 @test_phi(i32 %argc) {
|
||||
entry:
|
||||
%cmp = icmp ugt i32 %argc, 0
|
||||
br i1 %cmp, label %case1, label %case2
|
||||
|
||||
case1: ; preds = %entry
|
||||
%tmp11 = add i32 %argc, 1
|
||||
br label %return
|
||||
|
||||
case2: ; preds = %entry
|
||||
%tmp22 = add i32 %argc, 2
|
||||
br label %return
|
||||
|
||||
return: ; preds = %case2, %case1
|
||||
%res = phi i32 [ %tmp11, %case1 ], [ %tmp22, %case2 ]
|
||||
ret i32 %res
|
||||
}
|
||||
|
||||
...
|
||||
---
|
||||
name: test_phi
|
||||
legalized: true
|
||||
regBankSelected: false
|
||||
selected: false
|
||||
tracksRegLiveness: true
|
||||
registers:
|
||||
- { id: 0, class: _, preferred-register: '' }
|
||||
- { id: 1, class: _, preferred-register: '' }
|
||||
- { id: 2, class: _, preferred-register: '' }
|
||||
- { id: 3, class: _, preferred-register: '' }
|
||||
- { id: 4, class: _, preferred-register: '' }
|
||||
- { id: 5, class: _, preferred-register: '' }
|
||||
- { id: 6, class: _, preferred-register: '' }
|
||||
- { id: 7, class: _, preferred-register: '' }
|
||||
- { id: 8, class: _, preferred-register: '' }
|
||||
- { id: 9, class: _, preferred-register: '' }
|
||||
liveins:
|
||||
body: |
|
||||
bb.1.entry:
|
||||
successors: %bb.2.case1(0x40000000), %bb.3.case2(0x40000000)
|
||||
liveins: %w0
|
||||
; This test makes sure that the Verifier catches G_PHI with mismatching types.
|
||||
; CHECK: Bad machine code: Generic Instruction G_PHI has operands with incompatible/missing types
|
||||
|
||||
%0(s32) = COPY %w0
|
||||
%1(s32) = G_CONSTANT i32 0
|
||||
%3(s32) = G_CONSTANT i32 1
|
||||
%5(s32) = G_CONSTANT i32 2
|
||||
%8(s32) = G_ICMP intpred(ugt), %0(s32), %1
|
||||
%2(s1) = G_TRUNC %8(s32)
|
||||
G_BRCOND %2(s1), %bb.2.case1
|
||||
G_BR %bb.3.case2
|
||||
|
||||
bb.2.case1:
|
||||
successors: %bb.4.return(0x80000000)
|
||||
|
||||
%4(s32) = G_ADD %0, %3
|
||||
%9(s16) = G_TRUNC %4(s32)
|
||||
G_BR %bb.4.return
|
||||
|
||||
bb.3.case2:
|
||||
successors: %bb.4.return(0x80000000)
|
||||
|
||||
%6(s32) = G_ADD %0, %5
|
||||
|
||||
bb.4.return:
|
||||
%7(s32) = G_PHI %9(s16), %bb.2.case1, %6(s32), %bb.3.case2
|
||||
%w0 = COPY %7(s32)
|
||||
RET_ReallyLR implicit %w0
|
||||
|
||||
...
|
Loading…
x
Reference in New Issue
Block a user