// 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 #include #include "paddle/ap/include/axpr/anf_expr_builder.h" #include "paddle/ap/include/axpr/core_expr.h" #include "paddle/common/enforce.h" namespace ap::axpr { class LetContext; class LetVar { public: LetVar(const LetVar&) = default; LetVar(LetVar&&) = default; LetVar& operator=(const LetVar& let_var); LetVar& operator=(const AnfExpr& anf_val); const std::string& name() const { return name_; } operator Atomic() const { return tVar{name()}; } LetVar& Attr(const std::string& attr_name) { return AttrImpl(Atomic{attr_name}); } LetVar& Attr(const LetVar& attr_name) { return AttrImpl(static_cast>(attr_name)); } void SetAttr(const std::string& attr_name, const AnfExpr& anf_expr) { return SetAttrImpl(Atomic{attr_name}, anf_expr); } void SetAttr(const LetVar& attr_name, const AnfExpr& anf_expr) { return SetAttrImpl(static_cast>(attr_name), anf_expr); } void SetAttrImpl(const Atomic& attr_name, const AnfExpr& anf_expr); LetVar& At(int64_t idx); LetVar& At(const Atomic& idx); template LetVar& Call(Args&&... args); template LetVar& Apply(Args&&... args); LetContext* ctx() const { return let_ctx_; } private: friend class LetContext; LetVar(LetContext* let_ctx, const std::string& name) : let_ctx_(let_ctx), name_(name) {} LetVar& AttrImpl(const Atomic& attr_name); LetContext* let_ctx_; std::string name_; }; class LetContext : public AtomicExprBuilder { public: explicit LetContext(const std::function& SeqNoGenerator) : SeqNoGenerator_(SeqNoGenerator) {} LetContext(const LetContext&) = delete; LetContext(LetContext&&) = delete; using var_type = LetVar; LetVar& Var(const std::string& name) { auto iter = let_var_storage_.find(name); if (iter == let_var_storage_.end()) { auto var = std::unique_ptr(new LetVar(this, name)); iter = let_var_storage_.emplace(name, std::move(var)).first; } return *iter->second; } template AnfExpr Call(const LetVar& f, Arg0 arg0, Args&&... args) { return ApplyImpl(f.name(), std::vector{std::forward(arg0), std::forward(args)...}); } template AnfExpr Call(const std::string& f, Arg0 arg0, Args&&... args) { return ApplyImpl(f, std::vector{std::forward(arg0), std::forward(args)...}); } AnfExpr Call(const LetVar& f) { return ApplyImpl(f.name(), std::vector{}); } AnfExpr Call(const std::string& f) { return ApplyImpl(f, std::vector{}); } AnfExpr Apply(const LetVar& f, const std::vector& vars) { return Apply(f.name(), vars); } AnfExpr Apply(const std::string& f, const std::vector& vars) { std::vector args; args.reserve(vars.size()); for (const auto& var : vars) { args.emplace_back(var); } return ApplyImpl(f, args); } AnfExpr Apply(const LetVar& f, const std::vector& args) { return ApplyImpl(f.name(), args); } AnfExpr Apply(const std::string& f, const std::vector& args) { return ApplyImpl(f, args); } AnfExpr Apply(const LetVar& f, const std::vector& args, const std::map& kwargs) { return Apply(f.name(), args, kwargs); } AnfExpr Apply(const LetVar& f, const std::map& kwargs) { return Apply(f.name(), std::vector{}, kwargs); } AnfExpr Apply(const std::string& f, const std::vector& args, const std::map& kwargs) { std::vector kwarg_list; for (const auto& [keyword, val] : kwargs) { const AnfExpr& item = this->Call(ap::axpr::kBuiltinList(), this->String(keyword), val); kwarg_list.emplace_back(item); } const AnfExpr& packed_args = this->Call(this->Var("__builtin_PackedArgs__"), this->Apply(ap::axpr::kBuiltinList(), args), this->Apply(ap::axpr::kBuiltinList(), kwarg_list)); return this->Call(f, packed_args); } LetVar& Attr(const AnfExpr& self, const std::string& attr_name) { const auto& var_name = NewTmpVarName(); Var(var_name) = self; return Var(var_name).Attr(attr_name); } const std::vector>& bindings() { return bindings_; } std::string NewTmpVarName() { static const std::string prefix = "___"; return prefix + std::to_string(SeqNoGenerator_()); } private: friend class LetVar; AnfExpr ApplyImpl(const std::string& f, const std::vector& args) { std::vector> atomic_args; atomic_args.reserve(args.size()); for (const auto& anf_expr : args) { anf_expr.Match( [&](const Atomic& atomic) { atomic_args.push_back(atomic); }, [&](const auto&) { atomic_args.push_back(BindToTmpVar(anf_expr)); }); } return AnfExprBuilder().Call(tVar{f}, atomic_args); } tVar BindToTmpVar(const AnfExpr& anf_val) { const tVar tmp_var_name{NewTmpVarName()}; AddBinding(tmp_var_name.value(), anf_val); return tmp_var_name; } void AddBinding(const std::string& name, const AnfExpr& anf_val) { AnfExprBuilder anf; anf_val.Match( [&](const Atomic& atomic) { const auto& combined = anf.Call(tVar{kBuiltinIdentity()}, {atomic}); bindings_.push_back(anf.Bind(name, combined)); }, [&](const Combined& combined) { bindings_.push_back(anf.Bind(name, combined)); }, [&](const Let& let) { const auto& lambda = anf.Lambda({}, let); const auto& combined = anf.Call(lambda, {}); bindings_.push_back(anf.Bind(name, combined)); }); } std::unordered_map> let_var_storage_; std::vector> bindings_; std::function SeqNoGenerator_; }; inline LetVar& LetVar::operator=(const LetVar& let_var) { AnfExprBuilder anf{}; return *this = anf.Call(tVar{kBuiltinIdentity()}, {tVar{let_var.name()}}); } inline LetVar& LetVar::operator=(const AnfExpr& anf_val) { let_ctx_->AddBinding(name_, anf_val); return *this; } inline LetVar& LetVar::AttrImpl(const Atomic& attr_name) { AnfExprBuilder anf{}; AnfExpr anf_expr = anf.Call(tVar{kBuiltinGetAttr()}, {tVar{name()}, attr_name}); return let_ctx_->Var(let_ctx_->BindToTmpVar(anf_expr).value()); } inline void LetVar::SetAttrImpl(const Atomic& attr_name, const AnfExpr& val) { const auto& atomic = val.Match( [&](const Atomic& atomic_val) -> Atomic { return atomic_val; }, [&](const auto& impl) -> Atomic { return let_ctx_->BindToTmpVar(val); }); AnfExprBuilder anf{}; const auto& method_anf_expr = anf.Call(tVar{kBuiltinSetAttr()}, {tVar{name()}, attr_name}); const auto& method = let_ctx_->BindToTmpVar(method_anf_expr); AnfExpr anf_expr = anf.Call(method, {attr_name, atomic}); let_ctx_->BindToTmpVar(anf_expr); } inline LetVar& LetVar::At(int64_t idx) { AnfExprBuilder anf{}; AnfExpr anf_expr = anf.Call(tVar{kBuiltinGetItem()}, {tVar{name()}, anf.Int64(idx)}); return let_ctx_->Var(let_ctx_->BindToTmpVar(anf_expr).value()); } inline LetVar& LetVar::At(const Atomic& idx) { AnfExprBuilder anf{}; AnfExpr anf_expr = anf.Call(tVar{kBuiltinGetItem()}, {tVar{name()}, idx}); return let_ctx_->Var(let_ctx_->BindToTmpVar(anf_expr).value()); } template inline LetVar& LetVar::Call(Args&&... args) { const auto& anf_expr = let_ctx_->Call(*this, std::forward(args)...); return let_ctx_->Var(let_ctx_->BindToTmpVar(anf_expr).value()); } template inline LetVar& LetVar::Apply(Args&&... args) { const auto& anf_expr = let_ctx_->Apply(*this, std::forward(args)...); return let_ctx_->Var(let_ctx_->BindToTmpVar(anf_expr).value()); } class LambdaExprBuilder { public: LambdaExprBuilder() : SeqNoGenerator_(&LambdaExprBuilder::GenSeqNo) {} explicit LambdaExprBuilder(const std::function& SeqNoGenerator) : SeqNoGenerator_(SeqNoGenerator) {} LambdaExprBuilder(const LambdaExprBuilder&) = delete; LambdaExprBuilder(LambdaExprBuilder&&) = delete; AnfExpr Lambda(const std::vector& args, const std::function& GetBody) { AnfExpr anf_expr = Let(GetBody); AnfExpr lambda_or_body = anf_expr.Match( [&](const ap::axpr::Let& let) { if (let->bindings.empty()) { return let->body; } else { return anf_expr; } }, [&](const auto&) { return anf_expr; }); return anf_.Lambda(MakeLambdaArgs(args), lambda_or_body); } AnfExpr Let(const std::function& GetBody) { LetContext let_ctx{SeqNoGenerator_}; AnfExpr ret = GetBody(let_ctx); return anf_.Let(let_ctx.bindings(), ret); } adt::Result TryLambda( const std::vector& args, const std::function(LetContext&)>& GetBody) { ADT_LET_CONST_REF(anf_expr, TryLet(GetBody)); AnfExpr lambda_or_body = anf_expr.Match( [&](const ap::axpr::Let& let) { if (let->bindings.empty()) { return let->body; } else { return anf_expr; } }, [&](const auto&) { return anf_expr; }); return anf_.Lambda(MakeLambdaArgs(args), lambda_or_body); } adt::Result TryLet( const std::function(LetContext&)>& GetBody) { LetContext let_ctx{SeqNoGenerator_}; ADT_LET_CONST_REF(ret, GetBody(let_ctx)); return anf_.Let(let_ctx.bindings(), ret); } std::vector> MakeLambdaArgs( const std::vector& args) { std::vector> lambda_args; lambda_args.reserve(args.size()); for (const auto& arg : args) { lambda_args.emplace_back(arg); } return lambda_args; } private: static size_t GenSeqNo() { static std::atomic seq_no(0); return seq_no++; } std::function SeqNoGenerator_; AnfExprBuilder anf_; }; } // namespace ap::axpr