[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:
Aditya Nandakumar 2017-08-23 20:45:48 +00:00
parent 5417eb62a0
commit 5f06407357
13 changed files with 203 additions and 11 deletions

View File

@ -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:

View File

@ -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);

View File

@ -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)

View File

@ -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());

View File

@ -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() ||

View File

@ -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);
}

View File

@ -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.

View File

@ -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())

View File

@ -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;

View File

@ -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;

View File

@ -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

View 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
...

View 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
...