From dc4ec737ec22d0d727d25d61ac49bde227cb5ef8 Mon Sep 17 00:00:00 2001 From: Mo Xiaoming <2188767+mo-xiaoming@users.noreply.github.com> Date: Fri, 17 Mar 2023 16:24:31 +0800 Subject: [PATCH 1/8] add perf log for "large" variant/record two environment variables: HOBBES_PERF_TRACE_THRESHOLD_IN_SECS: any *named* expression take more than this seconds to be unsweetenned, then some related info will be logged in file HOBBES_PERF_TRACE_FILE: logs will be written to this file --- include/hobbes/eval/cc.H | 3 ++ lib/hobbes/eval/cc.C | 88 +++++++++++++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/include/hobbes/eval/cc.H b/include/hobbes/eval/cc.H index ed41f8d1..bdc6f397 100644 --- a/include/hobbes/eval/cc.H +++ b/include/hobbes/eval/cc.H @@ -375,6 +375,9 @@ private: // the JIT engine that compiles our monotyped expressions jitcc *jit; + struct PerfTracer; + std::unique_ptr perfTracer; + public: // compiler-local type structure caches for internal use std::unordered_map unappTyDefns; diff --git a/lib/hobbes/eval/cc.C b/lib/hobbes/eval/cc.C index f128c3be..5b296704 100644 --- a/lib/hobbes/eval/cc.C +++ b/lib/hobbes/eval/cc.C @@ -1,4 +1,3 @@ - #include #include #include @@ -7,6 +6,8 @@ #include #include #include +#include +#include #include #include #include @@ -24,6 +25,7 @@ #include #include +#include #include #include #include @@ -32,6 +34,83 @@ namespace hobbes { +struct cc::PerfTracer { + PerfTracer() : ofs(std::getenv("HOBBES_PERF_TRACE_FILE")) { + if (!ofs) { + ofs.close(); + return; + } + + const char* th = std::getenv("HOBBES_PERF_TRACE_THRESHOLD_IN_SECS"); + if (th == nullptr) { + ofs.close(); + return; + } + threshold = std::chrono::seconds(atoi(th)); + } + + template + void log(const std::string& name, const ExprPtr& e, const Definitions& defs, TT delta) { + if (!isEnabled() || delta <= threshold || name.empty() || name.substr(0, 5) == ".rfn.") { + return; + } + + const auto normal = [](const std::string& n) { return (n.substr(0, 2) != ".p") ? 1 : 0; }; + const auto nonHiddenVar = [normal](const Variant& c) { + const auto& ms = c.members(); + return std::accumulate(ms.cbegin(), ms.cend(), 0, + [normal](int a, const auto& m) { return a + normal(m.selector); }); + }; + const auto nonHiddenRec = [normal](const Record& c) { + const auto& ms = c.members(); + return std::accumulate(ms.cbegin(), ms.cend(), 0, + [normal](int a, const auto& m) { return a + normal(m.field); }); + }; + + constexpr const char* MARK = "HOBBES_PERF"; + ofs << MARK << ':' << name + << ":BEGIN:" << std::chrono::duration_cast(delta).count() << ":"; + e->show(ofs); + ofs << '\n'; + + for (auto i = defs.size(); i > 0; --i) { + std::ostringstream oss; + if (Assump::type_case_id != defs[i - 1].second->case_id()) { + continue; + } + const auto a = rcast(defs[i - 1].second.get()); + if (!a->ty() || a->ty()->constraints().empty()) { + continue; + } + + const auto& cs = a->ty()->constraints()[0]; + if (cs->name() == VariantDeconstructor::constraintName()) { + const auto& var = cs->arguments()[1]; + var->show(oss); + const auto sz = nonHiddenVar(*is(var)); + i -= sz - 1U; + ofs << MARK << ':' << name << ':' << sz << ':' << std::move(oss).str() << '\n'; + } else if (cs->name() == RecordDeconstructor::constraintName()) { + const auto& rec = cs->arguments()[2]; + rec->show(oss); + const auto sz = nonHiddenRec(*is(rec)); + i -= sz - 1U; + ofs << MARK << ':' << name << ':' << sz << ':' << std::move(oss).str() << '\n'; + } + } + + ofs << MARK << ':' << name << ":END:" << defs.size() << '\n' << std::endl; + } + +private: + [[nodiscard]] bool isEnabled() const noexcept { + return ofs.good(); + } + + std::ofstream ofs; + std::chrono::seconds threshold{0}; +}; + // protect access to hobbes/llvm resources static std::recursive_mutex hccmtx; hlock:: hlock() { hccmtx.lock(); } @@ -52,7 +131,8 @@ cc::cc() : lowerPrimMatchTables(false), columnwiseMatches(false), tenv(new TEnv()), - objs(new Objs()) + objs(new Objs()), + perfTracer(std::make_unique()) { // protect access to LLVM hlock _; @@ -230,6 +310,7 @@ ExprPtr cc::unsweetenExpression(const TEnvPtr& te, const std::string& vname, con hlock _; Definitions ds; + const auto timingStart = std::chrono::steady_clock::now(); ExprPtr result; try { result = macroExpand(unqualifyTypes(te, validateType(te, vname, closureConvert(this->tenv, vname, e), &ds), &ds)); @@ -239,6 +320,9 @@ ExprPtr cc::unsweetenExpression(const TEnvPtr& te, const std::string& vname, con } drainUnqualifyDefs(ds); + const auto delta = std::chrono::duration_cast( + std::chrono::steady_clock::now() - timingStart); + perfTracer->log(vname, e, ds, delta); return result; } From b93b46ba32b9c82fabf010f68e1a617984f1c459 Mon Sep 17 00:00:00 2001 From: Mo Xiaoming <2188767+mo-xiaoming@users.noreply.github.com> Date: Sun, 19 Mar 2023 11:20:09 +0800 Subject: [PATCH 2/8] enable logging IR via env variable HOBBES_IR_TRACE_FILE: file that IR will be stored --- include/hobbes/eval/jitcc.H | 4 ++++ lib/hobbes/eval/jitcc.C | 48 ++++++++++++++++++++++++++++++++++--- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/include/hobbes/eval/jitcc.H b/include/hobbes/eval/jitcc.H index 53e5527b..d9b13b11 100644 --- a/include/hobbes/eval/jitcc.H +++ b/include/hobbes/eval/jitcc.H @@ -9,6 +9,7 @@ #include #include #include +#include #if LLVM_VERSION_MAJOR >= 11 #include @@ -260,6 +261,9 @@ private: #if LLVM_VERSION_MAJOR >= 11 std::unique_ptr orcjit; #endif + + struct IrTracer; + std::unique_ptr irTracer; }; // shorthand for compilation over a sequence of expressions diff --git a/lib/hobbes/eval/jitcc.C b/lib/hobbes/eval/jitcc.C index 6dd40518..e9256754 100644 --- a/lib/hobbes/eval/jitcc.C +++ b/lib/hobbes/eval/jitcc.C @@ -1,4 +1,5 @@ +#include #include #include #include @@ -12,10 +13,17 @@ #include #include #include +#include #include #include #include +#include +#include #include + +#include +#include +#include #include #include @@ -546,11 +554,43 @@ private: }; #endif +struct jitcc::IrTracer { + IrTracer() { + std::error_code ec; + const char* name = std::getenv("HOBBES_IR_TRACE_FILE"); + if (name == nullptr) { + return; + } + out = std::make_unique(name, ec); + if (ec != std::errc()) { + out = nullptr; + return; + } + } + + void log(const llvm::Module& m) { + if (!isEnabled()) { + return; + } + + m.print(*out, nullptr, false, true); + } + +private: + [[nodiscard]] bool isEnabled() const noexcept { + return !!out; + } + + std::unique_ptr out; +}; + #if LLVM_VERSION_MAJOR >= 11 jitcc::jitcc(const TEnvPtr& tenv) : tenv(tenv), vtenv(std::make_unique()), ignoreLocalScope(false), globals(std::make_unique()), globalData(32768 /* min global page size = 32K */), - constants(std::make_unique()) { + constants(std::make_unique()), + irTracer(std::make_unique()) + { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmParser(); llvm::InitializeNativeTargetAsmPrinter(); @@ -579,7 +619,8 @@ jitcc::~jitcc() { jitcc::jitcc(const TEnvPtr& tenv) : tenv(tenv), ignoreLocalScope(false), - globalData(32768 /* min global page size = 32K */) + globalData(32768 /* min global page size = 32K */), + irTracer(std::make_unique()) { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmParser(); @@ -743,6 +784,7 @@ void* jitcc::getMachineCode(llvm::Function* f, llvm::JITEventListener* listener) if (!this->currentModule) { throw std::runtime_error("Internal compiler error, can't derive machine code for unknown function"); } + irTracer->log(*this->currentModule); // make a new execution engine out of this module (finalizing the module) std::string err; @@ -792,7 +834,7 @@ void* jitcc::getMachineCode(llvm::Function* f, llvm::JITEventListener* listener) ee->finalizeObject(); // now we can't touch this module again - this->currentModule = 0; + this->currentModule = nullptr; // and _now_ we must be able to get machine code for this function void* pf = ee->getPointerToFunction(f); From 5ba5fbbd5113bf1adfe5bab4c0d452070ccf1c85 Mon Sep 17 00:00:00 2001 From: Mo Xiaoming <2188767+mo-xiaoming@users.noreply.github.com> Date: Sun, 19 Mar 2023 11:33:01 +0800 Subject: [PATCH 3/8] minor change to make old compiler happy --- lib/hobbes/eval/cc.C | 38 +++++++++++++++++++++----------------- lib/hobbes/eval/jitcc.C | 4 ++-- 2 files changed, 23 insertions(+), 19 deletions(-) diff --git a/lib/hobbes/eval/cc.C b/lib/hobbes/eval/cc.C index 5b296704..0d17a3a7 100644 --- a/lib/hobbes/eval/cc.C +++ b/lib/hobbes/eval/cc.C @@ -25,17 +25,25 @@ #include #include +#include #include #include #include #include +#include #include #include namespace hobbes { struct cc::PerfTracer { - PerfTracer() : ofs(std::getenv("HOBBES_PERF_TRACE_FILE")) { + PerfTracer() { + const char* name = std::getenv("HOBBES_PERF_TRACE_FILE"); + if (name == nullptr) { + return; + } + + ofs.open(name); if (!ofs) { ofs.close(); return; @@ -49,24 +57,20 @@ struct cc::PerfTracer { threshold = std::chrono::seconds(atoi(th)); } + template + [[nodiscard]] std::size_t nonHiddenSz(const T& t, F f) { + const auto& ms = t.members(); + return std::accumulate(ms.cbegin(), ms.cend(), 0UL, [f](auto a, const auto& m) { + return a + (f(m).substr(0, 2) != ".p" ? 1UL : 0UL); + }); + } + template void log(const std::string& name, const ExprPtr& e, const Definitions& defs, TT delta) { if (!isEnabled() || delta <= threshold || name.empty() || name.substr(0, 5) == ".rfn.") { return; } - const auto normal = [](const std::string& n) { return (n.substr(0, 2) != ".p") ? 1 : 0; }; - const auto nonHiddenVar = [normal](const Variant& c) { - const auto& ms = c.members(); - return std::accumulate(ms.cbegin(), ms.cend(), 0, - [normal](int a, const auto& m) { return a + normal(m.selector); }); - }; - const auto nonHiddenRec = [normal](const Record& c) { - const auto& ms = c.members(); - return std::accumulate(ms.cbegin(), ms.cend(), 0, - [normal](int a, const auto& m) { return a + normal(m.field); }); - }; - constexpr const char* MARK = "HOBBES_PERF"; ofs << MARK << ':' << name << ":BEGIN:" << std::chrono::duration_cast(delta).count() << ":"; @@ -87,14 +91,14 @@ struct cc::PerfTracer { if (cs->name() == VariantDeconstructor::constraintName()) { const auto& var = cs->arguments()[1]; var->show(oss); - const auto sz = nonHiddenVar(*is(var)); - i -= sz - 1U; + const int sz = nonHiddenSz(*is(var), [](const auto& m) { return m.selector; }); + i -= static_cast(sz - 1); ofs << MARK << ':' << name << ':' << sz << ':' << std::move(oss).str() << '\n'; } else if (cs->name() == RecordDeconstructor::constraintName()) { const auto& rec = cs->arguments()[2]; rec->show(oss); - const auto sz = nonHiddenRec(*is(rec)); - i -= sz - 1U; + const int sz = nonHiddenSz(*is(rec), [](const auto& m) { return m.field; }); + i -= static_cast(sz - 1); ofs << MARK << ':' << name << ':' << sz << ':' << std::move(oss).str() << '\n'; } } diff --git a/lib/hobbes/eval/jitcc.C b/lib/hobbes/eval/jitcc.C index e9256754..f18bb195 100644 --- a/lib/hobbes/eval/jitcc.C +++ b/lib/hobbes/eval/jitcc.C @@ -17,7 +17,7 @@ #include #include #include -#include +#include #include #include @@ -561,7 +561,7 @@ struct jitcc::IrTracer { if (name == nullptr) { return; } - out = std::make_unique(name, ec); + out = std::make_unique(name, ec, llvm::sys::fs::F_None); if (ec != std::errc()) { out = nullptr; return; From ea61ecff74cb7ce78c8706d6a8bda010ffd4e3d5 Mon Sep 17 00:00:00 2001 From: Mo Xiaoming <2188767+mo-xiaoming@users.noreply.github.com> Date: Mon, 20 Mar 2023 13:42:28 +0800 Subject: [PATCH 4/8] add more info when type constraints unsolved --- lib/hobbes/lang/expr.C | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/lib/hobbes/lang/expr.C b/lib/hobbes/lang/expr.C index 5689fa9d..db3081eb 100644 --- a/lib/hobbes/lang/expr.C +++ b/lib/hobbes/lang/expr.C @@ -1243,7 +1243,13 @@ const MonoTypePtr& requireMonotype(const TEnvPtr& tenv, const ExprPtr& e) { if (!e->type()->constraints().empty()) { Constraints cs = expandHiddenTCs(tenv, simplifyVarNames(e->type())->constraints()); std::ostringstream ss; - ss << "Failed to compile expression due to unresolved type constraint" << (cs.size() > 1 ? "s" : ""); + ss << "Failed to compile expression `"; + e->show(ss); + ss << "` due to unresolved type constraint" << (cs.size() > 1 ? "s" : ""); + for (const auto& c: cs) { + ss << "\n "; + c->show(ss); + } throw unsolved_constraints(*e, ss.str(), cs); } From 3a1d1fee7316560f39c34c1e7e10777ae3e56ab4 Mon Sep 17 00:00:00 2001 From: Mo Xiaoming <2188767+mo-xiaoming@users.noreply.github.com> Date: Mon, 20 Mar 2023 15:04:07 +0800 Subject: [PATCH 5/8] add toString() to be more gdb friendly. show(..) is often unresolvable --- include/hobbes/lang/expr.H | 1 + include/hobbes/lang/module.H | 2 ++ include/hobbes/lang/preds/class.H | 3 +++ include/hobbes/lang/type.H | 6 ++++++ lib/hobbes/lang/expr.C | 5 +++++ lib/hobbes/lang/module.C | 10 ++++++++++ lib/hobbes/lang/preds/class.C | 15 +++++++++++++++ lib/hobbes/lang/type.C | 32 +++++++++++++++++++++++++++++++ 8 files changed, 74 insertions(+) diff --git a/include/hobbes/lang/expr.H b/include/hobbes/lang/expr.H index 8303f19c..bb96c989 100644 --- a/include/hobbes/lang/expr.H +++ b/include/hobbes/lang/expr.H @@ -19,6 +19,7 @@ public: virtual ~Expr(); virtual void show(std::ostream&) const = 0; + [[nodiscard]] std::string toString() const; virtual void showAnnotated(std::ostream&) const = 0; virtual Expr* clone() const = 0; virtual bool operator==(const Expr&) const = 0; diff --git a/include/hobbes/lang/module.H b/include/hobbes/lang/module.H index 637f98be..6f984237 100644 --- a/include/hobbes/lang/module.H +++ b/include/hobbes/lang/module.H @@ -17,6 +17,7 @@ namespace hobbes { struct ModuleDef : public LexicallyAnnotated { virtual ~ModuleDef(); virtual void show(std::ostream&) const = 0; + [[nodiscard]] std::string toString() const; // improves performance of case-analysis over instances (to avoid 'dynamic_cast') public: @@ -243,6 +244,7 @@ public: const ModuleDefs& definitions() const; void show(std::ostream& out) const; + [[nodiscard]] std::string toString() const; // allow language options set within modules void setOption(const std::string&, const LexicalAnnotation&); diff --git a/include/hobbes/lang/preds/class.H b/include/hobbes/lang/preds/class.H index 04ac66f0..8c51c3c1 100644 --- a/include/hobbes/lang/preds/class.H +++ b/include/hobbes/lang/preds/class.H @@ -68,6 +68,7 @@ public: // show this class definition void show(std::ostream&) const; + [[nodiscard]] std::string toString() const; public: const TCInstances& instances() const; bool hasGroundInstanceAt(const MonoTypes&) const; @@ -122,6 +123,7 @@ public: // show this instance definition void show(std::ostream&) const; + [[nodiscard]] std::string toString() const; private: std::string tcname; MonoTypes itys; @@ -152,6 +154,7 @@ public: // show this instance-generator definition void show(std::ostream&) const; + [[nodiscard]] std::string toString() const; // produce a sequence of type variables and constraints unique to this invocation using IFnDef = std::pair; diff --git a/include/hobbes/lang/type.H b/include/hobbes/lang/type.H index 0db74564..42e571c9 100644 --- a/include/hobbes/lang/type.H +++ b/include/hobbes/lang/type.H @@ -41,6 +41,7 @@ public: using ptr = std::shared_ptr; virtual void show(std::ostream&) const = 0; + [[nodiscard]] std::string toString() const; bool operator==(const MonoType& rhs) const; // improves performance of case-analysis over instances (to avoid 'dynamic_cast') @@ -109,6 +110,8 @@ public: // get access to the parent and root type environment from here const TEnvPtr& parentTypeEnv() const; TEnv* root(); + + [[nodiscard]] std::string toString() const; public: // get access to the internal type environment map (should only be used for debugging) using PolyTypeEnv = std::map; @@ -179,6 +182,7 @@ public: QualTypePtr instantiate() const; void show(std::ostream&) const; + [[nodiscard]] std::string toString() const; bool operator==(const PolyType& rhs) const; // this should only be used if you don't mind capturing bound type variables @@ -206,6 +210,7 @@ public: void monoType(const MonoTypePtr&); void show(std::ostream&) const; + [[nodiscard]] std::string toString() const; bool operator==(const QualType& rhs) const; private: Constraints cs; @@ -238,6 +243,7 @@ public: // compare/show void show(std::ostream&) const; + [[nodiscard]] std::string toString() const; bool operator==(const Constraint& rhs) const; // are there free variables referenced in this constraint? diff --git a/lib/hobbes/lang/expr.C b/lib/hobbes/lang/expr.C index db3081eb..60156446 100644 --- a/lib/hobbes/lang/expr.C +++ b/lib/hobbes/lang/expr.C @@ -40,6 +40,11 @@ Expr::~Expr() = default; const QualTypePtr& Expr::type() const { return this->annotatedType; } void Expr::type(const QualTypePtr& ty) { this->annotatedType = ty; } int Expr::case_id() const { return this->cid; } +std::string Expr::toString() const { + std::ostringstream out; + this->show(out); + return std::move(out).str(); +} Primitive::Primitive(int cid, const LexicalAnnotation& la) : Expr(cid, la) { } bool PrimPtrLT::operator()(const PrimitivePtr& p0, const PrimitivePtr& p1) const { return *p0 < *p1; } diff --git a/lib/hobbes/lang/module.C b/lib/hobbes/lang/module.C index 8dd9f3a3..1d86cc2a 100644 --- a/lib/hobbes/lang/module.C +++ b/lib/hobbes/lang/module.C @@ -19,6 +19,11 @@ void Module::show(std::ostream& out) const { out << std::endl; } } +std::string Module::toString() const { + std::ostringstream out; + this->show(out); + return std::move(out).str(); +} bool isValidOption(const std::string& o) { return o == "Safe" || o == "SafeArrays" || o == "IgnoreUnreachableMatches"; @@ -39,6 +44,11 @@ const std::vector& Module::options() const { /* module defs */ ModuleDef::ModuleDef(int cid, const LexicalAnnotation& la) : LexicallyAnnotated(la), cid(cid) { } int ModuleDef::case_id() const { return this->cid; } +std::string ModuleDef::toString() const { + std::ostringstream out; + this->show(out); + return std::move(out).str(); +} ModuleDef::~ModuleDef() = default; // module imports diff --git a/lib/hobbes/lang/preds/class.C b/lib/hobbes/lang/preds/class.C index 30432782..ee761bd0 100644 --- a/lib/hobbes/lang/preds/class.C +++ b/lib/hobbes/lang/preds/class.C @@ -430,6 +430,11 @@ void TClass::show(std::ostream& out) const { out << " " << tcmember.first << " :: " << hobbes::show(tcmember.second) << "\n"; } } +std::string TClass::toString() const { + std::ostringstream out; + this->show(out); + return std::move(out).str(); +} const TCInstances& TClass::instances() const { return this->tcinstances; @@ -519,6 +524,11 @@ void TCInstance::show(std::ostream& out) const { out << " " << mm.first << " = " << hobbes::show(mm.second) << "\n"; } } +std::string TCInstance::toString() const { + std::ostringstream out; + this->show(out); + return std::move(out).str(); +} //////// // generate class instances from class instance functions @@ -757,6 +767,11 @@ void TCInstanceFn::show(std::ostream& out) const { out << " " << mm.first << " = " << hobbes::show(substitute(simpl, mm.second)) << "\n"; } } +std::string TCInstanceFn::toString() const { + std::ostringstream out; + this->show(out); + return std::move(out).str(); +} // generate a fresh type class and instance generator to control poly/qualtype instantiation in an expression void definePrivateClass(const TEnvPtr& tenv, const std::string& memberName, const ExprPtr& expr) { diff --git a/lib/hobbes/lang/type.C b/lib/hobbes/lang/type.C index 6d1c75da..3a0d0ef2 100644 --- a/lib/hobbes/lang/type.C +++ b/lib/hobbes/lang/type.C @@ -103,6 +103,14 @@ bool TEnv::hasImmediateBinding(const std::string& vname) const { return (this->ptenv.find(vname) != this->ptenv.end()) || (this->unquals && this->unquals->lookup(vname) != PolyTypePtr()); } +std::string TEnv::toString() const { + std::ostringstream out; + for (const auto& pe: ptenv) { + out << " " << pe.first << ": " << pe.second->toString() << '\n'; + } + return std::move(out).str(); +} + void TEnv::bind(const std::string& vname, const PolyTypePtr& pt) { if (hasImmediateBinding(vname)) { throw std::runtime_error("Variable already defined: " + vname); @@ -365,6 +373,12 @@ void PolyType::show(std::ostream& out) const { simplifyVarNames(instantiate())->show(out); } +std::string PolyType::toString() const { + std::ostringstream out; + this->show(out); + return std::move(out).str(); +} + bool PolyType::operator==(const PolyType& rhs) const { return this->vs == rhs.vs && *this->qt == *rhs.qt; } @@ -406,7 +420,14 @@ void QualType::show(std::ostream& out) const { } } +std::string QualType::toString() const { + std::ostringstream out; + this->show(out); + return std::move(out).str(); +} + bool QualType::operator==(const QualType& rhs) const { + return this->cs == rhs.cs && *this->mt == *rhs.mt; } @@ -474,6 +495,12 @@ void Constraint::show(std::ostream& out) const { } } +std::string Constraint::toString() const { + std::ostringstream out; + this->show(out); + return std::move(out).str(); +} + bool Constraint::operator==(const Constraint& rhs) const { if (this->cat != rhs.cat || this->mts.size() != rhs.mts.size()) { return false; @@ -492,6 +519,11 @@ bool Constraint::operator==(const Constraint& rhs) const { //////// MonoType::MonoType(int cid) : cid(cid), tgenCount(0), memorySize(-1) { } int MonoType::case_id() const { return this->cid; } +std::string MonoType::toString() const { + std::ostringstream out; + this->show(out); + return std::move(out).str(); +} MonoType::~MonoType() = default; bool MonoType::operator==(const MonoType& rhs) const { From c62659ded494573442a5fb8384ed1da124c0375f Mon Sep 17 00:00:00 2001 From: Mo Xiaoming <2188767+mo-xiaoming@users.noreply.github.com> Date: Tue, 21 Mar 2023 16:05:02 +0800 Subject: [PATCH 6/8] add tracer for code gen function --- include/hobbes/eval/jitcc.H | 2 + lib/hobbes/eval/jitcc.C | 81 ++++++++++++++++++++++++++++++++++--- 2 files changed, 77 insertions(+), 6 deletions(-) diff --git a/include/hobbes/eval/jitcc.H b/include/hobbes/eval/jitcc.H index d9b13b11..a24939fc 100644 --- a/include/hobbes/eval/jitcc.H +++ b/include/hobbes/eval/jitcc.H @@ -264,6 +264,8 @@ private: struct IrTracer; std::unique_ptr irTracer; + struct JitFuncTracer; + std::unique_ptr jitFuncTracer; }; // shorthand for compilation over a sequence of expressions diff --git a/lib/hobbes/eval/jitcc.C b/lib/hobbes/eval/jitcc.C index f18bb195..c06bd011 100644 --- a/lib/hobbes/eval/jitcc.C +++ b/lib/hobbes/eval/jitcc.C @@ -1,5 +1,4 @@ -#include #include #include #include @@ -9,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -21,10 +21,13 @@ #include #include +#include +#include #include #include #include #include +#include #include #pragma GCC diagnostic push @@ -569,11 +572,72 @@ struct jitcc::IrTracer { } void log(const llvm::Module& m) { - if (!isEnabled()) { + if (isEnabled()) { + m.print(*out, nullptr, false, true); + } + } + +private: + [[nodiscard]] bool isEnabled() const noexcept { + return !!out; + } + + std::unique_ptr out; +}; + +struct jitcc::JitFuncTracer { + JitFuncTracer() { + std::error_code ec; + const char* name = std::getenv("HOBBES_JIT_FUNC_TRACE_FILE"); + if (name == nullptr) { + return; + } + out = std::make_unique(name, ec, llvm::sys::fs::F_None); + if (ec != std::errc()) { + out = nullptr; return; } + } + +private: + struct ModuleFunctions { + ModuleFunctions() = default; + explicit ModuleFunctions(const llvm::Module& m) { + funcs.reserve(m.getFunctionList().size()); + for (const auto& f : m) { + if (!f.isDeclaration()) { + funcs.push_back(f.getName()); + } + } + } + + const std::vector& get() const noexcept { + return funcs; + } + + private: + std::vector funcs; + }; - m.print(*out, nullptr, false, true); +public: + ModuleFunctions getCollector(const llvm::Module& m) { + if (isEnabled()) { + return ModuleFunctions(m); + } + return {}; + } + + void log(const ModuleFunctions& mf, llvm::ExecutionEngine& ee) { + if (isEnabled()) { + for (const auto name : mf.get()) { + if (const auto a = ee.getFunctionAddress(name.str())) { + *out << name << ":0x"; + out->write_hex(a); + *out << '\n'; + } + } + out->flush(); + } } private: @@ -589,7 +653,8 @@ jitcc::jitcc(const TEnvPtr& tenv) : tenv(tenv), vtenv(std::make_unique()), ignoreLocalScope(false), globals(std::make_unique()), globalData(32768 /* min global page size = 32K */), constants(std::make_unique()), - irTracer(std::make_unique()) + irTracer(std::make_unique()), + jitFuncTracer(std::make_unique()) { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmParser(); @@ -620,7 +685,8 @@ jitcc::jitcc(const TEnvPtr& tenv) : tenv(tenv), ignoreLocalScope(false), globalData(32768 /* min global page size = 32K */), - irTracer(std::make_unique()) + irTracer(std::make_unique()), + jitFuncTracer(std::make_unique()) { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmParser(); @@ -786,6 +852,8 @@ void* jitcc::getMachineCode(llvm::Function* f, llvm::JITEventListener* listener) } irTracer->log(*this->currentModule); + const auto moduleFunctions = jitFuncTracer->getCollector(*this->currentModule); + // make a new execution engine out of this module (finalizing the module) std::string err; llvm::ExecutionEngine* ee = makeExecutionEngine(this->currentModule, reinterpret_cast(new jitmm(this))); @@ -839,6 +907,8 @@ void* jitcc::getMachineCode(llvm::Function* f, llvm::JITEventListener* listener) // and _now_ we must be able to get machine code for this function void* pf = ee->getPointerToFunction(f); + jitFuncTracer->log(moduleFunctions, *ee); + if (listener) { ee->UnregisterJITEventListener(listener); } @@ -1239,7 +1309,6 @@ void jitcc::defineGlobal(const std::string& vn, const ExprPtr& ue) { // compile and run this function, it should then perform the global variable assignment // (make sure that any allocation happens in the global context iff we need it) auto f = reinterpret_cast(getMachineCode(initfn)); - if (hasPointerRep(uety)) { size_t oldregion = pushGlobalRegion(); f(); From 47c80a0b3ea977c9d80ad6d8d4d69de969335219 Mon Sep 17 00:00:00 2001 From: Mo Xiaoming <2188767+mo-xiaoming@users.noreply.github.com> Date: Wed, 22 Mar 2023 17:43:57 +0800 Subject: [PATCH 7/8] remove tracer for ir and func address --- include/hobbes/eval/jitcc.H | 6 -- lib/hobbes/eval/jitcc.C | 108 +----------------------------------- 2 files changed, 2 insertions(+), 112 deletions(-) diff --git a/include/hobbes/eval/jitcc.H b/include/hobbes/eval/jitcc.H index a24939fc..53e5527b 100644 --- a/include/hobbes/eval/jitcc.H +++ b/include/hobbes/eval/jitcc.H @@ -9,7 +9,6 @@ #include #include #include -#include #if LLVM_VERSION_MAJOR >= 11 #include @@ -261,11 +260,6 @@ private: #if LLVM_VERSION_MAJOR >= 11 std::unique_ptr orcjit; #endif - - struct IrTracer; - std::unique_ptr irTracer; - struct JitFuncTracer; - std::unique_ptr jitFuncTracer; }; // shorthand for compilation over a sequence of expressions diff --git a/lib/hobbes/eval/jitcc.C b/lib/hobbes/eval/jitcc.C index c06bd011..dd28ded7 100644 --- a/lib/hobbes/eval/jitcc.C +++ b/lib/hobbes/eval/jitcc.C @@ -18,14 +18,10 @@ #include #include #include -#include #include #include #include -#include -#include -#include #include #include #include @@ -557,104 +553,11 @@ private: }; #endif -struct jitcc::IrTracer { - IrTracer() { - std::error_code ec; - const char* name = std::getenv("HOBBES_IR_TRACE_FILE"); - if (name == nullptr) { - return; - } - out = std::make_unique(name, ec, llvm::sys::fs::F_None); - if (ec != std::errc()) { - out = nullptr; - return; - } - } - - void log(const llvm::Module& m) { - if (isEnabled()) { - m.print(*out, nullptr, false, true); - } - } - -private: - [[nodiscard]] bool isEnabled() const noexcept { - return !!out; - } - - std::unique_ptr out; -}; - -struct jitcc::JitFuncTracer { - JitFuncTracer() { - std::error_code ec; - const char* name = std::getenv("HOBBES_JIT_FUNC_TRACE_FILE"); - if (name == nullptr) { - return; - } - out = std::make_unique(name, ec, llvm::sys::fs::F_None); - if (ec != std::errc()) { - out = nullptr; - return; - } - } - -private: - struct ModuleFunctions { - ModuleFunctions() = default; - explicit ModuleFunctions(const llvm::Module& m) { - funcs.reserve(m.getFunctionList().size()); - for (const auto& f : m) { - if (!f.isDeclaration()) { - funcs.push_back(f.getName()); - } - } - } - - const std::vector& get() const noexcept { - return funcs; - } - - private: - std::vector funcs; - }; - -public: - ModuleFunctions getCollector(const llvm::Module& m) { - if (isEnabled()) { - return ModuleFunctions(m); - } - return {}; - } - - void log(const ModuleFunctions& mf, llvm::ExecutionEngine& ee) { - if (isEnabled()) { - for (const auto name : mf.get()) { - if (const auto a = ee.getFunctionAddress(name.str())) { - *out << name << ":0x"; - out->write_hex(a); - *out << '\n'; - } - } - out->flush(); - } - } - -private: - [[nodiscard]] bool isEnabled() const noexcept { - return !!out; - } - - std::unique_ptr out; -}; - #if LLVM_VERSION_MAJOR >= 11 jitcc::jitcc(const TEnvPtr& tenv) : tenv(tenv), vtenv(std::make_unique()), ignoreLocalScope(false), globals(std::make_unique()), globalData(32768 /* min global page size = 32K */), - constants(std::make_unique()), - irTracer(std::make_unique()), - jitFuncTracer(std::make_unique()) + constants(std::make_unique()) { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmParser(); @@ -684,9 +587,7 @@ jitcc::~jitcc() { jitcc::jitcc(const TEnvPtr& tenv) : tenv(tenv), ignoreLocalScope(false), - globalData(32768 /* min global page size = 32K */), - irTracer(std::make_unique()), - jitFuncTracer(std::make_unique()) + globalData(32768 /* min global page size = 32K */) { llvm::InitializeNativeTarget(); llvm::InitializeNativeTargetAsmParser(); @@ -850,9 +751,6 @@ void* jitcc::getMachineCode(llvm::Function* f, llvm::JITEventListener* listener) if (!this->currentModule) { throw std::runtime_error("Internal compiler error, can't derive machine code for unknown function"); } - irTracer->log(*this->currentModule); - - const auto moduleFunctions = jitFuncTracer->getCollector(*this->currentModule); // make a new execution engine out of this module (finalizing the module) std::string err; @@ -907,8 +805,6 @@ void* jitcc::getMachineCode(llvm::Function* f, llvm::JITEventListener* listener) // and _now_ we must be able to get machine code for this function void* pf = ee->getPointerToFunction(f); - jitFuncTracer->log(moduleFunctions, *ee); - if (listener) { ee->UnregisterJITEventListener(listener); } From 2f74ec8492e14178b6517a252029c3ade16444d1 Mon Sep 17 00:00:00 2001 From: Mo Xiaoming <2188767+mo-xiaoming@users.noreply.github.com> Date: Thu, 23 Mar 2023 17:43:18 +0800 Subject: [PATCH 8/8] add functions to print memory pool and jit function addresses --- include/hobbes/eval/cc.H | 5 +++++ include/hobbes/eval/funcdefs.H | 2 ++ include/hobbes/eval/jitcc.H | 2 ++ lib/hobbes/eval/cc.C | 7 +++++++ lib/hobbes/eval/funcdefs.C | 12 ++++++++++++ lib/hobbes/eval/jitcc.C | 16 ++++++++++++++++ 6 files changed, 44 insertions(+) diff --git a/include/hobbes/eval/cc.H b/include/hobbes/eval/cc.H index bdc6f397..1dc2f08f 100644 --- a/include/hobbes/eval/cc.H +++ b/include/hobbes/eval/cc.H @@ -219,6 +219,11 @@ public: // dump the contents of the generated module (useful for debugging) void dumpModule(); +#if LLVM_VERSION_MAJOR < 11 + // show jit functions in name:addr format + [[nodiscard]] std::string showJitMemoryAddresses() const; +#endif + // get the x86 machine code for an expression (useful for debugging) using bytes = std::vector; bytes machineCodeForExpr(const std::string &expr); diff --git a/include/hobbes/eval/funcdefs.H b/include/hobbes/eval/funcdefs.H index df7ea445..23b644ca 100644 --- a/include/hobbes/eval/funcdefs.H +++ b/include/hobbes/eval/funcdefs.H @@ -15,6 +15,8 @@ char* memalloc(size_t, size_t); const array* makeString(const std::string& x); std::string makeStdString(const array* x); +[[nodiscard]] std::string showDetailedMemoryPool(); + } #endif diff --git a/include/hobbes/eval/jitcc.H b/include/hobbes/eval/jitcc.H index 53e5527b..a4f06d29 100644 --- a/include/hobbes/eval/jitcc.H +++ b/include/hobbes/eval/jitcc.H @@ -57,6 +57,8 @@ public: void* getSymbolAddress(const std::string&); #if LLVM_VERSION_MAJOR < 11 + [[nodiscard]] std::string showJitMemoryAddresses(); + // print all module contents void dump() const; #endif diff --git a/lib/hobbes/eval/cc.C b/lib/hobbes/eval/cc.C index 0d17a3a7..e01fe4d8 100644 --- a/lib/hobbes/eval/cc.C +++ b/lib/hobbes/eval/cc.C @@ -653,6 +653,13 @@ void cc::dumpModule() { #endif } +#if LLVM_VERSION_MAJOR < 11 +std::string cc::showJitMemoryAddresses() const { + hlock _; + return this->jit->showJitMemoryAddresses(); +} +#endif + cc::bytes cc::machineCodeForExpr(const std::string& expr) { hlock _; return this->jit->machineCodeForExpr(unsweetenExpression(readExpr(expr))); diff --git a/lib/hobbes/eval/funcdefs.C b/lib/hobbes/eval/funcdefs.C index cc859cd1..b91002c6 100644 --- a/lib/hobbes/eval/funcdefs.C +++ b/lib/hobbes/eval/funcdefs.C @@ -154,6 +154,18 @@ void printMemoryPool() { std::cout << showMemoryPool() << std::flush; } +std::string showDetailedMemoryPool() { + std::ostringstream oss; + oss << "{"; + std::string sep; + for (const auto& region: threadRegions()) { + oss << sep << "\n\"" << region.first << "\": " << region.second->show(); + sep = ","; + } + oss << "\n}"; + return std::move(oss).str(); +} + void resetMemoryPool() { threadRegion().clear(); } diff --git a/lib/hobbes/eval/jitcc.C b/lib/hobbes/eval/jitcc.C index dd28ded7..ef702b00 100644 --- a/lib/hobbes/eval/jitcc.C +++ b/lib/hobbes/eval/jitcc.C @@ -18,6 +18,7 @@ #include #include #include +#include #include #include @@ -670,6 +671,21 @@ llvm::Module* jitcc::module() { } return this->currentModule; } + +std::string jitcc::showJitMemoryAddresses() { + std::string s; + llvm::raw_string_ostream os(s); + for (const auto* m: this->modules) { + for (const auto& f: *m) { + if (!f.isDeclaration()) { + if (void* addr = this->getSymbolAddress(f.getName().str())) { + os << f.getName() << ':' << addr << '\n'; + } + } + } + } + return s; +} #endif #if LLVM_VERSION_MAJOR >= 11