summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuboš Luňák <l.lunak@collabora.com>2018-07-24 15:28:49 +0200
committerLuboš Luňák <l.lunak@collabora.com>2018-07-31 16:01:40 +0200
commit1bf7bc6f9929ceae0ea059b64ae0efa12228525f (patch)
treea682697177f49af89d62ec7042dd8ae2eb871663
parent58a15b452801f1f6f1b3e9f2fef49a1249538ac5 (diff)
try to detect that a formula does not contain any implicit intersection
Commit 67444cbe disabled svDoubleRef completely for OpenCL, which means many formulas weren't handled by OpenCL even if the implicit intersection problems are quite rare. This patch tries to detect formulas implicit intersections in formulas and if it's certain that a formula does not contain one, then it's ok to use OpenCL with svDoubleRef. The detection is done by having ScCompiler analyze each opcode call and its parameters, which should provide sufficient information to know if implicit intersection can take place or not. The extra compilation can be avoided by using OpenCL's compilation and doing the svDoubleRef conversion later on the RPN code, to be done later. This is opt-in, so if unsure don't do anything, if it turns out that some opcode needs special handling, it can be simply added. Change-Id: Iaa52fa7eb8b14dc8c2b92384a21e2ab8efe0ddd7 Reviewed-on: https://gerrit.libreoffice.org/57959 Tested-by: Jenkins Reviewed-by: Luboš Luňák <l.lunak@collabora.com>
-rw-r--r--formula/source/core/api/FormulaCompiler.cxx64
-rw-r--r--include/formula/FormulaCompiler.hxx9
-rw-r--r--sc/inc/compiler.hxx28
-rw-r--r--sc/source/core/data/formulacell.cxx22
-rw-r--r--sc/source/core/data/grouptokenconverter.cxx17
-rw-r--r--sc/source/core/tool/compiler.cxx167
-rw-r--r--sc/source/core/tool/token.cxx3
7 files changed, 213 insertions, 97 deletions
diff --git a/formula/source/core/api/FormulaCompiler.cxx b/formula/source/core/api/FormulaCompiler.cxx
index c22f9edbdbe1..3329db657812 100644
--- a/formula/source/core/api/FormulaCompiler.cxx
+++ b/formula/source/core/api/FormulaCompiler.cxx
@@ -1349,6 +1349,10 @@ bool FormulaCompiler::GetToken()
return HandleDbData();
case ocTableRef:
return HandleTableRef();
+ case ocPush:
+ if( mbComputeII )
+ HandleIIOpCode(mpToken.get(), nullptr, 0);
+ break;
default:
; // nothing
}
@@ -1556,7 +1560,6 @@ void FormulaCompiler::Factor()
else
{
// standard handling of 1-parameter opcodes
- OpCode eMyLastOp = eOp;
pFacToken = mpToken;
eOp = NextToken();
if( nNumFmt == SvNumFormatType::UNDEFINED && eOp == ocNot )
@@ -1574,10 +1577,10 @@ void FormulaCompiler::Factor()
else if ( pArr->GetCodeError() == FormulaError::NONE )
{
pFacToken->SetByte( 1 );
- if (mbComputeII && IsIIOpCode(eMyLastOp))
+ if (mbComputeII)
{
FormulaToken** pArg = pCode - 1;
- HandleIIOpCode(eMyLastOp, pFacToken->GetInForceArray(), &pArg, 1);
+ HandleIIOpCode(pFacToken, &pArg, 1);
}
}
PutCode( pFacToken );
@@ -1621,7 +1624,7 @@ void FormulaCompiler::Factor()
sal_uInt32 nSepCount = 0;
if( !bNoParam )
{
- bool bDoIICompute = mbComputeII && IsIIOpCode(eMyLastOp);
+ bool bDoIICompute = mbComputeII;
// Array of FormulaToken double pointers to collect the parameters of II opcodes.
FormulaToken*** pArgArray = nullptr;
if (bDoIICompute)
@@ -1648,7 +1651,7 @@ void FormulaCompiler::Factor()
pArgArray[nSepCount - 1] = pCode - 1; // Add rest of the arguments
}
if (bDoIICompute)
- HandleIIOpCode(eMyLastOp, pFacToken->GetInForceArray(), pArgArray,
+ HandleIIOpCode(pFacToken, pArgArray,
std::min(nSepCount, static_cast<sal_uInt32>(FORMULA_MAXPARAMSII)));
}
if (bBadName)
@@ -1873,10 +1876,10 @@ void FormulaCompiler::UnaryLine()
FormulaTokenRef p = mpToken;
NextToken();
UnaryLine();
- if (mbComputeII && IsIIOpCode(p->GetOpCode()))
+ if (mbComputeII)
{
FormulaToken** pArg = pCode - 1;
- HandleIIOpCode(p->GetOpCode(), p->GetInForceArray(), &pArg, 1);
+ HandleIIOpCode(p.get(), &pArg, 1);
}
PutCode( p );
}
@@ -1889,10 +1892,10 @@ void FormulaCompiler::PostOpLine()
UnaryLine();
while ( mpToken->GetOpCode() == ocPercentSign )
{ // this operator _follows_ its operand
- if (mbComputeII && IsIIOpCode(mpToken->GetOpCode()))
+ if (mbComputeII)
{
FormulaToken** pArg = pCode - 1;
- HandleIIOpCode(mpToken->GetOpCode(), mpToken->GetInForceArray(), &pArg, 1);
+ HandleIIOpCode(mpToken.get(), &pArg, 1);
}
PutCode( mpToken );
NextToken();
@@ -1905,16 +1908,15 @@ void FormulaCompiler::PowLine()
while (mpToken->GetOpCode() == ocPow)
{
FormulaTokenRef p = mpToken;
- bool bDoIICompute = mbComputeII && IsIIOpCode(p->GetOpCode());
FormulaToken** pArgArray[2];
- if (bDoIICompute)
+ if (mbComputeII)
pArgArray[0] = pCode - 1; // Add first argument
NextToken();
PostOpLine();
- if (bDoIICompute)
+ if (mbComputeII)
{
pArgArray[1] = pCode - 1; // Add second argument
- HandleIIOpCode(p->GetOpCode(), p->GetInForceArray(), pArgArray, 2);
+ HandleIIOpCode(p.get(), pArgArray, 2);
}
PutCode(p);
}
@@ -1926,16 +1928,15 @@ void FormulaCompiler::MulDivLine()
while (mpToken->GetOpCode() == ocMul || mpToken->GetOpCode() == ocDiv)
{
FormulaTokenRef p = mpToken;
- bool bDoIICompute = mbComputeII && IsIIOpCode(p->GetOpCode());
FormulaToken** pArgArray[2];
- if (bDoIICompute)
+ if (mbComputeII)
pArgArray[0] = pCode - 1; // Add first argument
NextToken();
PowLine();
- if (bDoIICompute)
+ if (mbComputeII)
{
pArgArray[1] = pCode - 1; // Add second argument
- HandleIIOpCode(p->GetOpCode(), p->GetInForceArray(), pArgArray, 2);
+ HandleIIOpCode(p.get(), pArgArray, 2);
}
PutCode(p);
}
@@ -1947,16 +1948,15 @@ void FormulaCompiler::AddSubLine()
while (mpToken->GetOpCode() == ocAdd || mpToken->GetOpCode() == ocSub)
{
FormulaTokenRef p = mpToken;
- bool bDoIICompute = mbComputeII && IsIIOpCode(p->GetOpCode());
FormulaToken** pArgArray[2];
- if (bDoIICompute)
+ if (mbComputeII)
pArgArray[0] = pCode - 1; // Add first argument
NextToken();
MulDivLine();
- if (bDoIICompute)
+ if (mbComputeII)
{
pArgArray[1] = pCode - 1; // Add second argument
- HandleIIOpCode(p->GetOpCode(), p->GetInForceArray(), pArgArray, 2);
+ HandleIIOpCode(p.get(), pArgArray, 2);
}
PutCode(p);
}
@@ -1968,16 +1968,15 @@ void FormulaCompiler::ConcatLine()
while (mpToken->GetOpCode() == ocAmpersand)
{
FormulaTokenRef p = mpToken;
- bool bDoIICompute = mbComputeII && IsIIOpCode(p->GetOpCode());
FormulaToken** pArgArray[2];
- if (bDoIICompute)
+ if (mbComputeII)
pArgArray[0] = pCode - 1; // Add first argument
NextToken();
AddSubLine();
- if (bDoIICompute)
+ if (mbComputeII)
{
pArgArray[1] = pCode - 1; // Add second argument
- HandleIIOpCode(p->GetOpCode(), p->GetInForceArray(), pArgArray, 2);
+ HandleIIOpCode(p.get(), pArgArray, 2);
}
PutCode(p);
}
@@ -1989,16 +1988,15 @@ void FormulaCompiler::CompareLine()
while (mpToken->GetOpCode() >= ocEqual && mpToken->GetOpCode() <= ocGreaterEqual)
{
FormulaTokenRef p = mpToken;
- bool bDoIICompute = mbComputeII && IsIIOpCode(p->GetOpCode());
FormulaToken** pArgArray[2];
- if (bDoIICompute)
+ if (mbComputeII)
pArgArray[0] = pCode - 1; // Add first argument
NextToken();
ConcatLine();
- if (bDoIICompute)
+ if (mbComputeII)
{
pArgArray[1] = pCode - 1; // Add second argument
- HandleIIOpCode(p->GetOpCode(), p->GetInForceArray(), pArgArray, 2);
+ HandleIIOpCode(p.get(), pArgArray, 2);
}
PutCode(p);
}
@@ -2018,16 +2016,15 @@ OpCode FormulaCompiler::Expression()
{
FormulaTokenRef p = mpToken;
mpToken->SetByte( 2 ); // 2 parameters!
- bool bDoIICompute = mbComputeII && IsIIOpCode(p->GetOpCode());
FormulaToken** pArgArray[2];
- if (bDoIICompute)
+ if (mbComputeII)
pArgArray[0] = pCode - 1; // Add first argument
NextToken();
CompareLine();
- if (bDoIICompute)
+ if (mbComputeII)
{
pArgArray[1] = pCode - 1; // Add second argument
- HandleIIOpCode(p->GetOpCode(), p->GetInForceArray(), pArgArray, 2);
+ HandleIIOpCode(p.get(), pArgArray, 2);
}
PutCode(p);
}
@@ -2101,6 +2098,7 @@ bool FormulaCompiler::CompileTokenArray()
// Some trailing garbage that doesn't form an expression?
if (eOp != ocStop)
SetError( FormulaError::OperatorExpected);
+ PostProcessCode();
FormulaError nErrorBeforePop = pArr->GetCodeError();
diff --git a/include/formula/FormulaCompiler.hxx b/include/formula/FormulaCompiler.hxx
index 1f0bb0d95c5b..d50a2e2f8be1 100644
--- a/include/formula/FormulaCompiler.hxx
+++ b/include/formula/FormulaCompiler.hxx
@@ -330,12 +330,13 @@ protected:
bool MergeRangeReference( FormulaToken * * const pCode1, FormulaToken * const * const pCode2 );
// Returns whether the opcode has implicit intersection ranges as parameters.
- // This is no-op for this class.
- virtual bool IsIIOpCode(OpCode /*nOpCode*/) const { return false; }
- // Handles II opcode and passes the parameter array and number of parameters.
- virtual void HandleIIOpCode(OpCode /*nOpCode*/, formula::ParamClass /*eClass*/,
+ // Called for (most) opcodes to possibly handle implicit intersection for the parameters.
+ virtual void HandleIIOpCode(FormulaToken* /*token*/,
FormulaToken*** /*pppToken*/, sal_uInt8 /*nNumParams*/) {}
+ // Called from CompileTokenArray() after RPN code generation is done.
+ virtual void PostProcessCode() {}
+
OUString aCorrectedFormula; // autocorrected Formula
OUString aCorrectedSymbol; // autocorrected Symbol
diff --git a/sc/inc/compiler.hxx b/sc/inc/compiler.hxx
index 2b10a67ef3ab..17e87347a23d 100644
--- a/sc/inc/compiler.hxx
+++ b/sc/inc/compiler.hxx
@@ -34,6 +34,7 @@
#include <com/sun/star/i18n/ParseResult.hpp>
#include <vector>
#include <memory>
+#include <set>
#include <com/sun/star/uno/Sequence.hxx>
#include <formula/FormulaCompiler.hxx>
@@ -302,6 +303,21 @@ private:
};
std::vector<TableRefEntry> maTableRefs; /// "stack" of currently active ocTableRef tokens
+ // Optimizing implicit intersection is done only at the end of code generation, because the usage context may
+ // be important. Store candidate parameters and the operation they are the argument for.
+ struct PendingImplicitIntersectionOptimization
+ {
+ PendingImplicitIntersectionOptimization(formula::FormulaToken** p, const formula::FormulaToken* o)
+ : parameter( p ), operation( o ) {}
+ formula::FormulaToken** parameter;
+ const formula::FormulaToken* operation;
+ };
+ std::vector< PendingImplicitIntersectionOptimization > mPendingImplicitIntersectionOptimizations;
+ std::set<formula::FormulaTokenRef> mUnhandledPossibleImplicitIntersections;
+#ifdef DBG_UTIL
+ std::set<OpCode> mUnhandledPossibleImplicitIntersectionsOpCodes;
+#endif
+
bool NextNewToken(bool bInArray);
virtual void SetError(FormulaError nError) override;
@@ -457,6 +473,11 @@ public:
static bool DoubleRefToPosSingleRefScalarCase(const ScRange& rRange, ScAddress& rAdr,
const ScAddress& rFormulaPos);
+ bool HasUnhandledPossibleImplicitIntersections() const { return !mUnhandledPossibleImplicitIntersections.empty(); }
+#ifdef DBG_UTIL
+ const std::set<OpCode>& UnhandledPossibleImplicitIntersectionsOpCodes() { return mUnhandledPossibleImplicitIntersectionsOpCodes; }
+#endif
+
private:
// FormulaCompiler
virtual OUString FindAddInFunction( const OUString& rUpperName, bool bLocalFirst ) const override;
@@ -485,8 +506,11 @@ private:
ScCharFlags GetCharTableFlags( sal_Unicode c, sal_Unicode cLast )
{ return c < 128 ? pConv->getCharTableFlags(c, cLast) : ScCharFlags::NONE; }
- bool IsIIOpCode(OpCode nOpCode) const override;
- void HandleIIOpCode(OpCode nOpCode, formula::ParamClass eClass, formula::FormulaToken*** pppToken, sal_uInt8 nNumParams) override;
+ virtual void HandleIIOpCode(formula::FormulaToken* token, formula::FormulaToken*** pppToken, sal_uInt8 nNumParams) override;
+ bool HandleIIOpCodeInternal(formula::FormulaToken* token, formula::FormulaToken*** pppToken, sal_uInt8 nNumParams);
+ bool SkipImplicitIntersectionOptimization(const formula::FormulaToken* token) const;
+ virtual void PostProcessCode() override;
+ static bool ParameterMayBeImplicitIntersection(const formula::FormulaToken* token, int parameter);
void ReplaceDoubleRefII(formula::FormulaToken** ppDoubleRefTok);
bool AdjustSumRangeShape(const ScComplexRefData& rBaseRange, ScComplexRefData& rSumRange);
void CorrectSumRange(const ScComplexRefData& rBaseRange, ScComplexRefData& rSumRange, formula::FormulaToken** ppSumRangeToken);
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 112d215bc2f7..9ff780be7e6a 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -68,6 +68,7 @@
#include <listenerqueryids.hxx>
#include <grouparealistener.hxx>
#include <formulalogger.hxx>
+#include <com/sun/star/sheet/FormulaLanguage.hpp>
#if HAVE_FEATURE_OPENCL
#include <opencl/openclwrapper.hxx>
@@ -4788,9 +4789,26 @@ bool ScFormulaCell::InterpretFormulaGroupOpenCL(sc::FormulaLogger::GroupScope& a
ScTokenArray aCode;
ScGroupTokenConverter aConverter(aCode, *pDocument, *this, xGroup->mpTopCell->aPos);
- if (!aConverter.convert(*pCode, aScope))
+ // TODO avoid this extra compilation
+ ScCompiler aComp( pDocument, xGroup->mpTopCell->aPos, *pCode, formula::FormulaGrammar::GRAM_UNSPECIFIED, true, cMatrixFlag != ScMatrixMode::NONE );
+ aComp.CompileTokenArray();
+ if (aComp.HasUnhandledPossibleImplicitIntersections() || !aConverter.convert(*pCode, aScope))
{
- SAL_INFO("sc.opencl", "conversion of group " << this << " failed, disabling");
+ if(aComp.HasUnhandledPossibleImplicitIntersections())
+ {
+ SAL_INFO("sc.opencl", "group " << xGroup->mpTopCell->aPos << " has unhandled implicit intersections, disabling");
+#ifdef DBG_UTIL
+ for( const OpCode opcode : aComp.UnhandledPossibleImplicitIntersectionsOpCodes())
+ {
+ SAL_INFO("sc.opencl", "unhandled implicit intersection opcode "
+ << formula::FormulaCompiler().GetOpCodeMap(com::sun::star::sheet::FormulaLanguage::ENGLISH)->getSymbol(opcode)
+ << "(" << int(opcode) << ")");
+ }
+#endif
+ }
+ else
+ SAL_INFO("sc.opencl", "conversion of group " << xGroup->mpTopCell->aPos << " failed, disabling");
+
mxGroup->meCalcState = sc::GroupCalcDisabled;
// Undo the hack above
diff --git a/sc/source/core/data/grouptokenconverter.cxx b/sc/source/core/data/grouptokenconverter.cxx
index a388b34d04ec..c5e99737afab 100644
--- a/sc/source/core/data/grouptokenconverter.cxx
+++ b/sc/source/core/data/grouptokenconverter.cxx
@@ -167,18 +167,12 @@ bool ScGroupTokenConverter::convert( const ScTokenArray& rCode, sc::FormulaLogge
break;
case svDoubleRef:
{
- /* FIXME: this simply does not work, it doesn't know
- * a) the context of implicit intersection, for which creating
- two arrays does not only result in huge unnecessary matrix
- operations but also produces wrong results, e.g. =B:B/C:C
- * b) when to keep a reference as a reference depending on the
- expected parameter type, e.g. INDEX(), OFFSET() and
- others (though that *may* be disabled by OpCode already).
- * Until both are solved keep the reference. */
- mrGroupTokens.AddToken(*p);
- break;
+ // This code may break in case of implicit intersection, leading to unnecessarily large
+ // matrix operations and possibly incorrect results (=C:C/D:D). That is handled by
+ // having ScCompiler check that there are no possible implicit intersections.
+ // Additionally some functions such as INDEX() and OFFSET() require a reference,
+ // that is handled by blacklisting those opcodes in ScTokenArray::CheckToken().
-#if 0
ScComplexRefData aRef = *p->GetDoubleRef();
if( aRef.IsDeleted())
return false;
@@ -253,7 +247,6 @@ bool ScGroupTokenConverter::convert( const ScTokenArray& rCode, sc::FormulaLogge
//ensure that backing storage exists for our lifetime
mxFormulaGroupContext = mrDoc.GetFormulaGroupContext();
}
-#endif
}
break;
case svIndex:
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index ab5920a61aed..895a9a9f2994 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -5798,40 +5798,67 @@ formula::ParamClass ScCompiler::GetForceArrayParameter( const formula::FormulaTo
return ScParameterClassification::GetParameterType( pToken, nParam);
}
-bool ScCompiler::IsIIOpCode(OpCode nOpCode) const
+bool ScCompiler::ParameterMayBeImplicitIntersection(const FormulaToken* token, int parameter)
{
- if (nOpCode == ocSumIf || nOpCode == ocAverageIf || (nOpCode >= SC_OPCODE_START_1_PAR && nOpCode < SC_OPCODE_STOP_1_PAR)
- || (nOpCode >= SC_OPCODE_START_BIN_OP && nOpCode < SC_OPCODE_STOP_UN_OP
- && nOpCode != ocIntersect && nOpCode != ocUnion && nOpCode != ocRange
- && nOpCode != ocAnd && nOpCode != ocOr && nOpCode != ocXor )
- || nOpCode == ocPercentSign)
+ formula::ParamClass param = ScParameterClassification::GetParameterType( token, parameter );
+ return param == Value || param == Array;
+}
+
+bool ScCompiler::SkipImplicitIntersectionOptimization(const FormulaToken* token) const
+{
+ if (mbMatrixFlag)
+ return true;
+ formula::ParamClass paramClass = token->GetInForceArray();
+ if (paramClass == formula::ForceArray
+ || paramClass == formula::ReferenceOrForceArray
+ || paramClass == formula::SuppressedReferenceOrForceArray)
{
return true;
}
-
+ formula::ParamClass returnType = ScParameterClassification::GetParameterType( token, SAL_MAX_UINT16 );
+ if( returnType == formula::Reference )
+ return true;
return false;
}
-void ScCompiler::HandleIIOpCode(OpCode nOpCode, formula::ParamClass eClass, FormulaToken*** pppToken, sal_uInt8 nNumParams)
+void ScCompiler::HandleIIOpCode(FormulaToken* token, FormulaToken*** pppToken, sal_uInt8 nNumParams)
{
if (!mbComputeII)
return;
+#ifdef DBG_UTIL
+ if(!HandleIIOpCodeInternal(token, pppToken, nNumParams))
+ mUnhandledPossibleImplicitIntersectionsOpCodes.insert(token->GetOpCode());
+#else
+ HandleIIOpCodeInternal(token, pppToken, nNumParams);
+#endif
+}
- if (nOpCode == ocSumIf || nOpCode == ocAverageIf)
+// return true if opcode is handled
+bool ScCompiler::HandleIIOpCodeInternal(FormulaToken* token, FormulaToken*** pppToken, sal_uInt8 nNumParams)
+{
+ const OpCode nOpCode = token->GetOpCode();
+
+ if (nOpCode == ocPush)
+ {
+ if(token->GetType() == svDoubleRef)
+ mUnhandledPossibleImplicitIntersections.insert( token );
+ return true;
+ }
+ else if (nOpCode == ocSumIf || nOpCode == ocAverageIf)
{
if (nNumParams != 3)
- return;
+ return false;
if (!(pppToken[0] && pppToken[2] && *pppToken[0] && *pppToken[2]))
- return;
+ return false;
if ((*pppToken[0])->GetType() != svDoubleRef)
- return;
+ return false;
const StackVar eSumRangeType = (*pppToken[2])->GetType();
if ( eSumRangeType != svSingleRef && eSumRangeType != svDoubleRef )
- return;
+ return false;
const ScComplexRefData& rBaseRange = *(*pppToken[0])->GetDoubleRef();
@@ -5845,57 +5872,113 @@ void ScCompiler::HandleIIOpCode(OpCode nOpCode, formula::ParamClass eClass, Form
aSumRange = *(*pppToken[2])->GetDoubleRef();
CorrectSumRange(rBaseRange, aSumRange, pppToken[2]);
+ // TODO mark parameters as handled
+ return true;
}
else if (nOpCode >= SC_OPCODE_START_1_PAR && nOpCode < SC_OPCODE_STOP_1_PAR)
{
if (nNumParams != 1)
- return;
-
- // NOTE: eClass is the CurrentFactor token's GetInForceArray() state,
- // not the function's parameter classification.
- if (mbMatrixFlag ||
- eClass == formula::ForceArray ||
- eClass == formula::ReferenceOrForceArray ||
- eClass == formula::SuppressedReferenceOrForceArray)
- return;
+ return false;
- /* TODO: this assumes that there is no function taking a single
- * parameter that does evaluate a range reference. There currently
- * isn't any, but we should rather check that. */
+ if( !ParameterMayBeImplicitIntersection( token, 0 ))
+ return false;
+ if (SkipImplicitIntersectionOptimization(token))
+ return false;
if ((*pppToken[0])->GetType() != svDoubleRef)
- return;
+ return false;
- ReplaceDoubleRefII(pppToken[0]);
+ mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
+ return true;
}
- else if (nOpCode >= SC_OPCODE_START_BIN_OP && nOpCode < SC_OPCODE_STOP_BIN_OP)
+ else if ((nOpCode >= SC_OPCODE_START_BIN_OP && nOpCode < SC_OPCODE_STOP_BIN_OP)
+ || nOpCode == ocRound || nOpCode == ocRoundUp || nOpCode == ocRoundDown)
{
- assert(nOpCode != ocIntersect && nOpCode != ocUnion && nOpCode != ocRange);
-
if (nNumParams != 2)
- return;
+ return false;
- if (eClass == formula::ForceArray || mbMatrixFlag)
- return;
+ if( !ParameterMayBeImplicitIntersection( token, 0 ) || !ParameterMayBeImplicitIntersection( token, 1 ))
+ return false;
+ if (SkipImplicitIntersectionOptimization(token))
+ return false;
- // Convert only if the other parameter is not a matrix nor an array (which would force the result to be a matrix).
- if ((*pppToken[0])->GetType() == svDoubleRef && (*pppToken[1])->GetType() != svMatrix && (*pppToken[1])->IsInForceArray())
- ReplaceDoubleRefII(pppToken[0]);
- if ((*pppToken[1])->GetType() == svDoubleRef && (*pppToken[0])->GetType() != svMatrix && (*pppToken[0])->IsInForceArray())
- ReplaceDoubleRefII(pppToken[1]);
+ // Convert only if the other parameter is not a matrix (which would force the result to be a matrix).
+ if ((*pppToken[0])->GetType() == svDoubleRef && (*pppToken[1])->GetType() != svMatrix)
+ mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
+ if ((*pppToken[1])->GetType() == svDoubleRef && (*pppToken[0])->GetType() != svMatrix)
+ mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[1], token );
+ return true;
}
else if ((nOpCode >= SC_OPCODE_START_UN_OP && nOpCode < SC_OPCODE_STOP_UN_OP)
|| nOpCode == ocPercentSign)
{
if (nNumParams != 1)
- return;
+ return false;
- if (eClass == formula::ForceArray || mbMatrixFlag)
- return;
+ if( !ParameterMayBeImplicitIntersection( token, 0 ))
+ return false;
+ if (SkipImplicitIntersectionOptimization(token))
+ return false;
+
+ if ((*pppToken[0])->GetType() == svDoubleRef)
+ mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
+ return true;
+ }
+ else if (nOpCode == ocVLookup)
+ {
+ if (nNumParams != 3 && nNumParams != 4)
+ return false;
+ if (SkipImplicitIntersectionOptimization(token))
+ return false;
+
+ assert( ParameterMayBeImplicitIntersection( token, 0 ));
+ assert( !ParameterMayBeImplicitIntersection( token, 1 ));
+ assert( ParameterMayBeImplicitIntersection( token, 2 ));
+ assert( ParameterMayBeImplicitIntersection( token, 3 ));
+ if ((*pppToken[2])->GetType() == svDoubleRef)
+ mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[2], token );
if ((*pppToken[0])->GetType() == svDoubleRef)
- ReplaceDoubleRefII(pppToken[0]);
+ mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[0], token );
+ if (nNumParams == 4 && (*pppToken[3])->GetType() == svDoubleRef)
+ mPendingImplicitIntersectionOptimizations.emplace_back( pppToken[3], token );
+ // a range for the second parameters is not an implicit intersection
+ mUnhandledPossibleImplicitIntersections.erase( *pppToken[ 1 ] );
+ return true;
+ }
+ else
+ {
+ bool possibleII = false;
+ for( int i = 0; i < nNumParams; ++i )
+ {
+ if( ParameterMayBeImplicitIntersection( token, i ))
+ {
+ possibleII = true;
+ break;
+ }
+ }
+ if( !possibleII )
+ {
+ // all parameters have been handled, they are not implicit intersections
+ for( int i = 0; i < nNumParams; ++i )
+ mUnhandledPossibleImplicitIntersections.erase( *pppToken[ i ] );
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ScCompiler::PostProcessCode()
+{
+ for( const PendingImplicitIntersectionOptimization& item : mPendingImplicitIntersectionOptimizations )
+ {
+ // E.g. "SUMPRODUCT(I5:I6+1)" shouldn't do implicit intersection.
+ if( item.operation->IsInForceArray())
+ continue;
+ ReplaceDoubleRefII( item.parameter );
}
+ mPendingImplicitIntersectionOptimizations.clear();
}
void ScCompiler::ReplaceDoubleRefII(FormulaToken** ppDoubleRefTok)
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index f16b5339f266..760e0652f8f2 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -1599,11 +1599,10 @@ void ScTokenArray::CheckToken( const FormulaToken& r )
// Don't change the state.
break;
case svSingleRef:
+ case svDoubleRef:
// Depends on the reference state.
meVectorState = FormulaVectorCheckReference;
break;
- case svDoubleRef:
- // Does not work yet, see ScGroupTokenConverter::convert()
case svError:
case svEmptyCell:
case svExternal: