Index: llvm/lib/Target/SystemZ/CMakeLists.txt =================================================================== --- llvm/lib/Target/SystemZ/CMakeLists.txt +++ llvm/lib/Target/SystemZ/CMakeLists.txt @@ -22,6 +22,7 @@ SystemZElimCompare.cpp SystemZFrameLowering.cpp SystemZHazardRecognizer.cpp + SystemZISelCleanupPass.cpp SystemZISelDAGToDAG.cpp SystemZISelLowering.cpp SystemZInstrInfo.cpp Index: llvm/lib/Target/SystemZ/SystemZ.h =================================================================== --- llvm/lib/Target/SystemZ/SystemZ.h +++ llvm/lib/Target/SystemZ/SystemZ.h @@ -187,6 +187,7 @@ } } // end namespace SystemZ +FunctionPass *createSystemZISelCleanupPass(); FunctionPass *createSystemZISelDag(SystemZTargetMachine &TM, CodeGenOpt::Level OptLevel); FunctionPass *createSystemZElimComparePass(SystemZTargetMachine &TM); Index: llvm/lib/Target/SystemZ/SystemZISelCleanupPass.cpp =================================================================== --- /dev/null +++ llvm/lib/Target/SystemZ/SystemZISelCleanupPass.cpp @@ -0,0 +1,155 @@ +//===------ SystemZLDCleanup.cpp - Clean up instruction selection ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass cleans up instruction selection (with global register usage info): +// +// - LOC:s after ICMP NE 0 (or ICMP EQ 1) do not need a prior immediate load +// of the known 0 (1). +// +//===----------------------------------------------------------------------===// + +#include "SystemZMachineFunctionInfo.h" +#include "SystemZTargetMachine.h" +#include "llvm/CodeGen/MachineDominators.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/CodeGen/MachineRegisterInfo.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetRegisterInfo.h" +#include "llvm/Target/TargetMachine.h" + +using namespace llvm; + +namespace { + +class SystemZISelCleanup : public MachineFunctionPass { +public: + static char ID; + SystemZISelCleanup() : MachineFunctionPass(ID), MRI(nullptr), MF(nullptr) {} + + StringRef getPassName() const override { + return "SystemZ Instruction Selection Clean-up"; + } + + bool processBlock(MachineBasicBlock &MBB); + bool runOnMachineFunction(MachineFunction &MF) override; + void getAnalysisUsage(AnalysisUsage &AU) const override; + +private: + + MachineRegisterInfo *MRI; + MachineFunction *MF; +}; + +char SystemZISelCleanup::ID = 0; + +} // end anonymous namespace + +FunctionPass *llvm::createSystemZISelCleanupPass() { + return new SystemZISelCleanup(); +} + +void SystemZISelCleanup::getAnalysisUsage(AnalysisUsage &AU) const { + AU.setPreservesCFG(); + MachineFunctionPass::getAnalysisUsage(AU); + // XXX Try loop aware? +} + +// Return true if MI loads 0, or -1. +static bool isLoadZero(MachineInstr *MI) { + if (MI->getOpcode() != SystemZ::LHIMux && MI->getOpcode() != SystemZ::LGHI) + return false; + return MI->getOperand(1).getImm() == 0; +} + +// Return 0 or 1 if MI compares against it, or -1. +static int64_t getComparedZeroOrOne(MachineInstr *Compare) { + if (Compare->getOpcode() != SystemZ::CHIMux && + Compare->getOpcode() != SystemZ::CGHI) + return -1; + int64_t Imm = Compare->getOperand(1).getImm(); + return (Imm == 0 || Imm == 1) ? Imm : -1; +} + +// EXPERIMENTAL +#include "llvm/Support/CommandLine.h" +static cl::opt MultipleCmpOpUsers("isel-clean-multiple-users", cl::init(true)); +static cl::opt CheckOneUser("isel-clean-check-one", cl::init(true)); + +bool SystemZISelCleanup::processBlock(MachineBasicBlock &MBB) { + bool Changed = false; + + MachineInstr *CmpMI = nullptr; + for (MachineInstr &MI : MBB) { + unsigned Opc = MI.getOpcode(); + if (MI.definesRegister(SystemZ::CC)) { + CmpMI = (Opc == SystemZ::CHIMux || Opc == SystemZ::CGHI) ? &MI : nullptr; + if (CmpMI && !MultipleCmpOpUsers) { + Register CmpSrcReg = CmpMI->getOperand(0).getReg(); + if (CheckOneUser) { + if (!MRI->hasOneNonDBGUse(CmpSrcReg)) + CmpMI = nullptr; + } else { + for (const MachineInstr &Use : MRI->use_nodbg_instructions(CmpSrcReg)) + if (Use.getParent() != &MBB) { + CmpMI = nullptr; + break; + } + } + } + continue; + } + + if (CmpMI != nullptr && + (Opc == SystemZ::LOCHIMux || + (Opc == SystemZ::LOCGHI && CmpMI->getOpcode() == SystemZ::CGHI)) && + isLoadZero(MRI->getVRegDef(MI.getOperand(1).getReg())) && + MI.getOperand(2).getImm() == 1) { + int64_t CCMask = MI.getOperand(4).getImm(); + int64_t CmpImm = getComparedZeroOrOne(CmpMI); + bool NE0Case = CCMask == SystemZ::CCMASK_CMP_NE && CmpImm == 0; + bool EQ1Case = CCMask == SystemZ::CCMASK_CMP_EQ && CmpImm == 1; + if (!NE0Case && !EQ1Case) + continue; + + MachineOperand &LOCSrcMO = MI.getOperand(1); + MachineOperand &CmpSrcMO = CmpMI->getOperand(0); + LOCSrcMO.setReg(CmpSrcMO.getReg()); + CmpSrcMO.setIsKill(false); + unsigned CmpOpc = CmpMI->getOpcode(); + if (Opc == SystemZ::LOCHIMux) { + if (CmpOpc == SystemZ::CHIMux) + LOCSrcMO.setSubReg(CmpSrcMO.getSubReg()); + else if (CmpOpc == SystemZ::CGHI) + LOCSrcMO.setSubReg(SystemZ::subreg_l32); + } + if (EQ1Case) { + MI.getOperand(2).setImm(0); + MI.getOperand(4).setImm(SystemZ::CCMASK_CMP_NE); + } + + Changed = true; + } + } + + return Changed; +} + +bool SystemZISelCleanup::runOnMachineFunction(MachineFunction &F) { + if (skipFunction(F.getFunction())) + return false; + + MRI = &F.getRegInfo(); + MF = &F; + + bool Changed = false; + for (auto &MBB : F) + Changed |= processBlock(MBB); + + return Changed; +} Index: llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp =================================================================== --- llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp +++ llvm/lib/Target/SystemZ/SystemZTargetMachine.cpp @@ -241,8 +241,10 @@ bool SystemZPassConfig::addInstSelector() { addPass(createSystemZISelDag(getSystemZTargetMachine(), getOptLevel())); - if (getOptLevel() != CodeGenOpt::None) + if (getOptLevel() != CodeGenOpt::None) { + addPass(createSystemZISelCleanupPass()); addPass(createSystemZLDCleanupPass(getSystemZTargetMachine())); + } return false; } Index: llvm/test/CodeGen/SystemZ/int-cmp-59.ll =================================================================== --- llvm/test/CodeGen/SystemZ/int-cmp-59.ll +++ llvm/test/CodeGen/SystemZ/int-cmp-59.ll @@ -13,7 +13,7 @@ ; CHECK-NEXT: %4:gr64bit = IMPLICIT_DEF ; CHECK-NEXT: %3:gr64bit = RISBGN %4, killed %2, 63, 191, 0 ; CHECK-NEXT: %5:gr64bit = LCGR killed %3, implicit-def dead $cc -; CHECK-NEXT: CGHI killed %5, 1, implicit-def $cc +; CHECK-NEXT: CGHI %5, 1, implicit-def $cc entry: %0 = load i32*, i32** @bPtr store i1 true, i1* @c Index: llvm/test/CodeGen/SystemZ/setcc-05.ll =================================================================== --- /dev/null +++ llvm/test/CodeGen/SystemZ/setcc-05.ll @@ -0,0 +1,130 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; Test SETCC for an integer comparison against 0. The 0 does not need to be +; loaded if the condition is NE. +; +; RUN: llc < %s -mtriple=s390x-linux-gnu -mcpu=z13 | FileCheck %s + +; ICMP NE 0: no need to load 0. +define i32 @fun0(i8 zeroext %b) { +; CHECK-LABEL: fun0: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: chi %r2, 0 +; CHECK-NEXT: lochilh %r2, 1 +; CHECK-NEXT: # kill: def $r2l killed $r2l killed $r2d +; CHECK-NEXT: br %r14 +entry: + %cc = icmp ne i8 %b, 0 + %conv = zext i1 %cc to i32 + ret i32 %conv +} + +; ICMP EQ 0: need to load 0. +define i32 @fun2(i8 zeroext %b) { +; CHECK-LABEL: fun2: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: chi %r2, 0 +; CHECK-NEXT: lhi %r2, 0 +; CHECK-NEXT: lochie %r2, 1 +; CHECK-NEXT: br %r14 +entry: + %cc = icmp eq i8 %b, 0 + %conv = zext i1 %cc to i32 + ret i32 %conv +} + +; ICMP NE 0: The whole register is not checked, so need to load 0. +define i32 @fun3(i32 %b) { +; CHECK-LABEL: fun3: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: tmll %r2, 255 +; CHECK-NEXT: lhi %r2, 0 +; CHECK-NEXT: lochine %r2, 1 +; CHECK-NEXT: br %r14 +entry: + %t = trunc i32 %b to i8 + %cc = icmp ne i8 %t, 0 + %conv = zext i1 %cc to i32 + ret i32 %conv +} + +; ICMP NE 0: i64 with i32 use +define i32 @fun4(i64 %b) { +; CHECK-LABEL: fun4: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: cghi %r2, 0 +; CHECK-NEXT: lochilh %r2, 1 +; CHECK-NEXT: # kill: def $r2l killed $r2l killed $r2d +; CHECK-NEXT: br %r14 +entry: + %cc = icmp ne i64 %b, 0 + %conv = zext i1 %cc to i32 + ret i32 %conv +} + +; ICMP NE 0: i64 with i64 use. +define i64 @fun5(i64 %b) { +; CHECK-LABEL: fun5: +; CHECK: # %bb.0: # %bb +; CHECK-NEXT: cghi %r2, 0 +; CHECK-NEXT: locghilh %r2, 1 +; CHECK-NEXT: br %r14 +bb: + %cc = icmp ne i64 %b, 0 + %conv = zext i1 %cc to i64 + ret i64 %conv +} + +; ICMP EQ 1: no need to load 1. +define i32 @fun6(i8 zeroext %b) { +; CHECK-LABEL: fun6: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: chi %r2, 1 +; CHECK-NEXT: lochilh %r2, 0 +; CHECK-NEXT: # kill: def $r2l killed $r2l killed $r2d +; CHECK-NEXT: br %r14 +entry: + %cc = icmp eq i8 %b, 1 + %conv = zext i1 %cc to i32 + ret i32 %conv +} + +; ICMP NE 1: need to load 1. +define i32 @fun7(i8 zeroext %b) { +; CHECK-LABEL: fun7: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: chi %r2, 1 +; CHECK-NEXT: lhi %r2, 0 +; CHECK-NEXT: lochilh %r2, 1 +; CHECK-NEXT: br %r14 +entry: + %cc = icmp ne i8 %b, 1 + %conv = zext i1 %cc to i32 + ret i32 %conv +} + +; ICMP EQ 1: i64 with i32 use +define i32 @fun8(i64 %b) { +; CHECK-LABEL: fun8: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: cghi %r2, 1 +; CHECK-NEXT: lochilh %r2, 0 +; CHECK-NEXT: # kill: def $r2l killed $r2l killed $r2d +; CHECK-NEXT: br %r14 +entry: + %cc = icmp eq i64 %b, 1 + %conv = zext i1 %cc to i32 + ret i32 %conv +} + +; ICMP EQ 1: i64 with i64 use +define i64 @fun9(i64 %b) { +; CHECK-LABEL: fun9: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: cghi %r2, 1 +; CHECK-NEXT: locghilh %r2, 0 +; CHECK-NEXT: br %r14 +entry: + %cc = icmp eq i64 %b, 1 + %conv = zext i1 %cc to i64 + ret i64 %conv +}