#include "AST.h" #include "code-util.h" #include "pseudocode.h" /* This file implements a pass over the AST to verify that it is in the proper IF. */ /* See lower.cc for a specification of the IF. */ /* If t->checkIF(true) is invoked then any non-conformance to spec will result in an error message being printed (via error()) and exit(-9) being called. If t->checkIF(true) returns at all it will return true. If t->checkIF(false) is invoked then true will be returned if the tree t is in spec and false will be returned if not. No error messages are output. */ extern bool verifyIsVar(const TreeNode *t, bool p); extern bool isVar(const TreeNode *t); #define checkvar(t) verifyIsVar((t), true) static bool generic_checkIF(TreeNode::ConstChildIter l, bool serious) { bool result = true; foriter (p, l, TreeNode::ConstChildIter) if (!(*p)->checkIF(serious)) result = false; return result; } #define generic_error(x) \ do { \ string s(x); \ if (s == "") \ s = string("Internal Error: nodetype ") + oper_name() + \ " not allowed in IF"; \ cerr.flush(); \ error() << s << endl; \ cerr.flush(); \ cout.flush(); \ pseudoprint(cout, 2); \ cout << endl; \ cout.flush(); \ exit(-9); \ } while (0) // Returns whether a tree conforms to the intermediate form spec. // Check everything even if when we know we could return false. bool TreeNode::checkIF(bool serious) const { if (absent()) return true; if (serious) generic_error(""); generic_checkIF(allChildren(), serious); return false; } #define do_not_checkIF(T) \ bool T::checkIF(bool) const \ { \ return true; \ } #define basic_checkIF(T) \ bool T::checkIF(bool serious) const \ { \ return generic_checkIF(allChildren(), serious); \ } basic_checkIF(ExpressionStmtNode) basic_checkIF(ForEachStmtNode) basic_checkIF(CaseNode) basic_checkIF(SwitchBranchNode) basic_checkIF(LabeledStmtNode) basic_checkIF(MonitorFetchNode) basic_checkIF(MonitorUseNode) basic_checkIF(TryStmtNode) basic_checkIF(TryNode) basic_checkIF(CatchNode) basic_checkIF(CatchListNode) basic_checkIF(FinallyNode) basic_checkIF(StaticInitNode) basic_checkIF(OverlapNode) basic_checkIF(EmptyStmtNode) basic_checkIF(InterfaceDeclNode) basic_checkIF(ClassDeclNode) basic_checkIF(CompileUnitNode) basic_checkIF(ConstructorDeclNode) basic_checkIF(ImportNode) basic_checkIF(ImportOnDemandNode) basic_checkIF(MethodNode) basic_checkIF(NameNode) basic_checkIF(ParameterNode) basic_checkIF(TypeListNode) basic_checkIF(TreeListNode) basic_checkIF(TemplateInstanceTypeNode) basic_checkIF(TemplateInstanceDeclNode) basic_checkIF(CheckNullNode) basic_checkIF(PragmaNode) basic_checkIF(UpdatePointBeforeStmtNode) basic_checkIF(ReorderNode) /* Expression nodes that can be handled with the basic check */ basic_checkIF(NullPntrNode) basic_checkIF(ThisNode) basic_checkIF(ArrayInitNode) /* Special nodes that are lowered late, at codegen, when making class init functions etc. */ do_not_checkIF(FieldDeclNode) /* Nodes that are always fine */ do_not_checkIF(GotoNode) do_not_checkIF(DummyNode) do_not_checkIF(CodeLiteralNode) do_not_checkIF(TemplateDeclNode) do_not_checkIF(ForEachSetupNode) ///////////////////////////////////////////////////////////////////////////// // Checking of Blocks and VarDecls ///////////////////////////////////////////////////////////////////////////// bool VarDeclNode::checkIF(bool serious) const { return generic_checkIF(allChildren(), serious) && initExpr()->absent() && !needsDefaultInitialization(); } bool BlockNode::checkIF(bool serious) const { bool result = true; foriter (p, stmts()->allChildren(), TreeNode::ChildIter) if ((*p)->absent() || !isVarDeclNode(*p) && !(*p)->isStatementNode()) { result = false; if (serious) generic_error("BlockNode may only contain VarDeclNodes and StatementNodes, and this one contains a " + string((*p)->oper_name())); } return result & generic_checkIF(allChildren(), serious); } ///////////////////////////////////////////////////////////////////////////// // Checking of Statements ///////////////////////////////////////////////////////////////////////////// bool ForEachPairNode::checkIF(bool serious) const { return isVar(initExpr()); } bool StrippedForEachNode::checkIF(bool serious) const { return stmt()->checkIF(serious); } bool ReturnNode::checkIF(bool serious) const { // cleanup operations should have been // extracted into regular statements bool result = true; result &= !cleanups(); result &= (expr()->absent() || isVar(expr())); result &= generic_checkIF(allChildren(), serious); return result; } bool IfStmtNode::checkIF(bool serious) const { bool result = true; result &= isVar(condition()); result &= generic_checkIF(allChildren(), serious); return result; } bool SwitchNode::checkIF(bool serious) const { bool result = true; result &= isVar(expr()); result &= generic_checkIF(allChildren(), serious); return result; } bool ThrowNode::checkIF(bool serious) const { bool result = true; result &= isVar(expr()); result &= generic_checkIF(allChildren(), serious); return result; } ///////////////////////////////////////////////////////////////////////////// // Checking of Expressions ///////////////////////////////////////////////////////////////////////////// bool CodeLiteralFieldAccessNode::checkIF(bool serious) const { return opnd0()->checkIF(serious); } static inline bool hasType(const ExprNode *t) { return (const_cast(t)->type() != NULL); } static bool verifyHasType(const ExprNode *t, string &s) { if (hasType(t)) return true; else { s = s + "Expression has no type\n"; return false; } } /* See lower.cc for definition of a "var" in this context... it includes constants etc. */ static bool isVar(const TreeNode *t, string &s) { return isVar(t); } static bool isVarDotField(const TreeNode *t, string &s) { assert(!isSFAN(t)); assert(!isThisFAN(t)); if (isTypeFAN(t)) return true; if (isOFAN(t)) { bool result = true; if (!isVar(t->object()) && !((t->decl()->modifiers() & Common::Static) && (isObjectNode(t->object()) || isThisNode(t->object())))) { result = false; s = s + "object.field (ObjectFieldAccessNode) not simplified\n"; } return result; } return false; } static bool isVarDotDotDotField(const TreeNode *t, string &s) { // to be added later // bug! return false; } static bool isUnaryOpVar(const TreeNode *t, string &s) { if (isUnaryArithNode(t) || isNotNode(t) || isComplementNode(t)) { bool result = true; if (!isVar(t->opnd0())) { result = false; s = s + "Argument to unary op not simplified\n"; } return result; } return false; } static bool isVarBinaryOpVar(const TreeNode *t, string &s) { if (isBinaryArithNode(t) || isShiftNode(t) || isEqualityNode(t) || isRelationNode(t) || isBitwiseNode(t)) { bool result = true; if (!isVar(t->opnd0())) { result = false; s = s + "opnd0 of binary op not simplified\n"; } if (!isVar(t->opnd1())) { result = false; s = s + "opnd1 of binary op not simplified\n"; } return result; } return false; } static bool isListOfVars(const TreeNode *t) { for (int i = t->arity(); i-- > 0; ) if (!isVar(t->child(i))) return false; return true; } static bool isIFMethodCall(const TreeNode *t, string &s, bool serious) { if (isMethodCallNode(t) && t->method()->checkIF(serious) && isListOfVars(t->args())) { // check condition that non-void method calls have a return target Decl *d = t->method()->simpName()->decl(); Decl *cd = d->container(); return d->type()->returnType()->kind() == Common::VoidKind || (t->parent() != NULL && isAssignNode(t->parent())) || // PR580: DOB: yuk - foreach opts currently generate an illegal method call to RD.isNotNull (lift.cc:413), // so still allow that to pass the serious check, but ensure that lowering correctly // happens on non-void RD method calls when called on enclosing code (cd->asType()->isDomainOrRectDomainType() && serious); } else return false; } static bool isIFPointConstructor(const TreeNode *t) { return isPointNode(t) && isListOfVars(t->args()); } static bool isListOfPoints(const TreeNode *t) { if (!isTreeListNode(t)) return false; for (int i = t->arity(); i-- > 0; ) if (!isVar(t->child(i)) && !isIFPointConstructor(t->child(i))) return false; return true; } static bool isListOfListOfPoints(const TreeNode *t) { if (!isTreeListNode(t)) return false; for (int i = t->arity(); i-- > 0; ) if (!isListOfPoints(t->child(i))) return false; return true; } bool isIFPointOrDomainConstructor(const TreeNode *t) { if (isPointNode(t)) return isIFPointConstructor(t); else if (isDomainNode(t)) return (isListOfPoints(t->args()) || isListOfListOfPoints(t->args())); else return false; } static bool isIFArrayInit(const TreeNode *t, string &s, bool serious) { return isArrayInitNode(t) && t->initializers()->checkIF(serious); } static bool isIFArrayAccess(const TreeNode *t, string &s, bool serious) { return t->isArrayAccessNode() && (isVar(t->array()) & isVar(t->index())); } static bool isIFAllocation(const TreeNode *t, string &s, bool serious) { bool result = true; if (isAllocateSpaceNode(t)) { if (!t->region()->absent()) if (!t->region()->checkIF(serious)) result = false; } else if (isAllocateArrayNode(t)) { if (!t->region()->absent()) if (!t->region()->checkIF(serious)) result = false; if (!t->dimExprs()->absent()) if (!t->dimExprs()->checkIF(serious)) result = false; if (!t->initExpr()->absent()) result = false; } else return false; return result; } bool AllocateArrayDimensionNode::checkIF(bool serious) const { // PR780 return isVar(expr()); } static bool isIFDynamicTypeNode(const TreeNode *t, string &s) { if (isDynamicTypeNode(t)) { bool result = true; if (!isVar(t->opnd0())) { result = false; s = s + "operand is not simplified\n"; } return result; } return false; } static bool isIFBroadcast(const TreeNode *t, string &s) { if (isIBroadcastNode(t)) { bool result = true; if (!isVar(t->proc())) { result = false; s = s + "Processor in ibroadcast not simplified\n"; } if (!isVar(t->expr())) { result = false; s = s + "Expression to ibroadcast not simplified\n"; } return result; } return false; } static bool isIFCodeLiteralFAN(const ExprNode *e, bool serious) { return isCodeLiteralFAN(e) && e->checkIF(serious); } static bool isIFHasNoOverlapNode(const ExprNode *e, string &s) { return isHasNoOverlapNode(e) && isVar(e->opnd0(), s) && isVar(e->opnd1(), s); } /* Return true iff expression is a "baby expression" and has a type. */ static bool isBabyExpr(const ExprNode *e, bool serious) { bool result = true; string errmsg; if (!verifyHasType(e, errmsg)) result = false; if (!isVar(e, errmsg) && !isIFArrayInit(e, errmsg, serious) && !isVarDotField(e, errmsg) && !isVarDotDotDotField(e, errmsg) && !isUnaryOpVar(e, errmsg) && !isVarBinaryOpVar(e, errmsg) && !isIFPointOrDomainConstructor(e) && !isIFMethodCall(e, errmsg, serious) && !isIFArrayAccess(e, errmsg, serious) && !isIFAllocation(e, errmsg, serious) && !isIFDynamicTypeNode(e, errmsg) && !isIFCodeLiteralFAN(e, serious) && !isIFHasNoOverlapNode(e, errmsg) && !isIFBroadcast(e, errmsg)) result = false; if (!result && serious) { if (errmsg == "") errmsg = "Incomplete lowering"; cout << endl; cout.flush(); cerr.flush(); e->error() << "Internal intermediate form error: " << errmsg << endl; cerr.flush(); cout.flush(); e->pseudoprint(cout, 2); cout << endl; e->print(cout, 2); cout << endl; cout << "Ancestors of offending form: " << endl; int i = 1; for (const TreeNode *t = e; t; t = t->parent()) cout << "\nAncestor " << i++ << endl << endl << pseudocode(e); } return result; } bool ExprNode::checkIF(bool serious) const { return isBabyExpr(this, serious); } bool AssignNode::checkIF(bool serious) const { assert(hasType(this)); return isBabyExpr(static_cast(opnd0()), serious) & isBabyExpr(static_cast(opnd1()), serious); }