/*############################################################################## Copyright (C) 2011 HPCC Systems. All rights reserved. This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . ############################################################################## */ #include "jliball.hpp" #include "hql.hpp" #include "platform.h" #include "jlib.hpp" #include "jexcept.hpp" #include "jmisc.hpp" #include "hqlexpr.hpp" #include "hqlstmt.hpp" #include "hqlstmt.ipp" #include "hqlfunc.hpp" #include "hqlcpp.ipp" #include "hqlcatom.hpp" #include "hqlcpputil.hpp" #include "hqlutil.hpp" #define CLEAR_COPY_THRESHOLD 100 //--------------------------------------------------------------------------- struct HQLCPP_API HqlBoundDefinedValue : public HqlDefinedValue { public: HqlBoundDefinedValue(IHqlExpression * _original, const CHqlBoundExpr & _bound) : HqlDefinedValue(_original) { bound.set(_bound); } virtual IHqlExpression * queryExpr() const { return bound.expr; } virtual void getBound(CHqlBoundExpr & result) { result.set(bound); } public: CHqlBoundExpr bound; }; void HqlExprAssociation::getBound(CHqlBoundExpr & result) { result.expr.set(queryExpr()); } //--------------------------------------------------------------------------- BuildCtx::BuildCtx(HqlCppInstance & _state, _ATOM section) : state(_state) { init(state.ensureSection(section)); } BuildCtx::BuildCtx(HqlCppInstance & _state) : state(_state) { init(NULL); } BuildCtx::BuildCtx(BuildCtx & _owner) : state(_owner.state) { init(_owner.curStmts); ignoreInput = _owner.ignoreInput; curPriority = _owner.curPriority; nextPriority = _owner.nextPriority; ignoreInput = false; } BuildCtx::BuildCtx(BuildCtx & _owner, HqlStmts * _root) : state(_owner.state) { init(_root); } BuildCtx::~BuildCtx() { } void BuildCtx::set(_ATOM section) { init(state.ensureSection(section)); } void BuildCtx::set(BuildCtx & _owner) { init(_owner.curStmts); ignoreInput = _owner.ignoreInput; curPriority = _owner.curPriority; nextPriority = _owner.nextPriority; ignoreInput = false; } IHqlStmt * BuildCtx::addAlias(IHqlStmt * aliased) { if (ignoreInput) return NULL; HqlCompoundStmt * next = new HqlCompoundStmt(alias_stmt, curStmts); HqlStmt * cast = dynamic_cast(aliased); assertex(cast); next->code.append(*LINK(cast)); return appendSimple(next); } IHqlStmt * BuildCtx::addAssign(IHqlExpression * target, IHqlExpression * value) { if (ignoreInput) return NULL; HqlStmt * next = new HqlStmt(assign_stmt, curStmts); next->addExpr(LINK(target)); next->addExpr(LINK(value)); return appendSimple(next); } IHqlStmt * BuildCtx::addAssignIncrement(IHqlExpression * target, IHqlExpression * value) { if (ignoreInput) return NULL; if (value && !matchesConstantValue(value, 1)) { HqlStmt * next = new HqlStmt(assigninc_stmt, curStmts); next->addExpr(LINK(target)); next->addExpr(LINK(value)); return appendSimple(next); } else { OwnedHqlExpr inc = createValue(no_postinc, target->getType(), LINK(target)); return addExprOwn(inc.getClear()); } } IHqlStmt * BuildCtx::addAssignDecrement(IHqlExpression * target, IHqlExpression * value) { if (ignoreInput) return NULL; if (value && !matchesConstantValue(value, 1)) { HqlStmt * next = new HqlStmt(assigndec_stmt, curStmts); next->addExpr(LINK(target)); next->addExpr(LINK(value)); return appendSimple(next); } else { OwnedHqlExpr inc = createValue(no_postdec, LINK(target->getType()), LINK(target)); return addExprOwn(inc.getClear()); } } IHqlStmt * BuildCtx::addBlock() { if (ignoreInput) return NULL; HqlCompoundStmt * next = new HqlCompoundStmt(block_stmt, curStmts); return appendCompound(next); } IHqlStmt * BuildCtx::addBreak() { if (ignoreInput) return NULL; HqlStmt * next = new HqlStmt(break_stmt, curStmts); return appendSimple(next); } IHqlStmt * BuildCtx::addCase(IHqlStmt * _owner, IHqlExpression * source) { if (ignoreInput) return NULL; assertThrow(_owner->getStmt() == switch_stmt); HqlCompoundStmt & owner = (HqlCompoundStmt &)*_owner; curStmts = &owner.code; HqlCompoundStmt * next = new HqlCompoundStmt(case_stmt, curStmts); next->addExpr(LINK(source)); return appendCompound(next); } IHqlStmt * BuildCtx::addContinue() { if (ignoreInput) return NULL; HqlStmt * next = new HqlStmt(continue_stmt, curStmts); return appendSimple(next); } IHqlStmt * BuildCtx::addDeclare(IHqlExpression * name, IHqlExpression * value) { assertex(name->getOperator() == no_variable); if (ignoreInput) return NULL; HqlStmt * next = new HqlStmt(declare_stmt, curStmts); next->addExpr(LINK(name)); if (value) next->addExpr(LINK(value)); appendSimple(next); return next; } IHqlStmt * BuildCtx::addDeclareExternal(IHqlExpression * name) { assertex(name->getOperator() == no_variable); if (ignoreInput) return NULL; HqlStmt * next = new HqlStmt(external_stmt, curStmts); next->addExpr(LINK(name)); appendSimple(next); return next; } IHqlStmt * BuildCtx::addDeclareAssign(IHqlExpression * name, IHqlExpression * value) { addDeclare(name); return addAssign(name, value); } IHqlStmt * BuildCtx::addDefault(IHqlStmt * _owner) { if (ignoreInput) return NULL; assertThrow(_owner->getStmt() == switch_stmt); selectCompound(_owner); HqlCompoundStmt * next = new HqlCompoundStmt(default_stmt, curStmts); return appendCompound(next); } IHqlStmt * BuildCtx::addExpr(IHqlExpression * value) { if (ignoreInput) return NULL; HqlStmt * next = new HqlStmt(expr_stmt, curStmts); next->addExpr(LINK(value)); return appendSimple(next); } IHqlStmt * BuildCtx::addExprOwn(IHqlExpression * value) { if (ignoreInput) { value->Release(); return NULL; } HqlStmt * next = new HqlStmt(expr_stmt, curStmts); next->addExpr(value); return appendSimple(next); } IHqlStmt * BuildCtx::addReturn(IHqlExpression * value) { if (ignoreInput) return NULL; HqlStmt * next = new HqlStmt(return_stmt, curStmts); if (value) next->addExpr( LINK(value)); return appendSimple(next); } IHqlStmt * BuildCtx::addFilter(IHqlExpression * condition) { HqlCompoundStmt * next = new HqlCompoundStmt(filter_stmt, curStmts); next->addExpr(LINK(condition)); appendCompound(next); addBlock(); return next; } IHqlStmt * BuildCtx::addFunction(IHqlExpression * funcdef) { if (ignoreInput) return NULL; HqlCompoundStmt * next = new HqlCompoundStmt(function_stmt, curStmts); next->addExpr(LINK(funcdef)); return appendCompound(next); } IHqlStmt * BuildCtx::addIndirection(const BuildCtx & _parent) { HqlCompoundStmt * next = new HqlCompoundStmt(indirect_stmt, _parent.curStmts); return appendCompound(next); } IHqlStmt * BuildCtx::addLoop(IHqlExpression * cond, IHqlExpression * inc, bool atEnd) { if (ignoreInput) return NULL; HqlCompoundStmt * next = new HqlCompoundStmt(loop_stmt, curStmts); if (cond) next->addExpr(LINK(cond)); if (inc) next->addExpr(LINK(inc)); if (atEnd) next->addExpr(createAttribute(endAtom)); return appendCompound(next); } IHqlStmt * BuildCtx::addGoto(const char * labelText) { if (ignoreInput) return NULL; HqlStmt * next = new HqlStmt(goto_stmt, curStmts); IHqlExpression * label= createVariable(labelText, makeBoolType()); next->addExpr(label); return appendSimple(next); } IHqlStmt * BuildCtx::addGroup() { if (ignoreInput) return NULL; HqlCompoundStmt * next = new HqlCompoundStmt(group_stmt, curStmts); return appendCompound(next); } IHqlStmt * BuildCtx::addGroupPass(IHqlExpression * pass) { if (ignoreInput) return NULL; HqlCompoundStmt * next = new HqlCompoundStmt(pass_stmt, curStmts); next->addExpr(LINK(pass)); return appendCompound(next); } IHqlStmt * BuildCtx::addLabel(const char * labelText) { if (ignoreInput) return NULL; HqlStmt * next = new HqlStmt(label_stmt, curStmts); IHqlExpression * label= createVariable(labelText, makeBoolType()); next->addExpr(label); return appendSimple(next); } IHqlStmt * BuildCtx::addLine(const char * filename, unsigned lineNum) { if (ignoreInput) return NULL; HqlStmt * next = new HqlStmt(line_stmt, curStmts); if (filename) { next->addExpr(createConstant(filename)); next->addExpr(createConstant(createIntValue(lineNum, sizeof(int), true))); } return appendSimple(next); } IHqlStmt * BuildCtx::addQuoted(const char * text) { if (ignoreInput) return NULL; HqlStmt * next = new HqlQuoteStmt(quote_stmt, curStmts, text); return appendSimple(next); } IHqlStmt * BuildCtx::addQuotedF(const char * format, ...) { if (ignoreInput) return NULL; StringBuffer text; va_list args; va_start(args, format); text.valist_appendf(format, args); va_end(args); HqlStmt * next = new HqlQuoteStmt(quote_stmt, curStmts, text.str()); return appendSimple(next); } IHqlStmt * BuildCtx::addQuotedCompound(const char * text, const char * extra) { if (ignoreInput) return NULL; HqlCompoundStmt * next = new HqlQuoteStmt(quote_compound_stmt, curStmts, text); if (extra) next->addExpr(createQuoted(extra, makeVoidType())); return appendCompound(next); } IHqlStmt * BuildCtx::addQuotedCompoundOpt(const char * text, const char * extra) { if (ignoreInput) return NULL; HqlCompoundStmt * next = new HqlQuoteStmt(quote_compoundopt_stmt, curStmts, text); if (extra) next->addExpr(createQuoted(extra, makeVoidType())); return appendCompound(next); } IHqlStmt * BuildCtx::addSwitch(IHqlExpression * source) { if (ignoreInput) return NULL; HqlCompoundStmt * next = new HqlCompoundStmt(switch_stmt, curStmts); next->addExpr(LINK(source)); return appendCompound(next); } HqlStmt * BuildCtx::appendCompound(HqlCompoundStmt * next) { assertThrow(!ignoreInput); appendSimple(next); curStmts = &next->code; return next; } HqlStmt * BuildCtx::appendSimple(HqlStmt * next) { assertThrow(!ignoreInput); if (nextPriority == OutermostScopePrio) { appendToOutermostScope(next); } else { next->setPriority(nextPriority); curStmts->appendStmt(*next); } nextPriority = curPriority; return next; } void BuildCtx::appendToOutermostScope(HqlStmt * next) { HqlStmts * searchStmts = curStmts; HqlStmts * insertStmts = NULL; HqlStmt * insertBefore = NULL; loop { HqlStmt * owner = searchStmts->owner; if (!owner) break; HqlStmts * ownerStmts = owner->queryContainer(); switch (owner->getStmt()) { case quote_compound_stmt: case quote_compoundopt_stmt: case indirect_stmt: goto found; case group_stmt: break; default: insertBefore = owner; insertStmts = ownerStmts; break; } searchStmts = ownerStmts; } found: if (insertBefore) { next->setPriority(insertBefore->queryPriority()); insertStmts->add(*next, insertStmts->find(*insertBefore)); } else { next->setPriority(curPriority); curStmts->appendStmt(*next); } } void BuildCtx::associate(HqlExprAssociation & next) { assertex(next.represents->queryBody() == next.represents); if (!ignoreInput) { curStmts->appendOwn(OLINK(next)); } } void BuildCtx::associateOwn(HqlExprAssociation & next) { assertex(next.represents->queryBody() == next.represents); if (!ignoreInput) { curStmts->appendOwn(next); } else next.Release(); // can cause serious problems.... } HqlExprAssociation *BuildCtx::associateExpr(IHqlExpression * represents, IHqlExpression * expr) { if (!ignoreInput) { HqlExprAssociation * assoc = new HqlSimpleDefinedValue(represents->queryBody(), expr); curStmts->appendOwn(*assoc); return assoc; } return NULL; } HqlExprAssociation * BuildCtx::associateExpr(IHqlExpression * represents, const CHqlBoundExpr & bound) { if (!ignoreInput) { HqlExprAssociation * assoc = new HqlBoundDefinedValue(represents->queryBody(), bound); curStmts->appendOwn(*assoc); return assoc; } return NULL; } IHqlExpression * BuildCtx::getTempDeclare(ITypeInfo * type, IHqlExpression * value) { IHqlExpression * temp = createVariable(LINK(type)); addDeclare(temp, value); return temp; } bool BuildCtx::hasAssociation(HqlExprAssociation & search, bool unconditional) { HqlStmts * searchStmts = curStmts; loop { if (searchStmts->defs.contains(search)) return true; HqlStmt * limitStmt = searchStmts->queryStmt(); if (!limitStmt) return false; if (!unconditional) { switch (limitStmt->getStmt()) { case filter_stmt: if (!matchesBoolean(limitStmt->queryExpr(0), true)) return false; break; case quote_compound_stmt: case quote_compoundopt_stmt: case switch_stmt: case case_stmt: case default_stmt: case loop_stmt: return false; } } searchStmts = limitStmt->queryContainer(); } } void BuildCtx::walkAssociations(IAssociationVisitor & visitor) { HqlStmts * searchStmts = curStmts; // walk all associations in the tree before this one loop { CIArrayOf & defs = searchStmts->defs; ForEachItemInRev(idx, defs) { HqlExprAssociation & cur = defs.item(idx); if (visitor.visit(cur)) return; } HqlStmt * limitStmt = searchStmts->queryStmt(); if (!limitStmt) break; searchStmts = limitStmt->queryContainer(); } } HqlExprAssociation * BuildCtx::queryAssociation(IHqlExpression * search, AssocKind kind, HqlExprCopyArray * selectors) { HqlStmts * searchStmts = curStmts; if (!search) return NULL; search = search->queryBody(); // search all statements in the tree before this one, to see // if an expression already exists... If so return the target // of the assignment. loop { const CIArrayOf & defs = searchStmts->defs; #ifdef CACHE_DEFINITION_HASHES if (!selectors) { unsigned searchHash = getSearchHash(search); const DefinitionHashArray & hashes = searchStmts->exprHashes; ForEachItemInRev(idx, hashes) { if (hashes.item(idx) == searchHash) { HqlExprAssociation & cur = defs.item(idx); if (cur.represents == search) { if ((kind == AssocAny) || (cur.getKind() == kind)) return &cur; } } } } else #endif { ForEachItemInRev(idx, defs) { HqlExprAssociation & cur = defs.item(idx); IHqlExpression * represents = cur.represents.get(); if (selectors && (cur.getKind() == AssocCursor)) { if (selectors->contains(*represents)) return NULL; } if (represents == search) if ((kind == AssocAny) || (cur.getKind() == kind)) return &cur; } } HqlStmt * limitStmt = searchStmts->queryStmt(); if (!limitStmt) break; searchStmts = limitStmt->queryContainer(); } return NULL; } void BuildCtx::removeAssociation(HqlExprAssociation * search) { if (!search) return; HqlStmts * searchStmts = curStmts; loop { bool matched = searchStmts->zap(*search); if (matched) return; HqlStmt * limitStmt = searchStmts->queryStmt(); if (!limitStmt) break; searchStmts = limitStmt->queryContainer(); } assertex(!"Association not found"); } HqlExprAssociation * BuildCtx::queryFirstAssociation(AssocKind searchKind) { HqlStmts * searchStmts = curStmts; // search all statements in the tree before this one, to see // if an expression already exists... If so return the target // of the assignment. loop { CIArray & defs = searchStmts->defs; ForEachItemInRev(idx, defs) { HqlExprAssociation & cur = (HqlExprAssociation &)defs.item(idx); if (cur.getKind() == searchKind) return &cur; } HqlStmt * limitStmt = searchStmts->queryStmt(); if (!limitStmt) break; searchStmts = limitStmt->queryContainer(); } return NULL; } //Search for an association, but don't allow it to be conditional, or be hidden by the definition of a cursor. HqlExprAssociation * BuildCtx::queryFirstCommonAssociation(AssocKind searchKind) { HqlStmts * searchStmts = curStmts; // search all statements in the tree before this one, to see // if an expression already exists... If so return the target // of the assignment. loop { CIArray & defs = searchStmts->defs; ForEachItemInRev(idx, defs) { HqlExprAssociation & cur = (HqlExprAssociation &)defs.item(idx); AssocKind kind = cur.getKind(); if (kind == searchKind) return &cur; if (kind == AssocCursor) return NULL; } HqlStmt * limitStmt = searchStmts->queryStmt(); if (!limitStmt) break; switch (limitStmt->getStmt()) { //case quote_compound_stmt: //case quote_compoundopt_stmt, case filter_stmt: case label_stmt: case switch_stmt: case case_stmt: case default_stmt: case break_stmt: case continue_stmt: return NULL; } searchStmts = limitStmt->queryContainer(); } return NULL; } HqlExprAssociation * BuildCtx::queryMatchExpr(IHqlExpression * search) { HqlExprCopyArray selectors; search->gatherTablesUsed(NULL, &selectors); return queryAssociation(search, AssocExpr, selectors.ordinality() ? &selectors : NULL); } bool BuildCtx::getMatchExpr(IHqlExpression * expr, CHqlBoundExpr & tgt) { HqlExprAssociation * match = queryMatchExpr(expr); if (match) { match->getBound(tgt); return true; } return false; } void BuildCtx::init(HqlStmts * _root) { root = _root; curStmts = root; curPriority = NormalPrio; nextPriority = curPriority; ignoreInput = false; } bool BuildCtx::isChildOf(HqlStmt * stmt, HqlStmts * stmts) { //MORE: Could improve by using depths do { if (stmts->find(*stmt) != NotFound) return true; stmt = stmt->queryContainer()->queryStmt(); } while (stmt); return false; } void BuildCtx::selectCompound(IHqlStmt * stmt) { HqlCompoundStmt & owner = (HqlCompoundStmt &)*stmt; curStmts = &owner.code; } void BuildCtx::selectContainer() { HqlStmt * limitStmt = curStmts->queryStmt(); assertex(limitStmt); curStmts = limitStmt->queryContainer(); assertex(curStmts); } void BuildCtx::selectElse(IHqlStmt * stmt) { //Ignoring input does not work, because code expects associations to be kept assertex(stmt); switch (stmt->getStmt()) { case filter_stmt: assertThrow(stmt->numChildren() == 1); selectCompound(stmt); addBlock(); break; default: throwUnexpected(); break; } } unsigned BuildCtx::setPriority(unsigned newPrio) { unsigned oldPriority = curPriority; if (!ignoreInput) { curPriority = newPrio; nextPriority = curPriority; } return oldPriority; } void BuildCtx::setNextPriority(unsigned newPrio) { if (!ignoreInput) nextPriority = newPrio; } IHqlStmt * BuildCtx::recursiveGetBestContext(HqlStmts * searchStmts, HqlExprCopyArray & required) { //Is it ok to move the expression before the owner statement? //First fail if any of the datasets that this expression is dependent on are defined in this scope. if (required.ordinality()) { ForEachItemIn(i, searchStmts->defs) { HqlExprAssociation & cur = (HqlExprAssociation &)searchStmts->defs.item(i); if (required.contains(*cur.represents.get())) return NULL; } } HqlStmt * owner = searchStmts->owner; //Now check for poor places to hoist bool worthHoistingHere = false; switch (owner->getStmt()) { case block_stmt: case group_stmt: break; case quote_compound_stmt: case quote_compoundopt_stmt: case indirect_stmt: return NULL; case filter_stmt: case switch_stmt: //MORE: Should make whether something ishoisted dependent on efficiency of the filter condition break; case loop_stmt: //MORE: Should make it dependent on the ordinality of the loop condition. worthHoistingHere = true; break; case case_stmt: case default_stmt: //Can't hoist here break; default: throwUnexpected(); } HqlStmts * container = owner->queryContainer(); if (container->owner)// && canHoistInParent) { IHqlStmt * match = recursiveGetBestContext(container, required); if (match) return match; } if (!worthHoistingHere) return NULL; //We've found somewhere we can insert the expression.... unsigned existingPos = container->find(*owner); assertex(existingPos != NotFound); //insert a group just before the current statement HqlCompoundStmt * next = new HqlCompoundStmt(group_stmt, container); next->setPriority(owner->queryPriority()); container->add(*next, existingPos); curStmts = &next->code; return next; } IHqlStmt * BuildCtx::replaceExpr(IHqlStmt * stmt, IHqlExpression * expr) { //Highly dangerous - use with utmost care! assertex(stmt->getStmt() == expr_stmt); HqlStmt * castStmt = static_cast(stmt); castStmt->killExprs(); castStmt->addExpr(LINK(expr)); return castStmt; } IHqlStmt * BuildCtx::selectBestContext(IHqlExpression * expr) { if (containsTranslated(expr) || !curStmts->owner) return NULL; //MORE: Access to global context, context and other things... HqlExprCopyArray inScope; expr->gatherTablesUsed(NULL, &inScope); return recursiveGetBestContext(curStmts, inScope); } //--------------------------------------------------------------------------- HqlStmts::HqlStmts(HqlStmt * _owner) : owner(_owner) { } void HqlStmts::appendStmt(HqlStmt & stmt) { unsigned newPrio = stmt.queryPriority(); unsigned right = ordinality(); if (right == 0) { append(stmt); } else if (newPrio >= item(right-1).queryPriority()) { while (item(right-1).isIncomplete()) { if (newPrio > item(right-1).queryPriority()) break; right--; if (right == 0) break; } add(stmt, right); } else { unsigned left = 0; while (right - left >= 2) { unsigned mid = (left + right - 1) / 2; HqlStmt & cur = item(mid); if (newPrio >= cur.queryPriority()) left = mid+1; else right = mid+1; } if (newPrio >= item(left).queryPriority()) ++left; while (left && item(left-1).isIncomplete() && (newPrio == item(left-1).queryPriority())) --left; add(stmt, left); } } //--------------------------------------------------------------------------- HqlStmt::HqlStmt(StmtKind _kind, HqlStmts * _container) { kind = _kind; container = _container; incomplete = false; included = true; } void HqlStmt::addExpr(IHqlExpression * expr) { exprs.append(*expr); } StmtKind HqlStmt::getStmt() { return (StmtKind)kind; } StringBuffer & HqlStmt::getTextExtra(StringBuffer & out) { return out; } static bool isEmptyGroup(IHqlStmt * stmt) { if (!stmt) return true; switch (stmt->getStmt()) { case group_stmt: case block_stmt: return stmt->numChildren() == 0; } return false; } bool HqlStmt::hasChildren() const { if (numChildren() == 0) return false; unsigned count = numChildren(); for (unsigned index = 0; index < count; index++) { IHqlStmt * cur = queryChild(index); if (cur->isIncluded()) return true; } return false; } bool HqlStmt::isIncluded() const { if (!included) return false; switch (kind) { case quote_compoundopt_stmt: case group_stmt: return hasChildren(); case filter_stmt: if (isEmptyGroup(queryChild(0)) && isEmptyGroup(queryChild(1))) return false; break; } return true; } unsigned HqlStmt::numChildren() const { return 0; } IHqlStmt * HqlStmt::queryChild(unsigned index) const { return NULL; } HqlStmts * HqlStmt::queryContainer() { return container; } IHqlExpression * HqlStmt::queryExpr(unsigned index) { if (exprs.isItem(index)) return &exprs.item(index); return NULL; } void HqlStmts::inheritDefinitions(HqlStmts & other) { ForEachItemIn(i, other.defs) { defs.append(OLINK(other.defs.item(i))); #ifdef CACHE_DEFINITION_HASHES exprHashes.append(other.exprHashes.item(i)); #endif } } //--------------------------------------------------------------------------- #ifdef _MSC_VER #pragma warning(push) #pragma warning( disable : 4355 ) // 'this' : used in base member initializer list #endif HqlCompoundStmt::HqlCompoundStmt(StmtKind _kind, HqlStmts * _container) : HqlStmt(_kind, _container), code(this) { } #ifdef _MSC_VER #pragma warning(pop) #endif void HqlCompoundStmt::mergeScopeWithContainer() { container->inheritDefinitions(code); } unsigned HqlCompoundStmt::numChildren() const { return code.ordinality(); } IHqlStmt * HqlCompoundStmt::queryChild(unsigned index) const { if (code.isItem(index)) return &code.item(index); return NULL; } //--------------------------------------------------------------------------- StringBuffer & HqlQuoteStmt::getTextExtra(StringBuffer & out) { return out.append(text); } //--------------------------------------------------------------------------- int queryMemsetChar(IHqlExpression * expr) { if (expr->getOperator() != no_constant) return -1; unsigned size = expr->queryType()->getSize(); if (size == 0) return -1; const byte * data = (const byte *)expr->queryValue()->queryValue(); byte match = data[0]; while (--size) if (*++data != match) return -1; return match; } static IHqlExpression * extractNonConstant(IHqlExpression * expr, unsigned & delta) { switch (expr->getOperator()) { case no_constant: delta += (unsigned)getIntValue(expr); return NULL; case no_add: { IHqlExpression * left = expr->queryChild(0); OwnedHqlExpr newLeft = extractNonConstant(left, delta); IHqlExpression * right = expr->queryChild(1); OwnedHqlExpr newRight = extractNonConstant(right, delta); if (!newLeft) return newRight.getClear(); if (!newRight) return newLeft.getClear(); if ((left == newLeft) && (right == newRight)) return LINK(expr); return createValue(no_add, expr->getType(), newLeft.getClear(), newRight.getClear()); } } return LINK(expr); } IHqlExpression * peepholeAddExpr(IHqlExpression * left, IHqlExpression * right) { unsigned delta = 0; IHqlExpression * simpleLeft = extractNonConstant(left, delta); IHqlExpression * simpleRight = extractNonConstant(right, delta); IHqlExpression * ret; if (simpleLeft) { if (simpleRight) ret = createValue(no_add, left->getType(), simpleLeft, simpleRight); else ret = simpleLeft; } else ret = simpleRight; if (!delta && ret) return ret; IHqlExpression * value = getSizetConstant(delta); if (!ret) return value; return createValue(no_add, left->getType(), ret, value); if ((left->getOperator() == no_constant) && (right->getOperator() == no_constant)) return getSizetConstant((size32_t)left->queryValue()->getIntValue() + (size32_t)right->queryValue()->getIntValue()); return createValue(no_add, left->getType(), LINK(left), LINK(right)); } bool rightFollowsLeft(IHqlExpression * left, IHqlExpression * leftLen, IHqlExpression * right) { OwnedHqlExpr sum = peepholeAddExpr(left, leftLen); if (sum == right) return true; if (left->getOperator() != right->getOperator()) { if (right->getOperator() == no_add) { if ((left == right->queryChild(0)) && (leftLen == right->queryChild(1))) return true; if ((left == right->queryChild(1)) && (leftLen == right->queryChild(0))) return true; } return false; } switch (left->getOperator()) { case no_constant: { ITypeInfo * leftType = left->queryType(); ITypeInfo * rightType = right->queryType(); if (leftType->getTypeCode() != rightType->getTypeCode()) return false; switch (leftType->getTypeCode()) { case type_int: if (leftLen->getOperator() != no_constant) return false; if (left->queryValue()->getIntValue() + leftLen->queryValue()->getIntValue() == right->queryValue()->getIntValue()) return true; return false; } return false; } case no_add: if ((left == right->queryChild(0)) && (leftLen == right->queryChild(1))) return true; if (left->queryChild(1) == right->queryChild(1)) { if (rightFollowsLeft(left->queryChild(0), leftLen, right->queryChild(0))) return true; } //fall through case no_index: if (left->queryChild(0) != right->queryChild(0)) return false; return rightFollowsLeft(left->queryChild(1), leftLen, right->queryChild(1)); } unsigned numLeft = left->numChildren(); if (numLeft == 0 || (numLeft != right->numChildren())) return false; ForEachChild(idx, left) { if (!rightFollowsLeft(left->queryChild(idx), leftLen, right->queryChild(idx))) return false; } return true; } static IHqlExpression * createDataForMemset(unsigned size, byte value) { if (size < 100) { void * temp = alloca(size); memset(temp, value, size); return createConstant(createDataValue((char *)temp, size)); } void * temp = malloc(size); memset(temp, value, size); IHqlExpression * ret = createConstant(createDataValue((char *)temp, size)); free(temp); return ret; } static IHqlExpression * createDataForIntegerZero(unsigned size) { return createDataForMemset(size, 0); } class SpecialFunction { public: SpecialFunction() { wasAssign = false; name = NULL; } HqlStmt * createStmt(HqlStmts & curStmts, HqlCppTranslator & translator); bool canOptimize() const; void expandValue(void * target) const; bool extractIsSpecial(IHqlStmt & stmt, bool memsetOnly, unsigned peepholeOptions); bool isBigClear() const; int queryClearValue() const; bool queryCombine(const SpecialFunction & next, bool memsetOnly, size32_t combineStringLimit); private: _ATOM name; HqlExprAttr src; HqlExprAttr tgt; HqlExprAttr srcLen; HqlExprAttr tgtLen; bool wasAssign; }; //Always convert rtlWriteInt(rtlReadInt()) the inline memcpy is going to be much better. //It should probably be converted earlier.... bool isAwkwardIntSize(IHqlExpression * size) { IValue * value = size->queryValue(); if (value) { switch (value->getIntValue()) { case 3: case 5: case 6: case 7: return true; } } return false; } bool SpecialFunction::canOptimize() const { if ((name == memcpyAtom) && (queryMemsetChar(src) >= 0)) { if ((getIntValue(srcLen, 0) > 1) || !wasAssign) return true; } if ((name == memcpyAtom) && isAwkwardIntSize(srcLen)) return true; return false; } HqlStmt * SpecialFunction::createStmt(HqlStmts & curStmts, HqlCppTranslator & translator) { HqlExprArray args; _ATOM func = name; if (name == memsetAtom) { func = memsetAtom; args.append(*LINK(tgt)); args.append(*LINK(src)); args.append(*LINK(srcLen)); } else if (name == memcpyAtom) { int clearByte = queryMemsetChar(src); size32_t size = (size32_t)getIntValue(srcLen, 0); if (clearByte == 0) { //if length is 1,2,4 then use an assignment instead. switch (size) { case 1: case 2: case 4: case 8: { OwnedITypeInfo type = makeIntType(size, false); OwnedHqlExpr castTgt = createValue(no_cast, makePointerType(LINK(type)), LINK(tgt)); OwnedHqlExpr deref = createValue(no_deref, makeReferenceModifier(LINK(type)), LINK(castTgt)); OwnedHqlExpr src = createConstant(type->castFrom(true, (__int64)0)); HqlStmt * next = new HqlStmt(assign_stmt, &curStmts); next->addExpr(LINK(deref)); next->addExpr(LINK(src)); return next; } } } //MORE: assignment of 1,2,4 bytes possibly better as an assign? if (clearByte >= 0) { func = memsetAtom; args.append(*LINK(tgt)); args.append(*createConstant(createIntValue(clearByte, sizeof(int), true))); args.append(*LINK(srcLen)); } else { args.append(*LINK(tgt)); args.append(*LINK(src)); args.append(*LINK(srcLen)); } } else if (name == deserializerReadNAtom || name == serializerPutAtom) { args.append(*LINK(src)); args.append(*LINK(tgtLen)); args.append(*LINK(tgt)); } else if ((name == ebcdic2asciiAtom) || (name == ascii2ebcdicAtom)) { args.append(*LINK(tgtLen)); args.append(*LINK(tgt)); args.append(*LINK(srcLen)); args.append(*LINK(src)); } else if (name == deserializerSkipNAtom) { args.append(*LINK(src)); args.append(*LINK(srcLen)); } else UNIMPLEMENTED; HqlStmt * next = new HqlStmt(expr_stmt, &curStmts); next->addExpr(translator.bindTranslatedFunctionCall(func, args)); return next; } IHqlExpression * stripTranslatedCasts(IHqlExpression * e) { loop { switch (e->getOperator()) { case no_cast: case no_implicitcast: case no_typetransfer: { IHqlExpression * child = e->queryChild(0); if (hasWrapperModifier(child->queryType())) return e; e = child; break; } default: return e; } } } void SpecialFunction::expandValue(void * target) const { size32_t size = (size32_t)getIntValue(srcLen); if (name == memsetAtom) memset(target, (int)getIntValue(src), size); else memcpy(target, src->queryValue()->queryValue(), size); } bool SpecialFunction::extractIsSpecial(IHqlStmt & stmt, bool memsetOnly, unsigned peepholeOptions) { if (stmt.getStmt() == expr_stmt) { IHqlExpression * expr = stmt.queryExpr(0); if (expr->getOperator() != no_externalcall) return false; name = expr->queryName(); if (name == memcpyAtom) { src.set(stripTranslatedCasts(expr->queryChild(1))); if (memsetOnly && (queryMemsetChar(src) == -1)) return false; tgt.set(stripTranslatedCasts(expr->queryChild(0))); srcLen.set(expr->queryChild(2)); tgtLen.set(srcLen); return true; } if (name == deserializerReadNAtom || name == serializerPutAtom) { if (memsetOnly) return false; src.set(expr->queryChild(0)); tgt.set(stripTranslatedCasts(expr->queryChild(2))); tgtLen.set(expr->queryChild(1)); return true; } if ((name == ebcdic2asciiAtom) || (name == ascii2ebcdicAtom)) { if (memsetOnly) return false; src.set(expr->queryChild(3)); tgt.set(expr->queryChild(1)); srcLen.set(expr->queryChild(2)); tgtLen.set(expr->queryChild(0)); return srcLen == tgtLen; } if (name == memsetAtom) { IHqlExpression * value = expr->queryChild(1); IHqlExpression * len = expr->queryChild(2); if (len->queryValue() && value->queryValue()) { tgt.set(stripTranslatedCasts(expr->queryChild(0))); src.set(value); srcLen.set(len); tgtLen.set(srcLen); } return true; } if (name == deserializerSkipNAtom) { src.set(expr->queryChild(0)); srcLen.set(expr->queryChild(1)); return true; } unsigned size = 0; if (name == writeIntAtom[3]) size = 3; else if (name == writeIntAtom[5]) size = 5; else if (name == writeIntAtom[6]) size = 6; else if (name == writeIntAtom[7]) size = 7; if (size) { IHqlExpression * value = expr->queryChild(1); if (isZero(value)) { name = memcpyAtom; src.setown(createDataForIntegerZero(size)); tgt.set(stripTranslatedCasts(expr->queryChild(0))); srcLen.setown(getSizetConstant(size)); tgtLen.set(srcLen); return true; } if (memsetOnly) return false; while (value->getOperator() == no_typetransfer) value = value->queryChild(0); if (value->getOperator() == no_externalcall) { if ((value->queryName() == readIntAtom[size][true]) || (value->queryName() == readIntAtom[size][false])) { name = memcpyAtom; src.set(stripTranslatedCasts(value->queryChild(0))); tgt.set(stripTranslatedCasts(expr->queryChild(0))); srcLen.setown(getSizetConstant(size)); tgtLen.set(srcLen); return true; } } } return false; } else if (stmt.getStmt() == assign_stmt) { wasAssign = true; tgt.set(stmt.queryExpr(0)); src.set(stmt.queryExpr(1)); ITypeInfo * tgtType = tgt->queryType(); ITypeInfo * srcType = src->queryType(); if (!isSameBasicType(srcType, tgtType)) { if (!isSameFullyUnqualifiedType(srcType, tgtType)) return false; } while (tgt->getOperator() == no_typetransfer) tgt.set(tgt->queryChild(0)); if (tgt->getOperator() != no_deref) return false; while (src->getOperator() == no_typetransfer) src.set(src->queryChild(0)); size32_t targetSize = tgtType->getSize(); if (src->getOperator() != no_deref) { type_t tc = tgtType->getTypeCode(); OwnedHqlExpr newSrcExpr; switch (tc) { case type_pointer: case type_table: //case type_row: if (src->getOperator() == no_nullptr) { void * ptr = NULL; targetSize = sizeof(void *); newSrcExpr.setown(createConstant(createDataValue((const char *)&ptr, targetSize))); } break; } if (!memsetOnly) { switch (tc) { case type_int: case type_swapint: case type_boolean: if (src->getOperator() == no_constant) { OwnedHqlExpr cast = ensureExprType(src, tgtType); IValue * castValue = cast->queryValue(); if (castValue) { void * temp = alloca(targetSize); castValue->toMem(temp); newSrcExpr.setown(createConstant(createDataValue((const char *)temp, targetSize))); } } break; case type_real: if ((peepholeOptions & PHOconvertReal) && (src->getOperator() == no_constant)) { OwnedHqlExpr cast = ensureExprType(src, tgtType); IValue * castValue = cast->queryValue(); if (castValue) { void * temp = alloca(targetSize); castValue->toMem(temp); newSrcExpr.setown(createConstant(createDataValue((const char *)temp, targetSize))); } } break; } } if (!newSrcExpr && isZero(src)) newSrcExpr.setown(createDataForIntegerZero(targetSize)); if (!newSrcExpr) return false; src.set(newSrcExpr); } else { if (memsetOnly) return false; src.set(src->queryChild(0)); } srcLen.setown(getSizetConstant(targetSize)); tgtLen.set(srcLen); tgt.set(stripTranslatedCasts(tgt->queryChild(0))); src.set(stripTranslatedCasts(src)); name = memcpyAtom; return true; } return false; } bool SpecialFunction::isBigClear() const { if ((name == memsetAtom) || (name == memcpyAtom)) return getIntValue(srcLen, 0) > CLEAR_COPY_THRESHOLD; return false; } int SpecialFunction::queryClearValue() const { if (name == memcpyAtom) return queryMemsetChar(src); if (name == memsetAtom) return (int)getIntValue(src, -1); return -1; } bool SpecialFunction::queryCombine(const SpecialFunction & next, bool memsetOnly, size32_t combineStringLimit) { if (name != next.name) { if (!((name == memsetAtom) && (next.name == memcpyAtom)) && !((name == memcpyAtom) && (next.name == memsetAtom))) return false; } if (name == deserializerSkipNAtom) { if (src != next.src) return false; srcLen.setown(peepholeAddExpr(srcLen, next.srcLen)); return true; } if (rightFollowsLeft(tgt, tgtLen, next.tgt)) { if ((name != memsetAtom) && (next.name != memsetAtom)) { if (name == deserializerReadNAtom || name == serializerPutAtom) { tgtLen.setown(peepholeAddExpr(tgtLen, next.tgtLen)); return true; } if (rightFollowsLeft(src, srcLen, next.src)) { srcLen.setown(peepholeAddExpr(srcLen, next.srcLen)); tgtLen.setown(peepholeAddExpr(tgtLen, next.tgtLen)); return true; } } IValue * srcValue = src->queryValue(); IValue * nextValue = next.src->queryValue(); if (srcValue && nextValue) { //Don't combine things that can be converted to different memsets. //This would work better if we first combined items that could be done as memsets and then combined strings //processing in order we hit them doesn't work as well. int clearValue = queryClearValue(); int nextClearValue = next.queryClearValue(); if (memsetOnly) { assertex((clearValue != -1) && (nextClearValue != -1)); if (clearValue != nextClearValue) return false; } if ((isBigClear() && (clearValue != -1)) || (next.isBigClear() && (nextClearValue != -1))) { if (clearValue != nextClearValue) return false; } size32_t curSize = (size32_t)getIntValue(srcLen); size32_t nextSize = (size32_t)getIntValue(next.srcLen); if (curSize + nextSize > combineStringLimit) return false; byte * temp = (byte *)malloc(curSize + nextSize); expandValue(temp); next.expandValue(temp+curSize); src.setown(createConstant(createDataValue((const char *)temp, curSize + nextSize))); free(temp); srcLen.setown(getSizetConstant(curSize + nextSize)); tgtLen.set(srcLen); if (name == memsetAtom) name = memcpyAtom; return true; } } return false; } PeepHoleOptimizer::PeepHoleOptimizer(HqlCppTranslator & _translator) : translator(_translator) { combineStringLimit = -1; peepholeOptions = 0; if (translator.queryOptions().convertRealAssignToMemcpy) peepholeOptions |= PHOconvertReal; switch (translator.queryOptions().targetCompiler) { case Vs6CppCompiler: combineStringLimit = 32000; break; } } void PeepHoleOptimizer::optimize(HqlStmts & stmts) { unsigned max = stmts.ordinality(); // can change as processing proceeds. SpecialFunction prevMatch; for (unsigned pass=0; pass < 2; pass++) { bool memsetOnly = (pass == 0); for (unsigned i =0; i < max; i++) { IHqlStmt & cur = stmts.item(i); if (prevMatch.extractIsSpecial(cur, memsetOnly, peepholeOptions)) { unsigned j = i+1; unsigned prevMax = max; SpecialFunction nextMatch; while (j < max) { IHqlStmt & next = stmts.item(j); if (!nextMatch.extractIsSpecial(next, memsetOnly, peepholeOptions)) break; if (!prevMatch.queryCombine(nextMatch, memsetOnly, combineStringLimit)) break; stmts.remove(i); max--; } if (max != prevMax || prevMatch.canOptimize()) { stmts.remove(i); stmts.add(*prevMatch.createStmt(stmts, translator), i); } } } } for (unsigned i2=0; i2 < max; i2++) { IHqlStmt & cur = stmts.item(i2); if (cur.numChildren() != 0) optimize(((HqlCompoundStmt&)cur).code); } } void peepholeOptimize(HqlCppInstance & instance, HqlCppTranslator & translator) { PeepHoleOptimizer optimizer(translator); ForEachItemIn(idx, instance.sections) optimizer.optimize(((HqlCppSection&)instance.sections.item(idx)).stmts); } //--------------------------------------------------------------------------- AssociationIterator::AssociationIterator(BuildCtx & ctx) { rootStmts = ctx.curStmts; curStmts = NULL; curIdx = 0; } bool AssociationIterator::first() { curStmts = rootStmts; curIdx = curStmts->defs.ordinality(); return doNext(); } bool AssociationIterator::doNext() { loop { if (curIdx-- != 0) return true; HqlStmt * limitStmt = curStmts->queryStmt(); if (!limitStmt) { curStmts = NULL; return false; } curStmts = limitStmt->queryContainer(); curIdx = curStmts->defs.ordinality(); } } HqlExprAssociation & AssociationIterator::get() { CIArray & defs = curStmts->defs; return (HqlExprAssociation &)defs.item(curIdx); } //--------------------------------------------------------------------------- bool FilteredAssociationIterator::doNext() { while (AssociationIterator::doNext()) { HqlExprAssociation & cur = AssociationIterator::get(); if (cur.getKind() == searchKind) return true; } return false; } bool RowAssociationIterator::doNext() { while (AssociationIterator::doNext()) { HqlExprAssociation & cur = AssociationIterator::get(); if (cur.isRowAssociation()) return true; } return false; }; unsigned calcTotalChildren(IHqlStmt * stmt) { if (!stmt->isIncluded()) return 0; unsigned num = stmt->numChildren(); unsigned total = 1; switch (stmt->getStmt()) { case alias_stmt: case group_stmt: case pass_stmt: case indirect_stmt: total = 0; break; } for (unsigned i=0; i < num; i++) total += calcTotalChildren(stmt->queryChild(i)); return total; }