llvm-project/llvm/lib/Target/SystemZ/SystemZShortenInst.cpp
Ulrich Weigand 9db13b5a7d [FPEnv] Constrained FCmp intrinsics
This adds support for constrained floating-point comparison intrinsics.

Specifically, we add:

      declare <ty2>
      @llvm.experimental.constrained.fcmp(<type> <op1>, <type> <op2>,
                                          metadata <condition code>,
                                          metadata <exception behavior>)
      declare <ty2>
      @llvm.experimental.constrained.fcmps(<type> <op1>, <type> <op2>,
                                           metadata <condition code>,
                                           metadata <exception behavior>)

The first variant implements an IEEE "quiet" comparison (i.e. we only
get an invalid FP exception if either argument is a SNaN), while the
second variant implements an IEEE "signaling" comparison (i.e. we get
an invalid FP exception if either argument is any NaN).

The condition code is implemented as a metadata string.  The same set
of predicates as for the fcmp instruction is supported (except for the
"true" and "false" predicates).

These new intrinsics are mapped by SelectionDAG codegen onto two new
ISD opcodes, ISD::STRICT_FSETCC and ISD::STRICT_FSETCCS, again
representing quiet vs. signaling comparison operations.  Otherwise
those nodes look like SETCC nodes, with an additional chain argument
and result as usual for strict FP nodes.  The patch includes support
for the common legalization operations for those nodes.

The patch also includes full SystemZ back-end support for the new
ISD nodes, mapping them to all available SystemZ instruction to
fully implement strict semantics (scalar and vector).

Differential Revision: https://reviews.llvm.org/D69281
2019-12-07 11:28:39 +01:00

388 lines
12 KiB
C++

