chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:40:42 +08:00
commit e25996e7db
15472 changed files with 3536181 additions and 0 deletions
@@ -0,0 +1,117 @@
// Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <unordered_set>
#include "paddle/common/union_find_set.h"
#include "paddle/pir/include/dialect/shape/utils/dim_expr.h"
namespace symbol {
class IR_API ConstraintsManager {
public:
ConstraintsManager() = default;
ConstraintsManager(const ConstraintsManager&) = delete;
ConstraintsManager(ConstraintsManager&&) = delete;
void AddEqCstr(const DimExpr& lhs, const DimExpr& rhs);
bool IsEqual(const DimExpr& lhs, const DimExpr& rhs) const;
void AddGTOneCstr(const DimExpr& dim_expr);
bool IsGTOne(const DimExpr& dim_expr) const;
void AddBroadcastableCstr(const DimExpr& lhs, const DimExpr& rhs);
bool IsBroadcastable(const DimExpr& lhs, const DimExpr& rhs) const;
struct Range {
std::int64_t min;
std::int64_t max;
// TODO(Hongqing-work): Substitute INT32_MAX with a more meaningful value.
Range() : min(1), max(INT32_MAX) {}
Range(int min_val, int max_val) : min(min_val), max(max_val) {}
};
void AddInputRangeCstr(const DimExpr& dim_expr, const Range& range);
bool IsBoundedInput(const DimExpr& dim_expr) const;
const Range& GetRangeOfBoundedInput(const DimExpr& dim_expr) const;
using EqualConstraints = common::UnionFindSet<DimExpr>;
using GTOneConstraints = std::unordered_set<DimExpr>;
using BroadcastableConstraints = std::unordered_set<Broadcastable<DimExpr>>;
using InputRangeConstraints = std::unordered_map<DimExpr, Range>;
void VisitEqualClusters(
const std::function<void(const std::vector<DimExpr>&)>& DoEachCluster)
const;
void EqualConstraintsVisitor(
const std::function<void(std::unordered_map<DimExpr, DimExpr>::iterator)>&
DoEach);
void GTOneConstraintsVisitor(
const std::function<void(GTOneConstraints::iterator)>& DoEach);
void GTOneConstraintsVisitor(
const std::function<void(GTOneConstraints::const_iterator)>& DoEach)
const;
void BroadcastableConstraintsVisitor(
const std::function<void(BroadcastableConstraints::iterator)>& DoEach);
void BroadcastableConstraintsVisitor(
const std::function<void(BroadcastableConstraints::const_iterator)>&
DoEach) const;
void InputRangeConstraintsVisitor(
const std::function<void(InputRangeConstraints::iterator)>& DoEach);
void InputRangeConstraintsVisitor(
const std::function<void(InputRangeConstraints::const_iterator)>& DoEach)
const;
using EqualCallbackFunc = std::function<void(const DimExpr&, const DimExpr&)>;
void SetEqualCallbackFunc(EqualCallbackFunc equal_callback_func);
const EqualConstraints& equals() const { return equals_; }
const GTOneConstraints& gtones() const { return gtones_; }
const BroadcastableConstraints& broadcastables() const {
return broadcastables_;
}
const InputRangeConstraints& input_ranges() const { return input_ranges_; }
private:
void SubstituteInConstraint(const DimExpr& lhs, const DimExpr& rhs);
private:
EqualCallbackFunc equal_callback_func_ = nullptr;
EqualConstraints equals_;
GTOneConstraints gtones_;
BroadcastableConstraints broadcastables_;
InputRangeConstraints input_ranges_;
};
std::ostream& operator<<(std::ostream& os,
const ConstraintsManager& constraints_manager);
} // namespace symbol
@@ -0,0 +1,272 @@
// Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <functional>
#include <memory>
#include <optional>
#include <ostream>
#include <string>
#include <variant>
#include <vector>
#include "glog/logging.h"
#include "paddle/common/enforce.h"
#include "paddle/common/overloaded.h"
#include "paddle/pir/include/core/dll_decl.h"
#include "paddle/pir/include/core/utils.h"
namespace symbol {
#define SYMBOL_NOT_IMPLEMENTED \
PADDLE_THROW(common::errors::Unimplemented("Not Implemented"))
template <typename T>
struct UnaryDimExpr {
explicit UnaryDimExpr(const T& d) : data(std::make_shared<Data>(d)) {}
struct Data {
explicit Data(const T& d) : data(d) {}
T data;
};
const Data& operator*() const { return *data; }
Data& operator*() { return *data; }
const Data* operator->() const { return data.get(); }
Data* operator->() { return data.get(); }
std::shared_ptr<Data> data;
};
template <typename T>
struct BinaryDimExpr {
explicit BinaryDimExpr(const T& l, const T& r)
: data(std::make_shared<Data>(l, r)) {}
struct Data {
explicit Data(const T& l, const T& r) : lhs(l), rhs(r) {}
T lhs;
T rhs;
};
const Data& operator*() const { return *data; }
Data& operator*() { return *data; }
const Data* operator->() const { return data.get(); }
Data* operator->() { return data.get(); }
std::shared_ptr<Data> data;
};
template <typename T>
class List final {
public:
List(const List&) = default;
List(List&&) = default;
List& operator=(const List&) = default;
List& operator=(List&&) = default;
using value_type = T;
explicit List() : vector_(std::make_shared<std::vector<T>>()) {}
template <
typename Arg,
std::enable_if_t<!std::is_same_v<std::decay_t<Arg>, List>, bool> = true>
explicit List(Arg&& arg)
: vector_(std::make_shared<std::vector<T>>(
std::vector<T>{std::forward<Arg>(arg)})) {}
template <typename Arg0, typename Arg1, typename... Args>
List(Arg0&& arg0, Arg1&& arg1, Args&&... args)
: vector_(std::make_shared<std::vector<T>>(
std::vector<T>{std::forward<Arg0>(arg0),
std::forward<Arg1>(arg1),
std::forward<Args>(args)...})) {}
bool operator==(const List& other) const {
if (&vector() == &other.vector()) {
return true;
}
return vector() == other.vector();
}
bool operator!=(const List& other) const { return !(*this == other); }
std::vector<T>& operator*() const { return *vector_; }
std::vector<T>* operator->() const { return vector_.get(); }
const std::vector<T>& vector() const { return *vector_; }
const auto& Get(std::size_t idx) const { return vector_->at(idx); }
private:
std::shared_ptr<std::vector<T>> vector_;
};
template <typename T>
struct Negative final : public UnaryDimExpr<T> {
using UnaryDimExpr<T>::UnaryDimExpr;
};
template <typename T>
struct Add final {
List<T> operands;
};
template <typename T>
struct Mul final {
List<T> operands;
};
template <typename T>
struct Div final : public BinaryDimExpr<T> {
using BinaryDimExpr<T>::BinaryDimExpr;
};
template <typename T>
struct Max final {
List<T> operands;
};
template <typename T>
struct Min final {
List<T> operands;
};
template <typename T>
struct Broadcast final {
List<T> operands;
};
template <typename T>
struct Equal final : public BinaryDimExpr<T> {
using BinaryDimExpr<T>::BinaryDimExpr;
};
template <typename T>
struct Broadcastable final : public BinaryDimExpr<T> {
using BinaryDimExpr<T>::BinaryDimExpr;
bool operator==(const Broadcastable& other) const {
return (this->data->lhs == other.data->lhs &&
this->data->rhs == other.data->rhs) ||
(this->data->lhs == other.data->rhs &&
this->data->rhs == other.data->lhs);
}
};
class DimExpr;
// DimExpr = std::int64_t
// | std::string
// | Negative DimExpr
// | Add DimExpr
// | Mul DimExpr
// | Div DimExpr
// | Max DimExpr
// | Min DimExpr
// | Broadcast DimExpr
using DimExprBase = std::variant<std::int64_t,
std::string,
Negative<DimExpr>,
Add<DimExpr>,
Mul<DimExpr>,
Div<DimExpr>,
Max<DimExpr>,
Min<DimExpr>,
Broadcast<DimExpr>>;
class IR_API DimExpr : public DimExprBase {
public:
using DimExprBase::DimExprBase;
template <typename T>
bool isa() const {
return std::holds_alternative<T>(*this);
}
template <typename T>
const T& dyn_cast() const {
return std::get<T>(*this);
}
template <typename T>
bool Has() const {
return std::holds_alternative<T>(*this);
}
template <typename T>
const T& Get() const {
return std::get<T>(*this);
}
const DimExprBase& variant() const {
return static_cast<const DimExprBase&>(*this);
}
DEFINE_MATCH_METHOD();
DimExpr operator+(const DimExpr& other) const;
DimExpr operator-(const DimExpr& other) const;
DimExpr operator*(const DimExpr& other) const;
DimExpr operator/(const DimExpr& other) const;
bool operator==(const DimExpr& other) const;
bool operator!=(const DimExpr& other) const;
};
// DimExprConstraint = Equal DimExpr
// | Broadcastable DimExpr
using DimExprConstraint = std::variant<Equal<DimExpr>, Broadcastable<DimExpr>>;
IR_API std::string ToString(const DimExpr& dim_expr);
IR_API std::ostream& operator<<(std::ostream&, const DimExpr& dim_expr);
IR_API std::ostream& operator<<(std::ostream&,
const std::vector<DimExpr>& dim_exprs);
IR_API std::size_t GetHashValue(const DimExpr& dim_expr);
} // namespace symbol
namespace std {
template <>
struct hash<symbol::DimExpr> {
std::size_t operator()(const symbol::DimExpr& dim_expr) const {
return symbol::GetHashValue(dim_expr);
}
};
template <>
struct hash<std::vector<symbol::DimExpr>> {
std::size_t operator()(const std::vector<symbol::DimExpr>& dim_exprs) const {
std::size_t hash_value = 0;
const auto hash_func = std::hash<symbol::DimExpr>();
for (const auto& dim_expr : dim_exprs) {
hash_value = pir::detail::hash_combine(hash_value, hash_func(dim_expr));
}
return hash_value;
}
};
template <>
struct hash<symbol::Broadcastable<symbol::DimExpr>> {
std::size_t operator()(
const symbol::Broadcastable<symbol::DimExpr>& broadcastable) const {
return pir::detail::hash_combine(GetHashValue(broadcastable.data->lhs),
GetHashValue(broadcastable.data->rhs));
}
};
} // namespace std
@@ -0,0 +1,41 @@
// Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "paddle/pir/include/core/dll_decl.h"
#include "paddle/pir/include/dialect/shape/utils/dim_expr.h"
namespace symbol {
class IR_API DimExprBuilder {
public:
DimExpr ConstSize(std::int64_t dim);
DimExpr Symbol(const std::string& symbol_name);
DimExpr Add(const DimExpr& lhs, const DimExpr& rhs);
DimExpr Any(const DimExpr& lhs, const DimExpr& rhs);
DimExpr Mul(const DimExpr& lhs, const DimExpr& rhs);
DimExpr Div(const DimExpr& lhs, const DimExpr& rhs);
DimExpr Max(const DimExpr& lhs, const DimExpr& rhs);
DimExpr Min(const DimExpr& lhs, const DimExpr& rhs);
DimExpr Broadcast(const DimExpr& lhs, const DimExpr& rhs);
std::vector<DimExpr> ConstShape(const std::vector<std::int64_t>& dims);
std::vector<DimExpr> Concat(const std::vector<DimExpr>& lhs,
const std::vector<DimExpr>& rhs);
std::pair<std::vector<DimExpr>, std::vector<DimExpr>> SplitAt(
const std::vector<DimExpr>, int index);
};
} // namespace symbol
@@ -0,0 +1,53 @@
// Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <unordered_map>
#include <unordered_set>
#include "paddle/pir/include/dialect/shape/utils/dim_expr.h"
namespace symbol {
IR_API DimExpr SimplifyDimExpr(const DimExpr& dim_expr);
IR_API DimExpr SubstituteDimExpr(
const DimExpr& dim_expr,
const std::unordered_map<DimExpr, DimExpr>& pattern_to_replacement);
IR_API int GetDimExprPriority(const DimExpr& dim_expr);
enum class PriorityComparisonStatus {
HIGHER, // lhs has a higher priority than rhs
EQUAL, // lhs and rhs have equal priority
LOWER // lhs has a lower priority than rhs
};
IR_API PriorityComparisonStatus CompareDimExprPriority(const DimExpr& lhs,
const DimExpr& rhs);
enum class DimExprCompareResult {
GT, // lhs is greater than rhs
EQ, // lhs and rhs is equal
LT, // lhs is less than rhs
UNKNOWN, // lhs and rhs is not comparable
};
IR_API DimExprCompareResult Compare(const DimExpr& lhs, const DimExpr& rhs);
IR_API std::unordered_set<std::string> CollectDimExprSymbols(
const DimExpr& dim_expr);
IR_API int64_t CountExprSymbols(const DimExpr& dim_expr);
} // namespace symbol
@@ -0,0 +1,47 @@
// Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <map>
#include <set>
#include "paddle/pir/include/core/builtin_attribute.h"
namespace pir {
std::map<std::string, Attribute> GetOrderedOriginalAttributes(
std::string op_name,
const std::unordered_map<std::string, Attribute>& attributes);
class OriginalAttributesFilter {
public:
static OriginalAttributesFilter& Instance();
OriginalAttributesFilter(const OriginalAttributesFilter&) = delete;
OriginalAttributesFilter(OriginalAttributesFilter&&) = delete;
OriginalAttributesFilter& operator=(const OriginalAttributesFilter&) = delete;
void SetOriginalAttributesMap(
const std::unordered_map<std::string, std::set<std::string>>&
original_attributes_map) {
original_attributes_map_ = original_attributes_map;
}
private:
OriginalAttributesFilter() {}
std::unordered_map<std::string, std::set<std::string>>
original_attributes_map_;
friend std::map<std::string, Attribute> GetOrderedOriginalAttributes(
std::string op_name,
const std::unordered_map<std::string, Attribute>& attributes);
};
} // namespace pir
@@ -0,0 +1,302 @@
// Copyright (c) 2023 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <memory>
#include <optional>
#include "paddle/pir/include/core/builtin_attribute.h"
#include "paddle/pir/include/core/builtin_op.h"
#include "paddle/pir/include/core/builtin_type_interfaces.h"
#include "paddle/pir/include/core/dll_decl.h"
#include "paddle/pir/include/core/utils.h"
#include "paddle/pir/include/dialect/shape/ir/shape_op.h"
#include "paddle/pir/include/dialect/shape/utils/constraints_manager.h"
#include "paddle/pir/include/dialect/shape/utils/dim_expr_builder.h"
#include "paddle/pir/include/dialect/shape/utils/shape_or_data_expr.h"
namespace pir {
using InferSymbolicShapeCacheValue = std::vector<symbol::ShapeOrDataDimExprs>;
enum TransLayoutType { NCHW2NHWC, NHWC2NCHW, INVALID };
/**
* This class represents information needed to determine the output
* shape of an operator, which includes the operator's name, input shapes, and
* attributes.
*/
class IR_API InferSymbolicShapeCacheKey {
public:
InferSymbolicShapeCacheKey(
const std::string& op_name,
const std::vector<symbol::ShapeOrDataDimExprs>& input_shape_or_datas,
const std::map<std::string, Attribute>& attributes)
: op_name_(op_name),
input_shape_or_datas_(input_shape_or_datas),
attributes_(attributes) {}
bool operator==(const InferSymbolicShapeCacheKey& other) const;
std::size_t GetHashValue() const;
friend std::ostream& operator<<(std::ostream& os,
const InferSymbolicShapeCacheKey& info);
friend class InferSymbolicShapeContext;
private:
std::string op_name_;
std::vector<symbol::ShapeOrDataDimExprs> input_shape_or_datas_;
std::map<std::string, Attribute> attributes_;
const std::vector<symbol::ShapeOrDataDimExprs>& GetInputShapeOrDatas() const;
void SetInputShapeOrDatas(
const std::vector<symbol::ShapeOrDataDimExprs>& input_shape_or_datas);
};
struct InputDynamicDimSpec {
std::string dim_name;
// input_bind = [(input_name, dim_index)]
std::vector<std::pair<std::string, int>> input_bind;
symbol::ConstraintsManager::Range range;
};
} // namespace pir
namespace std {
template <>
struct hash<pir::InferSymbolicShapeCacheKey> {
std::size_t operator()(const pir::InferSymbolicShapeCacheKey& obj) const {
return obj.GetHashValue();
}
};
} // namespace std
namespace pir {
void InferSymExprForAllValues(ModuleOp module_op);
class IR_API InferSymbolicShapeContext {
public:
InferSymbolicShapeContext() = default;
InferSymbolicShapeContext(const InferSymbolicShapeContext&) = delete;
InferSymbolicShapeContext(InferSymbolicShapeContext&&) = delete;
void Init(const std::vector<InputDynamicDimSpec>& input_dynamic_dim_spec);
// Note: Only initialize the symbol info, the value info is not update.
void RegisterSymbolConstraintFromContext(
const InferSymbolicShapeContext& other);
const std::string GetNextSymName();
bool HasShapeOrDataForValue(Value val) const;
const symbol::ShapeOrDataDimExprs& GetShapeOrDataForValue(Value val) const;
void SetSymbolForValueByStaticShape(Value val);
void SetShapeOrDataForValue(Value val,
const symbol::ShapeOrDataDimExprs& shape_or_data);
void AddEqualCstr(const symbol::DimExpr& lhs, const symbol::DimExpr& rhs);
// Add equal constraints for each dim in lhs and rhs.
void AddEqualCstr(const std::vector<symbol::DimExpr>& lhs,
const std::vector<symbol::DimExpr>& rhs);
bool IsEqual(const symbol::DimExpr& lhs, const symbol::DimExpr& rhs) const;
// Returns true if:
// lhs[i] == rhs[i] for all i
bool IsEqual(const std::vector<symbol::DimExpr>& lhs,
const std::vector<symbol::DimExpr>& rhs) const;
void AddGreatThanOneCstr(const symbol::DimExpr& dim_expr);
bool IsGreatThanOne(const symbol::DimExpr& dim_expr) const;
void AddBroadcastableCstr(const symbol::DimExpr& lhs,
const symbol::DimExpr& rhs);
bool IsBroadcastable(const symbol::DimExpr& lhs,
const symbol::DimExpr& rhs) const;
bool HasPredefinedRange(const symbol::DimExpr& dim_expr) const;
void PrintShapeOrDatas() const;
void SetOpInferSymbolicShapeCache(
const InferSymbolicShapeCacheKey& op_infer_cache_key,
InferSymbolicShapeCacheValue result_shape);
std::optional<InferSymbolicShapeCacheValue> GetOpInferSymbolicShapeCache(
const InferSymbolicShapeCacheKey& op_infer_cache_key) const;
void ClearOpInferSymbolicShapeCache();
const symbol::ConstraintsManager& constraints_manager() const {
return constraints_manager_;
}
struct DimIndexAndExpr {
int index;
symbol::DimExpr dim_expr;
DimIndexAndExpr(int index_val, const symbol::DimExpr& dim_expr_val)
: index(index_val), dim_expr(dim_expr_val) {}
};
bool HasPredefinedDimExprForInputName(const std::string& input_name) const;
const std::vector<DimIndexAndExpr> GetPredefinedDimExprForInputName(
const std::string& input_name) const;
private:
symbol::ShapeOrDataDimExprs SimplifyBroadcastForShapeOrData(
const symbol::ShapeOrDataDimExprs& shape_or_data);
void SubstituteDimExpr(const symbol::DimExpr& origin,
const symbol::DimExpr& substituted);
int64_t sym_idx_begin_ = 0;
int64_t next_sym_idx_ = 0;
std::unordered_map<uint64_t, symbol::ShapeOrDataDimExprs>
value_id_to_shape_or_data_;
symbol::ConstraintsManager constraints_manager_;
using DimExprSubstitutionPattern =
std::unordered_map<symbol::DimExpr, symbol::DimExpr>;
DimExprSubstitutionPattern substitution_pattern_;
std::unordered_map<InferSymbolicShapeCacheKey, InferSymbolicShapeCacheValue>
infer_symbolic_shape_cache_;
std::unordered_map<std::string, std::vector<DimIndexAndExpr>>
predefined_dimexpr_map_for_inputs_;
std::unordered_map<std::string, symbol::DimExpr>
input_dynamic_dim_name_spec_to_dimexpr_map_;
};
class IR_API ShapeConstraintIRAnalysis final
: public std::enable_shared_from_this<ShapeConstraintIRAnalysis> {
public:
ShapeConstraintIRAnalysis() = default;
ShapeConstraintIRAnalysis(const ShapeConstraintIRAnalysis&) = delete;
ShapeConstraintIRAnalysis(ShapeConstraintIRAnalysis&&) = delete;
void InitInferContext();
void RegisterSymbolConstraintFromShapeAnalysis(
const ShapeConstraintIRAnalysis& other);
const std::string GetNextSymName();
const symbol::ShapeOrDataDimExprs& GetShapeOrDataForValue(Value val);
void SetShapeOrDataForValue(Value val,
const symbol::ShapeOrDataDimExprs& shape_or_data);
// Set ShapeOrData of `to` value by ShapeOrData of `from` value.
void ShareShapeOrData(Value from, Value to);
// Update Symbol Shape for value by layout transformation.
void UpdateShapeOrDataByTransLayout(Value val,
TransLayoutType trans_layout_type);
void AddEqualCstr(const symbol::DimExpr& lhs, const symbol::DimExpr& rhs);
bool IsEqual(const symbol::DimExpr& lhs, const symbol::DimExpr& rhs) const;
bool IsGreatThanOne(const symbol::DimExpr& dim_expr) const;
bool IsBroadcastable(const symbol::DimExpr& lhs,
const symbol::DimExpr& rhs) const;
// Used to debug
void PrintShapeOrDatas() const;
// Returns true if the two value have the same symbolic shape.
bool IsShapeEqual(Value lhs, Value rhs);
// Suppose:
// lhs_dim_idxs = {ld0, ld1, ...}
// rhs_dim_idxs = {rd0, rd1, ...}
// Returns true if:
// lhs.shape[ld0] * lhs.shape[ld1] * ... ==
// rhs.shape[rd0] * rhs.shape[rd1] * ...
bool IsProductEqual(Value lhs,
const std::vector<int>& lhs_dim_idxs,
Value rhs,
const std::vector<int>& rhs_dim_idxs);
// Returns true if:
// lhs.shape[lhs_from] * ... lhs.shape[lhs_to-1] ==
// rhs.shape[rhs_from] * ... rhs.shape[rhs_to-1]
bool IsProductEqual(
Value lhs, int lhs_from, int lhs_to, Value rhs, int rhs_from, int rhs_to);
// Returns true if the two value have the same number elements.
bool IsSameNumel(Value lhs, Value rhs);
pir::PrintHooks PrintHook();
symbol::DimExpr GetProductDimExpr(Value lhs,
const std::vector<int>& lhs_dim_idxs);
const symbol::ConstraintsManager& constraints_manager() const {
return context_.constraints_manager();
}
void ClearOpInferSymbolicShapeCache() {
context_.ClearOpInferSymbolicShapeCache();
}
void SetInputDynamicDimSpec(
const std::vector<InputDynamicDimSpec>& input_dynamic_dim_spec);
void AppendInputDynamicDimSpec(
const std::vector<InputDynamicDimSpec>& input_dynamic_dim_spec);
private:
InferSymbolicShapeContext* MutInferSymbolicShapeContext() {
return &context_;
}
friend void InferSymExprForAllValues(ModuleOp module_op);
void SetSymbolForValueByStaticShape(Value val);
void InferShapeOrDataForValue(Value val);
private:
InferSymbolicShapeContext context_;
std::vector<InputDynamicDimSpec> input_dynamic_dim_spec_;
};
class IR_API ShapeAnalysisManager {
public:
static ShapeAnalysisManager& Instance();
ShapeConstraintIRAnalysis& Get(const pir::Program* program);
ShapeAnalysisManager(const ShapeAnalysisManager&) = delete;
ShapeAnalysisManager(ShapeAnalysisManager&&) = delete;
ShapeAnalysisManager& operator=(const ShapeAnalysisManager&) = delete;
private:
ShapeAnalysisManager() {}
std::unordered_map<uint64_t, std::shared_ptr<ShapeConstraintIRAnalysis>>
tables_;
};
#define OP_DECLARE_INFER_SYMBOLIC_SHAPE(name) \
bool name##OpInferSymbolicShape( \
pir::Operation* op, pir::InferSymbolicShapeContext* infer_context);
bool IsStaticShape(const Value& value);
} // namespace pir
@@ -0,0 +1,281 @@
// Copyright (c) 2024 PaddlePaddle Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <sstream>
#include "paddle/common/overloaded.h"
#include "paddle/pir/include/dialect/shape/utils/dim_expr.h"
#include "paddle/pir/include/dialect/shape/utils/dim_expr_util.h"
namespace symbol {
template <typename T>
class ShapeOrData {
public:
explicit ShapeOrData(const std::vector<T>& shape)
: shape_(shape), data_(std::nullopt) {}
explicit ShapeOrData(const std::vector<T>& shape, const std::vector<T>& data)
: shape_(shape), data_(data) {
// Valid check
if (shape.size() == 0) {
PADDLE_ENFORCE_EQ(
data.size(),
1UL,
common::errors::InvalidArgument(
"When shape is 0-D, size of data should be 1, but got %d.",
data.size()));
} else if (shape.size() == 1) {
PADDLE_ENFORCE_EQ(shape[0].template Has<int64_t>(),
true,
common::errors::InvalidArgument(
"When shape is 1-D, value of shape should be int"));
PADDLE_ENFORCE_EQ(
shape[0].template Get<int64_t>() == static_cast<int64_t>(data.size()),
true,
common::errors::InvalidArgument(
"When shape is 1-D, size of data should be the same as "
"value[%d] of shape, but got [%d].",
shape[0].template Get<std::int64_t>(),
data.size()));
} else {
int64_t numel = 1;
for (const auto& expr : shape) {
PADDLE_ENFORCE_EQ(expr.template isa<int64_t>(),
true,
::common::errors::InvalidArgument(
"When data has value, the expr of shape should "
"be int, but got %s.",
ToString(expr)));
numel *= expr.template Get<int64_t>();
}
PADDLE_ENFORCE_EQ(numel,
data.size(),
::common::errors::InvalidArgument(
"Size of data should be the same as "
"product of value[%d] of shape, but got [%d].",
numel,
data.size()));
}
}
ShapeOrData() = default;
ShapeOrData(const ShapeOrData&) = default;
ShapeOrData(ShapeOrData&&) = default;
ShapeOrData& operator=(const ShapeOrData&) = default;
ShapeOrData& operator=(ShapeOrData&&) = default;
// Tensor's real shape
const std::vector<T>& shape() const { return shape_; }
// Specific for Tensor generated by shape-relevant ops
const std::optional<std::vector<T>>& data() const { return data_; }
void SetData(const std::vector<T>& data) { data_ = data; }
bool operator==(const ShapeOrData<T>& other) const {
if (data_.has_value() && !other.data_.has_value()) return false;
if (!data_.has_value() && other.data_.has_value()) return false;
if (shape_.size() != other.shape_.size()) return false;
if (data_.has_value() && other.data_.has_value()) {
if (data_.value().size() != other.data_.value().size()) return false;
for (size_t i = 0; i < data_.value().size(); ++i) {
DimExpr dim0 = symbol::SimplifyDimExpr(data_.value()[i]);
DimExpr dim1 = symbol::SimplifyDimExpr(other.data_.value()[i]);
if (dim0 != dim1) return false;
}
}
for (size_t i = 0; i < shape_.size(); ++i) {
DimExpr dim0 = symbol::SimplifyDimExpr(shape_[i]);
DimExpr dim1 = symbol::SimplifyDimExpr(other.shape_[i]);
if (dim0 != dim1) return false;
}
return true;
}
bool operator!=(const ShapeOrData<T>& other) const {
return !(*this == other);
}
private:
std::vector<T> shape_;
std::optional<std::vector<T>> data_;
};
using NullShapeOrDataDimExpr = std::monostate;
using TensorShapeOrDataDimExprs = ShapeOrData<DimExpr>;
using TensorListShapeOrDataDimExprs = std::vector<TensorShapeOrDataDimExprs>;
/* TensorArray can append tensors dynamically. In a static graph, we only
* store the shape of one element as a hint, because we assume that all elements
* in the TensorArray have the same rank, and with equal constraints on specific
* dimensions. */
class RankedTensorArrayShapeOrDataDimExprs {
public:
RankedTensorArrayShapeOrDataDimExprs() = default;
explicit RankedTensorArrayShapeOrDataDimExprs(
const std::vector<DimExpr>& shape)
: shape_hint_{shape} {}
const std::vector<DimExpr>& GetShapeHint() const { return shape_hint_; }
bool operator==(const RankedTensorArrayShapeOrDataDimExprs& other) const {
if (shape_hint_.size() != other.shape_hint_.size()) return false;
for (size_t i = 0; i < shape_hint_.size(); ++i) {
DimExpr dim0 = symbol::SimplifyDimExpr(shape_hint_[i]);
DimExpr dim1 = symbol::SimplifyDimExpr(other.shape_hint_[i]);
if (dim0 != dim1) return false;
}
return true;
}
private:
std::vector<DimExpr> shape_hint_;
};
using ShapeOrDataDimExprsBase =
std::variant<NullShapeOrDataDimExpr,
TensorShapeOrDataDimExprs,
TensorListShapeOrDataDimExprs,
RankedTensorArrayShapeOrDataDimExprs>;
class ShapeOrDataDimExprs : public ShapeOrDataDimExprsBase {
public:
ShapeOrDataDimExprs() = delete;
ShapeOrDataDimExprs(
const TensorShapeOrDataDimExprs& tensor_dim_exprs) // NOLINT
: ShapeOrDataDimExprsBase(tensor_dim_exprs) {}
ShapeOrDataDimExprs(
const TensorListShapeOrDataDimExprs& tensor_list_dim_exprs)
: ShapeOrDataDimExprsBase(tensor_list_dim_exprs) {}
ShapeOrDataDimExprs(const RankedTensorArrayShapeOrDataDimExprs&
tensor_array_dim_exprs) // NOLINT
: ShapeOrDataDimExprsBase(tensor_array_dim_exprs) {}
ShapeOrDataDimExprs(const NullShapeOrDataDimExpr& null_dim_expr) // NOLINT
: ShapeOrDataDimExprsBase(null_dim_expr) {}
template <typename T>
bool isa() const {
return std::holds_alternative<T>(*this);
}
template <typename T>
const T& dyn_cast() const {
return std::get<T>(*this);
}
const ShapeOrDataDimExprsBase& variant() const {
return static_cast<const ShapeOrDataDimExprsBase&>(*this);
}
DEFINE_MATCH_METHOD();
bool operator==(const ShapeOrDataDimExprs& other) const {
return this->variant() == other.variant();
}
bool operator!=(const ShapeOrDataDimExprs& other) const {
return !(*this == other);
}
const std::vector<DimExpr>& shape() const {
PADDLE_ENFORCE_EQ(std::holds_alternative<TensorShapeOrDataDimExprs>(*this),
true,
common::errors::PreconditionNotMet(
"Shape of ShapeOrData is not a vector, "
"check whether the value is a "
"tensor-list or not."));
return std::get<TensorShapeOrDataDimExprs>(*this).shape();
}
const std::optional<std::vector<DimExpr>>& data() const {
PADDLE_ENFORCE_EQ(
std::holds_alternative<TensorShapeOrDataDimExprs>(*this),
true,
common::errors::PreconditionNotMet(
"Data of ShapeOrData is not a vector, check whether the value is a "
"tensor-list or not."));
return std::get<TensorShapeOrDataDimExprs>(*this).data();
}
void SetData(const std::vector<DimExpr>& data) {
PADDLE_ENFORCE_EQ(
std::holds_alternative<TensorShapeOrDataDimExprs>(*this),
true,
common::errors::PreconditionNotMet(
"Data of ShapeOrData is not a vector, check whether the value is a "
"tensor-list or not."));
std::get<TensorShapeOrDataDimExprs>(*this).SetData(data);
}
};
IR_API ShapeOrDataDimExprs SubstituteShapeOrData(
const ShapeOrDataDimExprs& shape_or_data,
const std::unordered_map<DimExpr, DimExpr>& substitution_pattern);
IR_API std::ostream& operator<<(std::ostream&,
const ShapeOrDataDimExprs& dim_expr);
} // namespace symbol
namespace std {
template <>
struct hash<symbol::TensorShapeOrDataDimExprs> {
std::size_t operator()(const symbol::TensorShapeOrDataDimExprs& obj) const {
const auto hash_func = std::hash<std::vector<symbol::DimExpr>>();
std::size_t ret = hash_func(obj.shape());
ret = pir::detail::hash_combine(ret, obj.data().has_value());
if (obj.data().has_value()) {
ret = pir::detail::hash_combine(ret, hash_func(obj.data().value()));
}
return ret;
}
};
template <>
struct hash<symbol::TensorListShapeOrDataDimExprs> {
std::size_t operator()(
const symbol::TensorListShapeOrDataDimExprs& obj) const {
const auto hash_func = std::hash<symbol::TensorShapeOrDataDimExprs>();
std::size_t ret = 0;
for (const auto& shape_or_data : obj) {
ret = pir::detail::hash_combine(ret, hash_func(shape_or_data));
}
return ret;
}
};
template <>
struct hash<symbol::RankedTensorArrayShapeOrDataDimExprs> {
std::size_t operator()(
const symbol::RankedTensorArrayShapeOrDataDimExprs& obj) const {
return std::hash<std::vector<symbol::DimExpr>>()(obj.GetShapeHint());
}
};
template <>
struct hash<symbol::ShapeOrDataDimExprs> {
std::size_t operator()(const symbol::ShapeOrDataDimExprs& obj) const {
return obj.Match([](const auto& impl) {
using T = std::decay_t<decltype(impl)>;
return std::hash<T>()(impl);
});
}
};
} // namespace std