Skip to content

Instantly share code, notes, and snippets.

@seven-mile
Created July 15, 2022 16:21
Show Gist options
  • Save seven-mile/5456888d81fe61216d84c55af2efe204 to your computer and use it in GitHub Desktop.
Save seven-mile/5456888d81fe61216d84c55af2efe204 to your computer and use it in GitHub Desktop.
kamiya suki
//
// 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