&& 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; }
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:
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);
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)
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());
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() ||
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);
}
}
}
+ I.setDesc(TII.get(TargetOpcode::PHI));
return RBI.constrainGenericRegister(DefReg, *DefRC, MRI);
}
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.
// 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())
// 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;
// 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;
; 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
;
; 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:
}
; 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
; 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
--- /dev/null
+# 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
+
+...
--- /dev/null
+#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
+
+...