| //===- AMDGPUMCExpr.cpp - AMDGPU specific MC expression classes -----------===// | |
| // | |
| // 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 | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "AMDGPUMCExpr.h" | |
| #include "Utils/AMDGPUBaseInfo.h" | |
| #include "llvm/MC/MCAsmInfo.h" | |
| #include "llvm/MC/MCAssembler.h" | |
| #include "llvm/MC/MCContext.h" | |
| #include "llvm/MC/MCStreamer.h" | |
| #include "llvm/MC/MCSubtargetInfo.h" | |
| #include "llvm/MC/MCSymbol.h" | |
| #include "llvm/MC/MCValue.h" | |
| #include "llvm/Support/AMDHSAKernelDescriptor.h" | |
| #include "llvm/Support/KnownBits.h" | |
| #include "llvm/Support/MathExtras.h" | |
| #include "llvm/Support/raw_ostream.h" | |
| #include <functional> | |
| #include <optional> | |
| using namespace llvm; | |
| using namespace llvm::AMDGPU; | |
| AMDGPUMCExpr::AMDGPUMCExpr(VariantKind Kind, ArrayRef<const MCExpr *> Args, | |
| MCContext &Ctx) | |
| : Kind(Kind), Ctx(Ctx) { | |
| assert(Args.size() >= 1 && "Needs a minimum of one expression."); | |
| assert(Kind != AGVK_None && "Cannot construct AMDGPUMCExpr of kind none."); | |
| assert((getNumExpectedArgs(Kind) == 0 || | |
| Args.size() == getNumExpectedArgs(Kind)) && | |
| "wrong number of operands for AMDGPUMCExpr kind."); | |
| // Allocating the variadic arguments through the same allocation mechanism | |
| // that the object itself is allocated with so they end up in the same memory. | |
| // | |
| // Will result in an asan failure if allocated on the heap through standard | |
| // allocation (e.g., through SmallVector's grow). | |
| RawArgs = static_cast<const MCExpr **>( | |
| Ctx.allocate(sizeof(const MCExpr *) * Args.size())); | |
| llvm::uninitialized_copy(Args, RawArgs); | |
| this->Args = ArrayRef<const MCExpr *>(RawArgs, Args.size()); | |
| } | |
| AMDGPUMCExpr::~AMDGPUMCExpr() { Ctx.deallocate(RawArgs); } | |
| const AMDGPUMCExpr *AMDGPUMCExpr::create(VariantKind Kind, | |
| ArrayRef<const MCExpr *> Args, | |
| MCContext &Ctx) { | |
| return new (Ctx) AMDGPUMCExpr(Kind, Args, Ctx); | |
| } | |
| unsigned AMDGPUMCExpr::getNumExpectedArgs(VariantKind Kind) { | |
| switch (Kind) { | |
| case AGVK_None: | |
| llvm_unreachable("AGVK_None is not a valid AMDGPUMCExpr kind."); | |
| case AGVK_Or: | |
| case AGVK_Max: | |
| case AGVK_Min: | |
| // Variadic (parser requires >= 1). | |
| return 0; | |
| case AGVK_Lit: | |
| case AGVK_Lit64: | |
| case AGVK_InstPrefSize: | |
| return 1; | |
| case AGVK_TotalNumVGPRs: | |
| case AGVK_AlignTo: | |
| return 2; | |
| case AGVK_ExtraSGPRs: | |
| return 3; | |
| case AGVK_Occupancy: | |
| return 9; | |
| } | |
| llvm_unreachable("unknown AMDGPUMCExpr kind."); | |
| } | |
| const MCExpr *AMDGPUMCExpr::getSubExpr(size_t Index) const { | |
| assert(Index < Args.size() && "Indexing out of bounds AMDGPUMCExpr sub-expr"); | |
| return Args[Index]; | |
| } | |
| void AMDGPUMCExpr::printImpl(raw_ostream &OS, const MCAsmInfo *MAI) const { | |
| switch (Kind) { | |
| default: | |
| llvm_unreachable("Unknown AMDGPUMCExpr kind."); | |
| case AGVK_Or: | |
| OS << "or("; | |
| break; | |
| case AGVK_Max: | |
| OS << "max("; | |
| break; | |
| case AGVK_Min: | |
| OS << "min("; | |
| break; | |
| case AGVK_ExtraSGPRs: | |
| OS << "extrasgprs("; | |
| break; | |
| case AGVK_TotalNumVGPRs: | |
| OS << "totalnumvgprs("; | |
| break; | |
| case AGVK_AlignTo: | |
| OS << "alignto("; | |
| break; | |
| case AGVK_Occupancy: | |
| OS << "occupancy("; | |
| break; | |
| case AGVK_InstPrefSize: | |
| OS << "instprefsize("; | |
| break; | |
| case AGVK_Lit: | |
| OS << "lit("; | |
| break; | |
| case AGVK_Lit64: | |
| OS << "lit64("; | |
| break; | |
| } | |
| for (const auto *It = Args.begin(); It != Args.end(); ++It) { | |
| MAI->printExpr(OS, **It); | |
| if ((It + 1) != Args.end()) | |
| OS << ", "; | |
| } | |
| OS << ')'; | |
| } | |
| static int64_t op(AMDGPUMCExpr::VariantKind Kind, int64_t Arg1, int64_t Arg2) { | |
| switch (Kind) { | |
| default: | |
| llvm_unreachable("Unknown AMDGPUMCExpr kind."); | |
| case AMDGPUMCExpr::AGVK_Max: | |
| return std::max(Arg1, Arg2); | |
| case AMDGPUMCExpr::AGVK_Or: | |
| return Arg1 | Arg2; | |
| case AMDGPUMCExpr::AGVK_Min: | |
| return std::min(Arg1, Arg2); | |
| } | |
| } | |
| static bool | |
| evaluateMCExprs(ArrayRef<const MCExpr *> Exprs, const MCAssembler *Asm, | |
| std::initializer_list<std::reference_wrapper<uint64_t>> Vals) { | |
| return llvm::all_of(llvm::zip_equal(Exprs, Vals), [&](const auto &Pair) { | |
| auto [Expr, ValRef] = Pair; | |
| uint64_t &Val = ValRef.get(); | |
| MCValue MCVal; | |
| if (!Expr->evaluateAsRelocatable(MCVal, Asm) || !MCVal.isAbsolute()) | |
| return false; | |
| Val = MCVal.getConstant(); | |
| return true; | |
| }); | |
| } | |
| bool AMDGPUMCExpr::evaluateExtraSGPRs(MCValue &Res, | |
| const MCAssembler *Asm) const { | |
| const MCSubtargetInfo &STI = *Ctx.getSubtargetInfo(); | |
| uint64_t VCCUsed = 0, FlatScrUsed = 0, XNACKUsed = 0; | |
| if (!evaluateMCExprs(Args, Asm, {VCCUsed, FlatScrUsed, XNACKUsed})) | |
| return false; | |
| uint64_t ExtraSGPRs = IsaInfo::getNumExtraSGPRs( | |
| STI, (bool)VCCUsed, (bool)FlatScrUsed, (bool)XNACKUsed); | |
| Res = MCValue::get(ExtraSGPRs); | |
| return true; | |
| } | |
| bool AMDGPUMCExpr::evaluateTotalNumVGPR(MCValue &Res, | |
| const MCAssembler *Asm) const { | |
| const MCSubtargetInfo &STI = *Ctx.getSubtargetInfo(); | |
| uint64_t NumAGPR = 0, NumVGPR = 0; | |
| bool Has90AInsts = AMDGPU::isGFX90A(STI); | |
| if (!evaluateMCExprs(Args, Asm, {NumAGPR, NumVGPR})) | |
| return false; | |
| uint64_t TotalNum = Has90AInsts && NumAGPR ? alignTo(NumVGPR, 4) + NumAGPR | |
| : std::max(NumVGPR, NumAGPR); | |
| Res = MCValue::get(TotalNum); | |
| return true; | |
| } | |
| bool AMDGPUMCExpr::evaluateAlignTo(MCValue &Res, const MCAssembler *Asm) const { | |
| uint64_t Value = 0, Align = 0; | |
| if (!evaluateMCExprs(Args, Asm, {Value, Align})) | |
| return false; | |
| Res = MCValue::get(alignTo(Value, Align)); | |
| return true; | |
| } | |
| bool AMDGPUMCExpr::evaluateOccupancy(MCValue &Res, | |
| const MCAssembler *Asm) const { | |
| uint64_t InitOccupancy, MaxWaves, Granule, TargetTotalNumVGPRs, NumSGPRs, | |
| NumVGPRs, SGPRTotal, SGPRGranule, SGPRTrapReserve; | |
| // Leading operands are known constants (wave/VGPR caps + the SGPR budget | |
| // total/granule/trap reserve baked in by createOccupancy); only NumSGPRs and | |
| // NumVGPRs can still be symbolic. The SGPR budget makes the SGPR-limited | |
| // occupancy match getMaxNumSGPRs(). | |
| bool Success = | |
| evaluateMCExprs(Args.slice(0, 7), Asm, | |
| {MaxWaves, Granule, TargetTotalNumVGPRs, InitOccupancy, | |
| SGPRTotal, SGPRGranule, SGPRTrapReserve}); | |
| assert(Success && "Arguments 1 to 7 for Occupancy should be known constants"); | |
| if (!Success || !evaluateMCExprs(Args.slice(7, 2), Asm, {NumSGPRs, NumVGPRs})) | |
| return false; | |
| unsigned Occupancy = InitOccupancy; | |
| if (NumSGPRs) | |
| Occupancy = std::min(Occupancy, IsaInfo::getOccupancyWithNumSGPRs( | |
| NumSGPRs, MaxWaves, SGPRTotal, | |
| SGPRGranule, SGPRTrapReserve)); | |
| if (NumVGPRs) | |
| Occupancy = std::min(Occupancy, | |
| IsaInfo::getNumWavesPerEUWithNumVGPRs( | |
| NumVGPRs, Granule, MaxWaves, TargetTotalNumVGPRs)); | |
| Res = MCValue::get(Occupancy); | |
| return true; | |
| } | |
| /// Get the inst_pref_size field width for the given subtarget. | |
| static unsigned getInstPrefSizeFieldWidth(const MCSubtargetInfo &STI) { | |
| if (AMDGPU::isGFX12Plus(STI)) | |
| return amdhsa::COMPUTE_PGM_RSRC3_GFX12_PLUS_INST_PREF_SIZE_WIDTH; | |
| return amdhsa::COMPUTE_PGM_RSRC3_GFX11_INST_PREF_SIZE_WIDTH; | |
| } | |
| bool AMDGPUMCExpr::evaluateInstPrefSize(MCValue &Res, | |
| const MCAssembler *Asm) const { | |
| uint64_t CodeSizeInBytes = 0; | |
| if (!evaluateMCExprs(Args, Asm, {CodeSizeInBytes})) | |
| return false; | |
| const MCSubtargetInfo *STI = Ctx.getSubtargetInfo(); | |
| unsigned FieldWidth = getInstPrefSizeFieldWidth(*STI); | |
| unsigned CacheLineSize = AMDGPU::IsaInfo::getInstCacheLineSize(*STI); | |
| uint64_t CodeSizeInLines = divideCeil(CodeSizeInBytes, CacheLineSize); | |
| uint64_t MaxVal = (1u << FieldWidth) - 1; | |
| Res = MCValue::get(std::min(CodeSizeInLines, MaxVal)); | |
| return true; | |
| } | |
| bool AMDGPUMCExpr::isSymbolUsedInExpression(const MCSymbol *Sym, | |
| const MCExpr *E) { | |
| switch (E->getKind()) { | |
| case MCExpr::Constant: | |
| return false; | |
| case MCExpr::Unary: | |
| return isSymbolUsedInExpression( | |
| Sym, static_cast<const MCUnaryExpr *>(E)->getSubExpr()); | |
| case MCExpr::Binary: { | |
| const MCBinaryExpr *BE = static_cast<const MCBinaryExpr *>(E); | |
| return isSymbolUsedInExpression(Sym, BE->getLHS()) || | |
| isSymbolUsedInExpression(Sym, BE->getRHS()); | |
| } | |
| case MCExpr::SymbolRef: { | |
| const MCSymbol &S = static_cast<const MCSymbolRefExpr *>(E)->getSymbol(); | |
| if (S.isVariable()) | |
| return isSymbolUsedInExpression(Sym, S.getVariableValue()); | |
| return &S == Sym; | |
| } | |
| case MCExpr::Specifier: | |
| case MCExpr::Target: { | |
| auto *TE = static_cast<const AMDGPUMCExpr *>(E); | |
| for (const MCExpr *E : TE->getArgs()) | |
| if (isSymbolUsedInExpression(Sym, E)) | |
| return true; | |
| return false; | |
| } | |
| } | |
| llvm_unreachable("Unknown expr kind!"); | |
| } | |
| bool AMDGPUMCExpr::evaluateAsRelocatableImpl(MCValue &Res, | |
| const MCAssembler *Asm) const { | |
| std::optional<int64_t> Total; | |
| switch (Kind) { | |
| default: | |
| break; | |
| case AGVK_ExtraSGPRs: | |
| return evaluateExtraSGPRs(Res, Asm); | |
| case AGVK_AlignTo: | |
| return evaluateAlignTo(Res, Asm); | |
| case AGVK_TotalNumVGPRs: | |
| return evaluateTotalNumVGPR(Res, Asm); | |
| case AGVK_Occupancy: | |
| return evaluateOccupancy(Res, Asm); | |
| case AGVK_InstPrefSize: | |
| return evaluateInstPrefSize(Res, Asm); | |
| case AGVK_Lit: | |
| case AGVK_Lit64: | |
| return Args[0]->evaluateAsRelocatable(Res, Asm); | |
| } | |
| for (const MCExpr *Arg : Args) { | |
| MCValue ArgRes; | |
| if (!Arg->evaluateAsRelocatable(ArgRes, Asm) || !ArgRes.isAbsolute()) | |
| return false; | |
| if (!Total.has_value()) | |
| Total = ArgRes.getConstant(); | |
| Total = op(Kind, *Total, ArgRes.getConstant()); | |
| } | |
| Res = MCValue::get(*Total); | |
| return true; | |
| } | |
| void AMDGPUMCExpr::visitUsedExpr(MCStreamer &Streamer) const { | |
| for (const MCExpr *Arg : Args) | |
| Streamer.visitUsedExpr(*Arg); | |
| } | |
| MCFragment *AMDGPUMCExpr::findAssociatedFragment() const { | |
| for (const MCExpr *Arg : Args) { | |
| if (Arg->findAssociatedFragment()) | |
| return Arg->findAssociatedFragment(); | |
| } | |
| return nullptr; | |
| } | |
| /// Allow delayed MCExpr resolve of ExtraSGPRs (in case VCCUsed or FlatScrUsed | |
| /// are unresolvable but needed for further MCExprs). Derived from | |
| /// implementation of IsaInfo::getNumExtraSGPRs in AMDGPUBaseInfo.cpp. | |
| /// | |
| const AMDGPUMCExpr *AMDGPUMCExpr::createExtraSGPRs(const MCExpr *VCCUsed, | |
| const MCExpr *FlatScrUsed, | |
| bool XNACKUsed, | |
| MCContext &Ctx) { | |
| return create(AGVK_ExtraSGPRs, | |
| {VCCUsed, FlatScrUsed, MCConstantExpr::create(XNACKUsed, Ctx)}, | |
| Ctx); | |
| } | |
| const AMDGPUMCExpr *AMDGPUMCExpr::createTotalNumVGPR(const MCExpr *NumAGPR, | |
| const MCExpr *NumVGPR, | |
| MCContext &Ctx) { | |
| return create(AGVK_TotalNumVGPRs, {NumAGPR, NumVGPR}, Ctx); | |
| } | |
| const AMDGPUMCExpr * | |
| AMDGPUMCExpr::createInstPrefSize(const MCExpr *CodeSizeBytes, MCContext &Ctx) { | |
| return create(AGVK_InstPrefSize, {CodeSizeBytes}, Ctx); | |
| } | |
| const AMDGPUMCExpr *AMDGPUMCExpr::createLit(LitModifier Lit, int64_t Value, | |
| MCContext &Ctx) { | |
| assert(Lit == LitModifier::Lit || Lit == LitModifier::Lit64); | |
| return create(Lit == LitModifier::Lit ? VariantKind::AGVK_Lit | |
| : VariantKind::AGVK_Lit64, | |
| {MCConstantExpr::create(Value, Ctx, /*PrintInHex=*/true)}, Ctx); | |
| } | |
| static KnownBits fromOptionalToKnownBits(std::optional<bool> CompareResult) { | |
| static constexpr unsigned BitWidth = 64; | |
| const APInt True(BitWidth, 1); | |
| const APInt False(BitWidth, 0); | |
| if (CompareResult) { | |
| return *CompareResult ? KnownBits::makeConstant(True) | |
| : KnownBits::makeConstant(False); | |
| } | |
| KnownBits UnknownBool(/*BitWidth=*/1); | |
| return UnknownBool.zext(BitWidth); | |
| } | |
| using KnownBitsMap = DenseMap<const MCExpr *, KnownBits>; | |
| static void knownBitsMapHelper(const MCExpr *Expr, KnownBitsMap &KBM, | |
| unsigned Depth = 0); | |
| static void binaryOpKnownBitsMapHelper(const MCExpr *Expr, KnownBitsMap &KBM, | |
| unsigned Depth) { | |
| static constexpr unsigned BitWidth = 64; | |
| const MCBinaryExpr *BExpr = cast<MCBinaryExpr>(Expr); | |
| const MCExpr *LHS = BExpr->getLHS(); | |
| const MCExpr *RHS = BExpr->getRHS(); | |
| knownBitsMapHelper(LHS, KBM, Depth + 1); | |
| knownBitsMapHelper(RHS, KBM, Depth + 1); | |
| KnownBits LHSKnown = KBM[LHS]; | |
| KnownBits RHSKnown = KBM[RHS]; | |
| switch (BExpr->getOpcode()) { | |
| default: | |
| KBM[Expr] = KnownBits(BitWidth); | |
| return; | |
| case MCBinaryExpr::Opcode::Add: | |
| KBM[Expr] = KnownBits::add(LHSKnown, RHSKnown); | |
| return; | |
| case MCBinaryExpr::Opcode::And: | |
| KBM[Expr] = LHSKnown & RHSKnown; | |
| return; | |
| case MCBinaryExpr::Opcode::Div: | |
| KBM[Expr] = KnownBits::sdiv(LHSKnown, RHSKnown); | |
| return; | |
| case MCBinaryExpr::Opcode::EQ: { | |
| std::optional<bool> CompareRes = KnownBits::eq(LHSKnown, RHSKnown); | |
| KBM[Expr] = fromOptionalToKnownBits(CompareRes); | |
| return; | |
| } | |
| case MCBinaryExpr::Opcode::NE: { | |
| std::optional<bool> CompareRes = KnownBits::ne(LHSKnown, RHSKnown); | |
| KBM[Expr] = fromOptionalToKnownBits(CompareRes); | |
| return; | |
| } | |
| case MCBinaryExpr::Opcode::GT: { | |
| std::optional<bool> CompareRes = KnownBits::sgt(LHSKnown, RHSKnown); | |
| KBM[Expr] = fromOptionalToKnownBits(CompareRes); | |
| return; | |
| } | |
| case MCBinaryExpr::Opcode::GTE: { | |
| std::optional<bool> CompareRes = KnownBits::sge(LHSKnown, RHSKnown); | |
| KBM[Expr] = fromOptionalToKnownBits(CompareRes); | |
| return; | |
| } | |
| case MCBinaryExpr::Opcode::LAnd: { | |
| std::optional<bool> CompareRes; | |
| const APInt False(BitWidth, 0); | |
| std::optional<bool> LHSBool = | |
| KnownBits::ne(LHSKnown, KnownBits::makeConstant(False)); | |
| std::optional<bool> RHSBool = | |
| KnownBits::ne(RHSKnown, KnownBits::makeConstant(False)); | |
| if (LHSBool && RHSBool) | |
| CompareRes = *LHSBool && *RHSBool; | |
| KBM[Expr] = fromOptionalToKnownBits(CompareRes); | |
| return; | |
| } | |
| case MCBinaryExpr::Opcode::LOr: { | |
| const APInt False(BitWidth, 0); | |
| KnownBits Bits = LHSKnown | RHSKnown; | |
| std::optional<bool> CompareRes = | |
| KnownBits::ne(Bits, KnownBits::makeConstant(False)); | |
| KBM[Expr] = fromOptionalToKnownBits(CompareRes); | |
| return; | |
| } | |
| case MCBinaryExpr::Opcode::LT: { | |
| std::optional<bool> CompareRes = KnownBits::slt(LHSKnown, RHSKnown); | |
| KBM[Expr] = fromOptionalToKnownBits(CompareRes); | |
| return; | |
| } | |
| case MCBinaryExpr::Opcode::LTE: { | |
| std::optional<bool> CompareRes = KnownBits::sle(LHSKnown, RHSKnown); | |
| KBM[Expr] = fromOptionalToKnownBits(CompareRes); | |
| return; | |
| } | |
| case MCBinaryExpr::Opcode::Mod: | |
| KBM[Expr] = KnownBits::srem(LHSKnown, RHSKnown); | |
| return; | |
| case MCBinaryExpr::Opcode::Mul: | |
| KBM[Expr] = KnownBits::mul(LHSKnown, RHSKnown); | |
| return; | |
| case MCBinaryExpr::Opcode::Or: | |
| KBM[Expr] = LHSKnown | RHSKnown; | |
| return; | |
| case MCBinaryExpr::Opcode::Shl: | |
| KBM[Expr] = KnownBits::shl(LHSKnown, RHSKnown); | |
| return; | |
| case MCBinaryExpr::Opcode::AShr: | |
| KBM[Expr] = KnownBits::ashr(LHSKnown, RHSKnown); | |
| return; | |
| case MCBinaryExpr::Opcode::LShr: | |
| KBM[Expr] = KnownBits::lshr(LHSKnown, RHSKnown); | |
| return; | |
| case MCBinaryExpr::Opcode::Sub: | |
| KBM[Expr] = KnownBits::sub(LHSKnown, RHSKnown); | |
| return; | |
| case MCBinaryExpr::Opcode::Xor: | |
| KBM[Expr] = LHSKnown ^ RHSKnown; | |
| return; | |
| } | |
| } | |
| static void unaryOpKnownBitsMapHelper(const MCExpr *Expr, KnownBitsMap &KBM, | |
| unsigned Depth) { | |
| static constexpr unsigned BitWidth = 64; | |
| const MCUnaryExpr *UExpr = cast<MCUnaryExpr>(Expr); | |
| knownBitsMapHelper(UExpr->getSubExpr(), KBM, Depth + 1); | |
| KnownBits KB = KBM[UExpr->getSubExpr()]; | |
| switch (UExpr->getOpcode()) { | |
| default: | |
| KBM[Expr] = KnownBits(BitWidth); | |
| return; | |
| case MCUnaryExpr::Opcode::Minus: { | |
| KB.makeNegative(); | |
| KBM[Expr] = std::move(KB); | |
| return; | |
| } | |
| case MCUnaryExpr::Opcode::Not: { | |
| KnownBits AllOnes(BitWidth); | |
| AllOnes.setAllOnes(); | |
| KBM[Expr] = KB ^ AllOnes; | |
| return; | |
| } | |
| case MCUnaryExpr::Opcode::Plus: { | |
| KB.makeNonNegative(); | |
| KBM[Expr] = std::move(KB); | |
| return; | |
| } | |
| } | |
| } | |
| static void targetOpKnownBitsMapHelper(const MCExpr *Expr, KnownBitsMap &KBM, | |
| unsigned Depth) { | |
| static constexpr unsigned BitWidth = 64; | |
| const AMDGPUMCExpr *AGVK = cast<AMDGPUMCExpr>(Expr); | |
| switch (AGVK->getKind()) { | |
| default: | |
| KBM[Expr] = KnownBits(BitWidth); | |
| return; | |
| case AMDGPUMCExpr::VariantKind::AGVK_Or: { | |
| knownBitsMapHelper(AGVK->getSubExpr(0), KBM, Depth + 1); | |
| KnownBits KB = KBM[AGVK->getSubExpr(0)]; | |
| for (const MCExpr *Arg : AGVK->getArgs()) { | |
| knownBitsMapHelper(Arg, KBM, Depth + 1); | |
| KB |= KBM[Arg]; | |
| } | |
| KBM[Expr] = std::move(KB); | |
| return; | |
| } | |
| case AMDGPUMCExpr::VariantKind::AGVK_Max: { | |
| knownBitsMapHelper(AGVK->getSubExpr(0), KBM, Depth + 1); | |
| KnownBits KB = KBM[AGVK->getSubExpr(0)]; | |
| for (const MCExpr *Arg : AGVK->getArgs()) { | |
| knownBitsMapHelper(Arg, KBM, Depth + 1); | |
| KB = KnownBits::smax(KB, KBM[Arg]); | |
| } | |
| KBM[Expr] = std::move(KB); | |
| return; | |
| } | |
| case AMDGPUMCExpr::VariantKind::AGVK_Min: { | |
| knownBitsMapHelper(AGVK->getSubExpr(0), KBM, Depth + 1); | |
| KnownBits KB = KBM[AGVK->getSubExpr(0)]; | |
| for (const MCExpr *Arg : AGVK->getArgs()) { | |
| knownBitsMapHelper(Arg, KBM, Depth + 1); | |
| KB = KnownBits::smin(KB, KBM[Arg]); | |
| } | |
| KBM[Expr] = std::move(KB); | |
| return; | |
| } | |
| case AMDGPUMCExpr::VariantKind::AGVK_ExtraSGPRs: | |
| case AMDGPUMCExpr::VariantKind::AGVK_TotalNumVGPRs: | |
| case AMDGPUMCExpr::VariantKind::AGVK_AlignTo: | |
| case AMDGPUMCExpr::VariantKind::AGVK_Occupancy: | |
| case AMDGPUMCExpr::VariantKind::AGVK_InstPrefSize: | |
| case AMDGPUMCExpr::VariantKind::AGVK_Lit: | |
| case AMDGPUMCExpr::VariantKind::AGVK_Lit64: { | |
| int64_t Val; | |
| if (AGVK->evaluateAsAbsolute(Val)) { | |
| APInt APValue(BitWidth, Val); | |
| KBM[Expr] = KnownBits::makeConstant(APValue); | |
| return; | |
| } | |
| if (AGVK->getKind() == AMDGPUMCExpr::VariantKind::AGVK_InstPrefSize) { | |
| // The result is clamped to (1 << FieldWidth) - 1, so upper bits are | |
| // known zero. FieldWidth is derived from the subtarget. | |
| const MCSubtargetInfo *STI = AGVK->getCtx().getSubtargetInfo(); | |
| unsigned FieldWidth = getInstPrefSizeFieldWidth(*STI); | |
| KnownBits KB(BitWidth); | |
| KB.Zero.setBitsFrom(FieldWidth); | |
| KBM[Expr] = KB; | |
| return; | |
| } | |
| KBM[Expr] = KnownBits(BitWidth); | |
| return; | |
| } | |
| } | |
| } | |
| static void knownBitsMapHelper(const MCExpr *Expr, KnownBitsMap &KBM, | |
| unsigned Depth) { | |
| static constexpr unsigned BitWidth = 64; | |
| int64_t Val; | |
| if (Expr->evaluateAsAbsolute(Val)) { | |
| APInt APValue(BitWidth, Val, /*isSigned=*/true); | |
| KBM[Expr] = KnownBits::makeConstant(APValue); | |
| return; | |
| } | |
| if (Depth == 16) { | |
| KBM[Expr] = KnownBits(BitWidth); | |
| return; | |
| } | |
| switch (Expr->getKind()) { | |
| case MCExpr::ExprKind::Binary: { | |
| binaryOpKnownBitsMapHelper(Expr, KBM, Depth); | |
| return; | |
| } | |
| case MCExpr::ExprKind::Constant: { | |
| const MCConstantExpr *CE = cast<MCConstantExpr>(Expr); | |
| APInt APValue(BitWidth, CE->getValue(), /*isSigned=*/true); | |
| KBM[Expr] = KnownBits::makeConstant(APValue); | |
| return; | |
| } | |
| case MCExpr::ExprKind::SymbolRef: { | |
| const MCSymbolRefExpr *RExpr = cast<MCSymbolRefExpr>(Expr); | |
| const MCSymbol &Sym = RExpr->getSymbol(); | |
| if (!Sym.isVariable()) { | |
| KBM[Expr] = KnownBits(BitWidth); | |
| return; | |
| } | |
| // Variable value retrieval is not for actual use but only for knownbits | |
| // analysis. | |
| const MCExpr *SymVal = Sym.getVariableValue(); | |
| knownBitsMapHelper(SymVal, KBM, Depth + 1); | |
| // Explicitly copy-construct so that there exists a local KnownBits in case | |
| // KBM[SymVal] gets invalidated after a potential growth through KBM[Expr]. | |
| KBM[Expr] = KnownBits(KBM[SymVal]); | |
| return; | |
| } | |
| case MCExpr::ExprKind::Unary: { | |
| unaryOpKnownBitsMapHelper(Expr, KBM, Depth); | |
| return; | |
| } | |
| case MCExpr::ExprKind::Target: { | |
| targetOpKnownBitsMapHelper(Expr, KBM, Depth); | |
| return; | |
| case MCExpr::Specifier: | |
| llvm_unreachable("unused by this backend"); | |
| } | |
| } | |
| } | |
| static const MCExpr *tryFoldHelper(const MCExpr *Expr, KnownBitsMap &KBM, | |
| MCContext &Ctx) { | |
| if (!KBM.count(Expr)) | |
| return Expr; | |
| auto ValueCheckKnownBits = [](KnownBits &KB, unsigned Value) -> bool { | |
| if (!KB.isConstant()) | |
| return false; | |
| return Value == KB.getConstant(); | |
| }; | |
| if (Expr->getKind() == MCExpr::ExprKind::Constant) | |
| return Expr; | |
| // Resolving unary operations to constants may make the value more ambiguous. | |
| // For example, `~62` becomes `-63`; however, to me it's more ambiguous if a | |
| // bit mask value is represented through a negative number. | |
| if (Expr->getKind() != MCExpr::ExprKind::Unary) { | |
| if (KBM[Expr].isConstant()) { | |
| APInt ConstVal = KBM[Expr].getConstant(); | |
| return MCConstantExpr::create(ConstVal.getSExtValue(), Ctx); | |
| } | |
| int64_t EvalValue; | |
| if (Expr->evaluateAsAbsolute(EvalValue)) | |
| return MCConstantExpr::create(EvalValue, Ctx); | |
| } | |
| switch (Expr->getKind()) { | |
| default: | |
| return Expr; | |
| case MCExpr::ExprKind::Binary: { | |
| const MCBinaryExpr *BExpr = cast<MCBinaryExpr>(Expr); | |
| const MCExpr *LHS = BExpr->getLHS(); | |
| const MCExpr *RHS = BExpr->getRHS(); | |
| switch (BExpr->getOpcode()) { | |
| default: | |
| return Expr; | |
| case MCBinaryExpr::Opcode::Sub: { | |
| if (ValueCheckKnownBits(KBM[RHS], 0)) | |
| return tryFoldHelper(LHS, KBM, Ctx); | |
| break; | |
| } | |
| case MCBinaryExpr::Opcode::Add: | |
| case MCBinaryExpr::Opcode::Or: { | |
| if (ValueCheckKnownBits(KBM[LHS], 0)) | |
| return tryFoldHelper(RHS, KBM, Ctx); | |
| if (ValueCheckKnownBits(KBM[RHS], 0)) | |
| return tryFoldHelper(LHS, KBM, Ctx); | |
| break; | |
| } | |
| case MCBinaryExpr::Opcode::Mul: { | |
| if (ValueCheckKnownBits(KBM[LHS], 1)) | |
| return tryFoldHelper(RHS, KBM, Ctx); | |
| if (ValueCheckKnownBits(KBM[RHS], 1)) | |
| return tryFoldHelper(LHS, KBM, Ctx); | |
| break; | |
| } | |
| case MCBinaryExpr::Opcode::Shl: | |
| case MCBinaryExpr::Opcode::AShr: | |
| case MCBinaryExpr::Opcode::LShr: { | |
| if (ValueCheckKnownBits(KBM[RHS], 0)) | |
| return tryFoldHelper(LHS, KBM, Ctx); | |
| if (ValueCheckKnownBits(KBM[LHS], 0)) | |
| return MCConstantExpr::create(0, Ctx); | |
| break; | |
| } | |
| case MCBinaryExpr::Opcode::And: { | |
| if (ValueCheckKnownBits(KBM[LHS], 0) || ValueCheckKnownBits(KBM[RHS], 0)) | |
| return MCConstantExpr::create(0, Ctx); | |
| break; | |
| } | |
| } | |
| const MCExpr *NewLHS = tryFoldHelper(LHS, KBM, Ctx); | |
| const MCExpr *NewRHS = tryFoldHelper(RHS, KBM, Ctx); | |
| if (NewLHS != LHS || NewRHS != RHS) | |
| return MCBinaryExpr::create(BExpr->getOpcode(), NewLHS, NewRHS, Ctx, | |
| BExpr->getLoc()); | |
| return Expr; | |
| } | |
| case MCExpr::ExprKind::Unary: { | |
| const MCUnaryExpr *UExpr = cast<MCUnaryExpr>(Expr); | |
| const MCExpr *SubExpr = UExpr->getSubExpr(); | |
| const MCExpr *NewSubExpr = tryFoldHelper(SubExpr, KBM, Ctx); | |
| if (SubExpr != NewSubExpr) | |
| return MCUnaryExpr::create(UExpr->getOpcode(), NewSubExpr, Ctx, | |
| UExpr->getLoc()); | |
| return Expr; | |
| } | |
| case MCExpr::ExprKind::Target: { | |
| const AMDGPUMCExpr *AGVK = cast<AMDGPUMCExpr>(Expr); | |
| SmallVector<const MCExpr *, 8> NewArgs; | |
| bool Changed = false; | |
| for (const MCExpr *Arg : AGVK->getArgs()) { | |
| const MCExpr *NewArg = tryFoldHelper(Arg, KBM, Ctx); | |
| NewArgs.push_back(NewArg); | |
| Changed |= Arg != NewArg; | |
| } | |
| return Changed ? AMDGPUMCExpr::create(AGVK->getKind(), NewArgs, Ctx) : Expr; | |
| } | |
| } | |
| return Expr; | |
| } | |
| const MCExpr *llvm::AMDGPU::foldAMDGPUMCExpr(const MCExpr *Expr, | |
| MCContext &Ctx) { | |
| KnownBitsMap KBM; | |
| knownBitsMapHelper(Expr, KBM); | |
| const MCExpr *NewExpr = tryFoldHelper(Expr, KBM, Ctx); | |
| return Expr != NewExpr ? NewExpr : Expr; | |
| } | |
| void llvm::AMDGPU::printAMDGPUMCExpr(const MCExpr *Expr, raw_ostream &OS, | |
| const MCAsmInfo *MAI) { | |
| int64_t Val; | |
| if (Expr->evaluateAsAbsolute(Val)) { | |
| OS << Val; | |
| return; | |
| } | |
| MAI->printExpr(OS, *Expr); | |
| } | |
| bool AMDGPU::isLitExpr(const MCExpr *Expr) { | |
| const auto *E = dyn_cast<AMDGPUMCExpr>(Expr); | |
| return E && (E->getKind() == AMDGPUMCExpr::AGVK_Lit || | |
| E->getKind() == AMDGPUMCExpr::AGVK_Lit64); | |
| } | |
| int64_t AMDGPU::getLitValue(const MCExpr *Expr) { | |
| assert(isLitExpr(Expr)); | |
| return cast<MCConstantExpr>(cast<AMDGPUMCExpr>(Expr)->getArgs()[0]) | |
| ->getValue(); | |
| } | |
| AMDGPUMCExpr::VariantKind AMDGPU::getExprKind(const MCExpr *Expr) { | |
| const auto *E = dyn_cast<AMDGPUMCExpr>(Expr); | |
| if (!E) | |
| return AMDGPUMCExpr::AGVK_None; | |
| return E->getKind(); | |
| } |