diff --git a/llvm/include/llvm/CodeGen/GlobalISel/OptMIRBuilder.h b/llvm/include/llvm/CodeGen/GlobalISel/OptMIRBuilder.h new file mode 100644 index 0000000000000..cba0c87611c24 --- /dev/null +++ b/llvm/include/llvm/CodeGen/GlobalISel/OptMIRBuilder.h @@ -0,0 +1,83 @@ +//===-- llvm/CodeGen/GlobalISel/OptMIRBuilder.h --*- C++ -*-==---------------// +// +// 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 +// +//===----------------------------------------------------------------------===// +/// \file This file implements a legal version of MachineIRBuilder which +/// optimizes insts while building. +//===----------------------------------------------------------------------===// +#ifndef LLVM_CODEGEN_GLOBALISEL_OPTMIRBUILDER_H +#define LLVM_CODEGEN_GLOBALISEL_OPTMIRBUILDER_H + +#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h" + +namespace llvm { + +class LegalizerInfo; +struct LegalityQuery; + +/// OptMIRBuilder optimizes instructions while building them. It +/// checks its operands whether they are constant or +/// G_IMPLICIT_DEF. It never checks whether an operand is defined by +/// G_FSINCOS. It checks operands and registers for G_IMPLICIT_DEF, +/// G_CONSTANT, G_BUILD_VECTOR, and G_SPLAT_VECTOR and nothing else. +/// Based on G_IMPLICIT_DEF, the constants and their values, it folds +/// instructions into constants, G_IMPLICIT_DEF, or other +/// instructions. For optmizations and constant folding it relies on +/// GIConstant. It can fold G_MUL into G_ADD and G_SUB. Before folding +/// it always queries the legalizer. When it fails to fold, it +/// delegates the building to the CSEMIRBuilder. It is the user's +/// responsibility to only attempt to build legal instructions pass +/// the legalizer. OptMIRBuilder can safely be used in optimization +/// passes pass the legalizer. +class OptMIRBuilder : public CSEMIRBuilder { + const LegalizerInfo *LI; + const bool IsPrelegalize; + + /// Legality tests. + bool isPrelegalize() const; + bool isLegal(const LegalityQuery &Query) const; + bool isConstantLegal(LLT); + bool isLegalOrBeforeLegalizer(const LegalityQuery &Query) const; + bool isConstantLegalOrBeforeLegalizer(LLT); + + /// Returns true if the register \p R is defined by G_IMPLICIT_DEF. + bool isUndef(Register R) const; + + /// Builds 0 - X. + MachineInstrBuilder buildNegation(const DstOp &, const SrcOp &); + + // Constants. + MachineInstrBuilder buildGIConstant(const DstOp &DstOp, const GIConstant &); + + /// Integer. + MachineInstrBuilder optimizeAdd(unsigned Opc, ArrayRef DstOps, + ArrayRef SrcOps, + std::optional Flag = std::nullopt); + MachineInstrBuilder optimizeSub(unsigned Opc, ArrayRef DstOps, + ArrayRef SrcOps, + std::optional Flag = std::nullopt); + MachineInstrBuilder optimizeMul(unsigned Opc, ArrayRef DstOps, + ArrayRef SrcOps, + std::optional Flag = std::nullopt); + +public: + OptMIRBuilder(MachineFunction &MF, GISelCSEInfo *CSEInfo, + GISelChangeObserver &Observer, const LegalizerInfo *LI, + bool IsPrelegalize) + : LI(LI), IsPrelegalize(IsPrelegalize) { + setMF(MF); + setCSEInfo(CSEInfo); + setChangeObserver(Observer); + }; + + MachineInstrBuilder + buildInstr(unsigned Opc, ArrayRef DstOps, ArrayRef SrcOps, + std::optional Flag = std::nullopt) override; +}; + +} // namespace llvm + +#endif // LLVM_CODEGEN_GLOBALISEL_OPTMIRBUILDER_H diff --git a/llvm/include/llvm/CodeGen/GlobalISel/Utils.h b/llvm/include/llvm/CodeGen/GlobalISel/Utils.h index 44141844f42f4..45e9f20565f98 100644 --- a/llvm/include/llvm/CodeGen/GlobalISel/Utils.h +++ b/llvm/include/llvm/CodeGen/GlobalISel/Utils.h @@ -635,8 +635,28 @@ class GIConstant { /// Returns the value, if this constant is a scalar. APInt getScalarValue() const; + /// Returns the value, if this constant is a scalable vector. + APInt getSplatValue() const; + + /// Returns the values, if this constant is a fixed vector. + ArrayRef getAsArrayRef() const; + static std::optional getConstant(Register Const, const MachineRegisterInfo &MRI); + + /// Returns a new constant where add(this, x) was applied. + GIConstant add(const GIConstant &) const; + + /// Returns a new constant where sub(this, x) was applied. + GIConstant sub(const GIConstant &) const; + + /// Returns a new constant where mul(this, x) was applied. + GIConstant mul(const GIConstant &) const; + + bool isZero() const; + bool isOne() const; + bool isTwo() const; + bool isAllOnes() const; }; /// An floating-point-like constant. diff --git a/llvm/lib/CodeGen/GlobalISel/CMakeLists.txt b/llvm/lib/CodeGen/GlobalISel/CMakeLists.txt index 554a2367eb835..87699ef0bcf87 100644 --- a/llvm/lib/CodeGen/GlobalISel/CMakeLists.txt +++ b/llvm/lib/CodeGen/GlobalISel/CMakeLists.txt @@ -26,6 +26,7 @@ add_llvm_component_library(LLVMGlobalISel Localizer.cpp LostDebugLocObserver.cpp MachineIRBuilder.cpp + OptMIRBuilder.cpp RegBankSelect.cpp Utils.cpp diff --git a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp index 359677027f52f..f96c65ae4993e 100644 --- a/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp +++ b/llvm/lib/CodeGen/GlobalISel/MachineIRBuilder.cpp @@ -321,8 +321,12 @@ MachineInstrBuilder MachineIRBuilder::buildConstant(const DstOp &Res, assert(EltTy.getScalarSizeInBits() == Val.getBitWidth() && "creating constant with the wrong size"); - assert(!Ty.isScalableVector() && - "unexpected scalable vector in buildConstant"); + if (Ty.isScalableVector()) { + auto Const = buildInstr(TargetOpcode::G_CONSTANT) + .addDef(getMRI()->createGenericVirtualRegister(EltTy)) + .addCImm(&Val); + return buildSplatVector(Res, Const); + } if (Ty.isFixedVector()) { auto Const = buildInstr(TargetOpcode::G_CONSTANT) diff --git a/llvm/lib/CodeGen/GlobalISel/OptMIRBuilder.cpp b/llvm/lib/CodeGen/GlobalISel/OptMIRBuilder.cpp new file mode 100644 index 0000000000000..66b879f5113dd --- /dev/null +++ b/llvm/lib/CodeGen/GlobalISel/OptMIRBuilder.cpp @@ -0,0 +1,206 @@ +//===-- llvm/CodeGen/GlobalISel/OptMIRBuilder.cpp -----------------*- C++-*-==// +// 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 +// +//===----------------------------------------------------------------------===// +/// \file +/// This file implements the OptMIRBuilder class which optimizes as it builds +/// instructions. +//===----------------------------------------------------------------------===// +// + +#include "llvm/CodeGen/GlobalISel/OptMIRBuilder.h" +#include "llvm/CodeGen/GlobalISel/CSEMIRBuilder.h" +#include "llvm/CodeGen/GlobalISel/LegalizerInfo.h" +#include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" +#include "llvm/CodeGen/GlobalISel/Utils.h" +#include "llvm/CodeGen/TargetOpcodes.h" + +using namespace llvm; + +bool OptMIRBuilder::isPrelegalize() const { return IsPrelegalize; } + +bool OptMIRBuilder::isLegal(const LegalityQuery &Query) const { + assert(LI != nullptr && "legalizer info is not available"); + return LI->isLegal(Query); +} + +bool OptMIRBuilder::isConstantLegal(LLT Ty) { + if (Ty.isScalar()) + return isLegal({TargetOpcode::G_CONSTANT, {Ty}}); + + LLT EltTy = Ty.getElementType(); + if (Ty.isFixedVector()) + return isLegal({TargetOpcode::G_BUILD_VECTOR, {Ty, EltTy}}) && + isLegal({TargetOpcode::G_CONSTANT, {EltTy}}); + + // scalable vector + assert(Ty.isScalableVector() && "Unexpected LLT"); + return isLegal({TargetOpcode::G_SPLAT_VECTOR, {Ty, EltTy}}) && + isLegal({TargetOpcode::G_CONSTANT, {EltTy}}); +} + +bool OptMIRBuilder::isLegalOrBeforeLegalizer(const LegalityQuery &Query) const { + return isPrelegalize() || isLegal(Query); +} + +bool OptMIRBuilder::isConstantLegalOrBeforeLegalizer(LLT Ty) { + return isPrelegalize() || isConstantLegal(Ty); +} + +bool OptMIRBuilder::isUndef(Register Reg) const { + const MachineInstr *MI = getMRI()->getVRegDef(Reg); + return MI->getOpcode() == TargetOpcode::G_IMPLICIT_DEF; +} + +MachineInstrBuilder OptMIRBuilder::buildNegation(const DstOp &DstOp, + const SrcOp &SrcOp) { + LLT DstTy = DstOp.getLLTTy(*getMRI()); + + auto Zero = buildConstant(DstTy, 0); + return buildSub(DstOp, Zero, SrcOp); +} + +MachineInstrBuilder OptMIRBuilder::buildGIConstant(const DstOp &DstOp, + const GIConstant &Const) { + LLT DstTy = DstOp.getLLTTy(*getMRI()); + + switch (Const.getKind()) { + case GIConstant::GIConstantKind::Scalar: + return buildConstant(DstOp, Const.getScalarValue()); + case GIConstant::GIConstantKind::FixedVector: + return buildBuildVectorConstant(DstOp, Const.getAsArrayRef()); + case GIConstant::GIConstantKind::ScalableVector: { + auto Constant = + buildConstant(DstTy.getElementType(), Const.getSplatValue()); + return buildSplatVector(DstOp, Constant); + } + } +} + +MachineInstrBuilder OptMIRBuilder::optimizeAdd(unsigned Opc, + ArrayRef DstOps, + ArrayRef SrcOps, + std::optional Flag) { + assert(SrcOps.size() == 2 && "Invalid sources"); + assert(DstOps.size() == 1 && "Invalid dsts"); + + LLT DstTy = DstOps[0].getLLTTy(*getMRI()); + + if (isUndef(SrcOps[1].getReg()) || isUndef(SrcOps[0].getReg())) + return buildUndef(DstTy); + + std::optional RHS = + GIConstant::getConstant(SrcOps[1].getReg(), *getMRI()); + if (!RHS) + return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag); + + if (RHS->isZero()) + return buildCopy(DstOps[0], SrcOps[0]); + + if (!isConstantLegalOrBeforeLegalizer(DstTy)) + return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag); + + std::optional LHS = + GIConstant::getConstant(SrcOps[0].getReg(), *getMRI()); + if (!LHS) + return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag); + + GIConstant Add = LHS->add(*RHS); + + return buildGIConstant(DstOps[0], Add); +} + +MachineInstrBuilder OptMIRBuilder::optimizeSub(unsigned Opc, + ArrayRef DstOps, + ArrayRef SrcOps, + std::optional Flag) { + assert(SrcOps.size() == 2 && "Invalid sources"); + assert(DstOps.size() == 1 && "Invalid dsts"); + + LLT DstTy = DstOps[0].getLLTTy(*getMRI()); + + if (isUndef(SrcOps[1].getReg()) || isUndef(SrcOps[0].getReg())) + return buildUndef(DstTy); + + std::optional RHS = + GIConstant::getConstant(SrcOps[1].getReg(), *getMRI()); + if (!RHS) + return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag); + + if (RHS->isZero()) + return buildCopy(DstOps[0], SrcOps[0]); + + if (!isConstantLegalOrBeforeLegalizer(DstTy)) + return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag); + + std::optional LHS = + GIConstant::getConstant(SrcOps[0].getReg(), *getMRI()); + if (!LHS) + return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag); + + GIConstant Sub = LHS->sub(*RHS); + + return buildGIConstant(DstOps[0], Sub); +} + +MachineInstrBuilder OptMIRBuilder::optimizeMul(unsigned Opc, + ArrayRef DstOps, + ArrayRef SrcOps, + std::optional Flag) { + assert(SrcOps.size() == 2 && "Invalid sources"); + assert(DstOps.size() == 1 && "Invalid dsts"); + + LLT DstTy = DstOps[0].getLLTTy(*getMRI()); + + if ((isUndef(SrcOps[1].getReg()) || isUndef(SrcOps[0].getReg())) && + isConstantLegalOrBeforeLegalizer(DstTy)) + return buildConstant(DstTy, 0); + + std::optional RHS = + GIConstant::getConstant(SrcOps[1].getReg(), *getMRI()); + if (!RHS) + return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag); + + if (RHS->isZero() && isConstantLegalOrBeforeLegalizer(DstTy)) + return buildConstant(DstTy, 0); + + if (RHS->isOne()) + return buildCopy(DstOps[0], SrcOps[0]); + + if (RHS->isAllOnes() && isConstantLegalOrBeforeLegalizer(DstTy) && + isLegalOrBeforeLegalizer({TargetOpcode::G_SUB, {DstTy}})) + return buildNegation(DstOps[0], SrcOps[0]); + + if (RHS->isTwo() && isLegalOrBeforeLegalizer({TargetOpcode::G_ADD, {DstTy}})) + return buildAdd(DstOps[0], SrcOps[0], SrcOps[0]); + + if (!isConstantLegalOrBeforeLegalizer(DstTy)) + return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag); + + std::optional LHS = + GIConstant::getConstant(SrcOps[0].getReg(), *getMRI()); + if (!LHS) + return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag); + + GIConstant Mul = LHS->mul(*RHS); + + return buildGIConstant(DstOps[0], Mul); +} + +MachineInstrBuilder OptMIRBuilder::buildInstr(unsigned Opc, + ArrayRef DstOps, + ArrayRef SrcOps, + std::optional Flag) { + switch (Opc) { + case TargetOpcode::G_ADD: + return optimizeAdd(Opc, DstOps, SrcOps, Flag); + case TargetOpcode::G_SUB: + return optimizeSub(Opc, DstOps, SrcOps, Flag); + case TargetOpcode::G_MUL: + return optimizeMul(Opc, DstOps, SrcOps, Flag); + default: + return CSEMIRBuilder::buildInstr(Opc, DstOps, SrcOps, Flag); + } +} diff --git a/llvm/lib/CodeGen/GlobalISel/Utils.cpp b/llvm/lib/CodeGen/GlobalISel/Utils.cpp index 223d69c362185..bf4aa11cfa56b 100644 --- a/llvm/lib/CodeGen/GlobalISel/Utils.cpp +++ b/llvm/lib/CodeGen/GlobalISel/Utils.cpp @@ -1997,6 +1997,19 @@ APInt llvm::GIConstant::getScalarValue() const { return Value; } +APInt llvm::GIConstant::getSplatValue() const { + assert(Kind == GIConstantKind::ScalableVector && + "Expected scalable constant"); + + return Value; +} + +ArrayRef llvm::GIConstant::getAsArrayRef() const { + assert(Kind == GIConstantKind::FixedVector && + "Expected fixed vector constant"); + return Values; +} + std::optional llvm::GIConstant::getConstant(Register Const, const MachineRegisterInfo &MRI) { MachineInstr *Constant = getDefIgnoringCopies(Const, MRI); @@ -2031,6 +2044,117 @@ llvm::GIConstant::getConstant(Register Const, const MachineRegisterInfo &MRI) { return GIConstant(MayBeConstant->Value, GIConstantKind::Scalar); } +/// Returns a new constant where add(x) was applied. +GIConstant llvm::GIConstant::add(const GIConstant &RHS) const { + switch (getKind()) { + case GIConstantKind::ScalableVector: + return GIConstant(Value + RHS.Value, GIConstantKind::ScalableVector); + case GIConstantKind::Scalar: { + return GIConstant(Value + RHS.Value, GIConstantKind::Scalar); + } + case GIConstantKind::FixedVector: { + SmallVector Adds; + for (unsigned I = 0, E = Values.size(); I < E; ++I) + Adds.push_back(Values[I] + RHS.Values[I]); + return GIConstant(Adds); + } + } +} + +/// Returns a new constant where sub(x) was applied. +GIConstant llvm::GIConstant::sub(const GIConstant &RHS) const { + switch (getKind()) { + case GIConstantKind::ScalableVector: + return GIConstant(Value - RHS.Value, GIConstantKind::ScalableVector); + case GIConstantKind::Scalar: { + return GIConstant(Value - RHS.Value, GIConstantKind::Scalar); + } + case GIConstantKind::FixedVector: { + SmallVector Subs; + for (unsigned I = 0, E = Values.size(); I < E; ++I) + Subs.push_back(Values[I] - RHS.Values[I]); + return GIConstant(Subs); + } + } +} + +/// Returns a new constant where mul(x) was applied. +GIConstant llvm::GIConstant::mul(const GIConstant &RHS) const { + switch (getKind()) { + case GIConstantKind::ScalableVector: + return GIConstant(Value * RHS.Value, GIConstantKind::ScalableVector); + case GIConstantKind::Scalar: { + return GIConstant(Value * RHS.Value, GIConstantKind::Scalar); + } + case GIConstantKind::FixedVector: { + SmallVector Muls; + for (unsigned I = 0, E = Values.size(); I < E; ++I) + Muls.push_back(Values[I] * RHS.Values[I]); + return GIConstant(Muls); + } + } +} + +bool llvm::GIConstant::isZero() const { + switch (Kind) { + case GIConstantKind::Scalar: + return Value.isZero(); + case GIConstantKind::ScalableVector: + return Value.isZero(); + case GIConstantKind::FixedVector: { + for (const APInt &V : Values) + if (!V.isZero()) + return false; + return true; + } + } +} + +bool llvm::GIConstant::isOne() const { + switch (Kind) { + case GIConstantKind::Scalar: + return Value.isOne(); + case GIConstantKind::ScalableVector: + return Value.isOne(); + case GIConstantKind::FixedVector: { + for (const APInt &V : Values) + if (!V.isOne()) + return false; + return true; + } + } +} + +bool llvm::GIConstant::isTwo() const { + switch (Kind) { + case GIConstantKind::Scalar: + return Value.getLimitedValue() == 2; + case GIConstantKind::ScalableVector: + return Value.getLimitedValue() == 2; + case GIConstantKind::FixedVector: { + for (const APInt &V : Values) + if (V.getLimitedValue() != 2) + return false; + return true; + } + } +} + +bool llvm::GIConstant::isAllOnes() const { + switch (Kind) { + case GIConstantKind::Scalar: + return Value.isAllOnes(); + case GIConstantKind::ScalableVector: + return Value.isAllOnes(); + case GIConstantKind::FixedVector: { + for (const APInt &V : Values) + if (!V.isAllOnes()) + return false; + return true; + } + } +} + APFloat llvm::GFConstant::getScalarValue() const { assert(Kind == GFConstantKind::Scalar && "Expected scalar constant"); diff --git a/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerCombiner.cpp b/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerCombiner.cpp index d4a14f8756304..3e077611fc287 100644 --- a/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerCombiner.cpp +++ b/llvm/lib/Target/AArch64/GISel/AArch64PostLegalizerCombiner.cpp @@ -32,6 +32,7 @@ #include "llvm/CodeGen/GlobalISel/GenericMachineInstrs.h" #include "llvm/CodeGen/GlobalISel/MIPatternMatch.h" #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h" +#include "llvm/CodeGen/GlobalISel/OptMIRBuilder.h" #include "llvm/CodeGen/GlobalISel/Utils.h" #include "llvm/CodeGen/MachineDominators.h" #include "llvm/CodeGen/MachineFunctionPass.h" @@ -440,6 +441,9 @@ void applyCombineMulCMLT(MachineInstr &MI, MachineRegisterInfo &MRI, class AArch64PostLegalizerCombinerImpl : public Combiner { protected: + std::unique_ptr Opt; + // hides Combiner::B + MachineIRBuilder &B; const CombinerHelper Helper; const AArch64PostLegalizerCombinerImplRuleConfig &RuleConfig; const AArch64Subtarget &STI; @@ -473,7 +477,9 @@ AArch64PostLegalizerCombinerImpl::AArch64PostLegalizerCombinerImpl( const AArch64Subtarget &STI, MachineDominatorTree *MDT, const LegalizerInfo *LI) : Combiner(MF, CInfo, TPC, &VT, CSEInfo), - Helper(Observer, B, /*IsPreLegalize*/ false, &VT, MDT, LI), + Opt(std::make_unique(MF, CSEInfo, Observer, LI, + /*IsPrelegalize=*/false)), + B(*Opt), Helper(Observer, B, /*IsPreLegalize*/ false, &VT, MDT, LI), RuleConfig(RuleConfig), STI(STI), #define GET_GICOMBINER_CONSTRUCTOR_INITS #include "AArch64GenPostLegalizeGICombiner.inc" diff --git a/llvm/test/CodeGen/AArch64/GlobalISel/opt-mir-builder.mir b/llvm/test/CodeGen/AArch64/GlobalISel/opt-mir-builder.mir new file mode 100644 index 0000000000000..e9f31fdc7edb3 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/GlobalISel/opt-mir-builder.mir @@ -0,0 +1,166 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -mtriple aarch64 -run-pass=aarch64-postlegalizer-combiner -verify-machineinstrs %s -o - | FileCheck %s + +--- +name: and_undef +legalized: true +body: | + bb.0: + liveins: $w0, $w1 + + ; CHECK-LABEL: name: and_undef + ; CHECK: liveins: $w0, $w1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: %undef:_(s32) = G_IMPLICIT_DEF + ; CHECK-NEXT: $w0 = COPY %undef(s32) + ; CHECK-NEXT: RET_ReallyLR implicit $w0 + %x:_(s32) = COPY $w0 + %undef:_(s32) = G_IMPLICIT_DEF + %add:_(s32) = G_ADD %x, %undef + $w0 = COPY %add + RET_ReallyLR implicit $w0 +... +--- +name: and_const +legalized: true +body: | + bb.0: + liveins: $w0, $w1 + + ; CHECK-LABEL: name: and_const + ; CHECK: liveins: $w0, $w1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: %x:_(s32) = COPY $w0 + ; CHECK-NEXT: %add:_(s32) = G_CONSTANT i32 8 + ; CHECK-NEXT: $w0 = COPY %add(s32) + ; CHECK-NEXT: RET_ReallyLR implicit $w0 + %x:_(s32) = COPY $w0 + %four:_(s32) = G_CONSTANT i32 4 + %add:_(s32) = G_ADD %four, %four + $w0 = COPY %add + RET_ReallyLR implicit $w0 +... +--- +name: sub_undef +legalized: true +body: | + bb.0: + liveins: $w0, $w1 + + ; CHECK-LABEL: name: sub_undef + ; CHECK: liveins: $w0, $w1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: %undef:_(s32) = G_IMPLICIT_DEF + ; CHECK-NEXT: $w0 = COPY %undef(s32) + ; CHECK-NEXT: RET_ReallyLR implicit $w0 + %x:_(s32) = COPY $w0 + %undef:_(s32) = G_IMPLICIT_DEF + %sub:_(s32) = G_SUB %x, %undef + $w0 = COPY %sub + RET_ReallyLR implicit $w0 +... +--- +name: mul_undef +legalized: true +body: | + bb.0: + liveins: $w0, $w1 + + ; CHECK-LABEL: name: mul_undef + ; CHECK: liveins: $w0, $w1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: %mul:_(s32) = G_CONSTANT i32 0 + ; CHECK-NEXT: $w0 = COPY %mul(s32) + ; CHECK-NEXT: RET_ReallyLR implicit $w0 + %x:_(s32) = COPY $w0 + %undef:_(s32) = G_IMPLICIT_DEF + %mul:_(s32) = G_MUL %x, %undef + $w0 = COPY %mul + RET_ReallyLR implicit $w0 +... +--- +name: mul_0 +legalized: true +body: | + bb.0: + liveins: $w0, $w1 + + ; CHECK-LABEL: name: mul_0 + ; CHECK: liveins: $w0, $w1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: %x:_(s32) = COPY $w0 + ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB %x, %x + ; CHECK-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 32 + ; CHECK-NEXT: %mul:_(s32) = G_SHL [[SUB]], [[C]](s64) + ; CHECK-NEXT: $w0 = COPY %mul(s32) + ; CHECK-NEXT: RET_ReallyLR implicit $w0 + %x:_(s32) = COPY $w0 + %zero:_(s32) = G_CONSTANT i32 0 + %mul:_(s32) = G_MUL %x, %zero + $w0 = COPY %mul + RET_ReallyLR implicit $w0 +... +--- +name: mul_1 +legalized: true +body: | + bb.0: + liveins: $w0, $w1 + + ; CHECK-LABEL: name: mul_1 + ; CHECK: liveins: $w0, $w1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: %x:_(s32) = COPY $w0 + ; CHECK-NEXT: [[C:%[0-9]+]]:_(s64) = G_CONSTANT i64 1 + ; CHECK-NEXT: [[SHL:%[0-9]+]]:_(s32) = G_SHL %x, [[C]](s64) + ; CHECK-NEXT: [[SUB:%[0-9]+]]:_(s32) = G_SUB [[SHL]], %x + ; CHECK-NEXT: $w0 = COPY [[SUB]](s32) + ; CHECK-NEXT: RET_ReallyLR implicit $w0 + %x:_(s32) = COPY $w0 + %one:_(s32) = G_CONSTANT i32 1 + %mul:_(s32) = G_MUL %x, %one + $w0 = COPY %mul + RET_ReallyLR implicit $w0 +... +--- +name: mul_2 +legalized: true +body: | + bb.0: + liveins: $w0, $w1 + + ; CHECK-LABEL: name: mul_2 + ; CHECK: liveins: $w0, $w1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: %x:_(s32) = COPY $w0 + ; CHECK-NEXT: %two:_(s32) = G_CONSTANT i32 2 + ; CHECK-NEXT: %mul:_(s32) = G_MUL %x, %two + ; CHECK-NEXT: $w0 = COPY %mul(s32) + ; CHECK-NEXT: RET_ReallyLR implicit $w0 + %x:_(s32) = COPY $w0 + %two:_(s32) = G_CONSTANT i32 2 + %mul:_(s32) = G_MUL %x, %two + $w0 = COPY %mul + RET_ReallyLR implicit $w0 +... +--- +name: mul_const +legalized: true +body: | + bb.0: + liveins: $w0, $w1 + + ; CHECK-LABEL: name: mul_const + ; CHECK: liveins: $w0, $w1 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: %x:_(s32) = COPY $w0 + ; CHECK-NEXT: %mul:_(s32) = G_CONSTANT i32 4 + ; CHECK-NEXT: $w0 = COPY %mul(s32) + ; CHECK-NEXT: RET_ReallyLR implicit $w0 + %x:_(s32) = COPY $w0 + %two:_(s32) = G_CONSTANT i32 2 + %mul:_(s32) = G_MUL %two, %two + $w0 = COPY %mul + RET_ReallyLR implicit $w0 + +... diff --git a/llvm/test/CodeGen/AArch64/fsh.ll b/llvm/test/CodeGen/AArch64/fsh.ll index c084813760b80..2cee2f2b2686c 100644 --- a/llvm/test/CodeGen/AArch64/fsh.ll +++ b/llvm/test/CodeGen/AArch64/fsh.ll @@ -3905,102 +3905,66 @@ entry: } define <8 x i8> @fshl_v8i8_c(<8 x i8> %a, <8 x i8> %b) { -; CHECK-SD-LABEL: fshl_v8i8_c: -; CHECK-SD: // %bb.0: // %entry -; CHECK-SD-NEXT: shl v0.8b, v0.8b, #3 -; CHECK-SD-NEXT: usra v0.8b, v1.8b, #5 -; CHECK-SD-NEXT: ret -; -; CHECK-GI-LABEL: fshl_v8i8_c: -; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: shl v0.8b, v0.8b, #3 -; CHECK-GI-NEXT: usra v0.8b, v1.8b, #5 -; CHECK-GI-NEXT: ret +; CHECK-LABEL: fshl_v8i8_c: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: shl v0.8b, v0.8b, #3 +; CHECK-NEXT: usra v0.8b, v1.8b, #5 +; CHECK-NEXT: ret entry: %d = call <8 x i8> @llvm.fshl(<8 x i8> %a, <8 x i8> %b, <8 x i8> ) ret <8 x i8> %d } define <8 x i8> @fshr_v8i8_c(<8 x i8> %a, <8 x i8> %b) { -; CHECK-SD-LABEL: fshr_v8i8_c: -; CHECK-SD: // %bb.0: // %entry -; CHECK-SD-NEXT: shl v0.8b, v0.8b, #5 -; CHECK-SD-NEXT: usra v0.8b, v1.8b, #3 -; CHECK-SD-NEXT: ret -; -; CHECK-GI-LABEL: fshr_v8i8_c: -; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: shl v0.8b, v0.8b, #5 -; CHECK-GI-NEXT: usra v0.8b, v1.8b, #3 -; CHECK-GI-NEXT: ret +; CHECK-LABEL: fshr_v8i8_c: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: shl v0.8b, v0.8b, #5 +; CHECK-NEXT: usra v0.8b, v1.8b, #3 +; CHECK-NEXT: ret entry: %d = call <8 x i8> @llvm.fshr(<8 x i8> %a, <8 x i8> %b, <8 x i8> ) ret <8 x i8> %d } define <16 x i8> @fshl_v16i8_c(<16 x i8> %a, <16 x i8> %b) { -; CHECK-SD-LABEL: fshl_v16i8_c: -; CHECK-SD: // %bb.0: // %entry -; CHECK-SD-NEXT: shl v0.16b, v0.16b, #3 -; CHECK-SD-NEXT: usra v0.16b, v1.16b, #5 -; CHECK-SD-NEXT: ret -; -; CHECK-GI-LABEL: fshl_v16i8_c: -; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: shl v0.16b, v0.16b, #3 -; CHECK-GI-NEXT: usra v0.16b, v1.16b, #5 -; CHECK-GI-NEXT: ret +; CHECK-LABEL: fshl_v16i8_c: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: shl v0.16b, v0.16b, #3 +; CHECK-NEXT: usra v0.16b, v1.16b, #5 +; CHECK-NEXT: ret entry: %d = call <16 x i8> @llvm.fshl(<16 x i8> %a, <16 x i8> %b, <16 x i8> ) ret <16 x i8> %d } define <16 x i8> @fshr_v16i8_c(<16 x i8> %a, <16 x i8> %b) { -; CHECK-SD-LABEL: fshr_v16i8_c: -; CHECK-SD: // %bb.0: // %entry -; CHECK-SD-NEXT: shl v0.16b, v0.16b, #5 -; CHECK-SD-NEXT: usra v0.16b, v1.16b, #3 -; CHECK-SD-NEXT: ret -; -; CHECK-GI-LABEL: fshr_v16i8_c: -; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: shl v0.16b, v0.16b, #5 -; CHECK-GI-NEXT: usra v0.16b, v1.16b, #3 -; CHECK-GI-NEXT: ret +; CHECK-LABEL: fshr_v16i8_c: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: shl v0.16b, v0.16b, #5 +; CHECK-NEXT: usra v0.16b, v1.16b, #3 +; CHECK-NEXT: ret entry: %d = call <16 x i8> @llvm.fshr(<16 x i8> %a, <16 x i8> %b, <16 x i8> ) ret <16 x i8> %d } define <4 x i16> @fshl_v4i16_c(<4 x i16> %a, <4 x i16> %b) { -; CHECK-SD-LABEL: fshl_v4i16_c: -; CHECK-SD: // %bb.0: // %entry -; CHECK-SD-NEXT: shl v0.4h, v0.4h, #3 -; CHECK-SD-NEXT: usra v0.4h, v1.4h, #13 -; CHECK-SD-NEXT: ret -; -; CHECK-GI-LABEL: fshl_v4i16_c: -; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: shl v0.4h, v0.4h, #3 -; CHECK-GI-NEXT: usra v0.4h, v1.4h, #13 -; CHECK-GI-NEXT: ret +; CHECK-LABEL: fshl_v4i16_c: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: shl v0.4h, v0.4h, #3 +; CHECK-NEXT: usra v0.4h, v1.4h, #13 +; CHECK-NEXT: ret entry: %d = call <4 x i16> @llvm.fshl(<4 x i16> %a, <4 x i16> %b, <4 x i16> ) ret <4 x i16> %d } define <4 x i16> @fshr_v4i16_c(<4 x i16> %a, <4 x i16> %b) { -; CHECK-SD-LABEL: fshr_v4i16_c: -; CHECK-SD: // %bb.0: // %entry -; CHECK-SD-NEXT: shl v0.4h, v0.4h, #13 -; CHECK-SD-NEXT: usra v0.4h, v1.4h, #3 -; CHECK-SD-NEXT: ret -; -; CHECK-GI-LABEL: fshr_v4i16_c: -; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: shl v0.4h, v0.4h, #13 -; CHECK-GI-NEXT: usra v0.4h, v1.4h, #3 -; CHECK-GI-NEXT: ret +; CHECK-LABEL: fshr_v4i16_c: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: shl v0.4h, v0.4h, #13 +; CHECK-NEXT: usra v0.4h, v1.4h, #3 +; CHECK-NEXT: ret entry: %d = call <4 x i16> @llvm.fshr(<4 x i16> %a, <4 x i16> %b, <4 x i16> ) ret <4 x i16> %d @@ -4087,34 +4051,22 @@ entry: } define <8 x i16> @fshl_v8i16_c(<8 x i16> %a, <8 x i16> %b) { -; CHECK-SD-LABEL: fshl_v8i16_c: -; CHECK-SD: // %bb.0: // %entry -; CHECK-SD-NEXT: shl v0.8h, v0.8h, #3 -; CHECK-SD-NEXT: usra v0.8h, v1.8h, #13 -; CHECK-SD-NEXT: ret -; -; CHECK-GI-LABEL: fshl_v8i16_c: -; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: shl v0.8h, v0.8h, #3 -; CHECK-GI-NEXT: usra v0.8h, v1.8h, #13 -; CHECK-GI-NEXT: ret +; CHECK-LABEL: fshl_v8i16_c: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: shl v0.8h, v0.8h, #3 +; CHECK-NEXT: usra v0.8h, v1.8h, #13 +; CHECK-NEXT: ret entry: %d = call <8 x i16> @llvm.fshl(<8 x i16> %a, <8 x i16> %b, <8 x i16> ) ret <8 x i16> %d } define <8 x i16> @fshr_v8i16_c(<8 x i16> %a, <8 x i16> %b) { -; CHECK-SD-LABEL: fshr_v8i16_c: -; CHECK-SD: // %bb.0: // %entry -; CHECK-SD-NEXT: shl v0.8h, v0.8h, #13 -; CHECK-SD-NEXT: usra v0.8h, v1.8h, #3 -; CHECK-SD-NEXT: ret -; -; CHECK-GI-LABEL: fshr_v8i16_c: -; CHECK-GI: // %bb.0: // %entry -; CHECK-GI-NEXT: shl v0.8h, v0.8h, #13 -; CHECK-GI-NEXT: usra v0.8h, v1.8h, #3 -; CHECK-GI-NEXT: ret +; CHECK-LABEL: fshr_v8i16_c: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: shl v0.8h, v0.8h, #13 +; CHECK-NEXT: usra v0.8h, v1.8h, #3 +; CHECK-NEXT: ret entry: %d = call <8 x i16> @llvm.fshr(<8 x i16> %a, <8 x i16> %b, <8 x i16> ) ret <8 x i16> %d