blob: fd2d6109ac23bc55a4b5bfb4f69c2e5b835dd461 [file] [log] [blame]
//===- 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 "GCNSubtarget.h"
#include "Utils/AMDGPUBaseInfo.h"
#include "llvm/IR/Function.h"
#include "llvm/MC/MCAsmInfo.h"
#include "llvm/MC/MCAssembler.h"
#include "llvm/MC/MCContext.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/MC/MCSymbol.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/KnownBits.h"
#include "llvm/Support/raw_ostream.h"
#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.");
// 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()));
std::uninitialized_copy(Args.begin(), Args.end(), 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);
}
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_ExtraSGPRs:
OS << "extrasgprs(";
break;
case AGVK_TotalNumVGPRs:
OS << "totalnumvgprs(";
break;
case AGVK_AlignTo:
OS << "alignto(";
break;
case AGVK_Occupancy:
OS << "occupancy(";
break;
}
for (const auto *It = Args.begin(); It != Args.end(); ++It) {
(*It)->print(OS, MAI, /*InParens=*/false);
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;
}
}
bool AMDGPUMCExpr::evaluateExtraSGPRs(MCValue &Res, const MCAssembler *Asm,
const MCFixup *Fixup) const {
auto TryGetMCExprValue = [&](const MCExpr *Arg, uint64_t &ConstantValue) {
MCValue MCVal;
if (!Arg->evaluateAsRelocatable(MCVal, Asm, Fixup) || !MCVal.isAbsolute())
return false;
ConstantValue = MCVal.getConstant();
return true;
};
assert(Args.size() == 3 &&
"AMDGPUMCExpr Argument count incorrect for ExtraSGPRs");
const MCSubtargetInfo *STI = Ctx.getSubtargetInfo();
uint64_t VCCUsed = 0, FlatScrUsed = 0, XNACKUsed = 0;
bool Success = TryGetMCExprValue(Args[2], XNACKUsed);
assert(Success && "Arguments 3 for ExtraSGPRs should be a known constant");
if (!Success || !TryGetMCExprValue(Args[0], VCCUsed) ||
!TryGetMCExprValue(Args[1], FlatScrUsed))
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 MCFixup *Fixup) const {
auto TryGetMCExprValue = [&](const MCExpr *Arg, uint64_t &ConstantValue) {
MCValue MCVal;
if (!Arg->evaluateAsRelocatable(MCVal, Asm, Fixup) || !MCVal.isAbsolute())
return false;
ConstantValue = MCVal.getConstant();
return true;
};
assert(Args.size() == 2 &&
"AMDGPUMCExpr Argument count incorrect for TotalNumVGPRs");
const MCSubtargetInfo *STI = Ctx.getSubtargetInfo();
uint64_t NumAGPR = 0, NumVGPR = 0;
bool Has90AInsts = AMDGPU::isGFX90A(*STI);
if (!TryGetMCExprValue(Args[0], NumAGPR) ||
!TryGetMCExprValue(Args[1], 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 MCFixup *Fixup) const {
auto TryGetMCExprValue = [&](const MCExpr *Arg, uint64_t &ConstantValue) {
MCValue MCVal;
if (!Arg->evaluateAsRelocatable(MCVal, Asm, Fixup) || !MCVal.isAbsolute())
return false;
ConstantValue = MCVal.getConstant();
return true;
};
assert(Args.size() == 2 &&
"AMDGPUMCExpr Argument count incorrect for AlignTo");
uint64_t Value = 0, Align = 0;
if (!TryGetMCExprValue(Args[0], Value) || !TryGetMCExprValue(Args[1], Align))
return false;
Res = MCValue::get(alignTo(Value, Align));
return true;
}
bool AMDGPUMCExpr::evaluateOccupancy(MCValue &Res, const MCAssembler *Asm,
const MCFixup *Fixup) const {
auto TryGetMCExprValue = [&](const MCExpr *Arg, uint64_t &ConstantValue) {
MCValue MCVal;
if (!Arg->evaluateAsRelocatable(MCVal, Asm, Fixup) || !MCVal.isAbsolute())
return false;
ConstantValue = MCVal.getConstant();
return true;
};
assert(Args.size() == 7 &&
"AMDGPUMCExpr Argument count incorrect for Occupancy");
uint64_t InitOccupancy, MaxWaves, Granule, TargetTotalNumVGPRs, Generation,
NumSGPRs, NumVGPRs;
bool Success = true;
Success &= TryGetMCExprValue(Args[0], MaxWaves);
Success &= TryGetMCExprValue(Args[1], Granule);
Success &= TryGetMCExprValue(Args[2], TargetTotalNumVGPRs);
Success &= TryGetMCExprValue(Args[3], Generation);
Success &= TryGetMCExprValue(Args[4], InitOccupancy);
assert(Success && "Arguments 1 to 5 for Occupancy should be known constants");
if (!Success || !TryGetMCExprValue(Args[5], NumSGPRs) ||
!TryGetMCExprValue(Args[6], NumVGPRs))
return false;
unsigned Occupancy = InitOccupancy;
if (NumSGPRs)
Occupancy = std::min(
Occupancy, IsaInfo::getOccupancyWithNumSGPRs(
NumSGPRs, MaxWaves,
static_cast<AMDGPUSubtarget::Generation>(Generation)));
if (NumVGPRs)
Occupancy = std::min(Occupancy,
IsaInfo::getNumWavesPerEUWithNumVGPRs(
NumVGPRs, Granule, MaxWaves, TargetTotalNumVGPRs));
Res = MCValue::get(Occupancy);
return true;
}
bool AMDGPUMCExpr::evaluateAsRelocatableImpl(MCValue &Res,
const MCAssembler *Asm,
const MCFixup *Fixup) const {
std::optional<int64_t> Total;
switch (Kind) {
default:
break;
case AGVK_ExtraSGPRs:
return evaluateExtraSGPRs(Res, Asm, Fixup);
case AGVK_AlignTo:
return evaluateAlignTo(Res, Asm, Fixup);
case AGVK_TotalNumVGPRs:
return evaluateTotalNumVGPR(Res, Asm, Fixup);
case AGVK_Occupancy:
return evaluateOccupancy(Res, Asm, Fixup);
}
for (const MCExpr *Arg : Args) {
MCValue ArgRes;
if (!Arg->evaluateAsRelocatable(ArgRes, Asm, Fixup) || !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);
}
/// Mimics GCNSubtarget::computeOccupancy for MCExpr.
///
/// Remove dependency on GCNSubtarget and depend only only the necessary values
/// for said occupancy computation. Should match computeOccupancy implementation
/// without passing \p STM on.
const AMDGPUMCExpr *AMDGPUMCExpr::createOccupancy(unsigned InitOcc,
const MCExpr *NumSGPRs,
const MCExpr *NumVGPRs,
const GCNSubtarget &STM,
MCContext &Ctx) {
unsigned MaxWaves = IsaInfo::getMaxWavesPerEU(&STM);
unsigned Granule = IsaInfo::getVGPRAllocGranule(&STM);
unsigned TargetTotalNumVGPRs = IsaInfo::getTotalNumVGPRs(&STM);
unsigned Generation = STM.getGeneration();
auto CreateExpr = [&Ctx](unsigned Value) {
return MCConstantExpr::create(Value, Ctx);
};
return create(AGVK_Occupancy,
{CreateExpr(MaxWaves), CreateExpr(Granule),
CreateExpr(TargetTotalNumVGPRs), CreateExpr(Generation),
CreateExpr(InitOcc), NumSGPRs, NumVGPRs},
Ctx);
}
bool AMDGPUMCExpr::isSymbolUsedInExpression(const MCSymbol *Sym) const {
for (const MCExpr *E : getArgs()) {
if (E->isSymbolUsedInExpression(Sym))
return true;
}
return false;
}
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] = KB;
return;
}
case MCUnaryExpr::Opcode::Not: {
KnownBits AllOnes(BitWidth);
AllOnes.setAllOnes();
KBM[Expr] = KB ^ AllOnes;
return;
}
case MCUnaryExpr::Opcode::Plus: {
KB.makeNonNegative();
KBM[Expr] = 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] = 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::umax(KB, KBM[Arg]);
}
KBM[Expr] = KB;
return;
}
case AMDGPUMCExpr::VariantKind::AGVK_ExtraSGPRs:
case AMDGPUMCExpr::VariantKind::AGVK_TotalNumVGPRs:
case AMDGPUMCExpr::VariantKind::AGVK_AlignTo:
case AMDGPUMCExpr::VariantKind::AGVK_Occupancy: {
int64_t Val;
if (AGVK->evaluateAsAbsolute(Val)) {
APInt APValue(BitWidth, Val);
KBM[Expr] = KnownBits::makeConstant(APValue);
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(/*setUsed=*/false);
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;
}
}
}
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;
}
Expr->print(OS, MAI);
}