Path: blob/main/contrib/llvm-project/llvm/lib/Target/X86/X86CompressEVEX.cpp
35294 views
//===- X86CompressEVEX.cpp ------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This pass compresses instructions from EVEX space to legacy/VEX/EVEX space9// when possible in order to reduce code size or facilitate HW decoding.10//11// Possible compression:12// a. AVX512 instruction (EVEX) -> AVX instruction (VEX)13// b. Promoted instruction (EVEX) -> pre-promotion instruction (legacy/VEX)14// c. NDD (EVEX) -> non-NDD (legacy)15// d. NF_ND (EVEX) -> NF (EVEX)16// e. NonNF (EVEX) -> NF (EVEX)17//18// Compression a, b and c can always reduce code size, with some exceptions19// such as promoted 16-bit CRC32 which is as long as the legacy version.20//21// legacy:22// crc32w %si, %eax ## encoding: [0x66,0xf2,0x0f,0x38,0xf1,0xc6]23// promoted:24// crc32w %si, %eax ## encoding: [0x62,0xf4,0x7d,0x08,0xf1,0xc6]25//26// From performance perspective, these should be same (same uops and same EXE27// ports). From a FMV perspective, an older legacy encoding is preferred b/c it28// can execute in more places (broader HW install base). So we will still do29// the compression.30//31// Compression d can help hardware decode (HW may skip reading the NDD32// register) although the instruction length remains unchanged.33//34// Compression e can help hardware skip updating EFLAGS although the instruction35// length remains unchanged.36//===----------------------------------------------------------------------===//3738#include "MCTargetDesc/X86BaseInfo.h"39#include "MCTargetDesc/X86InstComments.h"40#include "X86.h"41#include "X86InstrInfo.h"42#include "X86Subtarget.h"43#include "llvm/ADT/StringRef.h"44#include "llvm/CodeGen/MachineFunction.h"45#include "llvm/CodeGen/MachineFunctionPass.h"46#include "llvm/CodeGen/MachineInstr.h"47#include "llvm/CodeGen/MachineOperand.h"48#include "llvm/MC/MCInstrDesc.h"49#include "llvm/Pass.h"50#include <atomic>51#include <cassert>52#include <cstdint>5354using namespace llvm;5556#define COMP_EVEX_DESC "Compressing EVEX instrs when possible"57#define COMP_EVEX_NAME "x86-compress-evex"5859#define DEBUG_TYPE COMP_EVEX_NAME6061namespace {62// Including the generated EVEX compression tables.63#define GET_X86_COMPRESS_EVEX_TABLE64#include "X86GenInstrMapping.inc"6566class CompressEVEXPass : public MachineFunctionPass {67public:68static char ID;69CompressEVEXPass() : MachineFunctionPass(ID) {}70StringRef getPassName() const override { return COMP_EVEX_DESC; }7172bool runOnMachineFunction(MachineFunction &MF) override;7374// This pass runs after regalloc and doesn't support VReg operands.75MachineFunctionProperties getRequiredProperties() const override {76return MachineFunctionProperties().set(77MachineFunctionProperties::Property::NoVRegs);78}79};8081} // end anonymous namespace8283char CompressEVEXPass::ID = 0;8485static bool usesExtendedRegister(const MachineInstr &MI) {86auto isHiRegIdx = [](unsigned Reg) {87// Check for XMM register with indexes between 16 - 31.88if (Reg >= X86::XMM16 && Reg <= X86::XMM31)89return true;90// Check for YMM register with indexes between 16 - 31.91if (Reg >= X86::YMM16 && Reg <= X86::YMM31)92return true;93// Check for GPR with indexes between 16 - 31.94if (X86II::isApxExtendedReg(Reg))95return true;96return false;97};9899// Check that operands are not ZMM regs or100// XMM/YMM regs with hi indexes between 16 - 31.101for (const MachineOperand &MO : MI.explicit_operands()) {102if (!MO.isReg())103continue;104105Register Reg = MO.getReg();106assert(!X86II::isZMMReg(Reg) &&107"ZMM instructions should not be in the EVEX->VEX tables");108if (isHiRegIdx(Reg))109return true;110}111112return false;113}114115// Do any custom cleanup needed to finalize the conversion.116static bool performCustomAdjustments(MachineInstr &MI, unsigned NewOpc) {117(void)NewOpc;118unsigned Opc = MI.getOpcode();119switch (Opc) {120case X86::VALIGNDZ128rri:121case X86::VALIGNDZ128rmi:122case X86::VALIGNQZ128rri:123case X86::VALIGNQZ128rmi: {124assert((NewOpc == X86::VPALIGNRrri || NewOpc == X86::VPALIGNRrmi) &&125"Unexpected new opcode!");126unsigned Scale =127(Opc == X86::VALIGNQZ128rri || Opc == X86::VALIGNQZ128rmi) ? 8 : 4;128MachineOperand &Imm = MI.getOperand(MI.getNumExplicitOperands() - 1);129Imm.setImm(Imm.getImm() * Scale);130break;131}132case X86::VSHUFF32X4Z256rmi:133case X86::VSHUFF32X4Z256rri:134case X86::VSHUFF64X2Z256rmi:135case X86::VSHUFF64X2Z256rri:136case X86::VSHUFI32X4Z256rmi:137case X86::VSHUFI32X4Z256rri:138case X86::VSHUFI64X2Z256rmi:139case X86::VSHUFI64X2Z256rri: {140assert((NewOpc == X86::VPERM2F128rr || NewOpc == X86::VPERM2I128rr ||141NewOpc == X86::VPERM2F128rm || NewOpc == X86::VPERM2I128rm) &&142"Unexpected new opcode!");143MachineOperand &Imm = MI.getOperand(MI.getNumExplicitOperands() - 1);144int64_t ImmVal = Imm.getImm();145// Set bit 5, move bit 1 to bit 4, copy bit 0.146Imm.setImm(0x20 | ((ImmVal & 2) << 3) | (ImmVal & 1));147break;148}149case X86::VRNDSCALEPDZ128rri:150case X86::VRNDSCALEPDZ128rmi:151case X86::VRNDSCALEPSZ128rri:152case X86::VRNDSCALEPSZ128rmi:153case X86::VRNDSCALEPDZ256rri:154case X86::VRNDSCALEPDZ256rmi:155case X86::VRNDSCALEPSZ256rri:156case X86::VRNDSCALEPSZ256rmi:157case X86::VRNDSCALESDZr:158case X86::VRNDSCALESDZm:159case X86::VRNDSCALESSZr:160case X86::VRNDSCALESSZm:161case X86::VRNDSCALESDZr_Int:162case X86::VRNDSCALESDZm_Int:163case X86::VRNDSCALESSZr_Int:164case X86::VRNDSCALESSZm_Int:165const MachineOperand &Imm = MI.getOperand(MI.getNumExplicitOperands() - 1);166int64_t ImmVal = Imm.getImm();167// Ensure that only bits 3:0 of the immediate are used.168if ((ImmVal & 0xf) != ImmVal)169return false;170break;171}172173return true;174}175176static bool CompressEVEXImpl(MachineInstr &MI, const X86Subtarget &ST) {177uint64_t TSFlags = MI.getDesc().TSFlags;178179// Check for EVEX instructions only.180if ((TSFlags & X86II::EncodingMask) != X86II::EVEX)181return false;182183// Instructions with mask or 512-bit vector can't be converted to VEX.184if (TSFlags & (X86II::EVEX_K | X86II::EVEX_L2))185return false;186187auto IsRedundantNewDataDest = [&](unsigned &Opc) {188// $rbx = ADD64rr_ND $rbx, $rax / $rbx = ADD64rr_ND $rax, $rbx189// ->190// $rbx = ADD64rr $rbx, $rax191const MCInstrDesc &Desc = MI.getDesc();192Register Reg0 = MI.getOperand(0).getReg();193const MachineOperand &Op1 = MI.getOperand(1);194if (!Op1.isReg() || X86::getFirstAddrOperandIdx(MI) == 1 ||195X86::isCFCMOVCC(MI.getOpcode()))196return false;197Register Reg1 = Op1.getReg();198if (Reg1 == Reg0)199return true;200201// Op1 and Op2 may be commutable for ND instructions.202if (!Desc.isCommutable() || Desc.getNumOperands() < 3 ||203!MI.getOperand(2).isReg() || MI.getOperand(2).getReg() != Reg0)204return false;205// Opcode may change after commute, e.g. SHRD -> SHLD206ST.getInstrInfo()->commuteInstruction(MI, false, 1, 2);207Opc = MI.getOpcode();208return true;209};210211// EVEX_B has several meanings.212// AVX512:213// register form: rounding control or SAE214// memory form: broadcast215//216// APX:217// MAP4: NDD218//219// For AVX512 cases, EVEX prefix is needed in order to carry this information220// thus preventing the transformation to VEX encoding.221bool IsND = X86II::hasNewDataDest(TSFlags);222if (TSFlags & X86II::EVEX_B && !IsND)223return false;224unsigned Opc = MI.getOpcode();225// MOVBE*rr is special because it has semantic of NDD but not set EVEX_B.226bool IsNDLike = IsND || Opc == X86::MOVBE32rr || Opc == X86::MOVBE64rr;227bool IsRedundantNDD = IsNDLike ? IsRedundantNewDataDest(Opc) : false;228229auto GetCompressedOpc = [&](unsigned Opc) -> unsigned {230ArrayRef<X86TableEntry> Table = ArrayRef(X86CompressEVEXTable);231const auto I = llvm::lower_bound(Table, Opc);232if (I == Table.end() || I->OldOpc != Opc)233return 0;234235if (usesExtendedRegister(MI) || !checkPredicate(I->NewOpc, &ST) ||236!performCustomAdjustments(MI, I->NewOpc))237return 0;238return I->NewOpc;239};240// NonNF -> NF only if it's not a compressible NDD instruction and eflags is241// dead.242unsigned NewOpc = IsRedundantNDD243? X86::getNonNDVariant(Opc)244: ((IsNDLike && ST.hasNF() &&245MI.registerDefIsDead(X86::EFLAGS, /*TRI=*/nullptr))246? X86::getNFVariant(Opc)247: GetCompressedOpc(Opc));248249if (!NewOpc)250return false;251252const MCInstrDesc &NewDesc = ST.getInstrInfo()->get(NewOpc);253MI.setDesc(NewDesc);254unsigned AsmComment;255switch (NewDesc.TSFlags & X86II::EncodingMask) {256case X86II::LEGACY:257AsmComment = X86::AC_EVEX_2_LEGACY;258break;259case X86II::VEX:260AsmComment = X86::AC_EVEX_2_VEX;261break;262case X86II::EVEX:263AsmComment = X86::AC_EVEX_2_EVEX;264assert(IsND && (NewDesc.TSFlags & X86II::EVEX_NF) &&265"Unknown EVEX2EVEX compression");266break;267default:268llvm_unreachable("Unknown EVEX compression");269}270MI.setAsmPrinterFlag(AsmComment);271if (IsRedundantNDD)272MI.tieOperands(0, 1);273274return true;275}276277bool CompressEVEXPass::runOnMachineFunction(MachineFunction &MF) {278#ifndef NDEBUG279// Make sure the tables are sorted.280static std::atomic<bool> TableChecked(false);281if (!TableChecked.load(std::memory_order_relaxed)) {282assert(llvm::is_sorted(X86CompressEVEXTable) &&283"X86CompressEVEXTable is not sorted!");284TableChecked.store(true, std::memory_order_relaxed);285}286#endif287const X86Subtarget &ST = MF.getSubtarget<X86Subtarget>();288if (!ST.hasAVX512() && !ST.hasEGPR() && !ST.hasNDD())289return false;290291bool Changed = false;292293for (MachineBasicBlock &MBB : MF) {294// Traverse the basic block.295for (MachineInstr &MI : MBB)296Changed |= CompressEVEXImpl(MI, ST);297}298299return Changed;300}301302INITIALIZE_PASS(CompressEVEXPass, COMP_EVEX_NAME, COMP_EVEX_DESC, false, false)303304FunctionPass *llvm::createX86CompressEVEXPass() {305return new CompressEVEXPass();306}307308309