/* fold.cc: Definitions for member functions do constant folding */ /* This is done after resolve2, so types are available, but not checked yet */ #include "AST.h" #include "compiler.h" #include "template.h" #include "decls.h" #include "utils.h" #include "code-util.h" #include "pseudocode.h" #include "errors.h" #include "stoptifu/parameter.h" #include extern bool isCastNode(const TreeNode *t); #define intMask (Common::Kind)(Common::CharKind | Common::ByteKind | \ Common::ShortKind | Common::IntKind | \ Common::LongKind) #define arithMask (Common::Kind)(intMask | Common::FloatKind | Common::DoubleKind) #define primitiveMask (Common::Kind)(arithMask | Common::BoolKind) static bool foldedFinalField; /* enabled when folding for optimization, not manifest constant semantics - allow some more aggressive folding which is not guaranteed by Java semantics */ static bool fold_opt = false; static void fixParamName(string &name) { static set used; int i = 1; const string n = name; while (used.find(name) != used.end()) { name = n + " <" + int2string(i++) + '>'; } used.insert(name); } template class convert: public unary_function { public: to_T operator()(from_T arg) { return (to_T)arg; } }; // truncate the chacters in a string16 and return a string static string tostring(const string16& s) { string sout; sout.reserve(s.length()); transform(s.begin(),s.end(),sout.begin(),convert()); return sout; } /* t is a MethodCallNode to ti.lang.Param.XXX(YYY). fn and args indicate what XXX are YYY are. If possible, rewrite. If not, return t (perhaps we can rewrite it later, after more constant folding). */ static TreeNode *rewriteParamCall(TreeNode *t, const string *fn, TreeListNode *args) { #define SUCCEED(x) \ do { \ foldedFinalField = true; \ return new PrimitiveLitNode((x)); \ } while (0); vector v; int count = 0; foriter (p, args->allChildren(), TreeNode::ChildIter) { v.push_back(*p); count++; } // check for allowed argument patterns to simplify later case analysis if (!((count == 2 && isStringLitNode(v[0]) && isPrimitiveLitNode(v[1])) || (count == 3 && isStringLitNode(v[0]) && isStringLitNode(v[1]) && isPrimitiveLitNode(v[2])) || (count == 4 && isStringLitNode(v[0]) && isPrimitiveLitNode(v[1]) && isPrimitiveLitNode(v[2]) && isPrimitiveLitNode(v[3])) || (count == 5 && isStringLitNode(v[0]) && isStringLitNode(v[1]) && isPrimitiveLitNode(v[2]) && isPrimitiveLitNode(v[3]) && isPrimitiveLitNode(v[4])))) return t; if (*fn == "unsigned" && count == 3 && v[2]->constantType() == Common::IntKind) { int32 i = (int32) v[2]->literal().intValue(); if (i < 0) { Error(v[2]->position()) << "Default for unsigned parameter must be non-negative" << endl; i = 0; } string p = tostring(v[0]->text()); string desc = tostring(v[1]->text()); fixParamName(p); SUCCEED(Literal((int32) get_parameter(p, desc, NONNEG, i))); } if (*fn == "unsigned" && count == 2 && v[1]->constantType() == Common::IntKind) { int32 i = (int32) v[1]->literal().intValue(); if (i < 0) { Error(v[1]->position()) << "Default for unsigned parameter must be non-negative" << endl; i = 0; } string p = tostring(v[0]->text()); fixParamName(p); SUCCEED(Literal((int32) get_parameter(p, "", NONNEG, i))); } if (*fn == "integer" && count == 3 && v[2]->constantType() == Common::IntKind) { int32 i = (int32) v[2]->literal().intValue(); string p = tostring(v[0]->text()); string desc = tostring(v[1]->text()); fixParamName(p); SUCCEED(Literal((int32) get_parameter(p, desc, NORMAL, i))); } if (*fn == "integer" && count == 2 && v[1]->constantType() == Common::IntKind) { int32 i = (int32) v[1]->literal().intValue(); string p = tostring(v[0]->text()); fixParamName(p); SUCCEED(Literal((int32) get_parameter(p, "", NORMAL, i))); } if (*fn == "bool" && count == 3 && v[2]->constantType() == Common::BoolKind) { bool b = v[2]->literal().boolValue(); string p = tostring(v[0]->text()); string desc = tostring(v[1]->text()); fixParamName(p); SUCCEED(Literal(get_bool_parameter(p, desc, b))); } if (*fn == "bool" && count == 2 && v[1]->constantType() == Common::BoolKind) { bool b = v[1]->literal().boolValue(); string p = tostring(v[0]->text()); fixParamName(p); SUCCEED(Literal(get_bool_parameter(p, "", b))); } if (*fn == "range" && count == 5 && v[2]->constantType() == Common::IntKind && v[3]->constantType() == Common::IntKind && v[4]->constantType() == Common::IntKind) { int32 lo = (int32) v[2]->literal().intValue(), hi = (int32) v[3]->literal().intValue(), def = (int32) v[4]->literal().intValue(); if (lo > hi) { Error(v[3]->position()) << "in arguments to ti.lang.Param.range(), lo > hi" << endl; lo = hi = def; } if (def < lo || def > hi) { Error(v[4]->position()) << "in arguments to ti.lang.Param.range(), default is out of range" << endl; def = lo; } string p = tostring(v[0]->text()); string desc = tostring(v[1]->text()); fixParamName(p); SUCCEED(Literal((int32) get_parameter_in_range(p, desc, lo, hi, def, true))); } if (*fn == "range" && count == 4 && v[1]->constantType() == Common::IntKind && v[2]->constantType() == Common::IntKind && v[3]->constantType() == Common::IntKind) { int32 lo = (int32) v[1]->literal().intValue(), hi = (int32) v[2]->literal().intValue(), def = (int32) v[3]->literal().intValue(); if (lo > hi) { Error(v[2]->position()) << "in arguments to ti.lang.Param.range(), lo > hi" << endl; lo = hi = def; } if (def < lo || def > hi) { Error(v[3]->position()) << "in arguments to ti.lang.Param.range(), default is out of range" << endl; def = lo; } string p = tostring(v[0]->text()); fixParamName(p); SUCCEED(Literal((int32) get_parameter_in_range(p, "", lo, hi, def, true))); } return t; #undef SUCCEED } static TreeNode *postLoadRewrite(TreeNode *t) { foriter (p, t->allChildren(), TreeNode::ChildIter) *p = postLoadRewrite(*p); TypeNameNode *ty; if (isMethodCallNode(t) && isTypeFAN(t->child(0)) && isNameNode(t->child(0)->child(1)) && isTypeNameNode(t->child(0)->child(0)) && ((ty = static_cast(t->child(0)->child(0)))) && ty->decl() != NULL && ty->decl()->fullName() == "ti.lang.Param") { TreeNode *result = rewriteParamCall(t, t->child(0)->child(1)->ident(), t->args()); return result; } return t; } static void warnIfTiLangParamCalls(TreeNode *t) { foriter (p, t->allChildren(), TreeNode::ChildIter) warnIfTiLangParamCalls(*p); TypeNameNode *ty; if (isMethodCallNode(t) && isTypeFAN(t->child(0)) && isNameNode(t->child(0)->child(1)) && isTypeNameNode(t->child(0)->child(0)) && ((ty = static_cast(t->child(0)->child(0)))) && ty->decl() != NULL && ty->decl()->fullName() == "ti.lang.Param") Warning(t->position(),"param-call") << "call to ti.lang.Param.XXXX(...) " "not handled; will use default" << endl; } void foldConstants(bool fold_opt_pass) { // Could sort things to make this faster, of course. But probably // not worth the effort. if (fold_opt) assert(fold_opt_pass); /* once aggressive mode is on, it stays on */ fold_opt = fold_opt_pass; // Fold all final fields first, once that is done one pass (usually) suffices do { foldedFinalField = false; foreach (f, llist, *allFiles) { compile_status(2,string("folding constants: ") + *((*f)->ident())); foriter (p, (*f)->allChildren(), TreeNode::ChildIter) *p = postLoadRewrite(*p); (*f)->foldFields(); } templateEnv.foldFields(); } while (foldedFinalField); foreach (f, llist, *allFiles) do { (*f)->foldConstants(); foldedFinalField = false; foriter (p, (*f)->allChildren(), TreeNode::ChildIter) *p = postLoadRewrite(*p); } while (foldedFinalField); templateEnv.foldConstants(); assert(!foldedFinalField); foreach (f, llist, *allFiles) warnIfTiLangParamCalls(*f); } void foldInstantiations() { llist< TreeNode * > * const instances = templateEnv.allInstances(); do { foldedFinalField = false; foreach (f, llist, *instances) (*f)->foldFields(); } while (foldedFinalField); foreach (f, llist, *instances) (*f)->foldConstants(); assert(!foldedFinalField); free_all( instances ); } // Fold fields only. void TreeNode::foldFields() { foriter (p, allChildren(), ChildIter) (*p)->foldFields(); } // Fold fields, stop recursion in all other children of classes/interfaces void FieldDeclNode::foldFields() { foldConstants(); } void StaticInitNode::foldFields() { } void InstanceInitNode::foldFields() { } void MethodNode::foldFields() { } void ConstructorDeclNode::foldFields() { } TreeNode *TreeNode::foldConstants() { int fmask = foldMask(); const int numChildren = arity(); for (int sweep = 0; sweep < numChildren; ++sweep) { child(sweep, child(sweep)->foldConstants()); fmask &= child(sweep)->constantType(); } if (fmask) { #if 0 cout << "Folding\n"; print(cout); TreeNode *folded = _fold(); cout << "giving\n"; folded->print(cout); return folded; #else return _fold(); #endif } else return this; } TreeNode *TemplateDeclNode::foldConstants() { return this; } TreeNode *CompileUnitNode::foldConstants() { foriter (type, types()->allChildren(), ChildIter) (*type)->foldConstants(); return this; } void TemplateDeclNode::foldFields() { } TreeNode *FieldDeclNode::foldConstants() { dtype()->foldConstants(); TreeNode *oldInitExpr = initExpr(); TreeNode *newInitExpr = oldInitExpr->foldConstants(); initExpr(newInitExpr); if (oldInitExpr != newInitExpr && (flags() & Final)) foldedFinalField = true; return this; } TreeNode *TreeNode::_fold() { return this; } Common::Kind TreeNode::constantType() const { return (Common::Kind)0; } TreeNode *ObjectNode::foldConstants() { // fold final local variables with constant initializers if (decl() && !decl()->source()->absent() && isVarDeclNode(decl()->source()) && decl()->source()->isfinal() && !decl()->source()->initExpr()->absent() && !isLHSofAssignNode(this) && decl()->source()->initExpr()->constantType()) return decl()->source()->initExpr()->foldConstants()->deepClone(); else return TreeNode::foldConstants(); } Common::Kind PrimitiveLitNode::constantType() const { return literal().kind(); } Common::Kind TypeNode::constantType() const { // Hack: avoid typenodes confusing constant folding (cf cast) return primitiveMask; } Common::Kind TreeNode::foldMask() { return (Common::Kind)0; } /* Arithmetic constant folding */ Common::Kind ComplementNode::foldMask() { return intMask; } TreeNode *ComplementNode::_fold() { return new PrimitiveLitNode(~opnd0()->literal(), position()); } Common::Kind NotNode::foldMask() { return Common::BoolKind; } TreeNode *NotNode::_fold() { return new PrimitiveLitNode(!opnd0()->literal(), position()); } Common::Kind CastNode::foldMask() { return primitiveMask; } TreeNode *CastNode::_fold() { TypeNode *dt = dtype(); if (dt->isCastableFrom(opnd0()->type())) return new PrimitiveLitNode(opnd0()->literal().cast(dt->kind()), position()); else return this; } TreeNode *IfExprNode::foldConstants() { const int numChildren = arity(); for (int sweep = 0; sweep < numChildren; ++sweep) child(sweep, child(sweep)->foldConstants()); TreeNode *cond = condition(); if (cond->constantType() == Common::BoolKind) if (cond->literal().boolValue()) return thenOpnd(); else return elseOpnd(); else return this; } TreeNode *IfStmtNode::foldConstants() { const int numChildren = arity(); for (int sweep = 0; sweep < numChildren; ++sweep) child(sweep, child(sweep)->foldConstants()); TreeNode *cond = condition(); if (fold_opt && cond->constantType() == Common::BoolKind) { TreeNode *result = cond->literal().boolValue() ? thenPart() : elsePart(); return result->absent() ? new EmptyStmtNode(position()) : result; } else return this; } Common::Kind UnaryPlusNode::foldMask() { return arithMask; } TreeNode *UnaryPlusNode::_fold() { return opnd0(); } Common::Kind UnaryMinusNode::foldMask() { return arithMask; } TreeNode *UnaryMinusNode::_fold() { return new PrimitiveLitNode(-opnd0()->literal(), position()); } Common::Kind MultNode::foldMask() { return arithMask; } TreeNode *MultNode::_fold() { return new PrimitiveLitNode(opnd0()->literal() * opnd1()->literal(), position()); } Common::Kind DivNode::foldMask() { return arithMask; } static bool notZero(Literal l) { switch (l.kind()) { default: return true; case IntegerKind: return l.intValue() != 0; case Common::FloatKind: case Common::DoubleKind: return l.doubleValue() != 0.0; } } TreeNode *DivNode::_fold() { if (notZero(opnd1()->literal())) return new PrimitiveLitNode(opnd0()->literal() / opnd1()->literal(), position()); else return this; } Common::Kind RemNode::foldMask() { return arithMask; } TreeNode *RemNode::_fold() { if (notZero(opnd1()->literal())) return new PrimitiveLitNode(opnd0()->literal() % opnd1()->literal(), position()); else return this; } Common::Kind PlusNode::foldMask() { return arithMask; } TreeNode *PlusNode::_fold() { return new PrimitiveLitNode(opnd0()->literal() + opnd1()->literal(), position()); } Common::Kind MinusNode::foldMask() { return arithMask; } TreeNode *MinusNode::_fold() { return new PrimitiveLitNode(opnd0()->literal() - opnd1()->literal(), position()); } Common::Kind LeftShiftLogNode::foldMask() { return intMask; } TreeNode *LeftShiftLogNode::_fold() { return new PrimitiveLitNode(opnd0()->literal().lsl(opnd1()->literal()), position()); } Common::Kind RightShiftLogNode::foldMask() { return intMask; } TreeNode *RightShiftLogNode::_fold() { return new PrimitiveLitNode(opnd0()->literal().rsl(opnd1()->literal()), position()); } Common::Kind RightShiftArithNode::foldMask() { return intMask; } TreeNode *RightShiftArithNode::_fold() { return new PrimitiveLitNode(opnd0()->literal().rsa(opnd1()->literal()), position()); } Common::Kind LTNode::foldMask() { return arithMask; } TreeNode *LTNode::_fold() { return new PrimitiveLitNode(opnd0()->literal().lt(opnd1()->literal()), position()); } Common::Kind LENode::foldMask() { return arithMask; } TreeNode *LENode::_fold() { return new PrimitiveLitNode(opnd0()->literal().le(opnd1()->literal()), position()); } Common::Kind GTNode::foldMask() { return arithMask; } TreeNode *GTNode::_fold() { return new PrimitiveLitNode(opnd0()->literal().gt(opnd1()->literal()), position()); } Common::Kind GENode::foldMask() { return arithMask; } TreeNode *GENode::_fold() { return new PrimitiveLitNode(opnd0()->literal().ge(opnd1()->literal()), position()); } Common::Kind EQNode::foldMask() { return primitiveMask; } TreeNode *EQNode::_fold() { if ((opnd0()->constantType() == Common::BoolKind) == (opnd1()->constantType() == Common::BoolKind)) return new PrimitiveLitNode(opnd0()->literal().eq(opnd1()->literal()), position()); else return this; } Common::Kind NENode::foldMask() { return primitiveMask; } TreeNode *NENode::_fold() { if ((opnd0()->constantType() == Common::BoolKind) == (opnd1()->constantType() == Common::BoolKind)) return new PrimitiveLitNode(opnd0()->literal().ne(opnd1()->literal()), position()); else return this; } Common::Kind BitAndNode::foldMask() { return (Common::Kind)(Common::BoolKind | intMask); } TreeNode *BitAndNode::_fold() { if ((opnd0()->constantType() == Common::BoolKind) == (opnd1()->constantType() == Common::BoolKind)) return new PrimitiveLitNode(opnd0()->literal() & opnd1()->literal(), position()); else return this; } Common::Kind BitOrNode::foldMask() { return (Common::Kind)(Common::BoolKind | intMask); } TreeNode *BitOrNode::_fold() { if ((opnd0()->constantType() == Common::BoolKind) == (opnd1()->constantType() == Common::BoolKind)) return new PrimitiveLitNode(opnd0()->literal() | opnd1()->literal(), position()); else return this; } Common::Kind BitXorNode::foldMask() { return (Common::Kind)(Common::BoolKind | intMask); } TreeNode *BitXorNode::_fold() { if ((opnd0()->constantType() == Common::BoolKind) == (opnd1()->constantType() == Common::BoolKind)) return new PrimitiveLitNode(opnd0()->literal() ^ opnd1()->literal(), position()); else return this; } Common::Kind CandNode::foldMask() { return Common::BoolKind; } TreeNode *CandNode::_fold() { return new PrimitiveLitNode(opnd0()->literal() && opnd1()->literal(), position()); } Common::Kind CorNode::foldMask() { return Common::BoolKind; } TreeNode *CorNode::_fold() { return new PrimitiveLitNode(opnd0()->literal() || opnd1()->literal(), position()); } static TreeNode *foldField(FieldAccessNode *f) { Decl *d = f->decl(); // Only simple and qualified names will have decl()'s at this point, // and only those names should be folded... if (d && (d->modifiers() & Common::Final) && (d->category() == Decl::Field) && !d->source()->absent() && !isLHSofAssignNode(f) // may have an assignment error that hasn't been detected yet ) { TreeNode *source = d->source()->initExpr(); if (source->constantType()) { #if 0 cout << "Field folding\n"; f->print(cout); cout << "giving\n"; source->print(cout); #endif // make a new node so that the correct source position is retained // cast ensures field type is preserved (PR694) return new PrimitiveLitNode(source->literal().cast(d->type()->kind()), f->position()); } } return f; } // Don't fold ObjectFieldAccessNodes or SuperFieldAccessNodes // DOB: Java 15.27 says we should only fold final field accesses with constant initializers // of the form TypeName.Identifier (which implies only static final fields) // our language doc previously makes A.arity a manifest constant that should be folded, // but that requirement has since been removed (PR 489) TreeNode *TypeFieldAccessNode::foldConstants() { ftype()->foldConstants(); if (decl() && (decl()->modifiers() & Common::Static)) return foldField(this); else return TreeNode::foldConstants(); } TreeNode *ThisFieldAccessNode::foldConstants() { return foldField(this); } TreeNode *ObjectFieldAccessNode::foldConstants() { if (fold_opt && (decl()->category() & Decl::Field) && (decl()->modifiers() & Common::Final) && ( (decl()->modifiers() & Common::Static) // instance.staticfinalfield || (!decl()->source()->initExpr()->absent() // instance.instancefinalfield (with initializer) // don't propagate a compiler-generated default initializer (which may not be the final value) && !((FieldDeclNode*)decl()->source())->compilerGeneratedInitExpr() && !isLHSofAssignNode(this)) // ensure we never fold away the OFAN used as lvalue // to assign the value of a final instance field in a constructor ) ) { return foldField(this); } return TreeNode::foldConstants(); } /* Constant folding in templates. We don't have a real class yet, but we do have instantiated fields and methods (accessible via the Decl) */ void TemplateEnv::foldConstants(void) { foreach (i, llist, *instances) { Decl *inst = (*i)->d; if (DEBUG) cout << "Constant folding " << *inst->name() << "\n"; inst->asType()->foldConstants(); foriter (member, inst->environ()->allProperDecls(), EnvironIter) { member->source()->foldConstants(); member->type()->foldConstants(); } } } void TemplateEnv::foldFields(void) { foreach (i, llist, *instances) { Decl *inst = (*i)->d; if (DEBUG) cout << "Field folding " << *inst->name() << "\n"; foriter (member, inst->environ()->allProperDecls(), EnvironIter) member->source()->foldFields(); } }