/
redgpu
/
ispc
Обзор
Документация
Войти
/
redgpu
/
ispc
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
v1.28.2
src/ast.cpp
618 строк
21 KB
Aleksei Nurmukhametov
Fix sign-compare warnings
28 июл 2025, 12:52
28 июл 2025, 12:52
0fad216
Код
Авторство
О чём код?
/* Copyright (c) 2011-2025, Intel Corporation SPDX-License-Identifier: BSD-3-Clause */ /** @file ast.cpp @brief General functionality related to abstract syntax trees and traversal of them. */ #include "ast.h" #include "expr.h" #include "func.h" #include "stmt.h" #include "sym.h" #include "util.h" #include <llvm/Support/TimeProfiler.h> using namespace ispc; /////////////////////////////////////////////////////////////////////////// // Indent Indent::~Indent() { Assert(stack.empty() && "Indent stack is not empty on destruction"); // Any Print() call must be paired by Done() call Assert(printCalls == doneCalls && "AST dump has encountered a bug"); }; void Indent::pushSingle() { stack.push_back(1); } void Indent::pushList(int i) { if (i > 0) { stack.push_back(i); } } void Indent::setNextLabel(std::string s) { label = std::move(s); } // Print indent and an optional string void Indent::Print(const char *title) { printCalls++; Assert(!stack.empty()); int &top = stack.back(); Assert(top > 0); for (size_t i = 0; i < (stack.size() - 1); i++) { if (stack[i] == 0) { printf(" "); } else { printf("| "); } } if (top == 1) { printf("`-"); } else { printf("|-"); } top--; if (!label.empty()) { printf("(%s) ", label.c_str()); label.clear(); } // An optional string if (title != nullptr) { printf("%s", title); } } void Indent::Print(const char *title, const SourcePos &pos) { // Same as previous version Print(title); // Plus source position info pos.Print(); } void Indent::PrintLn(const char *title, const SourcePos &pos) { // Same as previous version Print(title, pos); // Plus end of line printf("\n"); } void Indent::Done() { doneCalls++; Assert(!stack.empty()); int &top = stack.back(); Assert(top >= 0); if (top == 0) { stack.pop_back(); } } /////////////////////////////////////////////////////////////////////////// // ASTNode ASTNode::~ASTNode() {} void ASTNode::Dump() const { Indent indent; indent.pushSingle(); Print(indent); fflush(stdout); } /////////////////////////////////////////////////////////////////////////// // AST AST::~AST() { for (auto p : functions) { delete p; } for (auto p : functionTemplates) { delete p; } } void AST::AddFunction(Symbol *sym, Stmt *code) { if (sym == nullptr) { return; } functions.push_back(new Function(sym, code)); } void AST::AddFunctionTemplate(TemplateSymbol *templSym, Stmt *code) { if (templSym == nullptr || code == nullptr) { return; } FunctionTemplate *funcTempl = new FunctionTemplate(templSym, code); templSym->functionTemplate = funcTempl; functionTemplates.push_back(funcTempl); } void AST::GenerateIR() { llvm::TimeTraceScope TimeScope("GenerateIR"); for (auto fn : functions) { fn->GenerateIR(); } for (auto templateFn : functionTemplates) { templateFn->GenerateIR(); } } void AST::Print(Globals::ASTDumpKind printKind) const { if (printKind == Globals::ASTDumpKind::None) { return; } printf("AST\n"); Indent indent; // Function templates int funcsToPrint = 0; int funcTemplsToPrint = 0; if (printKind == Globals::ASTDumpKind::All) { funcTemplsToPrint = functionTemplates.size(); funcsToPrint = functions.size(); } indent.pushList(funcTemplsToPrint + funcsToPrint); // Function templates for (unsigned int i = 0; i < functionTemplates.size(); ++i) { if (printKind == Globals::ASTDumpKind::All) { functionTemplates[i]->Print(indent); } } // Functions for (unsigned int i = 0; i < functions.size(); ++i) { if (printKind == Globals::ASTDumpKind::All) { functions[i]->Print(indent); } } fflush(stdout); } /////////////////////////////////////////////////////////////////////////// ASTNode *ispc::WalkAST(ASTNode *node, ASTPreCallBackFunc preFunc, ASTPostCallBackFunc postFunc, void *data) { if (node == nullptr) { return node; } // Call the callback function if (preFunc != nullptr) { if (preFunc(node, data) == false) { // The function asked us to not continue recursively, so stop. return node; } } //////////////////////////////////////////////////////////////////////////// // Handle Statements if (llvm::dyn_cast<Stmt>(node) != nullptr) { if (ExprStmt *es = llvm::dyn_cast<ExprStmt>(node)) { es->expr = (Expr *)WalkAST(es->expr, preFunc, postFunc, data); } else if (DeclStmt *ds = llvm::dyn_cast<DeclStmt>(node)) { for (unsigned int i = 0; i < ds->vars.size(); ++i) { ds->vars[i].init = (Expr *)WalkAST(ds->vars[i].init, preFunc, postFunc, data); } } else if (IfStmt *is = llvm::dyn_cast<IfStmt>(node)) { is->test = (Expr *)WalkAST(is->test, preFunc, postFunc, data); is->trueStmts = (Stmt *)WalkAST(is->trueStmts, preFunc, postFunc, data); is->falseStmts = (Stmt *)WalkAST(is->falseStmts, preFunc, postFunc, data); } else if (DoStmt *dos = llvm::dyn_cast<DoStmt>(node)) { dos->testExpr = (Expr *)WalkAST(dos->testExpr, preFunc, postFunc, data); dos->bodyStmts = (Stmt *)WalkAST(dos->bodyStmts, preFunc, postFunc, data); } else if (ForStmt *fs = llvm::dyn_cast<ForStmt>(node)) { fs->init = (Stmt *)WalkAST(fs->init, preFunc, postFunc, data); fs->test = (Expr *)WalkAST(fs->test, preFunc, postFunc, data); fs->step = (Stmt *)WalkAST(fs->step, preFunc, postFunc, data); fs->stmts = (Stmt *)WalkAST(fs->stmts, preFunc, postFunc, data); } else if (ForeachStmt *fes = llvm::dyn_cast<ForeachStmt>(node)) { for (unsigned int i = 0; i < fes->startExprs.size(); ++i) { fes->startExprs[i] = (Expr *)WalkAST(fes->startExprs[i], preFunc, postFunc, data); } for (unsigned int i = 0; i < fes->endExprs.size(); ++i) { fes->endExprs[i] = (Expr *)WalkAST(fes->endExprs[i], preFunc, postFunc, data); } fes->stmts = (Stmt *)WalkAST(fes->stmts, preFunc, postFunc, data); } else if (ForeachActiveStmt *fas = llvm::dyn_cast<ForeachActiveStmt>(node)) { fas->stmts = (Stmt *)WalkAST(fas->stmts, preFunc, postFunc, data); } else if (ForeachUniqueStmt *fus = llvm::dyn_cast<ForeachUniqueStmt>(node)) { fus->expr = (Expr *)WalkAST(fus->expr, preFunc, postFunc, data); fus->stmts = (Stmt *)WalkAST(fus->stmts, preFunc, postFunc, data); } else if (CaseStmt *cs = llvm::dyn_cast<CaseStmt>(node)) { cs->stmts = (Stmt *)WalkAST(cs->stmts, preFunc, postFunc, data); } else if (DefaultStmt *defs = llvm::dyn_cast<DefaultStmt>(node)) { defs->stmts = (Stmt *)WalkAST(defs->stmts, preFunc, postFunc, data); } else if (SwitchStmt *ss = llvm::dyn_cast<SwitchStmt>(node)) { ss->expr = (Expr *)WalkAST(ss->expr, preFunc, postFunc, data); ss->stmts = (Stmt *)WalkAST(ss->stmts, preFunc, postFunc, data); } else if (llvm::dyn_cast<BreakStmt>(node) != nullptr || llvm::dyn_cast<ContinueStmt>(node) != nullptr || llvm::dyn_cast<GotoStmt>(node) != nullptr) { // nothing } else if (LabeledStmt *ls = llvm::dyn_cast<LabeledStmt>(node)) { ls->stmt = (Stmt *)WalkAST(ls->stmt, preFunc, postFunc, data); } else if (ReturnStmt *rs = llvm::dyn_cast<ReturnStmt>(node)) { rs->expr = (Expr *)WalkAST(rs->expr, preFunc, postFunc, data); } else if (StmtList *sl = llvm::dyn_cast<StmtList>(node)) { std::vector<Stmt *> &sls = sl->stmts; for (unsigned int i = 0; i < sls.size(); ++i) { sls[i] = (Stmt *)WalkAST(sls[i], preFunc, postFunc, data); } } else if (PrintStmt *ps = llvm::dyn_cast<PrintStmt>(node)) { ps->values = (Expr *)WalkAST(ps->values, preFunc, postFunc, data); } else if (AssertStmt *as = llvm::dyn_cast<AssertStmt>(node)) { as->expr = (Expr *)WalkAST(as->expr, preFunc, postFunc, data); } else if (DeleteStmt *dels = llvm::dyn_cast<DeleteStmt>(node)) { dels->expr = (Expr *)WalkAST(dels->expr, preFunc, postFunc, data); } else if (UnmaskedStmt *ums = llvm::dyn_cast<UnmaskedStmt>(node)) { ums->stmts = (Stmt *)WalkAST(ums->stmts, preFunc, postFunc, data); } else { FATAL("Unhandled statement type in WalkAST()"); } } else { /////////////////////////////////////////////////////////////////////////// // Handle expressions Assert(llvm::dyn_cast<Expr>(node) != nullptr); if (UnaryExpr *ue = llvm::dyn_cast<UnaryExpr>(node)) { ue->expr = (Expr *)WalkAST(ue->expr, preFunc, postFunc, data); } else if (BinaryExpr *be = llvm::dyn_cast<BinaryExpr>(node)) { be->arg0 = (Expr *)WalkAST(be->arg0, preFunc, postFunc, data); be->arg1 = (Expr *)WalkAST(be->arg1, preFunc, postFunc, data); } else if (AssignExpr *ae = llvm::dyn_cast<AssignExpr>(node)) { ae->lvalue = (Expr *)WalkAST(ae->lvalue, preFunc, postFunc, data); ae->rvalue = (Expr *)WalkAST(ae->rvalue, preFunc, postFunc, data); } else if (SelectExpr *se = llvm::dyn_cast<SelectExpr>(node)) { se->test = (Expr *)WalkAST(se->test, preFunc, postFunc, data); se->expr1 = (Expr *)WalkAST(se->expr1, preFunc, postFunc, data); se->expr2 = (Expr *)WalkAST(se->expr2, preFunc, postFunc, data); } else if (ExprList *el = llvm::dyn_cast<ExprList>(node)) { for (unsigned int i = 0; i < el->exprs.size(); ++i) { el->exprs[i] = (Expr *)WalkAST(el->exprs[i], preFunc, postFunc, data); } } else if (FunctionCallExpr *fce = llvm::dyn_cast<FunctionCallExpr>(node)) { fce->func = (Expr *)WalkAST(fce->func, preFunc, postFunc, data); fce->args = (ExprList *)WalkAST(fce->args, preFunc, postFunc, data); for (int k = 0; k < 3; k++) { fce->launchCountExpr[k] = (Expr *)WalkAST(fce->launchCountExpr[k], preFunc, postFunc, data); } } else if (IndexExpr *ie = llvm::dyn_cast<IndexExpr>(node)) { ie->baseExpr = (Expr *)WalkAST(ie->baseExpr, preFunc, postFunc, data); ie->index = (Expr *)WalkAST(ie->index, preFunc, postFunc, data); } else if (MemberExpr *me = llvm::dyn_cast<MemberExpr>(node)) { me->expr = (Expr *)WalkAST(me->expr, preFunc, postFunc, data); } else if (TypeCastExpr *tce = llvm::dyn_cast<TypeCastExpr>(node)) { tce->expr = (Expr *)WalkAST(tce->expr, preFunc, postFunc, data); } else if (ReferenceExpr *re = llvm::dyn_cast<ReferenceExpr>(node)) { re->expr = (Expr *)WalkAST(re->expr, preFunc, postFunc, data); } else if (PtrDerefExpr *ptrderef = llvm::dyn_cast<PtrDerefExpr>(node)) { ptrderef->expr = (Expr *)WalkAST(ptrderef->expr, preFunc, postFunc, data); } else if (RefDerefExpr *refderef = llvm::dyn_cast<RefDerefExpr>(node)) { refderef->expr = (Expr *)WalkAST(refderef->expr, preFunc, postFunc, data); } else if (SizeOfExpr *soe = llvm::dyn_cast<SizeOfExpr>(node)) { soe->expr = (Expr *)WalkAST(soe->expr, preFunc, postFunc, data); } else if (AllocaExpr *alloce = llvm::dyn_cast<AllocaExpr>(node)) { alloce->expr = (Expr *)WalkAST(alloce->expr, preFunc, postFunc, data); } else if (AddressOfExpr *aoe = llvm::dyn_cast<AddressOfExpr>(node)) { aoe->expr = (Expr *)WalkAST(aoe->expr, preFunc, postFunc, data); } else if (NewExpr *newe = llvm::dyn_cast<NewExpr>(node)) { newe->countExpr = (Expr *)WalkAST(newe->countExpr, preFunc, postFunc, data); newe->initExpr = (Expr *)WalkAST(newe->initExpr, preFunc, postFunc, data); } else if (llvm::dyn_cast<SymbolExpr>(node) != nullptr || llvm::dyn_cast<ConstExpr>(node) != nullptr || llvm::dyn_cast<FunctionSymbolExpr>(node) != nullptr || llvm::dyn_cast<SyncExpr>(node) != nullptr || llvm::dyn_cast<NullPointerExpr>(node) != nullptr) { // nothing to do } else { FATAL("Unhandled expression type in WalkAST()."); } } // Call the callback function if (postFunc != nullptr) { return postFunc(node, data); } else { return node; } } static ASTNode *lTypeCheckNode(ASTNode *node, void *) { // Skip if already type-checked if (node->IsTypeChecked()) { return node; } if (node->IsTypeCheckInProgress()) { // It should not happen but if it did return node without marking it as type-checked to avoid recursion Assert(node->IsTypeCheckInProgress()); return node; } // Mark that we're starting the type check process for this node node->StartTypeCheck(); ASTNode *result = node->TypeCheck(); // Mark that we're finished with type checking this node node->FinishTypeCheck(); if (result) { // Mark result as type-checked too result->SetTypeChecked(); } return result; } static ASTNode *lOptimizeNode(ASTNode *node, void *) { // Skip if already optimized if (node->IsOptimized()) { return node; } if (node->IsOptimizeInProgress()) { // It should not happen but if it did return node without marking it as optimized to avoid recursion return node; } Assert(node->IsTypeChecked() && "Node must be type-checked before optimization"); // Mark that we're starting the optimization process for this node node->StartOptimize(); // Now proceed with optimization ASTNode *result = node->Optimize(); // Mark that we're finished with optimizing this node node->FinishOptimize(); if (result) { result->SetOptimized(); } return result; } ASTNode *ispc::Optimize(ASTNode *root) { return WalkAST(root, nullptr, lOptimizeNode, nullptr); } Expr *ispc::Optimize(Expr *expr) { return (Expr *)Optimize((ASTNode *)expr); } Stmt *ispc::Optimize(Stmt *stmt) { return (Stmt *)Optimize((ASTNode *)stmt); } ASTNode *ispc::TypeCheck(ASTNode *root) { return WalkAST(root, nullptr, lTypeCheckNode, nullptr); } Expr *ispc::TypeCheck(Expr *expr) { return (Expr *)TypeCheck((ASTNode *)expr); } Stmt *ispc::TypeCheck(Stmt *stmt) { return (Stmt *)TypeCheck((ASTNode *)stmt); } /** * Performs type checking followed by optimization on the given node. * This encapsulates the common pattern of calling TypeCheck() followed by Optimize(). */ ASTNode *ispc::TypeCheckAndOptimize(ASTNode *root) { if (root == nullptr) { return nullptr; } // First type check ASTNode *result = TypeCheck(root); if (result == nullptr) { return nullptr; } // Then optimize return Optimize(result); } /** * Convenience version of TypeCheckAndOptimize() for Expr *s */ Expr *ispc::TypeCheckAndOptimize(Expr *expr) { return (Expr *)TypeCheckAndOptimize((ASTNode *)expr); } /** * Convenience version of TypeCheckAndOptimize() for Stmt *s */ Stmt *ispc::TypeCheckAndOptimize(Stmt *stmt) { return (Stmt *)TypeCheckAndOptimize((ASTNode *)stmt); } struct CostData { CostData() { cost = foreachDepth = 0; } int cost; int foreachDepth; }; static bool lCostCallbackPre(ASTNode *node, void *d) { CostData *data = (CostData *)d; if (llvm::dyn_cast<ForeachStmt>(node) != nullptr) { ++data->foreachDepth; } if (data->foreachDepth == 0) { data->cost += node->EstimateCost(); } return true; } static ASTNode *lCostCallbackPost(ASTNode *node, void *d) { CostData *data = (CostData *)d; if (llvm::dyn_cast<ForeachStmt>(node) != nullptr) { --data->foreachDepth; } return node; } int ispc::EstimateCost(ASTNode *root) { CostData data; WalkAST(root, lCostCallbackPre, lCostCallbackPost, &data); return data.cost; } /** Given an AST node, check to see if it's safe if we happen to run the code for that node with the execution mask all off. */ static bool lCheckAllOffSafety(ASTNode *node, void *data) { bool *okPtr = (bool *)data; FunctionCallExpr *fce = llvm::dyn_cast<FunctionCallExpr>(node); if (fce != nullptr) { if (fce->func == nullptr) { return false; } const Type *type = fce->func->GetType(); const PointerType *pt = CastType<PointerType>(type); if (pt != nullptr) { type = pt->GetBaseType(); } const FunctionType *ftype = CastType<FunctionType>(type); Assert(ftype != nullptr); if (ftype->IsSafe() == false) { *okPtr = false; return false; } } if (llvm::dyn_cast<AssertStmt>(node) != nullptr) { // While it's fine to run the assert for varying tests, it's not // desirable to check an assert on a uniform variable if all of the // lanes are off. *okPtr = false; return false; } if (llvm::dyn_cast<PrintStmt>(node) != nullptr) { *okPtr = false; return false; } if (llvm::dyn_cast<NewExpr>(node) != nullptr || llvm::dyn_cast<DeleteStmt>(node) != nullptr) { // We definitely don't want to run the uniform variants of these if // the mask is all off. It's also worth skipping the overhead of // executing the varying versions of them in the all-off mask case. *okPtr = false; return false; } if (llvm::dyn_cast<ForeachStmt>(node) != nullptr || llvm::dyn_cast<ForeachActiveStmt>(node) != nullptr || llvm::dyn_cast<ForeachUniqueStmt>(node) != nullptr || llvm::dyn_cast<UnmaskedStmt>(node) != nullptr) { // The various foreach statements also shouldn't be run with an // all-off mask. Since they can re-establish an 'all on' mask, // this would be pretty unintuitive. (More generally, it's // possibly a little strange to allow foreach in the presence of // any non-uniform control flow...) // // Similarly, the implementation of foreach_unique assumes as a // precondition that the mask won't be all off going into it, so // we'll enforce that here... *okPtr = false; return false; } if (BinaryExpr *binaryExpr = llvm::dyn_cast<BinaryExpr>(node)) { if (binaryExpr->op == BinaryExpr::Mod || binaryExpr->op == BinaryExpr::Div) { *okPtr = false; return false; } } IndexExpr *ie = llvm::dyn_cast<IndexExpr>(node); if (ie != nullptr && ie->baseExpr != nullptr) { const Type *type = ie->baseExpr->GetType(); if (type == nullptr) { return true; } if (CastType<ReferenceType>(type) != nullptr) { type = type->GetReferenceTarget(); } ConstExpr *ce = llvm::dyn_cast<ConstExpr>(ie->index); if (ce == nullptr) { // indexing with a variable... -> not safe *okPtr = false; return false; } const PointerType *pointerType = CastType<PointerType>(type); if (pointerType != nullptr) { // pointer[index] -> can't be sure -> not safe *okPtr = false; return false; } const SequentialType *seqType = CastType<SequentialType>(type); Assert(seqType != nullptr); int nElements = seqType->GetElementCount(); if (nElements == 0) { // Unsized array, so we can't be sure -> not safe *okPtr = false; return false; } int32_t indices[ISPC_MAX_NVEC]; int count = ce->GetValues(indices); for (int i = 0; i < count; ++i) { if (indices[i] < 0 || indices[i] >= nElements) { // Index is out of bounds -> not safe *okPtr = false; return false; } } // All indices are in-bounds return true; } if (MemberExpr *me = llvm::dyn_cast<MemberExpr>(node)) { if (me->dereferenceExpr) { *okPtr = false; return false; } } if (llvm::dyn_cast<PtrDerefExpr>(node) != nullptr) { *okPtr = false; return false; } /* Don't allow turning if/else to straight-line-code if we assign to a uniform or post-/pre- increment/decrement. */ if (AssignExpr *ae = llvm::dyn_cast<AssignExpr>(node)) { if (ae->GetType() && ae->GetType()->IsUniformType()) { *okPtr = false; return false; } } if (UnaryExpr *ue = llvm::dyn_cast<UnaryExpr>(node)) { if (ue->op == UnaryExpr::PreInc || ue->op == UnaryExpr::PreDec || ue->op == UnaryExpr::PostInc || ue->op == UnaryExpr::PostDec) { if (ue->GetType() && ue->GetType()->IsUniformType()) { *okPtr = false; return false; } } } if (llvm::dyn_cast<SyncExpr>(node) != nullptr || llvm::dyn_cast<AllocaExpr>(node) != nullptr) { *okPtr = false; return false; } return true; } bool ispc::SafeToRunWithMaskAllOff(ASTNode *root) { bool safe = true; WalkAST(root, lCheckAllOffSafety, nullptr, &safe); return safe; }