Files
TheRock/rocm-libraries/shared/rocroller/lib/source/ExpressionTransformations/Simplify.cpp
T
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

941 lines
32 KiB
C++

// Copyright Advanced Micro Devices, Inc., or its affiliates.
// SPDX-License-Identifier: MIT
#include <rocRoller/Expression.hpp>
template <typename T>
constexpr auto cast_to_unsigned(T val)
{
return static_cast<typename std::make_unsigned<T>::type>(val);
}
namespace rocRoller
{
namespace Expression
{
/**
* Simplify trivial arithmetic expressions involving translation time constants.
*
* Simplifications:
* - Add two integers, or add 0
* - Multiply two integers, multiply by 0, multiply by 1
* - Divide by 1
* - Modulo by 1
* - Shift by 0
*/
template <typename T>
concept CIntegral = std::integral<T> && !std::same_as<bool, T>;
template <typename T>
concept CBoolean = std::same_as<bool, T>;
template <typename T>
struct SimplifyByConstant
{
VariableType resultVarType;
ExpressionPtr call(ExpressionPtr lhs, CommandArgumentValue rhs)
{
return nullptr;
}
};
template <typename T>
struct SimplifyByConstantLHS
{
VariableType resultVarType;
ExpressionPtr call(CommandArgumentValue lhs, ExpressionPtr rhs)
{
return nullptr;
}
};
template <>
struct SimplifyByConstant<Add>
{
VariableType resultVarType;
ExpressionPtr lhs;
template <typename RHS>
requires(CIntegral<RHS>) ExpressionPtr operator()(RHS rhs)
{
if(rhs == 0)
return lhs;
return nullptr;
}
template <typename RHS>
requires(!CIntegral<RHS>) ExpressionPtr operator()(RHS rhs)
{
return nullptr;
}
ExpressionPtr call(ExpressionPtr lhs_, CommandArgumentValue rhs)
{
lhs = lhs_;
return visit(*this, rhs);
}
};
template <>
struct SimplifyByConstant<Subtract>
{
VariableType resultVarType;
ExpressionPtr lhs;
template <typename RHS>
requires(CIntegral<RHS>) ExpressionPtr operator()(RHS rhs)
{
if(rhs == 0)
return lhs;
if constexpr(std::signed_integral<RHS>)
{
if(rhs < 0)
return lhs + literal(-rhs);
}
return nullptr;
}
template <typename RHS>
requires(!CIntegral<RHS>) ExpressionPtr operator()(RHS rhs)
{
return nullptr;
}
ExpressionPtr call(ExpressionPtr lhs_, CommandArgumentValue rhs)
{
lhs = lhs_;
return visit(*this, rhs);
}
};
template <CShift ShiftType>
struct SimplifyByConstant<ShiftType>
{
VariableType resultVarType;
ExpressionPtr lhs;
template <typename RHS>
requires(CIntegral<RHS>) ExpressionPtr operator()(RHS rhs)
{
if(rhs == 0)
return lhs;
if(CIsAnyOf<ShiftType, ShiftL, LogicalShiftR>)
{
auto const elementSize = resultVarType.getElementSize();
//
// Literal 0 can only accept Int32/UInt32/Int64/UInt64/Half/Float/Double
//
if((elementSize == 4u or elementSize == 8u) and (rhs >= elementSize * 8u))
{
return literal(0, resultVarType);
}
}
return nullptr;
}
template <typename RHS>
requires(!CIntegral<RHS>) ExpressionPtr operator()(RHS rhs)
{
return nullptr;
}
ExpressionPtr call(ExpressionPtr lhs_, CommandArgumentValue rhs)
{
lhs = lhs_;
return visit(*this, rhs);
}
};
template <>
struct SimplifyByConstant<BitwiseAnd>
{
VariableType resultVarType;
ExpressionPtr lhs;
template <typename RHS>
requires(CIntegral<RHS>) ExpressionPtr operator()(RHS rhs)
{
if(rhs == 0)
return literal(0, resultVarType);
return nullptr;
}
template <typename RHS>
requires(!CIntegral<RHS>) ExpressionPtr operator()(RHS rhs)
{
return nullptr;
}
ExpressionPtr call(ExpressionPtr lhs_, CommandArgumentValue rhs)
{
lhs = lhs_;
return visit(*this, rhs);
}
};
template <>
struct SimplifyByConstant<LogicalAnd>
{
VariableType resultVarType;
ExpressionPtr lhs;
template <typename RHS>
requires(CBoolean<RHS>) ExpressionPtr operator()(RHS rhs)
{
if(rhs == false)
return literal(false);
if(rhs == true)
return lhs;
return nullptr;
}
template <typename RHS>
requires(!CBoolean<RHS>) ExpressionPtr operator()(RHS rhs)
{
return nullptr;
}
ExpressionPtr call(ExpressionPtr lhs_, CommandArgumentValue rhs)
{
lhs = lhs_;
return visit(*this, rhs);
}
};
template <>
struct SimplifyByConstant<LogicalOr>
{
VariableType resultVarType;
ExpressionPtr lhs = nullptr;
template <typename RHS>
requires(CBoolean<RHS>) ExpressionPtr operator()(RHS rhs)
{
if(rhs == true)
return literal(true);
if(rhs == false)
return lhs;
return nullptr;
}
template <typename RHS>
requires(!CBoolean<RHS>) ExpressionPtr operator()(RHS rhs)
{
return nullptr;
}
ExpressionPtr call(ExpressionPtr lhs_, CommandArgumentValue rhs)
{
lhs = lhs_;
return visit(*this, rhs);
}
};
template <>
struct SimplifyByConstant<Multiply>
{
VariableType resultVarType;
ExpressionPtr lhs;
template <typename RHS>
requires(CIntegral<RHS>) ExpressionPtr operator()(RHS rhs)
{
if(rhs == 0)
return literal(0, resultVarType);
if(rhs == 1)
return lhs;
return nullptr;
}
template <typename RHS>
requires(!CIntegral<RHS>) ExpressionPtr operator()(RHS rhs)
{
return nullptr;
}
ExpressionPtr call(ExpressionPtr lhs_, CommandArgumentValue rhs)
{
lhs = lhs_;
return visit(*this, rhs);
}
};
template <>
struct SimplifyByConstant<Divide>
{
VariableType resultVarType;
ExpressionPtr lhs;
template <typename RHS>
requires(CIntegral<RHS>) ExpressionPtr operator()(RHS rhs)
{
if(rhs == 1)
return lhs;
return nullptr;
}
template <typename RHS>
requires(!CIntegral<RHS>) ExpressionPtr operator()(RHS rhs)
{
return nullptr;
}
ExpressionPtr call(ExpressionPtr lhs_, CommandArgumentValue rhs)
{
lhs = lhs_;
return visit(*this, rhs);
}
};
template <>
struct SimplifyByConstantLHS<Divide>
{
VariableType resultVarType;
ExpressionPtr rhs;
template <typename LHS>
requires(CIntegral<LHS>) ExpressionPtr operator()(LHS lhs)
{
if(lhs == 0)
return literal(0, resultVarType);
return nullptr;
}
template <typename LHS>
requires(!CIntegral<LHS>) ExpressionPtr operator()(LHS lhs)
{
return nullptr;
}
ExpressionPtr call(CommandArgumentValue lhs, ExpressionPtr rhs_)
{
rhs = rhs_;
return visit(*this, lhs);
}
};
template <>
struct SimplifyByConstant<Modulo>
{
VariableType resultVarType;
ExpressionPtr lhs;
template <typename RHS>
requires(CIntegral<RHS>) ExpressionPtr operator()(RHS rhs)
{
if(rhs == 1)
return literal(0, resultVarType);
return nullptr;
}
template <typename RHS>
requires(!CIntegral<RHS>) ExpressionPtr operator()(RHS rhs)
{
return nullptr;
}
ExpressionPtr call(ExpressionPtr lhs_, CommandArgumentValue rhs)
{
lhs = lhs_;
return visit(*this, rhs);
}
};
template <>
struct SimplifyByConstantLHS<Modulo>
{
VariableType resultVarType;
ExpressionPtr rhs;
template <typename LHS>
requires(CIntegral<LHS>) ExpressionPtr operator()(LHS lhs)
{
if(lhs == 0)
return literal(0, resultVarType);
return nullptr;
}
template <typename LHS>
requires(!CIntegral<LHS>) ExpressionPtr operator()(LHS lhs)
{
return nullptr;
}
ExpressionPtr call(CommandArgumentValue lhs, ExpressionPtr rhs_)
{
rhs = rhs_;
return visit(*this, lhs);
}
};
struct ConcatenatePartialSimplifyVisitor
{
ConcatenatePartialSimplifyVisitor(Concatenate const& concatenate)
: m_concatenate(concatenate)
{
}
ExpressionPtr operator()(CommandArgumentValue const& expr1,
CommandArgumentValue const& expr2) const
{
// TODO: Support partial 32 bit literal merging into 64 bit literal, codegen (copier) has problems
// Full merging is enabled because a concatenate of a single operand is simplified to the operand
// Partial merging would also require a Raw64 type
if(m_concatenate.operands.size() != 2
|| m_concatenate.destinationType != DataType::UInt64)
return {};
return std::visit(
[](auto const& val1, auto const& val2) -> ExpressionPtr {
using T1 = std::decay_t<decltype(val1)>;
using T2 = std::decay_t<decltype(val2)>;
if constexpr((std::is_same_v<T1, uint32_t> || std::is_same_v<T1, Raw32>)&&(
std::is_same_v<T2, uint32_t> || std::is_same_v<T2, Raw32>))
{
auto get_value = [](auto const& val) -> uint32_t {
if constexpr(std::is_same_v<std::decay_t<decltype(val)>, Raw32>)
return val.value;
else
return val;
};
uint64_t result = (static_cast<uint64_t>(get_value(val2)) << 32)
| static_cast<uint64_t>(get_value(val1));
return literal(result);
}
return {};
},
expr1,
expr2);
}
ExpressionPtr operator()(BitFieldExtract const& expr1,
BitFieldExtract const& expr2) const
{
if(identical(expr1.arg, expr2.arg)
&& resultVariableType(expr1.arg).getElementSize() == 8 && expr1.offset == 0
&& expr1.width == 32 && expr2.offset == 32 && expr2.width == 32)
{
return expr1.arg;
}
return {};
}
template <typename ARG1, typename ARG2>
ExpressionPtr operator()(ARG1 const& expr1, ARG2 const& expr2) const
{
return {};
}
ExpressionPtr call(ExpressionPtr expr1, ExpressionPtr expr2) const
{
return std::visit(*this, *expr1, *expr2);
}
private:
Concatenate m_concatenate;
};
/**
* Merges consecutive Concatenate operands where possible.
* Checks if a pair of operands are two 32-bit BitFieldExtracts from the same 64-bit source
*/
Concatenate concatenatePartialSimplify(Concatenate const& expr)
{
Concatenate cpy = expr;
std::vector<ExpressionPtr> operands;
auto const visitor = ConcatenatePartialSimplifyVisitor(cpy);
for(size_t i = 0; i < expr.operands.size(); ++i)
{
if(i + 1 < expr.operands.size())
{
if(auto simplified = visitor.call(expr.operands[i], expr.operands[i + 1]))
{
operands.push_back(simplified);
++i;
continue;
}
}
operands.push_back(expr.operands[i]);
}
cpy.operands = std::move(operands);
return cpy;
}
struct BitfieldCombineSinkVisitor
{
BitfieldCombineSinkVisitor(ExpressionPtr src,
uint32_t srcOffset,
uint32_t dstOffset,
uint32_t width)
: m_src(src)
, m_srcOffset(srcOffset)
, m_dstOffset(dstOffset)
, m_width(width)
, m_srcIsZero(std::nullopt)
, m_dstIsZero(std::nullopt)
, m_sunk(false)
{
}
ExpressionPtr operator()(BitfieldCombine const& expr) const
{
uint32_t combineStartBit = expr.dstOffset;
uint32_t combineEndBit = expr.dstOffset + expr.width - 1;
uint32_t startBit = m_dstOffset;
uint32_t endBit = m_dstOffset + m_width - 1;
// If expr BitfieldCombine is fully contained the the sinking BitfieldCombine, remove it
if(startBit <= combineStartBit && combineEndBit <= endBit)
return call(expr.rhs);
// Give up if BitfieldCombines overlap
if(combineStartBit <= endBit && combineEndBit >= startBit)
return std::make_shared<Expression>(expr);
BitfieldCombine cpy = expr;
// Recusively sink into nested BitfieldCombines
cpy.rhs = call(expr.rhs);
return std::make_shared<Expression>(cpy);
}
ExpressionPtr operator()(CommandArgumentValue const& expr) const
{
auto sunkBitfieldCombine = tryEvaluate(bfc(m_src,
literal(expr),
m_srcOffset,
m_dstOffset,
m_width,
m_srcIsZero,
m_dstIsZero));
if(!sunkBitfieldCombine.has_value())
return std::make_shared<Expression>(expr);
m_sunk = true;
return literal(sunkBitfieldCombine.value());
}
template <typename Expr>
ExpressionPtr operator()(Expr const& expr) const
{
return std::make_shared<Expression>(expr);
}
ExpressionPtr call(ExpressionPtr expr) const
{
if(!expr)
return expr;
return std::visit(*this, *expr);
}
bool sunk() const
{
return m_sunk;
}
private:
ExpressionPtr m_src;
uint32_t m_srcOffset;
uint32_t m_dstOffset;
uint32_t m_width;
std::optional<bool> m_srcIsZero;
std::optional<bool> m_dstIsZero;
mutable bool m_sunk;
};
/**
* Tries to sink a BitfieldCombine into its destination if its source is a literal.
* Only done in nested BitfieldCombines where the destination is another BitfieldCombine.
* Also removes BitfieldCombines that are fully overridden by BitfieldCombine argument
*/
ExpressionPtr sinkBitfieldCombine(BitfieldCombine const& expr)
{
BitfieldCombine cpy = expr;
auto const visitor
= BitfieldCombineSinkVisitor{cpy.lhs, cpy.srcOffset, cpy.dstOffset, cpy.width};
auto result = visitor.call(cpy.rhs);
// BitfieldCombine was folded away
if(visitor.sunk())
return result;
return bfc(cpy.lhs,
result,
cpy.srcOffset,
cpy.dstOffset,
cpy.width,
cpy.srcIsZero,
cpy.dstIsZero);
}
struct DeepBitfieldExtractVisitor
{
DeepBitfieldExtractVisitor(uint32_t offset, uint32_t width, DataType outputDataType)
: m_offset(offset)
, m_width(width)
, m_outputDataType(outputDataType)
{
}
ExpressionPtr operator()(BitfieldCombine const& expr) const
{
uint32_t combineStartBit = expr.dstOffset;
uint32_t combineEndBit = expr.dstOffset + expr.width - 1;
uint32_t endBit = m_offset + m_width - 1;
// No overlap with this dword
if(combineStartBit > endBit || combineEndBit < m_offset)
{
return call(expr.rhs);
}
return std::make_shared<Expression>(expr);
}
ExpressionPtr operator()(Concatenate const& expr) const
{
uint32_t operandStartBit = 0;
uint32_t operandEndBit = 0;
uint32_t startBit = m_offset;
uint32_t endBit = m_offset + m_width - 1;
std::vector<ExpressionPtr> overlapOperands;
uint32_t firstOperandStartBit = 0;
for(int i = 0; i < expr.operands.size(); ++i)
{
operandStartBit = operandEndBit;
operandEndBit += resultVariableType(expr.operands[i]).getElementSize() * 8;
// bitfield overlaps with this operand
if(operandStartBit <= endBit && startBit <= (operandEndBit - 1))
{
if(overlapOperands.empty())
firstOperandStartBit = operandStartBit;
overlapOperands.push_back(expr.operands[i]);
}
}
// bitfield is fully contained within this operand
if(overlapOperands.size() == 1)
{
this->m_offset -= firstOperandStartBit;
return call(overlapOperands[0]);
}
uint32_t operandsSize = 0;
for(auto const& operand : overlapOperands)
operandsSize += resultVariableType(operand).getElementSize();
// The overlapping operands compose a single operand of type that matches the BitFieldExtract output type
if(operandsSize == DataTypeInfo::Get(m_outputDataType).elementBytes)
{
this->m_offset -= firstOperandStartBit;
return concat(overlapOperands, m_outputDataType);
}
return std::make_shared<Expression>(expr);
}
ExpressionPtr operator()(BitFieldExtract const& expr) const
{
uint32_t endBit = m_offset + m_width - 1;
uint32_t exprEndBit = expr.width - 1;
// Bitfield is fully contained within this BitFieldExtract
if(endBit <= exprEndBit)
{
m_offset += expr.offset;
return call(expr.arg);
}
return std::make_shared<Expression>(expr);
}
template <typename Expr>
ExpressionPtr operator()(Expr const& expr) const
{
return std::make_shared<Expression>(expr);
}
ExpressionPtr call(ExpressionPtr expr) const
{
if(!expr)
return expr;
return std::visit(*this, *expr);
}
uint32_t get_offset() const
{
return m_offset;
}
private:
mutable uint32_t m_offset;
uint32_t m_width;
DataType m_outputDataType;
};
/**
* Returns a BitFieldExtract expression that extracts the specified bitfield from the given expression.
* Looks through Concatenate expressions to find the corresponding operand to extract.
* Looks through BitfieldCombine expressions to extract from its destination operand if the BitfieldCombine and BitFieldExtract do not overlap.
* Looks through nested BitFieldExtract expressions, if the extraction is fully contained within the inner BitFieldExtract.
*/
BitFieldExtract deepBitFieldExtract(BitFieldExtract expr)
{
auto visitor = DeepBitfieldExtractVisitor(expr.offset, expr.width, expr.outputDataType);
auto extracted = visitor.call(expr.arg);
uint32_t offset = visitor.get_offset();
return BitFieldExtract{{extracted}, expr.outputDataType, offset, expr.width};
}
struct SimplifyExpressionVisitor
{
template <CUnary Expr>
ExpressionPtr operator()(Expr const& expr) const
{
if constexpr(Expr::Type == Category::Conversion)
{
if(expr.arg)
{
if(resultVariableType(expr) == resultVariableType(expr.arg))
return call(expr.arg);
}
}
Expr cpy = expr;
cpy.arg = call(expr.arg);
return std::make_shared<Expression>(cpy);
}
ExpressionPtr operator()(Reinterpret const& expr) const
{
if(resultVariableType(expr.arg).dataType == expr.destinationType)
return call(expr.arg);
if(expr.arg && std::holds_alternative<Reinterpret>(*expr.arg))
{
// Collapse: reinterpret(B, reinterpret(A, x)) -> reinterpret(B, x)
auto const& innerReinterpret = std::get<Reinterpret>(*expr.arg);
return call(reinterpret(expr.destinationType, innerReinterpret.arg));
}
Reinterpret cpy = expr;
cpy.arg = call(expr.arg);
return std::make_shared<Expression>(cpy);
}
template <CBinary Expr>
ExpressionPtr operator()(Expr const& expr) const
{
auto resultVarType = resultVariableType(expr);
auto lhs = call(expr.lhs);
auto rhs = call(expr.rhs);
bool eval_lhs = evaluationTimes(lhs)[EvaluationTime::Translate];
bool eval_rhs = evaluationTimes(rhs)[EvaluationTime::Translate];
auto simplifier = SimplifyByConstant<Expr>{resultVarType};
ExpressionPtr rv = nullptr;
if(eval_lhs && eval_rhs)
{
rv = literal(evaluate(Expr({lhs, rhs})));
}
else if(CCommutativeBinary<Expr> && eval_lhs)
{
rv = simplifier.call(rhs, evaluate(lhs));
}
else if(eval_rhs)
{
rv = simplifier.call(lhs, evaluate(rhs));
}
else if(!CCommutativeBinary<Expr> && eval_lhs)
{
auto simplifierLHS = SimplifyByConstantLHS<Expr>{resultVarType};
rv = simplifierLHS.call(evaluate(lhs), rhs);
}
if(rv != nullptr)
{
if(resultVariableType(rv) != resultVarType)
{
AssertFatal(!resultVarType.isPointer(),
ShowValue(expr),
ShowValue(rv),
ShowValue(resultVarType));
rv = convert(resultVarType.dataType, rv);
}
copyComment(rv, expr);
return rv;
}
return std::make_shared<Expression>(Expr({lhs, rhs, expr.comment}));
}
ExpressionPtr operator()(BitfieldCombine const& expr) const
{
auto cpy = expr;
auto rhsVarType = resultVariableType(cpy.rhs);
// Nothing to combine
if(cpy.width == 0)
return call(cpy.rhs);
// Src overrides entire dst
else if(cpy.dstOffset == 0 && cpy.width == rhsVarType.getElementSize() * 8)
return call(bfe(rhsVarType.dataType, cpy.lhs, cpy.srcOffset, cpy.width));
cpy.lhs = call(expr.lhs);
cpy.rhs = call(expr.rhs);
return sinkBitfieldCombine(cpy);
}
ExpressionPtr operator()(BitFieldExtract const& expr) const
{
AssertFatal(expr.width > 0, "BitfieldExtract with width 0");
BitFieldExtract cpy = deepBitFieldExtract(expr);
// Evaluation might be possitble after deep extraction due to Concatenate expressions
auto result = tryEvaluate(cpy);
if(result.has_value())
return literal(result.value());
// Extracting the entire arg with no offset
auto argVarType = resultVariableType(cpy.arg);
if(cpy.offset == 0 && cpy.width == argVarType.getElementSize() * 8
&& argVarType.getElementSize()
== DataTypeInfo::Get(cpy.outputDataType).elementBytes)
return call(reinterpret(cpy.outputDataType, cpy.arg));
cpy.arg = call(cpy.arg);
return std::make_shared<Expression>(cpy);
}
ExpressionPtr operator()(Concatenate const& expr) const
{
auto cpy = expr;
AssertFatal(!cpy.operands.empty(), "Concatenate with no operands");
std::ranges::for_each(cpy.operands, [this](auto& op) { op = call(op); });
cpy = concatenatePartialSimplify(cpy);
if(cpy.operands.size() == 1)
return cpy.operands[0];
return std::make_shared<Expression>(cpy);
}
ExpressionPtr operator()(ScaledMatrixMultiply const& expr) const
{
ScaledMatrixMultiply cpy = expr;
cpy.matA = call(expr.matA);
cpy.matB = call(expr.matB);
cpy.matC = call(expr.matC);
cpy.scaleA = call(expr.scaleA);
cpy.scaleB = call(expr.scaleB);
return std::make_shared<Expression>(cpy);
}
ExpressionPtr operator()(Conditional const& expr) const
{
// Check if the condition can be evaluated at Translate time or not.
bool const eval_lhs = evaluationTimes(expr.lhs)[EvaluationTime::Translate];
if(eval_lhs)
{
bool const condFalse = std::visit(
[](auto&& arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr(std::is_pointer_v<T>)
return arg == nullptr;
else
return arg == T();
},
evaluate(expr.lhs));
if(condFalse)
return call(expr.r2hs);
else
return call(expr.r1hs);
}
auto cpy = expr;
cpy.lhs = call(expr.lhs);
cpy.r1hs = call(expr.r1hs);
cpy.r2hs = call(expr.r2hs);
return std::make_shared<Expression>(cpy);
}
template <CTernary Expr>
ExpressionPtr operator()(Expr const& expr) const
{
Expr cpy = expr;
cpy.lhs = call(expr.lhs);
cpy.r1hs = call(expr.r1hs);
cpy.r2hs = call(expr.r2hs);
return std::make_shared<Expression>(cpy);
}
template <CNary Expr>
ExpressionPtr operator()(Expr const& expr) const
{
auto cpy = expr;
std::ranges::for_each(cpy.operands, [this](auto& op) { op = call(op); });
return std::make_shared<Expression>(std::move(cpy));
}
template <CValue Value>
ExpressionPtr operator()(Value const& expr) const
{
return std::make_shared<Expression>(expr);
}
ExpressionPtr call(ExpressionPtr expr) const
{
if(!expr)
return expr;
auto evalTimes = evaluationTimes(expr);
if(evalTimes[EvaluationTime::Translate])
{
auto rv = literal(evaluate(expr));
AssertFatal(resultVariableType(rv) == resultVariableType(expr),
ShowValue(rv),
ShowValue(expr));
return rv;
}
auto rv = std::visit(*this, *expr);
return rv;
}
};
ExpressionPtr simplify(ExpressionPtr expr)
{
auto visitor = SimplifyExpressionVisitor();
return visitor.call(expr);
}
}
}