//===-- SystemZShortenInst.cpp - Instruction-shortening pass --------------===//
//
// 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 tries to replace instructions with shorter forms. For example,
// IILF can be replaced with LLILL or LLILH if the constant fits and if the
// other 32 bits of the GR64 destination are not live.
//
//===----------------------------------------------------------------------===//
#include "SystemZTargetMachine.h"
#include "llvm/CodeGen/LivePhysRegs.h"
#include "llvm/CodeGen/MachineFunctionPass.h"
#include "llvm/CodeGen/MachineInstrBuilder.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
using namespace llvm;
#define DEBUG_TYPE "systemz-shorten-inst"
namespace {
class SystemZShortenInst : public MachineFunctionPass {
public:
static char ID;
SystemZShortenInst(const SystemZTargetMachine &tm);
StringRef getPassName() const override {
return "SystemZ Instruction Shortening";
}
bool processBlock(MachineBasicBlock &MBB);
bool runOnMachineFunction(MachineFunction &F) override;
MachineFunctionProperties getRequiredProperties() const override {
return MachineFunctionProperties().set(
MachineFunctionProperties::Property::NoVRegs);
}
private:
bool shortenIIF(MachineInstr &MI, unsigned LLIxL, unsigned LLIxH);
bool shortenOn0(MachineInstr &MI, unsigned Opcode);
bool shortenOn01(MachineInstr &MI, unsigned Opcode);
bool shortenOn001(MachineInstr &MI, unsigned Opcode);
bool shortenOn001AddCC(MachineInstr &MI, unsigned Opcode);
bool shortenFPConv(MachineInstr &MI, unsigned Opcode);
bool shortenSelect(MachineInstr &MI, unsigned Opcode);
const SystemZInstrInfo *TII;
const TargetRegisterInfo *TRI;
LivePhysRegs LiveRegs;
};
char SystemZShortenInst::ID = 0;
} // end anonymous namespace
FunctionPass *llvm::createSystemZShortenInstPass(SystemZTargetMachine &TM) {
return new SystemZShortenInst(TM);
}
SystemZShortenInst::SystemZShortenInst(const SystemZTargetMachine &tm)
: MachineFunctionPass(ID), TII(nullptr) {}
// Tie operands if MI has become a two-address instruction.
static void tieOpsIfNeeded(MachineInstr &MI) {
if (MI.getDesc().getOperandConstraint(0, MCOI::TIED_TO) &&
!MI.getOperand(0).isTied())
MI.tieOperands(0, 1);
}
// MI loads one word of a GPR using an IIxF instruction and LLIxL and LLIxH
// are the halfword immediate loads for the same word. Try to use one of them
// instead of IIxF.
bool SystemZShortenInst::shortenIIF(MachineInstr &MI, unsigned LLIxL,
unsigned LLIxH) {
Register Reg = MI.getOperand(0).getReg();
// The new opcode will clear the other half of the GR64 reg, so
// cancel if that is live.
unsigned thisSubRegIdx =
(SystemZ::GRH32BitRegClass.contains(Reg) ? SystemZ::subreg_h32
: SystemZ::subreg_l32);
unsigned otherSubRegIdx =
(thisSubRegIdx == SystemZ::subreg_l32 ? SystemZ::subreg_h32
: SystemZ::subreg_l32);
unsigned GR64BitReg =
TRI->getMatchingSuperReg(Reg, thisSubRegIdx, &SystemZ::GR64BitRegClass);
Register OtherReg = TRI->getSubReg(GR64BitReg, otherSubRegIdx);
if (LiveRegs.contains(OtherReg))
return false;
uint64_t Imm = MI.getOperand(1).getImm();
if (SystemZ::isImmLL(Imm)) {
MI.setDesc(TII->get(LLIxL));
MI.getOperand(0).setReg(SystemZMC::getRegAsGR64(Reg));
return true;
}
if (SystemZ::isImmLH(Imm)) {
MI.setDesc(TII->get(LLIxH));
MI.getOperand(0).setReg(SystemZMC::getRegAsGR64(Reg));
MI.getOperand(1).setImm(Imm >> 16);
return true;
}
return false;
}
// Change MI's opcode to Opcode if register operand 0 has a 4-bit encoding.
bool SystemZShortenInst::shortenOn0(MachineInstr &MI, unsigned Opcode) {
if (SystemZMC::getFirstReg(MI.getOperand(0).getReg()) < 16) {
MI.setDesc(TII->get(Opcode));
return true;
}
return false;
}
// Change MI's opcode to Opcode if register operands 0 and 1 have a
// 4-bit encoding.
bool SystemZShortenInst::shortenOn01(MachineInstr &MI, unsigned Opcode) {
if (SystemZMC::getFirstReg(MI.getOperand(0).getReg()) < 16 &&
SystemZMC::getFirstReg(MI.getOperand(1).getReg()) < 16) {
MI.setDesc(TII->get(Opcode));
return true;
}
return false;
}
// Change MI's opcode to Opcode if register operands 0, 1 and 2 have a
// 4-bit encoding and if operands 0 and 1 are tied. Also ties op 0
// with op 1, if MI becomes 2-address.
bool SystemZShortenInst::shortenOn001(MachineInstr &MI, unsigned Opcode) {
if (SystemZMC::getFirstReg(MI.getOperand(0).getReg()) < 16 &&
MI.getOperand(1).getReg() == MI.getOperand(0).getReg() &&
SystemZMC::getFirstReg(MI.getOperand(2).getReg()) < 16) {
MI.setDesc(TII->get(Opcode));
tieOpsIfNeeded(MI);
return true;
}
return false;
}
// Calls shortenOn001 if CCLive is false. CC def operand is added in
// case of success.
bool SystemZShortenInst::shortenOn001AddCC(MachineInstr &MI, unsigned Opcode) {
if (!LiveRegs.contains(SystemZ::CC) && shortenOn001(MI, Opcode)) {
MachineInstrBuilder(*MI.getParent()->getParent(), &MI)
.addReg(SystemZ::CC, RegState::ImplicitDefine | RegState::Dead);
return true;
}
return false;
}
// MI is a vector-style conversion instruction with the operand order:
// destination, source, exact-suppress, rounding-mode. If both registers
// have a 4-bit encoding then change it to Opcode, which has operand order:
// destination, rouding-mode, source, exact-suppress.
bool SystemZShortenInst::shortenFPConv(MachineInstr &MI, unsigned Opcode) {
if (SystemZMC::getFirstReg(MI.getOperand(0).getReg()) < 16 &&
SystemZMC::getFirstReg(MI.getOperand(1).getReg()) < 16) {
MachineOperand Dest(MI.getOperand(0));
MachineOperand Src(MI.getOperand(1));
MachineOperand Suppress(MI.getOperand(2));
MachineOperand Mode(MI.getOperand(3));
MI.RemoveOperand(3);
MI.RemoveOperand(2);
MI.RemoveOperand(1);
MI.RemoveOperand(0);
MI.setDesc(TII->get(Opcode));
MachineInstrBuilder(*MI.getParent()->getParent(), &MI)
.add(Dest)
.add(Mode)
.add(Src)
.add(Suppress);
return true;
}
return false;
}
// MI is a three-operand select instruction. If one of the sources match
// the destination, convert to the equivalent load-on-condition.
bool SystemZShortenInst::shortenSelect(MachineInstr &MI, unsigned Opcode) {
if (MI.getOperand(0).getReg() == MI.getOperand(1).getReg()) {
MI.setDesc(TII->get(Opcode));
MI.tieOperands(0, 1);
return true;
}
if (MI.getOperand(0).getReg() == MI.getOperand(2).getReg()) {
TII->commuteInstruction(MI, false, 1, 2);
MI.setDesc(TII->get(Opcode));
MI.tieOperands(0, 1);
return true;
}
return false;
}
// Process all instructions in MBB. Return true if something changed.
bool SystemZShortenInst::processBlock(MachineBasicBlock &MBB) {
bool Changed = false;
// Set up the set of live registers at the end of MBB (live out)
LiveRegs.clear();
LiveRegs.addLiveOuts(MBB);
// Iterate backwards through the block looking for instructions to change.
for (auto MBBI = MBB.rbegin(), MBBE = MBB.rend(); MBBI != MBBE; ++MBBI) {
MachineInstr &MI = *MBBI;
switch (MI.getOpcode()) {
case SystemZ::IILF:
Changed |= shortenIIF(MI, SystemZ::LLILL, SystemZ::LLILH);
break;
case SystemZ::IIHF:
Changed |= shortenIIF(MI, SystemZ::LLIHL, SystemZ::LLIHH);
break;
case SystemZ::SELR:
Changed |= shortenSelect(MI, SystemZ::LOCR);
break;
case SystemZ::SELFHR:
Changed |= shortenSelect(MI, SystemZ::LOCFHR);
break;
case SystemZ::SELGR:
Changed |= shortenSelect(MI, SystemZ::LOCGR);
break;
case SystemZ::WFADB:
Changed |= shortenOn001AddCC(MI, SystemZ::ADBR);
break;
case SystemZ::WFASB:
Changed |= shortenOn001AddCC(MI, SystemZ::AEBR);
break;
case SystemZ::WFDDB:
Changed |= shortenOn001(MI, SystemZ::DDBR);
break;
case SystemZ::WFDSB:
Changed |= shortenOn001(MI, SystemZ::DEBR);
break;
case SystemZ::WFIDB:
Changed |= shortenFPConv(MI, SystemZ::FIDBRA);
break;
case SystemZ::WFISB:
Changed |= shortenFPConv(MI, SystemZ::FIEBRA);
break;
case SystemZ::WLDEB:
Changed |= shortenOn01(MI, SystemZ::LDEBR);
break;
case SystemZ::WLEDB:
Changed |= shortenFPConv(MI, SystemZ::LEDBRA);
break;
case SystemZ::WFMDB:
Changed |= shortenOn001(MI, SystemZ::MDBR);
break;
case SystemZ::WFMSB:
Changed |= shortenOn001(MI, SystemZ::MEEBR);
break;
case SystemZ::WFLCDB:
Changed |= shortenOn01(MI, SystemZ::LCDFR);
break;
case SystemZ::WFLCSB:
Changed |= shortenOn01(MI, SystemZ::LCDFR_32);
break;
case SystemZ::WFLNDB:
Changed |= shortenOn01(MI, SystemZ::LNDFR);
break;
case SystemZ::WFLNSB:
Changed |= shortenOn01(MI, SystemZ::LNDFR_32);
break;
case SystemZ::WFLPDB:
Changed |= shortenOn01(MI, SystemZ::LPDFR);
break;
case SystemZ::WFLPSB:
Changed |= shortenOn01(MI, SystemZ::LPDFR_32);
break;
case SystemZ::WFSQDB:
Changed |= shortenOn01(MI, SystemZ::SQDBR);
break;
case SystemZ::WFSQSB:
Changed |= shortenOn01(MI, SystemZ::SQEBR);
break;
case SystemZ::WFSDB:
Changed |= shortenOn001AddCC(MI, SystemZ::SDBR);
break;
case SystemZ::WFSSB:
Changed |= shortenOn001AddCC(MI, SystemZ::SEBR);
break;
case SystemZ::WFCDB:
Changed |= shortenOn01(MI, SystemZ::CDBR);
break;
case SystemZ::WFCSB:
Changed |= shortenOn01(MI, SystemZ::CEBR);
break;
case SystemZ::WFKDB:
Changed |= shortenOn01(MI, SystemZ::KDBR);
break;
case SystemZ::WFKSB:
Changed |= shortenOn01(MI, SystemZ::KEBR);
break;
case SystemZ::VL32:
// For z13 we prefer LDE over LE to avoid partial register dependencies.
Changed |= shortenOn0(MI, SystemZ::LDE32);
break;
case SystemZ::VST32:
Changed |= shortenOn0(MI, SystemZ::STE);
break;
case SystemZ::VL64:
Changed |= shortenOn0(MI, SystemZ::LD);
break;
case SystemZ::VST64:
Changed |= shortenOn0(MI, SystemZ::STD);
break;
default: {
int TwoOperandOpcode = SystemZ::getTwoOperandOpcode(MI.getOpcode());
if (TwoOperandOpcode == -1)
break;
if ((MI.getOperand(0).getReg() != MI.getOperand(1).getReg()) &&
(!MI.isCommutable() ||
MI.getOperand(0).getReg() != MI.getOperand(2).getReg() ||
!TII->commuteInstruction(MI, false, 1, 2)))
break;
MI.setDesc(TII->get(TwoOperandOpcode));
MI.tieOperands(0, 1);
if (TwoOperandOpcode == SystemZ::SLL ||
TwoOperandOpcode == SystemZ::SLA ||
TwoOperandOpcode == SystemZ::SRL ||
TwoOperandOpcode == SystemZ::SRA) {
// These shifts only use the low 6 bits of the shift count.
MachineOperand &ImmMO = MI.getOperand(3);
ImmMO.setImm(ImmMO.getImm() & 0xfff);
}
Changed = true;
break;
}
}
LiveRegs.stepBackward(MI);
}
return Changed;
}
bool SystemZShortenInst::runOnMachineFunction(MachineFunction &F) {
if (skipFunction(F.getFunction()))
return false;
const SystemZSubtarget &ST = F.getSubtarget<SystemZSubtarget>();
TII = ST.getInstrInfo();
TRI = ST.getRegisterInfo();
LiveRegs.init(*TRI);
bool Changed = false;
for (auto &MBB : F)
Changed |= processBlock(MBB);
return Changed;
}