/*############################################################################## 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 "hql.hpp" #include "hqlfunc.hpp" #include "hqlcpp.ipp" #include "hqlwcpp.hpp" #include "hqlutil.hpp" #include "hqlcpputil.hpp" #include "hqlcatom.hpp" #include "hqlcerrors.hpp" #define INTEGER_SEARCH_THRESHOLD 30 // above this, a table search is generated. #define MAX_NUM_NOBREAK_CASE 80 // maximum number of case: without a break - compiler workaround #define INLINE_COMPARE_THRESHOLD 2 // above this, a loop is generated #define SWITCH_TABLE_DENSITY_THRESHOLD 3 // % used before use array index. #define MAX_NESTED_CASES 8 // to stop C++ compiler running out of scopes. //=========================================================================== static _ATOM searchDataTableAtom; static _ATOM searchEStringTableAtom; static _ATOM searchQStringTableAtom; static _ATOM searchStringTableAtom; static _ATOM searchVStringTableAtom; //=========================================================================== MODULE_INIT(INIT_PRIORITY_STANDARD) { searchDataTableAtom = createIdentifierAtom("searchDataTable"); searchEStringTableAtom = createIdentifierAtom("searchEStringTable"); searchQStringTableAtom = createIdentifierAtom("searchQStringTable"); searchStringTableAtom = createIdentifierAtom("searchStringTable"); searchVStringTableAtom = createIdentifierAtom("searchVStringTable"); return true; } MODULE_EXIT() { } //=========================================================================== //Case helper functions... void cvtChooseListToPairs(HqlExprArray & target, IHqlExpression * from, unsigned base) { unsigned max = from->numChildren(); unsigned idx; target.ensure(max); for (idx = 0; idx < max; idx++) { IHqlExpression * v1 = createConstant(createIntValue(idx+base, LINK(unsignedType))); IHqlExpression * v2 = from->queryChild(idx); ITypeInfo * type = v2->queryType(); target.append(* createValue(no_mapto, LINK(type), v1, LINK(v2))); } } void cvtIndexListToPairs(HqlExprArray & target, IHqlExpression * from) { unsigned max = from->numChildren(); unsigned idx; target.ensure(max); for (idx = 0; idx < max; idx++) { IHqlExpression * v1 = from->queryChild(idx); IHqlExpression * v2 = createConstant(createIntValue(idx+1, LINK(unsignedType))); ITypeInfo * type = v2->queryType(); target.append(* createValue(no_mapto, LINK(type), LINK(v1), v2)); } } void cvtInListToPairs(HqlExprArray & target, IHqlExpression * from, bool valueIfMatch) { unsigned max = from->numChildren(); unsigned idx; IHqlExpression * tValue = queryBoolExpr(valueIfMatch); ITypeInfo * type = queryBoolType(); target.ensure(max); for (idx = 0; idx < max; idx++) { IHqlExpression * v1 = from->queryChild(idx); target.append(* createValue(no_mapto, LINK(type), LINK(v1), LINK(tValue))); } } IHqlExpression * createNotFoundValue() { OwnedITypeInfo int4 = makeIntType(4, true); return createConstant(int4->castFrom(true, -1)); } //=========================================================================== static int comparePair(IInterface * * left, IInterface * * right) { IHqlExpression * lexpr = (IHqlExpression *)*left; IHqlExpression * rexpr = (IHqlExpression *)*right; return lexpr->queryChild(0)->queryValue()->compare(rexpr->queryChild(0)->queryValue()); } HqlCppCaseInfo::HqlCppCaseInfo(HqlCppTranslator & _translator) : translator(_translator) { complexCompare = false; constantCases = true; constantValues = true; indexType.setown(makeIntType(sizeof(int), true)); } void HqlCppCaseInfo::addPair(IHqlExpression * expr) { pairs.append(*LINK(expr)); IHqlExpression * compareValue = expr->queryChild(0); if (!compareValue->queryValue()) constantCases = false; if (!expr->queryChild(1)->queryValue()) constantValues = false; if (cond && !complexCompare) { ITypeInfo * valueType = compareValue->queryType(); if (valueType != cond->queryType()) complexCompare = isCompare3Valued(compareValue->queryType()); } updateResultType(expr->queryChild(1)); } void HqlCppCaseInfo::addPairs(HqlExprArray & _pairs) { pairs.ensure(_pairs.ordinality()); ForEachItemIn(idx, _pairs) addPair(&_pairs.item(idx)); } bool HqlCppCaseInfo::buildAssign(BuildCtx & ctx, const CHqlBoundTarget & target) { if (pairs.ordinality() == 0) { translator.buildExprAssign(ctx, target, defaultValue); } else if (cond.get() && constantCases) { processBranches(); BuildCtx subctx(ctx); CHqlBoundExpr test; buildSwitchCondition(ctx, test); if (complexCompare) { if ((pairs.ordinality() > INLINE_COMPARE_THRESHOLD) || (translator.getHints() & HintSize)) { if (okToAlwaysEvaluateDefault() || hasLibraryChop()) buildLoopChopMap(subctx, target, test); else buildGeneralAssign(subctx, target); } else { if (okToAlwaysEvaluateDefault()) buildChop3Map(subctx, target, test); else buildGeneralAssign(subctx, target); } } else { ITypeInfo * condType = test.queryType()->queryPromotedType(); if (!queryBuildArrayLookup(subctx, target, test)) { if (condType->getTypeCode() != type_real) { OwnedHqlExpr search = test.getTranslatedExpr(); if (constantValues && (condType->getTypeCode() == type_int) && (pairs.ordinality() > INTEGER_SEARCH_THRESHOLD) && canBuildStaticList(resultType)) buildIntegerSearchMap(subctx, target, search); else buildSwitchMap(subctx, &target, test.expr); } else { if (okToAlwaysEvaluateDefault() && pairs.ordinality() > 4) { translator.buildExprAssign(ctx, target, defaultValue); buildChop2Map(subctx, target, test, 0, pairs.ordinality()); } else buildGeneralAssign(subctx, target); } } } } else buildGeneralAssign(ctx, target); return true; } bool HqlCppCaseInfo::buildReturn(BuildCtx & ctx) { if (pairs.ordinality() == 0) { translator.buildReturn(ctx, defaultValue); } else if (cond.get() && constantCases) { processBranches(); BuildCtx subctx(ctx); CHqlBoundExpr test; buildSwitchCondition(ctx, test); if (complexCompare) { buildGeneralReturn(subctx); } else { //if canBuildArrayLookup(test) //if use a lookup table to map value->constants if (test.queryType()->getTypeCode() != type_real) buildSwitchMap(subctx, NULL, test.expr); else buildGeneralReturn(subctx); } } else buildGeneralReturn(ctx); return true; } void HqlCppCaseInfo::buildChop3Map(BuildCtx & ctx, const CHqlBoundTarget & target, CHqlBoundExpr & test, IHqlExpression * temp, unsigned start, unsigned end) { if ((end - start) <= 2) buildChop2Map(ctx, target, test, start, end); else { unsigned mid = (start + end) / 2; generateCompareVar(ctx, temp, test, queryCompare(mid)); OwnedHqlExpr test1 = createValue(no_eq, LINK(temp), getZero()); OwnedHqlExpr test2 = createValue(no_lt, LINK(temp), getZero()); BuildCtx subctx(ctx); IHqlStmt * if1 = subctx.addFilter(test1); // if (test == 0) translator.buildExprAssign(subctx, target, queryReturn(mid)); // target = value(n) subctx.selectElse(if1); // else IHqlStmt * if2 = subctx.addFilter(test2); // if (test < 0) buildChop3Map(subctx, target, test, temp, start, mid); // repeat for start..mid subctx.selectElse(if2); // else buildChop3Map(subctx, target, test, temp, mid+1, end); // repeat for min..end } } void HqlCppCaseInfo::buildChop3Map(BuildCtx & ctx, const CHqlBoundTarget & target, CHqlBoundExpr & test) { //need to hack it because there is no signed integer type OwnedHqlExpr tempVar = ctx.getTempDeclare(indexType, NULL); translator.buildExprAssign(ctx, target, defaultValue); buildChop3Map(ctx, target, test, tempVar, 0, getNumPairs()); } void HqlCppCaseInfo::buildChop2Map(BuildCtx & ctx, const CHqlBoundTarget & target, CHqlBoundExpr & test, unsigned start, unsigned end) { BuildCtx subctx(ctx); if ((end - start) <= 3) // (1,2,3) avg(2) cf. (2,2,3) avg(2.3) { //optimize the case where they all create the same value bool same = true; unsigned index; for (index=start+1; index < end; ++index) { if (queryReturn(index-1) != queryReturn(index)) { same = false; break; } } if (same) { CompoundBuilder cb(no_or); for (index=start; index < end; ++index) { IHqlExpression * compare = queryCompare(index); IHqlExpression * cond = createCompareExpr(no_eq, test.getTranslatedExpr(), LINK(compare)); cb.addOperand(cond); } OwnedHqlExpr compound = cb.getCompound(); translator.buildFilter(subctx, compound); translator.buildExprAssign(subctx, target, queryReturn(start)); } else { IHqlStmt * stmt = NULL; for (index=start; index < end; ++index) { if (stmt) subctx.selectElse(stmt); IHqlExpression * compare = queryCompare(index); OwnedHqlExpr cond = createCompareExpr(no_eq, test.getTranslatedExpr(), LINK(compare)); CHqlBoundExpr bound; translator.buildExpr(subctx, cond, bound); stmt = subctx.addFilter(bound.expr); translator.buildExprAssign(subctx, target, queryReturn(index)); } } } else { unsigned mid = (start + end) / 2; IHqlExpression * compare = queryCompare(mid); OwnedHqlExpr cond = createCompareExpr(no_lt, test.getTranslatedExpr(), LINK(compare)); CHqlBoundExpr bound; translator.buildExpr(subctx, cond, bound); IHqlStmt * stmt = subctx.addFilter(bound.expr); buildChop2Map(subctx, target, test, start, mid); subctx.selectElse(stmt); buildChop2Map(subctx, target, test, mid, end); } } IHqlExpression * HqlCppCaseInfo::buildIndexedMap(BuildCtx & ctx, IHqlExpression * test, unsigned lower, unsigned upper) { ITypeInfo * compareType = test->queryType()->queryPromotedType(); type_t compareTypeCode = compareType->getTypeCode(); HqlExprArray values; IHqlExpression * dft = queryActiveTableSelector(); // value doesn't matter as long as it will not occur unsigned num = (upper-lower+1); values.ensure(num); unsigned idx; for (idx = 0; idx < num; idx++) values.append(*LINK(dft)); ForEachItemIn(idx2, pairs) { IHqlExpression & cur = pairs.item(idx2); IValue * value = cur.queryChild(0)->queryValue(); unsigned replaceIndex; switch (compareTypeCode) { case type_int: replaceIndex = (int)value->getIntValue()-lower; break; case type_string: { StringBuffer temp; value->getStringValue(temp); replaceIndex = (int)(unsigned char)temp.charAt(0)-lower; break; } default: throwUnexpectedType(compareType); } IHqlExpression * mapTo = cur.queryChild(1); if (mapTo->getOperator() != no_constant) throwUnexpected(); if (replaceIndex >= num) translator.reportWarning(HQLWRN_CaseCanNeverMatch, "CASE entry %d can never match the test condition", replaceIndex); else values.replace(*LINK(mapTo),replaceIndex); } //Now replace the placeholders with the default values. for (idx = 0; idx < num; idx++) { if (&values.item(idx) == dft) values.replace(*defaultValue.getLink(),idx); } // use a var string type to get better C++ generated... ITypeInfo * storeType = getArrayElementType(resultType); ITypeInfo * listType = makeArrayType(storeType, values.ordinality()); OwnedHqlExpr lvalues = createValue(no_list, listType, values); CHqlBoundExpr boundTable; translator.buildExpr(ctx, lvalues, boundTable); IHqlExpression * tableIndex; switch (compareTypeCode) { case type_int: tableIndex = LINK(test); break; case type_string: tableIndex = createValue(no_index, makeCharType(), LINK(test), getZero()); break; default: throwUnexpectedType(compareType); } ITypeInfo * retType = resultType; IHqlExpression * ret = createValue(no_index, LINK(retType), LINK(boundTable.expr), tableIndex); return createTranslatedOwned(ret); } void HqlCppCaseInfo::buildLoopChopMap(BuildCtx & ctx, const CHqlBoundTarget & target, CHqlBoundExpr & test) { //Declare a table that contains all the strings... ITypeInfo * compareType = queryCompareType(); type_t ctc = compareType->getTypeCode(); if ((ctc == type_data) && !hasLibraryChop()) { buildGeneralAssign(ctx, target); return; } OwnedHqlExpr values = createCompareList(); CHqlBoundExpr boundTable; translator.buildExpr(ctx, values, boundTable); OwnedHqlExpr midVar = createVariable(LINK(indexType)); if (hasLibraryChop()) { ITypeInfo * tableEntryType = makeReferenceModifier(LINK(compareType)); ITypeInfo * tableType = makePointerType(tableEntryType); HqlExprArray args; _ATOM func; switch (ctc) { case type_data: func = searchDataTableAtom; break; case type_varstring: func = searchVStringTableAtom; break; case type_qstring: func = searchQStringTableAtom; break; case type_string: if (compareType->queryCharset()->queryName() == asciiAtom) func = searchStringTableAtom; else if (compareType->queryCharset()->queryName() == ebcdicAtom) func = searchEStringTableAtom; else UNIMPLEMENTED; break; case type_unicode: func = searchUnicodeTableAtom; break; case type_utf8: func = searchUtf8TableAtom; break; case type_varunicode: func = searchVUnicodeTableAtom; break; default: throwUnexpectedType(compareType); } args.append(*getSizetConstant(values->numChildren())); if ((ctc != type_varstring) && (ctc != type_varunicode)) args.append(*getSizetConstant(values->queryChild(0)->queryType()->getStringLen())); args.append(*createValue(no_address, tableType, createValue(no_index, LINK(tableEntryType), LINK(boundTable.expr), getZero()))); if ((ctc != type_varstring) && (ctc != type_varunicode)) args.append(*translator.getBoundLength(test)); args.append(*ensureIndexable(test.expr)); if ((ctc==type_unicode) || (ctc == type_varunicode) || (ctc == type_utf8)) args.append(*createConstant(compareType->queryLocale()->str())); OwnedHqlExpr call = translator.bindTranslatedFunctionCall(func, args); OwnedHqlExpr search = createTranslated(call); bool includeDefaultInResult = false; OwnedHqlExpr resultExpr = createResultsExpr(midVar, true, &includeDefaultInResult); OwnedHqlExpr simpleResult = queryCreateSimpleResultAssign(search, resultExpr); if (simpleResult) translator.buildExprAssign(ctx, target, simpleResult); else { ctx.addDeclare(midVar, NULL); translator.buildAssignToTemp(ctx, midVar, search); if (includeDefaultInResult) { translator.buildExprAssign(ctx, target, resultExpr); } else { OwnedHqlExpr compare = createBoolExpr(no_ne, LINK(midVar), createNotFoundValue()); BuildCtx subctx(ctx); IHqlStmt * stmt = subctx.addFilter(compare); translator.buildExprAssign(subctx, target, resultExpr); subctx.selectElse(stmt); translator.buildExprAssign(subctx, target, defaultValue); } } } else { #if 0 //Keep this as a reminder of what was needed to fix qstrings. if (ctc == type_qstring) { Linked compareType = queryCompareType(); compareType.setown(makeArrayType(compareType.getLink())); table = createValue(no_typetransfer, compareType.getLink(), table); } #endif OwnedHqlExpr resultExpr = createResultsExpr(midVar, false); //Now generate the code that performs the binary chop.. ctx.addDeclare(midVar, NULL); OwnedHqlExpr compareVar = ctx.getTempDeclare(indexType, NULL); OwnedHqlExpr leftVar = ctx.getTempDeclare(indexType, NULL); OwnedHqlExpr rightVar = ctx.getTempDeclare(indexType, NULL); OwnedHqlExpr numPairs = getSizetConstant(getNumPairs()); ctx.addAssign(leftVar, queryZero()); ctx.addAssign(rightVar, numPairs); translator.buildExprAssign(ctx, target, defaultValue); OwnedHqlExpr loopc = createBoolExpr(no_lt, LINK(leftVar), LINK(rightVar)); OwnedHqlExpr mid = createValue(no_div, createValue(no_add, LINK(leftVar), LINK(rightVar)), createConstant(indexType->castFrom(false, 2))); OwnedHqlExpr mid_p1 = createValue(no_add, LINK(indexType), LINK(midVar), createConstant(indexType->castFrom(false, 1))); OwnedHqlExpr curelem = createValue(no_index, LINK(compareType), boundTable.getTranslatedExpr(), createTranslated(mid_p1)); OwnedHqlExpr test1 = createBoolExpr(no_lt, LINK(compareVar), getZero()); OwnedHqlExpr test2 = createBoolExpr(no_gt, LINK(compareVar), getZero()); OwnedHqlExpr order = createValue(no_order, LINK(indexType), test.getTranslatedExpr(), LINK(curelem)); BuildCtx loopctx(ctx); loopctx.addLoop(loopc, NULL, true); translator.buildAssignToTemp(loopctx, midVar, mid); translator.buildAssignToTemp(loopctx, compareVar, order); BuildCtx subctx(loopctx); IHqlStmt * if1 = subctx.addFilter(test1); // if (test < 0) translator.buildAssignToTemp(subctx, rightVar, midVar); // right = mid; subctx.selectElse(if1); // else IHqlStmt * if2 = subctx.addFilter(test2); // if (test > 0) translator.buildAssignToTemp(subctx, leftVar, mid_p1); // left = mid + 1; subctx.selectElse(if2); // else //generate the default assignment... translator.buildExprAssign(subctx, target, resultExpr); subctx.addBreak(); } } void HqlCppCaseInfo::buildIntegerSearchMap(BuildCtx & ctx, const CHqlBoundTarget & target, IHqlExpression * test) { ITypeInfo * compareType = queryCompareType(); HqlExprArray args; args.append(*createCompareList()); args.append(*LINK(test)); _ATOM func; if (compareType->isSigned()) { if (compareType->getSize() > 4) func = searchTableInteger8Atom; else func = searchTableInteger4Atom; } else { if (compareType->getSize() > 4) func = searchTableUInteger8Atom; else func = searchTableUInteger4Atom; } OwnedHqlExpr search = translator.bindFunctionCall(func, args); OwnedHqlExpr midVar = createVariable(LINK(indexType)); bool includeDefaultInResult = false; OwnedHqlExpr resultExpr = createResultsExpr(midVar, true, &includeDefaultInResult); OwnedHqlExpr simpleResult = queryCreateSimpleResultAssign(search, resultExpr); if (simpleResult) translator.buildExprAssign(ctx, target, simpleResult); else { ctx.addDeclare(midVar, NULL); translator.buildAssignToTemp(ctx, midVar, search); if (includeDefaultInResult) { translator.buildExprAssign(ctx, target, resultExpr); } else { OwnedHqlExpr compare = createBoolExpr(no_ne, LINK(midVar), createNotFoundValue()); BuildCtx subctx(ctx); IHqlStmt * stmt = subctx.addFilter(compare); translator.buildExprAssign(subctx, target, resultExpr); subctx.selectElse(stmt); translator.buildExprAssign(subctx, target, defaultValue); } } } void HqlCppCaseInfo::buildSwitchCondition(BuildCtx & ctx, CHqlBoundExpr & bound) { OwnedHqlExpr simpleCond = complexCompare ? getSimplifyCompareArg(cond) : LINK(cond); translator.buildCachedExpr(ctx, simpleCond, bound); } void HqlCppCaseInfo::buildSwitchMap(BuildCtx & ctx, const CHqlBoundTarget * target, IHqlExpression * test) { bool isCharCompare = (test->queryType()->getTypeCode() == type_string); LinkedHqlExpr cond = test; if (isCharCompare) cond.setown(createValue(no_index, makeCharType(), LINK(test), getZero())); BuildCtx subctx(ctx); IHqlStmt * stmt = subctx.addSwitch(cond); // are all the comparisons against constant values? If so we optimize it... unsigned sameCount = 0; ForEachItemIn(idx, pairs) { IHqlExpression & cur = pairs.item(idx); LinkedHqlExpr compare = cur.queryChild(0); IHqlExpression * branchValue = cur.queryChild(1); if (isCharCompare) { OwnedITypeInfo charType = makeCharType(); IValue * cur = compare->queryValue(); IValue * next = cur->castTo(charType); compare.setown(createConstant(next)); } bool same = false; bool skip = false; if (idx != pairs.ordinality()-1) { IHqlExpression & next = pairs.item(idx+1); if (next.queryChild(1) == branchValue) same = true; //if save item is included twice, remove one so no error //from the case statement if (next.queryChild(0) == cur.queryChild(0)) { skip = true; #ifdef _DEBUG throwUnexpected(); // should have been removed as a duplicate #endif } } if (sameCount >= MAX_NUM_NOBREAK_CASE) same = false; if (!skip) { subctx.addCase(stmt, compare); // common up case labels that produce the same result if (!same) { sameCount = 1; if (target) translator.buildExprAssign(subctx, *target, branchValue); else translator.buildReturn(subctx, branchValue); } else sameCount++; } } if (target) { subctx.addDefault(stmt); translator.buildExprAssign(subctx, *target, defaultValue); } else translator.buildReturn(ctx, defaultValue); } void HqlCppCaseInfo::buildGeneralAssign(BuildCtx & ctx, const CHqlBoundTarget & target) { unsigned max = pairs.ordinality(); Owned subctx = new BuildCtx(ctx); CHqlBoundExpr pureCond; if (cond.get()) translator.buildCachedExpr(*subctx, cond, pureCond); OwnedHqlExpr testMore; if (max > MAX_NESTED_CASES) { //Too many nested blocks cause the compiler indigestion... Owned t = makeBoolType(); testMore.setown(ctx.getTempDeclare(t, queryBoolExpr(false))); } for (unsigned idx = 0; idx < max; idx++) { IHqlExpression & cur = pairs.item(idx); IHqlExpression * curtest = cur.queryChild(0); IHqlExpression * curvalue = cur.queryChild(1); OwnedHqlExpr compare; if (!pureCond.expr) compare.setown(ensureExprType(curtest, queryBoolType())); else compare.setown(createBoolExpr(no_eq, pureCond.getTranslatedExpr(), LINK(curtest))); if (idx && ((idx % MAX_NESTED_CASES) == 0)) { translator.buildAssignToTemp(*subctx,testMore,queryBoolExpr(true)); subctx.setown(new BuildCtx(ctx)); subctx->addFilter(testMore); translator.buildAssignToTemp(*subctx,testMore,queryBoolExpr(false)); } IHqlStmt * test = translator.buildFilterViaExpr(*subctx, compare); translator.buildExprAssign(*subctx, target, curvalue); subctx->selectElse(test); } translator.buildExprAssign(*subctx, target, defaultValue); } void HqlCppCaseInfo::buildGeneralReturn(BuildCtx & ctx) { unsigned max = pairs.ordinality(); CHqlBoundExpr pureCond; if (cond.get()) translator.buildCachedExpr(ctx, cond, pureCond); for (unsigned idx = 0; idx < max; idx++) { IHqlExpression & cur = pairs.item(idx); IHqlExpression * curtest = cur.queryChild(0); IHqlExpression * curvalue = cur.queryChild(1); OwnedHqlExpr compare; if (!pureCond.expr) compare.setown(ensureExprType(curtest, queryBoolType())); else compare.setown(createBoolExpr(no_eq, pureCond.getTranslatedExpr(), LINK(curtest))); BuildCtx subctx(ctx); translator.buildFilter(subctx, compare); translator.buildReturn(subctx, curvalue); } translator.buildReturn(ctx, defaultValue); } bool HqlCppCaseInfo::okToAlwaysEvaluateDefault() { return defaultValue->isPure(); } ITypeInfo * HqlCppCaseInfo::queryCompareType() { return cond->queryType()->queryPromotedType(); } IHqlExpression * HqlCppCaseInfo::createCompareList() { //NB: All cases need to have the same type because they are stored in a table Linked promoted = pairs.item(0).queryChild(0)->queryType(); unsigned max = pairs.ordinality(); for (unsigned idx1 = 1; idx1 < max; idx1++) { ITypeInfo * type = pairs.item(idx1).queryChild(0)->queryType(); promoted.setown(getPromotedECLType(promoted, type)); } HqlExprArray values; values.ensure(pairs.ordinality()); ForEachItemIn(idx, pairs) { IHqlExpression & pair = pairs.item(idx); IHqlExpression * compare = pair.queryChild(0); values.append(*ensureExprType(compare, promoted)); } Linked compareType = queryCompareType(); switch (compareType->getTypeCode()) { case type_string: case type_data: case type_varstring: case type_qstring: case type_utf8: compareType.setown(makePointerType(makeCharType(false))); break; case type_unicode: case type_varunicode: compareType.setown(makePointerType(makeClassType("UChar"))); break; } return createValue(no_list, makeArrayType(compareType.getLink()), values); } IHqlExpression * HqlCppCaseInfo::createResultsExpr(IHqlExpression * matchVar, bool canIncludeDefault, bool * includedDefault) { if (includedDefault) *includedDefault = false; //Look at all the return results and see what relation we can create //This can generate one of the following: //1. A single value. //2. A function of the result. e.g., IN... //3. A lookup in another table. [ all values are constant ] //4. A nested map statement bool areSame = true; bool areLinear = true; bool areConstant = true; __int64 linearStart = 0; __int64 linearMultiple = 1; ITypeInfo * retType = resultType; switch (retType->getTypeCode()) { case type_int: case type_boolean: break; case type_string: case type_data: // areConstant = false; // a temporary hack to stop incorrect table being generated. areLinear = false; break; default: areLinear = false; break; } IHqlExpression * prevValue = NULL; HqlExprArray values; values.ensure(pairs.ordinality()); ForEachItemIn(idx, pairs) { IHqlExpression & pair = pairs.item(idx); IHqlExpression * value = pair.queryChild(1); IValue * cvalue = value->queryValue(); if (cvalue) { if (areLinear) { __int64 val = cvalue->getIntValue(); if (idx == 0) linearStart = val; else if (idx == 1) linearMultiple = (val - linearStart); else if (val != linearStart + idx * linearMultiple) areLinear = false; } } else { areConstant = false; areLinear = false; } if (idx > 0) { if (areSame && (prevValue != value)) areSame = false; } values.append(*ensureExprType(value, resultType)); prevValue = value; } if (areSame) return LINK(prevValue); if (areLinear) { IHqlExpression * ret = ensureExprType(matchVar, resultType); if (linearMultiple != 1) ret = createValue(no_mul, LINK(resultType), ret, createConstant(resultType->castFrom(true, linearMultiple))); if (linearStart != 0) ret = createValue(no_add, LINK(resultType), ret, createConstant(resultType->castFrom(true, linearStart))); return ret; } unsigned firstMatchEntry = 0; if (canIncludeDefault) { //If all the values are constant, then can add the default as an extra 0th entry, because -1 will be the index for the default if (areConstant && defaultValue->isConstant() && defaultValue->queryType() == values.item(0).queryType()) { firstMatchEntry = 1; values.add(*LINK(defaultValue), 0); *includedDefault = true; } } // easy way to create a value list... ITypeInfo * storeType = getArrayElementType(retType); OwnedHqlExpr newlist = createValue(no_list, makeSetType(storeType), values); if (areConstant && canBuildStaticList(resultType)) { IHqlExpression * index = adjustValue(matchVar, 1+firstMatchEntry); return createValue(no_index, LINK(retType), LINK(newlist), index, createAttribute(noBoundCheckAtom)); } //Need to generate a case (switch integer case 1: ..... ) HqlExprArray choosePairs; cvtChooseListToPairs(choosePairs, newlist, 0); IHqlExpression * caseExpr = createOpenValue(no_case, LINK(retType)); caseExpr->addOperand(LINK(matchVar)); ForEachItemIn(idx2, choosePairs) caseExpr->addOperand(&choosePairs.item(idx2)); choosePairs.kill(true); caseExpr->addOperand(LINK(defaultValue)); return caseExpr->closeExpr(); } void HqlCppCaseInfo::generateCompareVar(BuildCtx & ctx, IHqlExpression * target, CHqlBoundExpr & test, IHqlExpression * other) { OwnedHqlExpr compare = createValue(no_order, test.getTranslatedExpr(), LINK(other)); translator.buildAssignToTemp(ctx, target, compare); } unsigned HqlCppCaseInfo::getNumPairs() { return pairs.ordinality(); } bool HqlCppCaseInfo::hasLibraryChop() { ITypeInfo * compareType = queryCompareType(); type_t ctc = compareType->getTypeCode(); switch (ctc) { case type_data: return canBuildStaticList(promotedElementType); case type_string: case type_varstring: case type_qstring: case type_unicode: case type_varunicode: case type_utf8: return true; } return false; } void HqlCppCaseInfo::processBranches() { sortPairs(); removeDuplicates(); promoteTypes(); } void HqlCppCaseInfo::promoteTypes() { Owned promoted = pairs.item(0).queryChild(0)->getType(); unsigned max = pairs.ordinality(); for (unsigned idx1 = 1; idx1 < max; idx1++) { ITypeInfo * type = pairs.item(idx1).queryChild(0)->queryType(); if (isStringType(promoted) && isStringType(type)) { if (promoted->getStringLen() != type->getStringLen()) { promoted.setown(::getPromotedECLType(promoted, type)); promoted.setown(getStretchedType(UNKNOWN_LENGTH, promoted)); } } promoted.setown(::getPromotedECLType(promoted, type)); } promotedElementType.set(promoted); if (isStringType(promoted)) promoted.setown(getStretchedType(UNKNOWN_LENGTH, promoted)); ITypeInfo * testType = queryCompareType(); if ((testType->queryCharset() != promoted->queryCharset()) || (testType->queryLocale() != promoted->queryLocale())) cond.setown(ensureExprType(cond, promoted)); } bool HqlCppCaseInfo::canBuildArrayLookup(const CHqlBoundExpr & test) { ITypeInfo * condType = test.queryType()->queryPromotedType(); //MORE: Also support this for high density tables that don't start at 0... - checking upper and lower bounds unsigned bitSize = condType->getBitSize(); if (constantValues && (bitSize && (bitSize <= 8) && !condType->isSigned())) { unsigned limit = (1 << bitSize); //use case if enough items, or above a certain density... if (pairs.ordinality() * 100 >= limit * SWITCH_TABLE_DENSITY_THRESHOLD) { if ((condType->getTypeCode() == type_int) || (condType->getTypeCode() == type_string)) return true; } } return false; } bool HqlCppCaseInfo::queryBuildArrayLookup(BuildCtx & ctx, const CHqlBoundTarget & target, const CHqlBoundExpr & test) { if (canBuildArrayLookup(test) && canBuildStaticList(resultType)) { //MORE: Also support this for high density tables that don't start at 0... - checking upper and lower bounds ITypeInfo * condType = test.queryType()->queryPromotedType(); unsigned bitSize = condType->getBitSize(); unsigned limit = (1 << bitSize); BuildCtx subctx(ctx); OwnedHqlExpr ret = buildIndexedMap(subctx, test.expr, 0, limit-1); translator.buildExprAssign(ctx, target, ret); return true; } return false; } void HqlCppCaseInfo::removeDuplicates() { unsigned num = pairs.ordinality(); if (num > 1) { num--; while (num--) { IHqlExpression & cur = pairs.item(num); IHqlExpression & next = pairs.item(num+1); if (cur.queryChild(0) == next.queryChild(0)) { if (cur.queryChild(1) == next.queryChild(1)) pairs.remove(num+1); else { // we need to keep the first in the original list.... Horrid, but it works... unsigned off1 = originalPairs.find(cur); unsigned off2 = originalPairs.find(next); assertex(off1 != NotFound && off2 != NotFound); if (off1 < off2) pairs.remove(num+1); else pairs.remove(num); } } } } } IHqlExpression * HqlCppCaseInfo::queryCreateSimpleResultAssign(IHqlExpression * search, IHqlExpression * resultExpr) { IHqlExpression * trueExpr = queryBoolExpr(true); IHqlExpression * falseExpr = queryBoolExpr(false); if (resultExpr == trueExpr && defaultValue == falseExpr) return createBoolExpr(no_ne, LINK(search), createNotFoundValue()); if (resultExpr == falseExpr && defaultValue == trueExpr) return createBoolExpr(no_eq, LINK(search), createNotFoundValue()); return NULL; } IHqlExpression * HqlCppCaseInfo::queryCompare(unsigned index) { return pairs.item(index).queryChild(0); } IHqlExpression * HqlCppCaseInfo::queryReturn(unsigned index) { return pairs.item(index).queryChild(1); } void HqlCppCaseInfo::setCond(IHqlExpression * expr) { cond.set(expr); if (isCompare3Valued(expr->queryType())) complexCompare = true; } void HqlCppCaseInfo::setDefault(IHqlExpression * expr) { defaultValue.set(expr); } void HqlCppCaseInfo::sortPairs() { appendArray(originalPairs, pairs); pairs.sort(comparePair); } void HqlCppCaseInfo::updateResultType(IHqlExpression * expr) { ITypeInfo * curResultType = expr->queryType(); if (resultType) resultType.setown(::getPromotedECLType(resultType, curResultType)); else resultType.set(curResultType); }