Created
July 15, 2022 16:21
-
-
Save seven-mile/5456888d81fe61216d84c55af2efe204 to your computer and use it in GitHub Desktop.
kamiya suki
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// Created by Myriad Dreamin on 2022/7/10. | |
// | |
#ifndef DX_INTERNAL_CODE_GEN_LLVM_BUILD_LEXICAL_RESOLVER_H | |
#define DX_INTERNAL_CODE_GEN_LLVM_BUILD_LEXICAL_RESOLVER_H | |
#include "BuildResolver.h" | |
#include <dx/AST/ASTFields.h> | |
#include <dx/AST/Visitor.h> | |
#include <dx/Support/Fmt.h> | |
#include <dx/Target/LLVM/SymbolAnalysis.h> | |
#include <dx/Token/Printer.h> | |
#include <dx/Trace/CodeGen/BuildLexicalResolver.h> | |
namespace dx::predicates { | |
// lexical scope1 | |
template <typename T> static inline constexpr bool isLexicalScope() { | |
return operator_utils::isOneOfType< | |
T, ClassDeclStmt, EnumDeclStmt, FuncDeclStmt, InterfaceDeclStmt, | |
ModuleDeclStmt>(); | |
} | |
// lexical scope2 | |
template <typename T> static inline constexpr bool isBlockScope() { | |
return operator_utils::isOneOfType< | |
T, SwitchStmt, DoStmt, WhileStmt, BlockStmt, ForOfStmt, ForStmt, ForInStmt, | |
TryStmt, WithStmt>(); | |
} | |
// variable decls | |
template <typename T> static inline constexpr bool isNamedDefinitionRest() { | |
return operator_utils::isOneOfType< | |
T, ParamDeclStmt, VarDeclStmt, ArrayBindingDeclStmt, ObjectBindingDeclStmt, | |
OmittedBindingDeclStmt>(); | |
} | |
// export decls | |
template <typename T> static inline constexpr bool isExportDeclStmt() { | |
return operator_utils::isOneOfType< | |
T, ExportExprDeclStmt, ExportNamespaceDeclStmt, | |
ExportDeclarationDeclStmt>(); | |
} | |
// import decls | |
template <typename T> static inline constexpr bool isImportDeclStmt() { | |
return operator_utils::isOneOfType<T, ImportDeclStmt, ImportExprDeclStmt>(); | |
} | |
// basic block reference | |
template <typename T> static inline constexpr bool isLabeledStmt() { | |
return operator_utils::isOneOfType<T, BreakStmt, ContinueStmt, LabeledStmt>(); | |
} | |
// value reference | |
template <typename T> static inline constexpr bool isNamedReference() { | |
return operator_utils::isOneOfType< | |
T, IdentExpr, ThisLiteralExpr, ReferenceTypeDecl>(); | |
} | |
// type definitions | |
template <typename T> static inline constexpr bool isTypeNamedDefinition() { | |
return operator_utils::isOneOfType<T, TypeAliasDeclStmt, ParamTypeDecl>(); | |
} | |
template <typename T> static inline constexpr bool isNamedDefinition() { | |
return isLexicalScope<T>() || isNamedDefinitionRest<T>(); | |
} | |
} // namespace dx::predicates | |
namespace dx::code_gen::llvm_target { | |
#define macroBuildLexicalResolverMarkAsTodo() \ | |
castToUser().doMarkAbort( \ | |
__func__, __FILE__, __LINE__, "build resolver not implemented") | |
#define macroBuildLexicalResolverMarkAsFutureTodo() macroBuildLexicalResolverMarkAsTodo() | |
#define macroBuildLexicalResolverMarkAsTodoAt(node) \ | |
castToUser().doMarkAbort(__func__, __FILE__, __LINE__, genPos(node)) | |
#define macroBuildLexicalResolverBugOn(msg) \ | |
castToUser().doMarkAbort(__func__, __FILE__, __LINE__, msg) | |
SemanticDiagReporter::range_t captureRange(const ASTNode &node) { | |
return {node.pos, node.pos + node.len}; | |
} | |
template <typename U> | |
class BuildLexicalResolver | |
: public RecursiveASTVisitorBase<BuildLexicalResolver<U>> { | |
using User = BuildResolvingUserModel<U>; | |
using Base = RecursiveASTVisitorBase<BuildLexicalResolver<U>>; | |
using ASTFields = dx::details::ASTFields; | |
bool drop_arr_; | |
int dep_; | |
template <ASTFields::Enum field> void printF() { | |
using namespace dx::details; | |
for (int i = 0; i < dep_; i++) { | |
fmt::print(" "); | |
} | |
fmt::print("{}::{}: ", astFieldASTCls<field>(), astFieldName<field>()); | |
} | |
protected: | |
LexicalScope guard_scope{}, global_scope{&guard_scope}; | |
LexicalScope *scope; | |
User &u; | |
SymbolAnalysis &sa; | |
public: | |
explicit BuildLexicalResolver(User &u) | |
: u(u), sa(u.context.sym_anal), scope(&global_scope), dep_(0), drop_arr_(false) { | |
trace_lexical_resolve(">>== %s =======", u.project_mangle.str().c_str()); | |
} | |
~BuildLexicalResolver() { | |
warnUnresolved(); | |
trace_lexical_resolve("<<== %s =======", u.project_mangle.str().c_str()); | |
} | |
auto &castToUser() { | |
return u; | |
} | |
void warnUnresolved() { | |
#if DX_TRACE_LEXICAL_RESOLUTION | |
for (auto &e : scope->def_uses) { | |
auto &resolving = e.second; | |
for (auto *ref : resolving.references) { | |
if (!trace_lexical_resolution_warn()) { | |
return; | |
} | |
trace_lexical_resolve( | |
"unresolved(%s): " trace_node_fmt, e.first().str().c_str(), | |
trace_node_arg(ref)); | |
} | |
} | |
#endif | |
} | |
template <typename T> static inline constexpr bool isPartialVisit() { | |
return operator_utils::isOneOfType< | |
T, PropAccessExpr, ElemAccessExpr, PropTypeDecl>(); | |
} | |
template <typename T> static inline constexpr bool isControlledVisit() { | |
return predicates::isNamedDefinition<T>() || isPartialVisit<T>(); | |
} | |
template <typename AN, ASTFields::Enum field, typename F> | |
static constexpr bool shouldVisit() { | |
using namespace ::dx::details; | |
constexpr bool kIsSelfField = | |
field == ASTFields::Self || field == ASTFields::SelfEnd; | |
if (isControlledVisit<AN>()) { | |
return kIsSelfField; | |
} | |
return kIsSelfField || astFieldIsAST<field>(); | |
} | |
#if 0 | |
template < | |
typename AN, ASTFields::Enum field, typename F, typename Condition = void> | |
struct Stack { | |
Stack(BuildLexicalResolver &, const F &) {} | |
}; | |
#endif | |
struct LexicalScopeRAII { | |
BuildLexicalResolver &r; | |
LexicalScope scope{}; | |
LexicalScopeRAII(BuildLexicalResolver &r) : r(r) { | |
scope.parent = r.scope; | |
r.scope = &scope; | |
fmt::print("enter new sub scope {} from parent {}\n", (void*)r.scope, (void*)r.scope->parent); | |
} | |
~LexicalScopeRAII() { | |
fmt::print("leave sub scope {} return to parent {}\n", (void*)r.scope, (void*)r.scope->parent); | |
r.scope = scope.parent; | |
} | |
}; | |
template <SymNamespace::Tag ns = SymNamespace::ValueNS> | |
LexicalScope::LexicalResolvingNode * | |
recordDefinition(ASTNode *def, llvm::StringRef name) { | |
auto &record = scope->def_uses[name]; | |
record.definitions.push_back(def); | |
auto &si = sa.ns_sym_infos[ns][def]; | |
if (si == nullptr) { | |
si = new SymInfo(); | |
} | |
si->is_definition = true; | |
if (name.contains("arena")) { | |
fmt::print("{}::{} is defined, ug = {}\n", ns, name.str(), (int)scope->feature.under_global); | |
} | |
if (scope->feature.under_global) { | |
llvm::StringMap<SymInfo*> &sis = u.context.global_sis[ns]; | |
if (auto it = sis.find(name); it != sis.end()) { | |
macroBuildLexicalResolverBugOn("global redefinition"); | |
} else { | |
sis[name] = si; | |
} | |
} | |
trace_lexical_resolve( | |
"definition(%s): " trace_node_fmt, name.str().c_str(), | |
trace_node_arg(def)); | |
return &record; | |
} | |
template <SymNamespace::Tag ns = SymNamespace::ValueNS> | |
LexicalScope::LexicalResolvingNode * | |
recordReference(ASTNode *ref, llvm::StringRef name) { | |
auto &record = scope->def_uses[name]; | |
record.references.push_back(ref); | |
auto &si = sa.ns_sym_infos[ns][ref]; | |
if (si == nullptr) { | |
si = new SymInfo(); | |
} | |
si->is_definition = false; | |
trace_lexical_resolve( | |
"reference(%s): " trace_node_fmt, name.str().c_str(), | |
trace_node_arg(ref)); | |
return &record; | |
} | |
template <typename F> void visitOne(const F &field_ins) { | |
if (field_ins) { | |
dep_++; | |
Base::traverse(field_ins); | |
dep_--; | |
} | |
} | |
template <typename F> void visitRepeated(const F &field_ins) { | |
if (!field_ins.empty()) { | |
dep_++; | |
for (auto &n : field_ins) { | |
drop_arr_ = true; | |
Base::traverse(n); | |
} | |
dep_--; | |
} | |
} | |
void resolveReferencesInScope() { | |
for (auto &e : scope->def_uses) { | |
auto &resolving = e.second; | |
if (resolving.definitions.empty()) { | |
auto &parent_resolving = scope->parent->def_uses[e.first()]; | |
parent_resolving.references.splice( | |
parent_resolving.references.end(), resolving.references); | |
} else { | |
bool resolved_flag = false; | |
for (int ns = SymNamespace::ValueNS; !resolved_flag && ns < SymNamespace::MaxNS; ns++) { | |
auto last_def = resolving.definitions.back(); | |
if (!resolving.references.empty()) { | |
if (auto it = sa.ns_sym_infos[ns].find(last_def); it != sa.ns_sym_infos[ns].end()) { | |
it->second->used = true; | |
} | |
} | |
for (auto *ref : resolving.references) { | |
auto &si_use = sa.ns_sym_infos[ns][ref]; | |
si_use->name_dep = last_def; | |
resolved_flag = true; | |
trace_lexical_resolve( | |
"resolved(%s): " trace_node_fmt " => " trace_node_fmt, | |
e.first().str().c_str(), trace_node_arg(ref), | |
trace_node_arg(last_def)); | |
} | |
} | |
} | |
} | |
} | |
template <SymNamespace::Tag ns> | |
void resolveImport(ImportDeclStmt const &node, SemaContext const *from_ctx) { | |
// import 'xxx' | |
if (!node.importClause) { | |
// import all namespaces | |
for (auto &entry : from_ctx->global_sis[ns]) { | |
recordDefinition<ns>(entry.second->name_dep, entry.getKey()); | |
} | |
} else { | |
auto *ic = reinterpret_cast<ImportClause*>(node.importClause); | |
for (auto *spec : ic->specifiers) { | |
llvm::StringRef name = getTokenStr(spec->name), alias = name; | |
if (spec->alias) { | |
alias = getTokenStr(spec->alias); | |
} | |
if (auto entry = from_ctx->global_sis[ns].find(name); | |
entry != from_ctx->global_sis[ns].end()) { | |
recordDefinition<ns>(entry->second->name_dep, alias); | |
} else { | |
macroBuildLexicalResolverBugOn("unknown import spec"); | |
} | |
} | |
} | |
} | |
template <typename AN> void resolveDefinition(AN &node) { | |
llvm::StringRef node_name; | |
macroMatchesType(AN, ClassDeclStmt, InterfaceDeclStmt) { | |
node_name = node.type->name; | |
} | |
macroMatchesType(AN, EnumDeclStmt, FuncDeclStmt) { node_name = node.name; } | |
macroExtendsType(AN, BindingDeclStmtProps) { node_name = node.alias; } | |
macroMatchesType(AN, VarDeclStmt, ParamDeclStmt) { | |
if (node.name) { | |
node_name = u.genCompileTimeStr(node.name); | |
} | |
} | |
macroMatchesType(AN, ModuleDeclStmt) { | |
if (node.name && node.moduleType != ModuleDeclStmt::Kind::Global) { | |
node_name = u.genCompileTimeStr(node.name); | |
} | |
} | |
macroMatchesType(AN, ImportDeclStmt) { | |
auto &ctx = reinterpret_cast<SemaContext&>(u.context); | |
llvm::StringRef from_module; | |
if (node.moduleSpecifier->kind != SyntaxKind::ExprStringLiteral) { | |
macroBuildLexicalResolverMarkAsTodo(); | |
} else { | |
auto *spec = reinterpret_cast<StringLiteralExpr*>(node.moduleSpecifier); | |
from_module = getTokenStr(spec->op); | |
} | |
auto const &[path, from_ctx] = ctx.dependencies[from_module]; | |
if (!from_ctx) { | |
macroBuildLexicalResolverBugOn("invalid dependency sema ctx"); | |
} | |
resolveImport<SymNamespace::ValueNS>(); | |
resolveImport<SymNamespace::TypeNS>(); | |
} | |
if (!node_name.empty()) { | |
macroMatchesType(AN, ClassDeclStmt, InterfaceDeclStmt, EnumDeclStmt, TypeAliasDeclStmt) { | |
recordDefinition<SymNamespace::TypeNS>(&node, node_name); | |
} else { | |
recordDefinition<SymNamespace::ValueNS>(&node, node_name); | |
} | |
} | |
} | |
template <typename AN> void resolveReference(AN &node) { | |
llvm::StringRef node_name; | |
macroMatchesType(AN, IdentExpr) { node_name = getTokenStr(node.ident); } | |
macroMatchesType(AN, ThisLiteralExpr) { node_name = "this"; } | |
if (!node_name.empty()) { | |
recordReference(&node, node_name); | |
} | |
} | |
template <typename AN> void visitLexicalScope(AN &node) { | |
// all the lexical scope are compile-time modifiable, visit them first | |
visitOne(node.decorators); | |
visitOne(node.modifiers); | |
// type level visit | |
macroMatchesType( | |
AN, FuncDeclStmt, ClassDeclStmt, InterfaceDeclStmt, EnumDeclStmt) { | |
visitOne(node.type); | |
} | |
// scope visit | |
macroMatchesType(AN, FuncDeclStmt, ModuleDeclStmt) { visitOne(node.body); } | |
macroMatchesType(AN, EnumDeclStmt) { visitRepeated(node.members); } | |
} | |
template <typename AN> void visitNamedDefinitionRest(AN &node) { | |
macroMatchesType(AN, VarDeclStmt, ParamDeclStmt) { | |
visitOne(node.type); | |
visitRepeated(node.bindings); | |
visitOne(node.initializer); | |
} | |
} | |
template <typename AN> void visitNamedReferenceInside(AN &node) { | |
macroMatchesType(AN, PropAccessExpr) { visitOne(node.exp); } | |
macroMatchesType(AN, ElemAccessExpr) { visitOne(node.exp); } | |
} | |
template <typename AN, ASTFields::Enum field, typename F> | |
void visit(const F &field_ins) { | |
using namespace dx::details; | |
if constexpr (field == ASTFields::Self) { | |
auto &node = *const_cast<AN *>(field_ins); | |
if constexpr (predicates::isLexicalScope<AN>()) { | |
resolveDefinition(node); | |
LexicalScopeRAII scope_raii(*this); | |
macroMatchesType(AN, ModuleDeclStmt) { | |
if (node.moduleType == ModuleDeclStmt::Kind::Global) { | |
fmt::print("global module set\n"); | |
scope->feature.under_global = 1; | |
} | |
} | |
visitLexicalScope(node); | |
resolveReferencesInScope(); | |
return; | |
} | |
if constexpr (predicates::isNamedDefinitionRest<AN>()) { | |
resolveDefinition(node); | |
return visitNamedDefinitionRest(node); | |
} | |
if constexpr (isPartialVisit<AN>()) { | |
return visitNamedReferenceInside(node); | |
} | |
if constexpr (predicates::isNamedReference<AN>()) { | |
return resolveReference(node); | |
} | |
return; | |
} | |
// to run rest task | |
if constexpr (field == ASTFields::SelfEnd) { | |
return; | |
} | |
if constexpr (astFieldIsAST<field>()) { | |
if constexpr (astFieldIsRepeated<field>()) { | |
visitRepeated<F>(field_ins); | |
} else { | |
visitOne<F>(field_ins); | |
} | |
return; | |
} | |
printf("invalid blr visit\n"); | |
abort(); | |
} | |
}; | |
} // namespace dx::code_gen::llvm_target | |
#include <dx/Trace/CodeGen/BuildLexicalResolverEnd.h> | |
#endif // DX_INTERNAL_CODE_GEN_LLVM_BUILD_LEXICAL_RESOLVER_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment