[Libreoffice-commits] core.git: 17 commits - include/sal sc/inc sc/Library_scfilt.mk sc/Library_sc.mk sc/qa sc/source

Tor Lillqvist tml at collabora.com
Wed Nov 20 10:24:11 PST 2013


 include/sal/log-areas.dox                   |    1 
 sc/Library_sc.mk                            |    2 
 sc/Library_scfilt.mk                        |    1 
 sc/inc/clkernelthread.hxx                   |   48 +++
 sc/inc/formulacell.hxx                      |   30 +-
 sc/inc/formulagroup.hxx                     |   19 +
 sc/inc/sharedformula.hxx                    |    2 
 sc/inc/types.hxx                            |    2 
 sc/qa/unit/filters-test.cxx                 |    4 
 sc/source/core/data/column3.cxx             |    5 
 sc/source/core/data/formulacell.cxx         |  355 +++++++---------------------
 sc/source/core/data/grouptokenconverter.cxx |  236 ++++++++++++++++++
 sc/source/core/inc/grouptokenconverter.hxx  |   38 ++
 sc/source/core/opencl/formulagroupcl.cxx    |  133 ++++++++--
 sc/source/core/tool/clkernelthread.cxx      |   98 +++++++
 sc/source/core/tool/formulagroup.cxx        |   15 -
 sc/source/core/tool/sharedformula.cxx       |   27 +-
 sc/source/filter/excel/excform.cxx          |    2 
 sc/source/filter/excel/impop.cxx            |    1 
 sc/source/filter/excel/namebuff.cxx         |    5 
 sc/source/filter/ftools/clkernelthread.cxx  |   26 --
 sc/source/filter/inc/clkernelthread.hxx     |   26 --
 sc/source/filter/oox/formulabuffer.cxx      |    6 
 23 files changed, 711 insertions(+), 371 deletions(-)

New commits:
commit b3ecd33b45d131e63bff287ae564c4225a946959
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Nov 20 18:10:56 2013 +0200

    DynamicKernel isn't needed outside formulagroupcl.cxx after all
    
    So put it back there.
    
    Change-Id: I7c78719c95123e33e4506ace912ff056df56b247

diff --git a/sc/source/core/inc/dynamickernel.hxx b/sc/source/core/inc/dynamickernel.hxx
deleted file mode 100644
index dd4d516..0000000
--- a/sc/source/core/inc/dynamickernel.hxx
+++ /dev/null
@@ -1,107 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * This file is part of the LibreOffice project.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-#ifndef INCLUDED_SC_SOURCE_CORE_INC_DYNAMICKERNEL_HXX
-#define INCLUDED_SC_SOURCE_CORE_INC_DYNAMICKERNEL_HXX
-
-#include <config_features.h>
-
-#include "formulagroup.hxx"
-
-#if !HAVE_FEATURE_OPENCL
-
-namespace sc { namespace opencl {
-
-class DynamikcKernel : public CompiledFormula
-{
-}
-
-} // namespace opencl
-
-} // namespace sc
-
-#else
-
-#include "clew.h"
-
-#include "document.hxx"
-#include "opbase.hxx"
-
-namespace sc { namespace opencl {
-
-class DynamicKernelArgument;
-class SlidingFunctionBase;
-
-/// Holds the symbol table for a given dynamic kernel
-class SymbolTable {
-public:
-    typedef std::map<const formula::FormulaToken *,
-        boost::shared_ptr<DynamicKernelArgument> > ArgumentMap;
-    // This avoids instability caused by using pointer as the key type
-    typedef std::list< boost::shared_ptr<DynamicKernelArgument> > ArgumentList;
-    SymbolTable(void):mCurId(0) {}
-    template <class T>
-    const DynamicKernelArgument *DeclRefArg(FormulaTreeNodeRef, SlidingFunctionBase* pCodeGen);
-    /// Used to generate sliding window helpers
-    void DumpSlidingWindowFunctions(std::stringstream &ss);
-    /// Memory mapping from host to device and pass buffers to the given kernel as
-    /// arguments
-    void Marshal(cl_kernel, int, cl_program);
-private:
-    unsigned int mCurId;
-    ArgumentMap mSymbols;
-    ArgumentList mParams;
-};
-
-class DynamicKernel : public CompiledFormula
-{
-public:
-    DynamicKernel(FormulaTreeNodeRef r):mpRoot(r),
-        mpProgram(NULL), mpKernel(NULL), mpResClmem(NULL), mpCode(NULL) {}
-    static DynamicKernel *create(ScDocument& rDoc,
-                                 const ScAddress& rTopPos,
-                                 ScTokenArray& rCode);
-    /// OpenCL code generation
-    void CodeGen();
-    /// Produce kernel hash
-    std::string GetMD5(void);
-    /// Create program, build, and create kerenl
-    /// TODO cache results based on kernel body hash
-    /// TODO: abstract OpenCL part out into OpenCL wrapper.
-    void CreateKernel(void);
-    /// Prepare buffers, marshal them to GPU, and launch the kernel
-    /// TODO: abstract OpenCL part out into OpenCL wrapper.
-    void Launch(size_t nr);
-    ~DynamicKernel();
-    cl_mem GetResultBuffer(void) const { return mpResClmem; }
-    void SetPCode(ScTokenArray *pCode) { mpCode = pCode; }
-
-private:
-    void TraverseAST(FormulaTreeNodeRef);
-    FormulaTreeNodeRef mpRoot;
-    SymbolTable mSyms;
-    std::string mKernelSignature, mKernelHash;
-    std::string mFullProgramSrc;
-    cl_program mpProgram;
-    cl_kernel mpKernel;
-    cl_mem mpResClmem; // Results
-    std::set<std::string> inlineDecl;
-    std::set<std::string> inlineFun;
-    ScTokenArray *mpCode;
-};
-
-}
-
-}
-
-#endif // HAVE_FEATURE_OPENCL
-
-#endif // INCLUDED_SC_SOURCE_CORE_INC_DYNAMICKERNEL_HXX
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/formulagroupcl.cxx b/sc/source/core/opencl/formulagroupcl.cxx
index 8ef18cf..5d14ddd 100644
--- a/sc/source/core/opencl/formulagroupcl.cxx
+++ b/sc/source/core/opencl/formulagroupcl.cxx
@@ -17,7 +17,6 @@
 #include "formula/vectortoken.hxx"
 #include "scmatrix.hxx"
 
-#include "dynamickernel.hxx"
 #include "openclwrapper.hxx"
 
 #include "op_financial.hxx"
@@ -2375,15 +2374,33 @@ DynamicKernelSoPArguments::DynamicKernelSoPArguments(
     }
 }
 
-/// Used to generate sliding window helpers
-void SymbolTable::DumpSlidingWindowFunctions(std::stringstream &ss)
-{
+/// Holds the symbol table for a given dynamic kernel
+class SymbolTable {
+public:
+    typedef std::map<const formula::FormulaToken *,
+        boost::shared_ptr<DynamicKernelArgument> > ArgumentMap;
+    // This avoids instability caused by using pointer as the key type
+    typedef std::list< boost::shared_ptr<DynamicKernelArgument> > ArgumentList;
+    SymbolTable(void):mCurId(0) {}
+    template <class T>
+    const DynamicKernelArgument *DeclRefArg(FormulaTreeNodeRef, SlidingFunctionBase* pCodeGen);
+    /// Used to generate sliding window helpers
+    void DumpSlidingWindowFunctions(std::stringstream &ss)
+    {
         for(ArgumentList::iterator it = mParams.begin(), e= mParams.end(); it!=e;
             ++it) {
             (*it)->GenSlidingWindowFunction(ss);
             ss << "\n";
         }
-}
+    }
+    /// Memory mapping from host to device and pass buffers to the given kernel as
+    /// arguments
+    void Marshal(cl_kernel, int, cl_program);
+private:
+    unsigned int mCurId;
+    ArgumentMap mSymbols;
+    ArgumentList mParams;
+};
 
 void SymbolTable::Marshal(cl_kernel k, int nVectorWidth, cl_program pProgram)
 {
@@ -2394,9 +2411,16 @@ void SymbolTable::Marshal(cl_kernel k, int nVectorWidth, cl_program pProgram)
     }
 }
 
-/// OpenCL code generation
-void DynamicKernel::CodeGen()
+class DynamicKernel : public CompiledFormula
 {
+public:
+    DynamicKernel(FormulaTreeNodeRef r):mpRoot(r),
+        mpProgram(NULL), mpKernel(NULL), mpResClmem(NULL), mpCode(NULL) {}
+    static DynamicKernel *create(ScDocument& rDoc,
+                                 const ScAddress& rTopPos,
+                                 ScTokenArray& rCode);
+    /// OpenCL code generation
+    void CodeGen() {
         // Travese the tree of expression and declare symbols used
         const DynamicKernelArgument *DK= mSyms.DeclRefArg<
             DynamicKernelSoPArguments>(mpRoot, new OpNop);
@@ -2432,11 +2456,10 @@ void DynamicKernel::CodeGen()
 #if 1
         std::cerr<< "Program to be compiled = \n" << mFullProgramSrc << "\n";
 #endif
-}
-
-/// Produce kernel hash
-std::string DynamicKernel::GetMD5(void)
-{
+    }
+    /// Produce kernel hash
+    std::string GetMD5(void)
+    {
 #ifdef MD5_KERNEL
         if (mKernelHash.empty()) {
             std::stringstream md5s;
@@ -2455,12 +2478,15 @@ std::string DynamicKernel::GetMD5(void)
 #else
         return "";
 #endif
-}
-
-/// Prepare buffers, marshal them to GPU, and launch the kernel
-/// TODO: abstract OpenCL part out into OpenCL wrapper.
-void DynamicKernel::Launch(size_t nr)
-{
+    }
+    /// Create program, build, and create kerenl
+    /// TODO cache results based on kernel body hash
+    /// TODO: abstract OpenCL part out into OpenCL wrapper.
+    void CreateKernel(void);
+    /// Prepare buffers, marshal them to GPU, and launch the kernel
+    /// TODO: abstract OpenCL part out into OpenCL wrapper.
+    void Launch(size_t nr)
+    {
         // Obtain cl context
         KernelEnv kEnv;
         OpenclDevice::setKernelEnv(&kEnv);
@@ -2481,7 +2507,24 @@ void DynamicKernel::Launch(size_t nr)
             global_work_size, NULL, 0, NULL, NULL);
         if (CL_SUCCESS != err)
             throw OpenCLError(err);
-}
+    }
+    ~DynamicKernel();
+    cl_mem GetResultBuffer(void) const { return mpResClmem; }
+    void SetPCode(ScTokenArray *pCode) { mpCode = pCode; }
+
+private:
+    void TraverseAST(FormulaTreeNodeRef);
+    FormulaTreeNodeRef mpRoot;
+    SymbolTable mSyms;
+    std::string mKernelSignature, mKernelHash;
+    std::string mFullProgramSrc;
+    cl_program mpProgram;
+    cl_kernel mpKernel;
+    cl_mem mpResClmem; // Results
+    std::set<std::string> inlineDecl;
+    std::set<std::string> inlineFun;
+    ScTokenArray *mpCode;
+};
 
 DynamicKernel::~DynamicKernel()
 {
diff --git a/sc/source/core/tool/formulagroup.cxx b/sc/source/core/tool/formulagroup.cxx
index 79d5de7..f3ec3e0 100644
--- a/sc/source/core/tool/formulagroup.cxx
+++ b/sc/source/core/tool/formulagroup.cxx
@@ -16,7 +16,6 @@
 #include "compiler.hxx"
 #include "interpre.hxx"
 #include "scmatrix.hxx"
-#include "dynamickernel.hxx"
 
 #include "formula/vectortoken.hxx"
 #include "rtl/bootstrap.hxx"
commit 0c7e7043bdeab5ad683e9621573441891ef4868b
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Nov 20 16:41:51 2013 +0200

    Fix a few of my own thinkos, and add some assertions
    
    Fix two obvious mistakes from 8c37bafbc5adc0c79f218f9db2dc25ca8e683125.
    
    Add asserts to make sure the calls to ScFormulaCell::CreateCellGroup() that
    now don't take the nStart parameter any more (but assume that the cell for
    which this methoid is called is the start one) match the old ones in semantics.
    
    Change-Id: I50cb0cdaf620253881225baf39ae9cd935917b27

diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index 141240f..7a7f0a7 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -2786,13 +2786,14 @@ public:
                 nRow += xCurGrp->mnLength;
                 std::advance(it, xCurGrp->mnLength);
                 pPrev->SetCellGroup(xCurGrp);
-                --xCurGrp->mpTopCell = pPrev;
+                xCurGrp->mpTopCell = pPrev;
                 ++xCurGrp->mnLength;
                 xPrevGrp = xCurGrp;
             }
             else
             {
                 // Both previous and current cells are regular cells.
+                assert(pPrev->aPos.Row() == (SCROW)(nRow - 1));
                 xPrevGrp = pPrev->CreateCellGroup(2, eCompState == ScFormulaCell::EqualInvariant);
                 pCur->SetCellGroup(xPrevGrp);
                 ++nRow;
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 9f6465f..6144c09 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -3329,8 +3329,12 @@ void            ScFormulaCell::SetNextTrack( ScFormulaCell* pF )       { pNextTr
 ScFormulaCellGroupRef ScFormulaCell::CreateCellGroup( SCROW nLen, bool bInvariant )
 {
     if (mxGroup)
+    {
         // You can't create a new group if the cell is already a part of a group.
+        // Is this a sign of some inconsistent or incorrect data structures? Or normal?
+        SAL_INFO("sc.opencl", "You can't create a new group if the cell is already a part of a group");
         return ScFormulaCellGroupRef();
+    }
 
     mxGroup.reset(new ScFormulaCellGroup);
     mxGroup->mpTopCell = this;
diff --git a/sc/source/core/tool/sharedformula.cxx b/sc/source/core/tool/sharedformula.cxx
index 20eef83..ed35690 100644
--- a/sc/source/core/tool/sharedformula.cxx
+++ b/sc/source/core/tool/sharedformula.cxx
@@ -151,6 +151,7 @@ void SharedFormulaUtil::joinFormulaCells(const CellStoreType::position_type& rPo
         else
         {
             // neither cells are shared.
+            assert(rCell1.aPos.Row() == (SCROW)(rPos.first->position + rPos.second));
             xGroup1 = rCell1.CreateCellGroup(2, eState == ScFormulaCell::EqualInvariant);
             rCell2.SetCellGroup(xGroup1);
         }
diff --git a/sc/source/filter/oox/formulabuffer.cxx b/sc/source/filter/oox/formulabuffer.cxx
index ca48eca..cb427f2 100644
--- a/sc/source/filter/oox/formulabuffer.cxx
+++ b/sc/source/filter/oox/formulabuffer.cxx
@@ -197,9 +197,11 @@ void applyCellFormulas(
                 ScFormulaCell& rPrev = *p->mpCell;
                 ScFormulaCellGroupRef xGroup = rPrev.GetCellGroup();
                 if (!xGroup)
+                {
                     // Last cell is not grouped yet. Start a new group.
-                    xGroup = rPrev.CreateCellGroup(p->mnRow, false);
-
+                    assert(rPrev.aPos.Row() == p->mnRow);
+                    xGroup = rPrev.CreateCellGroup(1, false);
+                }
                 ++xGroup->mnLength;
 
                 pCell = new ScFormulaCell(&rDoc.getDoc(), aPos, xGroup);
commit 4787398ff07fe1bfe34ea878e9c8ee96e5d9994f
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Nov 20 11:22:18 2013 +0200

    Bin comment that is now weirdly placed after merges
    
    Change-Id: I1cd92ae150be15c4b34249a8116c5f0a7b99f33f

diff --git a/sc/source/core/opencl/formulagroupcl.cxx b/sc/source/core/opencl/formulagroupcl.cxx
index cd1ca5e..8ef18cf 100644
--- a/sc/source/core/opencl/formulagroupcl.cxx
+++ b/sc/source/core/opencl/formulagroupcl.cxx
@@ -819,8 +819,6 @@ protected:
     cl_mem mpClmem2;
 };
 
-/// Abstract class for OpenCL source code generation
-
 class Reduction: public SlidingFunctionBase
 {
 public:
commit 440bd4526cbff522c1cac2aa376660848b58685f
Author: Tor Lillqvist <tml at collabora.com>
Date:   Tue Nov 19 16:18:39 2013 +0200

    Fix leftover wrongly named header in #include
    
    Change-Id: Id874d1b845dc2dc5265cb0aa4dccd2b8de57088e

diff --git a/sc/source/core/tool/clkernelthread.cxx b/sc/source/core/tool/clkernelthread.cxx
index f4debc6..e7bda8a 100644
--- a/sc/source/core/tool/clkernelthread.cxx
+++ b/sc/source/core/tool/clkernelthread.cxx
@@ -9,7 +9,7 @@
 
 #include <sal/log.hxx>
 
-#include "formulagroupinterpreter.hxx"
+#include "formulagroup.hxx"
 #include "grouptokenconverter.hxx"
 
 #include "clkernelthread.hxx"
commit 51a01d79ca88fe3eab6094338494e04a117831fa
Author: Tor Lillqvist <tml at collabora.com>
Date:   Mon Nov 18 20:27:48 2013 +0200

    Try to make the background OpenCL compilation conditional at run-time
    
    Change-Id: I2366465f4e786f905c32b17a15c16486c4c21d38

diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index f7b001b..9f6465f 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -18,6 +18,7 @@
  */
 
 #include "formulacell.hxx"
+#include "grouptokenconverter.hxx"
 
 #include "compiler.hxx"
 #include "document.hxx"
@@ -414,14 +415,18 @@ ScFormulaCellGroup::ScFormulaCellGroup() :
     mbSubTotal(false),
     meCalcState(sc::GroupCalcEnabled)
 {
-    if (ScInterpreter::GetGlobalConfig().mbOpenCLEnabled)
+    static bool bBackgroundCompilation = getenv("SC_BACKGROUND_COMPILATION") != NULL;
+    if (bBackgroundCompilation)
     {
-        osl::MutexGuard aGuard(getOpenCLCompilationThreadMutex());
-        if (mnCount++ == 0)
+        if (ScInterpreter::GetGlobalConfig().mbOpenCLEnabled)
         {
-            assert(!mxCLKernelThread.is());
-            mxCLKernelThread.set(new sc::CLBuildKernelThread);
-            mxCLKernelThread->launch();
+            osl::MutexGuard aGuard(getOpenCLCompilationThreadMutex());
+            if (mnCount++ == 0)
+            {
+                assert(!mxCLKernelThread.is());
+                mxCLKernelThread.set(new sc::CLBuildKernelThread);
+                mxCLKernelThread->launch();
+            }
         }
     }
 }
@@ -431,7 +436,7 @@ ScFormulaCellGroup::~ScFormulaCellGroup()
     if (ScInterpreter::GetGlobalConfig().mbOpenCLEnabled)
     {
         osl::MutexGuard aGuard(getOpenCLCompilationThreadMutex());
-        if (--mnCount == 0)
+        if (--mnCount == 0 && mxCLKernelThread.is())
             {
                 assert(mxCLKernelThread.is());
                 mxCLKernelThread->finish();
@@ -3477,17 +3482,35 @@ bool ScFormulaCell::InterpretFormulaGroup()
     if (mxGroup->mbInvariant && false)
         return InterpretInvariantFormulaGroup();
 
-    ScTokenArray aDummy;
     if (mxGroup->meCalcState == sc::GroupCalcEnabled)
+    {
+        ScTokenArray aCode;
+        ScAddress aTopPos = aPos;
+        aTopPos.SetRow(mxGroup->mpTopCell->aPos.Row());
+        ScGroupTokenConverter aConverter(aCode, *pDocument, *this, mxGroup->mpTopCell->aPos);
+        if (!aConverter.convert(*pCode))
+            {
+                mxGroup->meCalcState = sc::GroupCalcDisabled;
+                return false;
+            }
         mxGroup->meCalcState = sc::GroupCalcRunning;
-    if (!sc::FormulaGroupInterpreter::getStatic()->interpret(*pDocument, mxGroup->mpTopCell->aPos, mxGroup, aDummy))
+        if (!sc::FormulaGroupInterpreter::getStatic()->interpret(*pDocument, mxGroup->mpTopCell->aPos, mxGroup, aCode))
+        {
+            mxGroup->meCalcState = sc::GroupCalcDisabled;
+            return false;
+        }
+        mxGroup->meCalcState = sc::GroupCalcEnabled;
+    }
+    else
     {
-        mxGroup->meCalcState = sc::GroupCalcDisabled;
-        return false;
+        ScTokenArray aDummy;
+        if (!sc::FormulaGroupInterpreter::getStatic()->interpret(*pDocument, mxGroup->mpTopCell->aPos, mxGroup, aDummy))
+            {
+                mxGroup->meCalcState = sc::GroupCalcDisabled;
+                return false;
+            }
     }
 
-    if (mxGroup->meCalcState == sc::GroupCalcRunning)
-        mxGroup->meCalcState = sc::GroupCalcEnabled;
     return true;
 }
 
commit 94544378e5fcee7de5b481a9f06d7ef4a821d107
Author: Tor Lillqvist <tml at collabora.com>
Date:   Mon Nov 18 17:59:30 2013 +0200

    Fix some life-cycle issues for backgroup OpenCL kernel compilation
    
    Now it no longer asserts or crashes in light testing, but it doesn't
    properly recalculate either.
    
    Change-Id: I7dc117341eff5ac1b21d7400122cc6e04ae2baf7

diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 1fbe895..f7b001b 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -1769,7 +1769,7 @@ void ScFormulaCell::SetDirty( bool bDirtyFlag )
 void ScFormulaCell::SetDirtyVar()
 {
     bDirty = true;
-    if (mxGroup)
+    if (mxGroup && mxGroup->meCalcState == sc::GroupCalcRunning)
         mxGroup->meCalcState = sc::GroupCalcEnabled;
 
     // mark the sheet of this cell to be calculated
diff --git a/sc/source/core/inc/dynamickernel.hxx b/sc/source/core/inc/dynamickernel.hxx
index a390873..dd4d516 100644
--- a/sc/source/core/inc/dynamickernel.hxx
+++ b/sc/source/core/inc/dynamickernel.hxx
@@ -63,7 +63,7 @@ class DynamicKernel : public CompiledFormula
 {
 public:
     DynamicKernel(FormulaTreeNodeRef r):mpRoot(r),
-        mpProgram(NULL), mpKernel(NULL), mpResClmem(NULL) {}
+        mpProgram(NULL), mpKernel(NULL), mpResClmem(NULL), mpCode(NULL) {}
     static DynamicKernel *create(ScDocument& rDoc,
                                  const ScAddress& rTopPos,
                                  ScTokenArray& rCode);
@@ -80,6 +80,8 @@ public:
     void Launch(size_t nr);
     ~DynamicKernel();
     cl_mem GetResultBuffer(void) const { return mpResClmem; }
+    void SetPCode(ScTokenArray *pCode) { mpCode = pCode; }
+
 private:
     void TraverseAST(FormulaTreeNodeRef);
     FormulaTreeNodeRef mpRoot;
@@ -91,6 +93,7 @@ private:
     cl_mem mpResClmem; // Results
     std::set<std::string> inlineDecl;
     std::set<std::string> inlineFun;
+    ScTokenArray *mpCode;
 };
 
 }
diff --git a/sc/source/core/opencl/formulagroupcl.cxx b/sc/source/core/opencl/formulagroupcl.cxx
index 6b0f2b7..cd1ca5e 100644
--- a/sc/source/core/opencl/formulagroupcl.cxx
+++ b/sc/source/core/opencl/formulagroupcl.cxx
@@ -57,7 +57,6 @@ namespace sc { namespace opencl {
 size_t VectorRef::Marshal(cl_kernel k, int argno, int, cl_program)
 {
     FormulaToken *ref = mFormulaTree->GetFormulaToken();
-    assert(mpClmem == NULL);
     double *pHostBuffer = NULL;
     size_t szHostBuffer = 0;
     if (ref->GetType() == formula::svSingleVectorRef) {
@@ -311,7 +310,6 @@ public:
 size_t DynamicKernelStringArgument::Marshal(cl_kernel k, int argno, int, cl_program)
 {
     FormulaToken *ref = mFormulaTree->GetFormulaToken();
-    assert(mpClmem == NULL);
     // Obtain cl context
     KernelEnv kEnv;
     OpenclDevice::setKernelEnv(&kEnv);
@@ -2501,6 +2499,8 @@ DynamicKernel::~DynamicKernel()
         std::cerr<<"Freeing kernel "<< GetMD5() << " program\n";
         clReleaseProgram(mpProgram);
     }
+    if (mpCode)
+        delete mpCode;
 }
 /// Build code
 void DynamicKernel::CreateKernel(void)
@@ -2656,14 +2656,16 @@ CompiledFormula* FormulaGroupInterpreterOpenCL::createCompiledFormula(ScDocument
                                                                       ScFormulaCellGroupRef& xGroup,
                                                                       ScTokenArray& rCode)
 {
-    ScTokenArray aCode;
-    ScGroupTokenConverter aConverter(aCode, rDoc, *xGroup->mpTopCell, rTopPos);
+    ScTokenArray *pCode = new ScTokenArray();
+    ScGroupTokenConverter aConverter(*pCode, rDoc, *xGroup->mpTopCell, rTopPos);
     if (!aConverter.convert(rCode))
     {
         return NULL;
     }
 
-    return DynamicKernel::create(rDoc, rTopPos, aCode);
+    DynamicKernel *result = DynamicKernel::create(rDoc, rTopPos, *pCode);
+    result->SetPCode(pCode);
+    return result;
 }
 
 bool FormulaGroupInterpreterOpenCL::interpret( ScDocument& rDoc,
@@ -2691,6 +2693,7 @@ bool FormulaGroupInterpreterOpenCL::interpret( ScDocument& rDoc,
     }
     else
     {
+        assert(xGroup->meCalcState == sc::GroupCalcRunning);
         aGuard.clear();
         pKernel = static_cast<DynamicKernel*>(createCompiledFormula(rDoc, rTopPos, xGroup, rCode));
     }
@@ -2718,7 +2721,8 @@ bool FormulaGroupInterpreterOpenCL::interpret( ScDocument& rDoc,
         err = clEnqueueUnmapMemObject(kEnv.mpkCmdQueue, res, resbuf, 0, NULL, NULL);
         if (err != CL_SUCCESS)
             throw OpenCLError(err);
-        delete pKernel;
+        if (xGroup->meCalcState == sc::GroupCalcRunning)
+            delete pKernel;
     }
     catch (const UnhandledToken &ut) {
         std::cerr << "\nDynamic formual compiler: unhandled token: ";
commit 2f98f0ecf8964f5fce8e315fbff29760aa6449dd
Author: Tor Lillqvist <tml at collabora.com>
Date:   Thu Nov 14 19:30:36 2013 +0200

    WIP: Background ahead-of-time OpenCL compilation
    
    Work in progress, does not work. That's what WIP means.
    
    Change-Id: I31459624a45370384e00392937ac9a5b9cd893c2

diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk
index 3d57587..aa7099c 100644
--- a/sc/Library_sc.mk
+++ b/sc/Library_sc.mk
@@ -146,6 +146,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\
     sc/source/core/data/global \
     sc/source/core/data/global2 \
     sc/source/core/data/globalx \
+    sc/source/core/data/grouptokenconverter \
     sc/source/core/data/listenercontext \
     sc/source/core/data/markarr \
     sc/source/core/data/markdata \
diff --git a/sc/inc/formulagroup.hxx b/sc/inc/formulagroup.hxx
index faccbfb..9f8d09c 100644
--- a/sc/inc/formulagroup.hxx
+++ b/sc/inc/formulagroup.hxx
@@ -101,8 +101,9 @@ class SC_DLLPUBLIC FormulaGroupInterpreter
     virtual ScMatrixRef inverseMatrix(const ScMatrix& rMat) = 0;
     virtual CompiledFormula* createCompiledFormula(ScDocument& rDoc,
                                                    const ScAddress& rTopPos,
+                                                   ScFormulaCellGroupRef& xGroup,
                                                    ScTokenArray& rCode) = 0;
-    virtual bool interpret(ScDocument& rDoc, const ScAddress& rTopPos, const ScFormulaCellGroupRef& xGroup, ScTokenArray& rCode) = 0;
+    virtual bool interpret(ScDocument& rDoc, const ScAddress& rTopPos, ScFormulaCellGroupRef& xGroup, ScTokenArray& rCode) = 0;
 };
 
 /// Inherit from this for alternate formula group calculation approaches.
@@ -115,8 +116,9 @@ public:
     virtual ScMatrixRef inverseMatrix(const ScMatrix& rMat);
     virtual CompiledFormula* createCompiledFormula(ScDocument& rDoc,
                                                    const ScAddress& rTopPos,
+                                                   ScFormulaCellGroupRef& xGroup,
                                                    ScTokenArray& rCode) SAL_OVERRIDE;
-    virtual bool interpret(ScDocument& rDoc, const ScAddress& rTopPos, const ScFormulaCellGroupRef& xGroup, ScTokenArray& rCode);
+    virtual bool interpret(ScDocument& rDoc, const ScAddress& rTopPos, ScFormulaCellGroupRef& xGroup, ScTokenArray& rCode) SAL_OVERRIDE;
 };
 
 }
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 321c6af..1fbe895 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -3448,233 +3448,6 @@ ScFormulaCell::CompareState ScFormulaCell::CompareByTokenArray( ScFormulaCell& r
     return bInvariant ? EqualInvariant : EqualRelativeRef;
 }
 
-namespace {
-
-class GroupTokenConverter
-{
-    ScTokenArray& mrGroupTokens;
-    ScDocument& mrDoc;
-    ScFormulaCell& mrCell;
-    const ScAddress& mrPos;
-
-    bool isSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow)
-    {
-        if (rRefPos.Col() != mrPos.Col())
-            return false;
-
-        SCROW nLen = mrCell.GetCellGroup()->mnLength;
-        SCROW nEndRow = mrPos.Row() + nLen - 1;
-
-        if (nRelRow < 0)
-        {
-            SCROW nTest = nEndRow;
-            nTest += nRelRow;
-            if (nTest >= mrPos.Row())
-                return true;
-        }
-        else if (nRelRow > 0)
-        {
-            SCROW nTest = mrPos.Row(); // top row.
-            nTest += nRelRow;
-            if (nTest <= nEndRow)
-                return true;
-        }
-
-        return false;
-    }
-
-    bool isSelfReferenceAbsolute(const ScAddress& rRefPos)
-    {
-        if (rRefPos.Col() != mrPos.Col())
-            return false;
-
-        SCROW nLen = mrCell.GetCellGroup()->mnLength;
-        SCROW nEndRow = mrPos.Row() + nLen - 1;
-
-        if (rRefPos.Row() < mrPos.Row())
-            return false;
-
-        if (rRefPos.Row() > nEndRow)
-            return false;
-
-        return true;
-    }
-
-    SCROW trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen)
-    {
-        SCROW nLastRow = nRow + nRowLen - 1; // current last row.
-        nLastRow = mrDoc.GetLastDataRow(nTab, nCol1, nCol2, nLastRow);
-        if (nLastRow < (nRow + nRowLen - 1))
-            nRowLen = nLastRow - nRow + 1;
-        else if (nLastRow == 0)
-            // Column is empty.
-            nRowLen = 1;
-
-        return nRowLen;
-    }
-public:
-    GroupTokenConverter(ScTokenArray& rGroupTokens, ScDocument& rDoc, ScFormulaCell& rCell, const ScAddress& rPos) :
-        mrGroupTokens(rGroupTokens), mrDoc(rDoc), mrCell(rCell), mrPos(rPos) {}
-
-    bool convert(ScTokenArray& rCode)
-    {
-#if 0
-        { // debug to start with:
-            ScCompiler aComp( &mrDoc, mrPos, rCode);
-            aComp.SetGrammar(formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1);
-            OUStringBuffer aAsString;
-            aComp.CreateStringFromTokenArray(aAsString);
-        }
-#endif
-
-        rCode.Reset();
-        for (const formula::FormulaToken* p = rCode.First(); p; p = rCode.Next())
-        {
-            // A reference can be either absolute or relative.  If it's absolute,
-            // convert it to a static value token.  If relative, convert it to a
-            // vector reference token.  Note: we only care about relative vs
-            // absolute reference state for row directions.
-
-            const ScToken* pToken = static_cast<const ScToken*>(p);
-            SCROW nLen = mrCell.GetCellGroup()->mnLength;
-            switch (pToken->GetType())
-            {
-                case svSingleRef:
-                {
-                    ScSingleRefData aRef = pToken->GetSingleRef();
-                    ScAddress aRefPos = aRef.toAbs(mrPos);
-                    if (aRef.IsRowRel())
-                    {
-                        if (isSelfReferenceRelative(aRefPos, aRef.Row()))
-                            return false;
-
-                        // Trim data array length to actual data range.
-                        nLen = trimLength(aRefPos.Tab(), aRefPos.Col(), aRefPos.Col(), aRefPos.Row(), nLen);
-
-                        // Fetch double array guarantees that the length of the
-                        // returned array equals or greater than the requested
-                        // length.
-
-                        formula::VectorRefArray aArray = mrDoc.FetchVectorRefArray(aRefPos, nLen);
-                        if (!aArray.isValid())
-                            return false;
-
-                        formula::SingleVectorRefToken aTok(aArray, nLen);
-                        mrGroupTokens.AddToken(aTok);
-                    }
-                    else
-                    {
-                        // Absolute row reference.
-                        if (isSelfReferenceAbsolute(aRefPos))
-                            return false;
-
-                        formula::FormulaTokenRef pNewToken = mrDoc.ResolveStaticReference(aRefPos);
-                        if (!pNewToken)
-                            return false;
-
-                        mrGroupTokens.AddToken(*pNewToken);
-                    }
-                }
-                break;
-                case svDoubleRef:
-                {
-                    ScComplexRefData aRef = pToken->GetDoubleRef();
-                    ScRange aAbs = aRef.toAbs(mrPos);
-
-                    // Check for self reference.
-                    if (aRef.Ref1.IsRowRel())
-                    {
-                        if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row()))
-                            return false;
-                    }
-                    else if (isSelfReferenceAbsolute(aAbs.aStart))
-                        return false;
-
-                    if (aRef.Ref2.IsRowRel())
-                    {
-                        if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row()))
-                            return false;
-                    }
-                    else if (isSelfReferenceAbsolute(aAbs.aEnd))
-                        return false;
-
-                    // Row reference is relative.
-                    bool bAbsFirst = !aRef.Ref1.IsRowRel();
-                    bool bAbsLast = !aRef.Ref2.IsRowRel();
-                    ScAddress aRefPos = aAbs.aStart;
-                    size_t nCols = aAbs.aEnd.Col() - aAbs.aStart.Col() + 1;
-                    std::vector<formula::VectorRefArray> aArrays;
-                    aArrays.reserve(nCols);
-                    SCROW nRefRowSize = aAbs.aEnd.Row() - aAbs.aStart.Row() + 1;
-                    SCROW nArrayLength = nRefRowSize;
-                    if (!bAbsLast)
-                    {
-                        // range end position is relative. Extend the array length.
-                        SCROW nLastRefRowOffset = aAbs.aEnd.Row() - mrPos.Row();
-                        SCROW nLastRefRow = mrPos.Row() + nLen - 1 + nLastRefRowOffset;
-                        SCROW nNewLength = nLastRefRow - aAbs.aStart.Row() + 1;
-                        if (nNewLength > nArrayLength)
-                            nArrayLength = nNewLength;
-                    }
-
-                    // Trim trailing empty rows.
-                    nArrayLength = trimLength(aRefPos.Tab(), aAbs.aStart.Col(), aAbs.aEnd.Col(), aRefPos.Row(), nArrayLength);
-
-                    for (SCCOL i = aAbs.aStart.Col(); i <= aAbs.aEnd.Col(); ++i)
-                    {
-                        aRefPos.SetCol(i);
-                        formula::VectorRefArray aArray = mrDoc.FetchVectorRefArray(aRefPos, nArrayLength);
-                        if (!aArray.isValid())
-                            return false;
-
-                        aArrays.push_back(aArray);
-                    }
-
-                    formula::DoubleVectorRefToken aTok(aArrays, nArrayLength, nRefRowSize, bAbsFirst, bAbsLast);
-                    mrGroupTokens.AddToken(aTok);
-                }
-                break;
-                case svIndex:
-                {
-                    // Named range.
-                    ScRangeName* pNames = mrDoc.GetRangeName();
-                    if (!pNames)
-                        // This should never fail.
-                        return false;
-
-                    ScRangeData* pRange = pNames->findByIndex(p->GetIndex());
-                    if (!pRange)
-                        // No named range exists by that index.
-                        return false;
-
-                    ScTokenArray* pNamedTokens = pRange->GetCode();
-                    if (!pNamedTokens)
-                        // This named range is empty.
-                        return false;
-
-                    mrGroupTokens.AddOpCode(ocOpen);
-
-                    if (!convert(*pNamedTokens))
-                        return false;
-
-                    mrGroupTokens.AddOpCode(ocClose);
-                }
-                break;
-                default:
-                    mrGroupTokens.AddToken(*pToken);
-            }
-        }
-
-        ScCompiler aComp(&mrDoc, mrPos, mrGroupTokens);
-        aComp.SetGrammar(mrDoc.GetGrammar());
-        aComp.CompileTokenArray(); // Regenerate RPN tokens.
-
-        return true;
-    }
-};
-
-}
-
 bool ScFormulaCell::InterpretFormulaGroup()
 {
     if (!ScInterpreter::GetGlobalConfig().mbOpenCLEnabled)
@@ -3704,23 +3477,17 @@ bool ScFormulaCell::InterpretFormulaGroup()
     if (mxGroup->mbInvariant && false)
         return InterpretInvariantFormulaGroup();
 
-    ScTokenArray aCode;
-    ScAddress aTopPos = mxGroup->mpTopCell->aPos;
-    GroupTokenConverter aConverter(aCode, *pDocument, *this, aTopPos);
-    if (!aConverter.convert(*pCode))
+    ScTokenArray aDummy;
+    if (mxGroup->meCalcState == sc::GroupCalcEnabled)
+        mxGroup->meCalcState = sc::GroupCalcRunning;
+    if (!sc::FormulaGroupInterpreter::getStatic()->interpret(*pDocument, mxGroup->mpTopCell->aPos, mxGroup, aDummy))
     {
         mxGroup->meCalcState = sc::GroupCalcDisabled;
         return false;
     }
 
-    mxGroup->meCalcState = sc::GroupCalcRunning;
-    if (!sc::FormulaGroupInterpreter::getStatic()->interpret(*pDocument, aTopPos, mxGroup, aCode))
-    {
-        mxGroup->meCalcState = sc::GroupCalcDisabled;
-        return false;
-    }
-
-    mxGroup->meCalcState = sc::GroupCalcEnabled;
+    if (mxGroup->meCalcState == sc::GroupCalcRunning)
+        mxGroup->meCalcState = sc::GroupCalcEnabled;
     return true;
 }
 
diff --git a/sc/source/core/data/grouptokenconverter.cxx b/sc/source/core/data/grouptokenconverter.cxx
new file mode 100644
index 0000000..55b2709
--- /dev/null
+++ b/sc/source/core/data/grouptokenconverter.cxx
@@ -0,0 +1,236 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <formula/token.hxx>
+#include <formula/vectortoken.hxx>
+
+#include "compiler.hxx"
+#include "grouptokenconverter.hxx"
+
+using namespace formula;
+
+bool ScGroupTokenConverter::isSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow)
+{
+    if (rRefPos.Col() != mrPos.Col())
+        return false;
+
+    SCROW nLen = mrCell.GetCellGroup()->mnLength;
+    SCROW nEndRow = mrPos.Row() + nLen - 1;
+
+    if (nRelRow < 0)
+    {
+        SCROW nTest = nEndRow;
+        nTest += nRelRow;
+        if (nTest >= mrPos.Row())
+            return true;
+    }
+    else if (nRelRow > 0)
+    {
+        SCROW nTest = mrPos.Row(); // top row.
+        nTest += nRelRow;
+        if (nTest <= nEndRow)
+            return true;
+    }
+
+    return false;
+}
+
+bool ScGroupTokenConverter::isSelfReferenceAbsolute(const ScAddress& rRefPos)
+{
+    if (rRefPos.Col() != mrPos.Col())
+        return false;
+
+    SCROW nLen = mrCell.GetCellGroup()->mnLength;
+    SCROW nEndRow = mrPos.Row() + nLen - 1;
+
+    if (rRefPos.Row() < mrPos.Row())
+        return false;
+
+    if (rRefPos.Row() > nEndRow)
+        return false;
+
+    return true;
+}
+
+SCROW ScGroupTokenConverter::trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen)
+{
+    SCROW nLastRow = nRow + nRowLen - 1; // current last row.
+    nLastRow = mrDoc.GetLastDataRow(nTab, nCol1, nCol2, nLastRow);
+    if (nLastRow < (nRow + nRowLen - 1))
+        nRowLen = nLastRow - nRow + 1;
+    else if (nLastRow == 0)
+        // Column is empty.
+        nRowLen = 1;
+
+    return nRowLen;
+}
+
+ScGroupTokenConverter::ScGroupTokenConverter(ScTokenArray& rGroupTokens, ScDocument& rDoc, ScFormulaCell& rCell, const ScAddress& rPos) :
+        mrGroupTokens(rGroupTokens), mrDoc(rDoc), mrCell(rCell), mrPos(rPos)
+
+{
+}
+
+bool ScGroupTokenConverter::convert(ScTokenArray& rCode)
+{
+#if 0
+    { // debug to start with:
+        ScCompiler aComp( &mrDoc, mrPos, rCode);
+        aComp.SetGrammar(formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1);
+        OUStringBuffer aAsString;
+        aComp.CreateStringFromTokenArray(aAsString);
+    }
+#endif
+
+    rCode.Reset();
+    for (const formula::FormulaToken* p = rCode.First(); p; p = rCode.Next())
+    {
+        // A reference can be either absolute or relative.  If it's absolute,
+        // convert it to a static value token.  If relative, convert it to a
+        // vector reference token.  Note: we only care about relative vs
+        // absolute reference state for row directions.
+
+        const ScToken* pToken = static_cast<const ScToken*>(p);
+        SCROW nLen = mrCell.GetCellGroup()->mnLength;
+        switch (pToken->GetType())
+        {
+            case svSingleRef:
+            {
+                ScSingleRefData aRef = pToken->GetSingleRef();
+                ScAddress aRefPos = aRef.toAbs(mrPos);
+                if (aRef.IsRowRel())
+                {
+                    if (isSelfReferenceRelative(aRefPos, aRef.Row()))
+                        return false;
+
+                    // Trim data array length to actual data range.
+                    nLen = trimLength(aRefPos.Tab(), aRefPos.Col(), aRefPos.Col(), aRefPos.Row(), nLen);
+
+                    // Fetch double array guarantees that the length of the
+                    // returned array equals or greater than the requested
+                    // length.
+
+                    formula::VectorRefArray aArray = mrDoc.FetchVectorRefArray(aRefPos, nLen);
+                    if (!aArray.isValid())
+                        return false;
+
+                    formula::SingleVectorRefToken aTok(aArray, nLen);
+                    mrGroupTokens.AddToken(aTok);
+                }
+                else
+                {
+                    // Absolute row reference.
+                    if (isSelfReferenceAbsolute(aRefPos))
+                        return false;
+
+                    formula::FormulaTokenRef pNewToken = mrDoc.ResolveStaticReference(aRefPos);
+                    if (!pNewToken)
+                        return false;
+
+                    mrGroupTokens.AddToken(*pNewToken);
+                }
+            }
+            break;
+            case svDoubleRef:
+            {
+                ScComplexRefData aRef = pToken->GetDoubleRef();
+                ScRange aAbs = aRef.toAbs(mrPos);
+
+                // Check for self reference.
+                if (aRef.Ref1.IsRowRel())
+                {
+                    if (isSelfReferenceRelative(aAbs.aStart, aRef.Ref1.Row()))
+                        return false;
+                }
+                else if (isSelfReferenceAbsolute(aAbs.aStart))
+                    return false;
+
+                if (aRef.Ref2.IsRowRel())
+                {
+                    if (isSelfReferenceRelative(aAbs.aEnd, aRef.Ref2.Row()))
+                        return false;
+                }
+                else if (isSelfReferenceAbsolute(aAbs.aEnd))
+                    return false;
+
+                // Row reference is relative.
+                bool bAbsFirst = !aRef.Ref1.IsRowRel();
+                bool bAbsLast = !aRef.Ref2.IsRowRel();
+                ScAddress aRefPos = aAbs.aStart;
+                size_t nCols = aAbs.aEnd.Col() - aAbs.aStart.Col() + 1;
+                std::vector<formula::VectorRefArray> aArrays;
+                aArrays.reserve(nCols);
+                SCROW nRefRowSize = aAbs.aEnd.Row() - aAbs.aStart.Row() + 1;
+                SCROW nArrayLength = nRefRowSize;
+                if (!bAbsLast)
+                {
+                    // range end position is relative. Extend the array length.
+                    SCROW nLastRefRowOffset = aAbs.aEnd.Row() - mrPos.Row();
+                    SCROW nLastRefRow = mrPos.Row() + nLen - 1 + nLastRefRowOffset;
+                    SCROW nNewLength = nLastRefRow - aAbs.aStart.Row() + 1;
+                    if (nNewLength > nArrayLength)
+                        nArrayLength = nNewLength;
+                }
+
+                // Trim trailing empty rows.
+                nArrayLength = trimLength(aRefPos.Tab(), aAbs.aStart.Col(), aAbs.aEnd.Col(), aRefPos.Row(), nArrayLength);
+
+                for (SCCOL i = aAbs.aStart.Col(); i <= aAbs.aEnd.Col(); ++i)
+                {
+                    aRefPos.SetCol(i);
+                    formula::VectorRefArray aArray = mrDoc.FetchVectorRefArray(aRefPos, nArrayLength);
+                    if (!aArray.isValid())
+                        return false;
+
+                    aArrays.push_back(aArray);
+                }
+
+                formula::DoubleVectorRefToken aTok(aArrays, nArrayLength, nRefRowSize, bAbsFirst, bAbsLast);
+                mrGroupTokens.AddToken(aTok);
+            }
+            break;
+            case svIndex:
+            {
+                // Named range.
+                ScRangeName* pNames = mrDoc.GetRangeName();
+                if (!pNames)
+                    // This should never fail.
+                    return false;
+
+                ScRangeData* pRange = pNames->findByIndex(p->GetIndex());
+                if (!pRange)
+                    // No named range exists by that index.
+                    return false;
+
+                ScTokenArray* pNamedTokens = pRange->GetCode();
+                if (!pNamedTokens)
+                    // This named range is empty.
+                    return false;
+
+                mrGroupTokens.AddOpCode(ocOpen);
+
+                if (!convert(*pNamedTokens))
+                    return false;
+
+                mrGroupTokens.AddOpCode(ocClose);
+            }
+            break;
+            default:
+                mrGroupTokens.AddToken(*pToken);
+        }
+    }
+
+    ScCompiler aComp(&mrDoc, mrPos, mrGroupTokens);
+    aComp.SetGrammar(mrDoc.GetGrammar());
+    aComp.CompileTokenArray(); // Regenerate RPN tokens.
+
+    return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/inc/grouptokenconverter.hxx b/sc/source/core/inc/grouptokenconverter.hxx
new file mode 100644
index 0000000..9685681
--- /dev/null
+++ b/sc/source/core/inc/grouptokenconverter.hxx
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SC_SOURCE_CORE_INC_GROUPTOKENCONVERTER_HXX
+#define INCLUDED_SC_SOURCE_CORE_INC_GROUPTOKENCONVERTER_HXX
+
+#include "document.hxx"
+#include "formulacell.hxx"
+#include "scdllapi.h"
+#include "tokenarray.hxx"
+#include "types.hxx"
+
+class SC_DLLPUBLIC ScGroupTokenConverter
+{
+    ScTokenArray& mrGroupTokens;
+    ScDocument& mrDoc;
+    ScFormulaCell& mrCell;
+    const ScAddress& mrPos;
+
+    bool isSelfReferenceRelative(const ScAddress& rRefPos, SCROW nRelRow);
+    bool isSelfReferenceAbsolute(const ScAddress& rRefPos);
+    SCROW trimLength(SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nRow, SCROW nRowLen);
+
+public:
+    ScGroupTokenConverter(ScTokenArray& rGroupTokens, ScDocument& rDoc, ScFormulaCell& rCell, const ScAddress& rPos);
+
+    bool convert(ScTokenArray& rCode);
+};
+
+#endif // INCLUDED_SC_SOURCE_CORE_INC_GROUPTOKENCONVERTER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/formulagroupcl.cxx b/sc/source/core/opencl/formulagroupcl.cxx
index d4862ea..6b0f2b7 100644
--- a/sc/source/core/opencl/formulagroupcl.cxx
+++ b/sc/source/core/opencl/formulagroupcl.cxx
@@ -8,6 +8,7 @@
  */
 
 #include "formulagroup.hxx"
+#include "grouptokenconverter.hxx"
 #include "document.hxx"
 #include "formulacell.hxx"
 #include "tokenarray.hxx"
@@ -2568,12 +2569,13 @@ public:
     {
     }
 
-    virtual ScMatrixRef inverseMatrix( const ScMatrix& rMat );
+    virtual ScMatrixRef inverseMatrix( const ScMatrix& rMat ) SAL_OVERRIDE;
     virtual CompiledFormula* createCompiledFormula(ScDocument& rDoc,
                                                    const ScAddress& rTopPos,
-                                                   ScTokenArray& rCode);
+                                                   ScFormulaCellGroupRef& xGroup,
+                                                   ScTokenArray& rCode) SAL_OVERRIDE;
     virtual bool interpret( ScDocument& rDoc, const ScAddress& rTopPos,
-                const ScFormulaCellGroupRef& xGroup, ScTokenArray& rCode );
+                            ScFormulaCellGroupRef& xGroup, ScTokenArray& rCode ) SAL_OVERRIDE;
 };
 
 ScMatrixRef FormulaGroupInterpreterOpenCL::inverseMatrix( const ScMatrix& )
@@ -2631,9 +2633,10 @@ DynamicKernel* DynamicKernel::create(ScDocument& /* rDoc */,
     if (!pDynamicKernel)
         return NULL;
 
-    // OpenCL source code generation
+    // OpenCL source code generation and kernel compilation
     try {
         pDynamicKernel->CodeGen();
+        pDynamicKernel->CreateKernel();
     }
     catch (const UnhandledToken &ut) {
         std::cerr << "\nDynamic formual compiler: unhandled token: ";
@@ -2650,29 +2653,46 @@ DynamicKernel* DynamicKernel::create(ScDocument& /* rDoc */,
 
 CompiledFormula* FormulaGroupInterpreterOpenCL::createCompiledFormula(ScDocument& rDoc,
                                                                       const ScAddress& rTopPos,
+                                                                      ScFormulaCellGroupRef& xGroup,
                                                                       ScTokenArray& rCode)
 {
-    return DynamicKernel::create(rDoc, rTopPos, rCode);
+    ScTokenArray aCode;
+    ScGroupTokenConverter aConverter(aCode, rDoc, *xGroup->mpTopCell, rTopPos);
+    if (!aConverter.convert(rCode))
+    {
+        return NULL;
+    }
+
+    return DynamicKernel::create(rDoc, rTopPos, aCode);
 }
 
 bool FormulaGroupInterpreterOpenCL::interpret( ScDocument& rDoc,
-    const ScAddress& rTopPos, const ScFormulaCellGroupRef& xGroup,
+    const ScAddress& rTopPos, ScFormulaCellGroupRef& xGroup,
     ScTokenArray& rCode )
 {
     DynamicKernel *pKernel;
 
     osl::ResettableMutexGuard aGuard(xGroup->maMutex);
-    if (xGroup->meCalcState == sc::GroupCalcOpenCLKernelCompilationScheduled)
+    if (xGroup->meCalcState == sc::GroupCalcOpenCLKernelCompilationScheduled ||
+        xGroup->meCalcState == sc::GroupCalcOpenCLKernelBinaryCreated)
     {
-        aGuard.clear();
-        xGroup->maCompilationDone.wait();
-        xGroup->maCompilationDone.reset();
+        if (xGroup->meCalcState == sc::GroupCalcOpenCLKernelCompilationScheduled)
+        {
+            aGuard.clear();
+            xGroup->maCompilationDone.wait();
+            xGroup->maCompilationDone.reset();
+        }
+        else
+        {
+            aGuard.clear();
+        }
+
         pKernel = static_cast<DynamicKernel*>(xGroup->mpCompiledFormula);
     }
     else
     {
         aGuard.clear();
-        pKernel = DynamicKernel::create(rDoc, rTopPos, rCode);
+        pKernel = static_cast<DynamicKernel*>(createCompiledFormula(rDoc, rTopPos, xGroup, rCode));
     }
 
     if (!pKernel)
@@ -2682,8 +2702,6 @@ bool FormulaGroupInterpreterOpenCL::interpret( ScDocument& rDoc,
         // Obtain cl context
         KernelEnv kEnv;
         OpenclDevice::setKernelEnv(&kEnv);
-        // Compile kernel here!!!
-        pKernel->CreateKernel();
         // Run the kernel.
         pKernel->Launch(xGroup->mnLength);
         // Map results back
diff --git a/sc/source/core/tool/clkernelthread.cxx b/sc/source/core/tool/clkernelthread.cxx
index 7404a80..f4debc6 100644
--- a/sc/source/core/tool/clkernelthread.cxx
+++ b/sc/source/core/tool/clkernelthread.cxx
@@ -10,6 +10,7 @@
 #include <sal/log.hxx>
 
 #include "formulagroupinterpreter.hxx"
+#include "grouptokenconverter.hxx"
 
 #include "clkernelthread.hxx"
 
@@ -52,8 +53,10 @@ void CLBuildKernelThread::execute()
                 aWorkItem.mxGroup->mpCompiledFormula =
                     sc::FormulaGroupInterpreter::getStatic()->createCompiledFormula(*aWorkItem.mxGroup->mpTopCell->GetDocument(),
                                                                                     aWorkItem.mxGroup->mpTopCell->aPos,
+                                                                                    aWorkItem.mxGroup,
                                                                                     *aWorkItem.mxGroup->mpCode);
                 aWorkItem.mxGroup->meCalcState = sc::GroupCalcOpenCLKernelBinaryCreated;
+                SAL_INFO("sc.opencl.thread", "group " << aWorkItem.mxGroup << " compilation done");
                 aWorkItem.mxGroup->maCompilationDone.set();
                 break;
             case CLBuildKernelWorkItem::FINISH:
diff --git a/sc/source/core/tool/formulagroup.cxx b/sc/source/core/tool/formulagroup.cxx
index a315585..79d5de7 100644
--- a/sc/source/core/tool/formulagroup.cxx
+++ b/sc/source/core/tool/formulagroup.cxx
@@ -287,13 +287,14 @@ ScMatrixRef FormulaGroupInterpreterSoftware::inverseMatrix(const ScMatrix& /*rMa
 
 CompiledFormula* FormulaGroupInterpreterSoftware::createCompiledFormula(ScDocument& /* rDoc */,
                                                                         const ScAddress& /* rTopPos */,
+                                                                        ScFormulaCellGroupRef& /* xGroup */,
                                                                         ScTokenArray& /* rCode */)
 {
     return NULL;
 }
 
 bool FormulaGroupInterpreterSoftware::interpret(ScDocument& rDoc, const ScAddress& rTopPos,
-                                                const ScFormulaCellGroupRef& xGroup,
+                                                ScFormulaCellGroupRef& xGroup,
                                                 ScTokenArray& rCode)
 {
     typedef boost::unordered_map<const formula::FormulaToken*, formula::FormulaTokenRef> CachedTokensType;
@@ -494,8 +495,8 @@ public:
     FormulaGroupInterpreterOpenCLMissing() : FormulaGroupInterpreter() {}
     virtual ~FormulaGroupInterpreterOpenCLMissing() {}
     virtual ScMatrixRef inverseMatrix(const ScMatrix&) { return ScMatrixRef(); }
-    virtual CompiledFormula* createCompiledFormula(ScDocument&, const ScAddress&, ScTokenArray&) SAL_OVERRIDE { return NULL; }
-    virtual bool interpret(ScDocument&, const ScAddress&, const ScFormulaCellGroupRef&, ScTokenArray&) { return false; }
+    virtual CompiledFormula* createCompiledFormula(ScDocument&, const ScAddress&, ScFormulaCellGroupRef&, ScTokenArray&) SAL_OVERRIDE { return NULL; }
+    virtual bool interpret(ScDocument&, const ScAddress&, ScFormulaCellGroupRef&,  ScTokenArray&) { return false; }
 };
 
 static void SAL_CALL thisModule() {}
commit 25fb3d749c3184b96c79352be4990c9b4fa6c602
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Nov 13 18:06:32 2013 +0200

    WIP: Background ahead-of-time OpenCL compilation
    
    Change-Id: I6e9906fb68a22eb0adab753726ec0d62dd05fe9b

diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx
index 68648fc..656eebb 100644
--- a/sc/inc/formulacell.hxx
+++ b/sc/inc/formulacell.hxx
@@ -20,15 +20,19 @@
 #ifndef SC_FORMULACELL_HXX
 #define SC_FORMULACELL_HXX
 
-#include "formularesult.hxx"
+#include <set>
+
+#include <boost/noncopyable.hpp>
 
-#include "formula/tokenarray.hxx"
+#include <formula/tokenarray.hxx>
+#include <osl/conditn.hxx>
+#include <osl/mutex.hxx>
 #include <rtl/ref.hxx>
-#include "svl/listener.hxx"
+#include <svl/listener.hxx>
+
 #include "types.hxx"
 
-#include <set>
-#include <boost/noncopyable.hpp>
+#include "formularesult.hxx"
 
 namespace sc {
 
@@ -54,6 +58,7 @@ struct SC_DLLPUBLIC ScFormulaCellGroup : boost::noncopyable
 
     ScTokenArray* mpCode;
     osl::Mutex maMutex;
+    osl::Condition maCompilationDone;
     sc::CompiledFormula* mpCompiledFormula;
     ScFormulaCell *mpTopCell;
     SCROW mnLength; // How many of these do we have ?
diff --git a/sc/inc/types.hxx b/sc/inc/types.hxx
index 1704341..fc0e0e8 100644
--- a/sc/inc/types.hxx
+++ b/sc/inc/types.hxx
@@ -59,6 +59,7 @@ const sal_uInt16 MatrixEdgeOpen    = 32;
 enum GroupCalcState
 {
     GroupCalcEnabled,
+    GroupCalcOpenCLKernelCompilationScheduled,
     GroupCalcOpenCLKernelBinaryCreated,
     GroupCalcRunning,
     GroupCalcDisabled
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index a864b1d..321c6af 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -445,9 +445,12 @@ ScFormulaCellGroup::~ScFormulaCellGroup()
 
 void ScFormulaCellGroup::scheduleCompilation()
 {
+    osl::ResettableMutexGuard aGuard(maMutex);
+    meCalcState = sc::GroupCalcOpenCLKernelCompilationScheduled;
     sc::CLBuildKernelWorkItem aWorkItem;
     aWorkItem.meWhatToDo = sc::CLBuildKernelWorkItem::COMPILE;
     aWorkItem.mxGroup = this;
+    aGuard.clear();
     mxCLKernelThread->push(aWorkItem);
 }
 
diff --git a/sc/source/core/opencl/formulagroupcl.cxx b/sc/source/core/opencl/formulagroupcl.cxx
index bf3c1d3..d4862ea 100644
--- a/sc/source/core/opencl/formulagroupcl.cxx
+++ b/sc/source/core/opencl/formulagroupcl.cxx
@@ -2659,8 +2659,22 @@ bool FormulaGroupInterpreterOpenCL::interpret( ScDocument& rDoc,
     const ScAddress& rTopPos, const ScFormulaCellGroupRef& xGroup,
     ScTokenArray& rCode )
 {
-    // printf("Vector width = %d\n", xGroup->mnLength);
-    DynamicKernel *pKernel = DynamicKernel::create(rDoc, rTopPos, rCode);
+    DynamicKernel *pKernel;
+
+    osl::ResettableMutexGuard aGuard(xGroup->maMutex);
+    if (xGroup->meCalcState == sc::GroupCalcOpenCLKernelCompilationScheduled)
+    {
+        aGuard.clear();
+        xGroup->maCompilationDone.wait();
+        xGroup->maCompilationDone.reset();
+        pKernel = static_cast<DynamicKernel*>(xGroup->mpCompiledFormula);
+    }
+    else
+    {
+        aGuard.clear();
+        pKernel = DynamicKernel::create(rDoc, rTopPos, rCode);
+    }
+
     if (!pKernel)
         return false;
 
diff --git a/sc/source/core/tool/clkernelthread.cxx b/sc/source/core/tool/clkernelthread.cxx
index b89312b..7404a80 100644
--- a/sc/source/core/tool/clkernelthread.cxx
+++ b/sc/source/core/tool/clkernelthread.cxx
@@ -9,6 +9,8 @@
 
 #include <sal/log.hxx>
 
+#include "formulagroupinterpreter.hxx"
+
 #include "clkernelthread.hxx"
 
 using namespace std;
@@ -46,6 +48,13 @@ void CLBuildKernelThread::execute()
             {
             case CLBuildKernelWorkItem::COMPILE:
                 SAL_INFO("sc.opencl.thread", "told to compile group " << aWorkItem.mxGroup << " to binary");
+                assert(aWorkItem.mxGroup->meCalcState == sc::GroupCalcOpenCLKernelCompilationScheduled);
+                aWorkItem.mxGroup->mpCompiledFormula =
+                    sc::FormulaGroupInterpreter::getStatic()->createCompiledFormula(*aWorkItem.mxGroup->mpTopCell->GetDocument(),
+                                                                                    aWorkItem.mxGroup->mpTopCell->aPos,
+                                                                                    *aWorkItem.mxGroup->mpCode);
+                aWorkItem.mxGroup->meCalcState = sc::GroupCalcOpenCLKernelBinaryCreated;
+                aWorkItem.mxGroup->maCompilationDone.set();
                 break;
             case CLBuildKernelWorkItem::FINISH:
                 SAL_INFO("sc.opencl.thread", "told to finish");
commit 3e8df9eb9535349b2bbb1394794cffcae4b067ac
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Nov 13 15:37:34 2013 +0200

    Avoid crash in unit test
    
    Change-Id: Ic452c1a2a8a7099a22584130e6a161c25bccabae

diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index c17e4f3..a864b1d 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -3329,7 +3329,8 @@ ScFormulaCellGroupRef ScFormulaCell::CreateCellGroup( SCROW nLen, bool bInvarian
     mxGroup->mbInvariant = bInvariant;
     mxGroup->mnLength = nLen;
     mxGroup->mpCode = pCode; // Move this to the shared location.
-    mxGroup->scheduleCompilation();
+    if (mxGroup->mxCLKernelThread.is())
+        mxGroup->scheduleCompilation();
     return mxGroup;
 }
 
commit 856e0ab368b4079df11cec23d669c050b45fc36e
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Nov 13 14:26:47 2013 +0200

    WaE: unused parameters
    
    Change-Id: Ic7c0b5273bc585757abb070ed71ab0cc04bd0304

diff --git a/sc/source/core/opencl/formulagroupcl.cxx b/sc/source/core/opencl/formulagroupcl.cxx
index e2a7460..bf3c1d3 100644
--- a/sc/source/core/opencl/formulagroupcl.cxx
+++ b/sc/source/core/opencl/formulagroupcl.cxx
@@ -2581,8 +2581,8 @@ ScMatrixRef FormulaGroupInterpreterOpenCL::inverseMatrix( const ScMatrix& )
     return NULL;
 }
 
-DynamicKernel* DynamicKernel::create(ScDocument& rDoc,
-                                     const ScAddress& rTopPos,
+DynamicKernel* DynamicKernel::create(ScDocument& /* rDoc */,
+                                     const ScAddress& /* rTopPos */,
                                      ScTokenArray& rCode)
 {
     // Constructing "AST"
commit c0307ec2bafa0751e7d8ea1f4d6caac58c5cc5dc
Author: Tor Lillqvist <tml at collabora.com>
Date:   Wed Nov 13 10:50:12 2013 +0200

    WIP commit of threaded OpenCL compilation work
    
    Change-Id: I210d0ded9cb38569b0831355522d8309cee05f56

diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox
index a6ffef9..eb22ea0 100644
--- a/include/sal/log-areas.dox
+++ b/include/sal/log-areas.dox
@@ -84,6 +84,7 @@ certain functionality.
 
 @li @c sc
 @li @c sc.opencl
+ at li @c sc.opencl.thread
 @li @c sc.core
 @li @c sc.filter - Calc filter
 @li @c sc.ui - Calc UI
diff --git a/sc/inc/clkernelthread.hxx b/sc/inc/clkernelthread.hxx
index c780c28..89ae860 100644
--- a/sc/inc/clkernelthread.hxx
+++ b/sc/inc/clkernelthread.hxx
@@ -7,11 +7,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+#include <queue>
+
 #include <osl/conditn.hxx>
 #include <salhelper/thread.hxx>
 
+#include "formulacell.hxx"
+
 namespace sc {
 
+struct CLBuildKernelWorkItem
+{
+    enum { COMPILE, FINISH } meWhatToDo;
+    ScFormulaCellGroupRef mxGroup;
+};
+
 class CLBuildKernelThread : public salhelper::Thread
 {
 public:
@@ -20,11 +30,15 @@ public:
 
     void finish();
 
+    void push(CLBuildKernelWorkItem item);
+
 protected:
     virtual void execute();
 
 private:
-    osl::Condition maConsumeCondition;
+    osl::Mutex maMutex;
+    osl::Condition maCondition;
+    std::queue<CLBuildKernelWorkItem> maQueue;
     void produce();
     void consume();
 };
diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx
index 4e56b6a..68648fc 100644
--- a/sc/inc/formulacell.hxx
+++ b/sc/inc/formulacell.hxx
@@ -53,6 +53,7 @@ struct SC_DLLPUBLIC ScFormulaCellGroup : boost::noncopyable
     mutable size_t mnRefCount;
 
     ScTokenArray* mpCode;
+    osl::Mutex maMutex;
     sc::CompiledFormula* mpCompiledFormula;
     ScFormulaCell *mpTopCell;
     SCROW mnLength; // How many of these do we have ?
@@ -64,6 +65,8 @@ struct SC_DLLPUBLIC ScFormulaCellGroup : boost::noncopyable
     ScFormulaCellGroup();
     ~ScFormulaCellGroup();
 
+    void scheduleCompilation();
+
     void setCode( const ScTokenArray& rCode );
     void compileCode(
         ScDocument& rDoc, const ScAddress& rPos, formula::FormulaGrammar::Grammar eGram );
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index e0ee5a7..c17e4f3 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -418,11 +418,11 @@ ScFormulaCellGroup::ScFormulaCellGroup() :
     {
         osl::MutexGuard aGuard(getOpenCLCompilationThreadMutex());
         if (mnCount++ == 0)
-            {
-                assert(!mxCLKernelThread.is());
-                mxCLKernelThread.set(new sc::CLBuildKernelThread);
-                mxCLKernelThread->launch();
-            }
+        {
+            assert(!mxCLKernelThread.is());
+            mxCLKernelThread.set(new sc::CLBuildKernelThread);
+            mxCLKernelThread->launch();
+        }
     }
 }
 
@@ -443,6 +443,14 @@ ScFormulaCellGroup::~ScFormulaCellGroup()
     delete mpCode;
 }
 
+void ScFormulaCellGroup::scheduleCompilation()
+{
+    sc::CLBuildKernelWorkItem aWorkItem;
+    aWorkItem.meWhatToDo = sc::CLBuildKernelWorkItem::COMPILE;
+    aWorkItem.mxGroup = this;
+    mxCLKernelThread->push(aWorkItem);
+}
+
 void ScFormulaCellGroup::setCode( const ScTokenArray& rCode )
 {
     delete mpCode;
@@ -929,7 +937,9 @@ void ScFormulaCell::CompileTokenArray( bool bNoListening )
 {
     // Not already compiled?
     if( !pCode->GetLen() && !aResult.GetHybridFormula().isEmpty() )
+    {
         Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar);
+    }
     else if( bCompile && !pDocument->IsClipOrUndo() && !pCode->GetCodeError() )
     {
         // RPN length may get changed
@@ -3319,6 +3329,7 @@ ScFormulaCellGroupRef ScFormulaCell::CreateCellGroup( SCROW nLen, bool bInvarian
     mxGroup->mbInvariant = bInvariant;
     mxGroup->mnLength = nLen;
     mxGroup->mpCode = pCode; // Move this to the shared location.
+    mxGroup->scheduleCompilation();
     return mxGroup;
 }
 
@@ -3509,7 +3520,6 @@ public:
             aComp.SetGrammar(formula::FormulaGrammar::GRAM_NATIVE_XL_R1C1);
             OUStringBuffer aAsString;
             aComp.CreateStringFromTokenArray(aAsString);
-            SAL_DEBUG("interpret formula: " << aAsString.makeStringAndClear());
         }
 #endif
 
diff --git a/sc/source/core/tool/clkernelthread.cxx b/sc/source/core/tool/clkernelthread.cxx
index 90c9496..b89312b 100644
--- a/sc/source/core/tool/clkernelthread.cxx
+++ b/sc/source/core/tool/clkernelthread.cxx
@@ -26,7 +26,43 @@ CLBuildKernelThread::~CLBuildKernelThread()
 
 void CLBuildKernelThread::execute()
 {
-    SAL_INFO("sc.opencl", "opencl-buildkernel-thread running");
+    SAL_INFO("sc.opencl.thread", "running");
+
+    bool done = false;
+    while (!done)
+    {
+        SAL_INFO("sc.opencl.thread", "waiting for condition");
+        maCondition.wait();
+        SAL_INFO("sc.opencl.thread", "got condition");
+        osl::ResettableMutexGuard aGuard(maMutex);
+        maCondition.reset();
+        while (!maQueue.empty())
+        {
+            CLBuildKernelWorkItem aWorkItem = maQueue.front();
+            maQueue.pop();
+            aGuard.clear();
+
+            switch (aWorkItem.meWhatToDo)
+            {
+            case CLBuildKernelWorkItem::COMPILE:
+                SAL_INFO("sc.opencl.thread", "told to compile group " << aWorkItem.mxGroup << " to binary");
+                break;
+            case CLBuildKernelWorkItem::FINISH:
+                SAL_INFO("sc.opencl.thread", "told to finish");
+                done = true;
+                break;
+            }
+
+            aGuard.reset();
+        }
+    }
+}
+
+void CLBuildKernelThread::push(CLBuildKernelWorkItem item)
+{
+    osl::MutexGuard guard(maMutex);
+    maQueue.push(item);
+    maCondition.set();
 }
 
 void CLBuildKernelThread::produce()
@@ -39,7 +75,10 @@ void CLBuildKernelThread::consume()
 
 void CLBuildKernelThread::finish()
 {
-    SAL_INFO("sc.opencl", "opencl-buildkernel-thread request to finish");
+    SAL_INFO("sc.opencl", "telling thread to finish");
+    CLBuildKernelWorkItem aWorkItem;
+    aWorkItem.meWhatToDo = CLBuildKernelWorkItem::FINISH;
+    push(aWorkItem);
 }
 
 }
commit feca57d41d515959e3f730eb372a3d33bb251a03
Author: Tor Lillqvist <tml at collabora.com>
Date:   Mon Nov 11 12:31:58 2013 +0200

    Use the more generic CompiledFormula type
    
    Change-Id: I91d2e956b76919c56d14712789198c3c10470742

diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx
index a6200c9..4e56b6a 100644
--- a/sc/inc/formulacell.hxx
+++ b/sc/inc/formulacell.hxx
@@ -33,6 +33,7 @@
 namespace sc {
 
 class CLBuildKernelThread;
+class CompiledFormula;
 class StartListeningContext;
 class EndListeningContext;
 struct RefUpdateContext;
@@ -40,12 +41,6 @@ struct RefUpdateInsertTabContext;
 struct RefUpdateDeleteTabContext;
 struct RefUpdateMoveTabContext;
 
-namespace opencl {
-
-class DynamicKernel;
-
-}
-
 }
 
 class ScFormulaCell;
@@ -58,7 +53,7 @@ struct SC_DLLPUBLIC ScFormulaCellGroup : boost::noncopyable
     mutable size_t mnRefCount;
 
     ScTokenArray* mpCode;
-    sc::opencl::DynamicKernel* mpDynamicKernel;
+    sc::CompiledFormula* mpCompiledFormula;
     ScFormulaCell *mpTopCell;
     SCROW mnLength; // How many of these do we have ?
     short mnFormatType;
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 3ab19a6..e0ee5a7 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -406,7 +406,7 @@ rtl::Reference<sc::CLBuildKernelThread> ScFormulaCellGroup::mxCLKernelThread;
 ScFormulaCellGroup::ScFormulaCellGroup() :
     mnRefCount(0),
     mpCode(NULL),
-    mpDynamicKernel(NULL),
+    mpCompiledFormula(NULL),
     mpTopCell(NULL),
     mnLength(0),
     mnFormatType(NUMBERFORMAT_NUMBER),
commit e064677047d924074d327081eeb8c15f76d62e48
Author: Tor Lillqvist <tml at collabora.com>
Date:   Fri Nov 8 17:25:42 2013 +0200

    Point to the group's top cell in ScFormulaCellGroup instead of the top row
    
    It will make implementing the background compilation of OpenCL kernels
    easier, I hope. That needs data accessible through the ScFormulaCell.
    
    Change-Id: I77ad5a94eb13551e6238d5f5285f1c916d7cb992

diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx
index c151e06..a6200c9 100644
--- a/sc/inc/formulacell.hxx
+++ b/sc/inc/formulacell.hxx
@@ -48,6 +48,7 @@ class DynamicKernel;
 
 }
 
+class ScFormulaCell;
 class ScProgress;
 class ScTokenArray;
 struct ScSimilarFormulaDelta;
@@ -58,7 +59,7 @@ struct SC_DLLPUBLIC ScFormulaCellGroup : boost::noncopyable
 
     ScTokenArray* mpCode;
     sc::opencl::DynamicKernel* mpDynamicKernel;
-    SCROW mnStart;  // Start offset of that cell
+    ScFormulaCell *mpTopCell;
     SCROW mnLength; // How many of these do we have ?
     short mnFormatType;
     bool mbInvariant:1;
@@ -347,7 +348,7 @@ public:
     /**
      * Turn a non-grouped cell into the top of a grouped cell.
      */
-    ScFormulaCellGroupRef CreateCellGroup( SCROW nStart, SCROW nLen, bool bInvariant );
+    ScFormulaCellGroupRef CreateCellGroup( SCROW nLen, bool bInvariant );
     ScFormulaCellGroupRef GetCellGroup();
     void SetCellGroup( const ScFormulaCellGroupRef &xRef );
 
diff --git a/sc/inc/sharedformula.hxx b/sc/inc/sharedformula.hxx
index 11493e646..571d73c 100644
--- a/sc/inc/sharedformula.hxx
+++ b/sc/inc/sharedformula.hxx
@@ -48,7 +48,7 @@ public:
             }
 
             // Create a new group.
-            xGroup = pPrev->CreateCellGroup(pPrev->aPos.Row(), 2, eState == ScFormulaCell::EqualInvariant);
+            xGroup = pPrev->CreateCellGroup(2, eState == ScFormulaCell::EqualInvariant);
             pCur->SetCellGroup(xGroup);
         }
     }
diff --git a/sc/qa/unit/filters-test.cxx b/sc/qa/unit/filters-test.cxx
index ce5f4b4..d12ea60 100644
--- a/sc/qa/unit/filters-test.cxx
+++ b/sc/qa/unit/filters-test.cxx
@@ -364,7 +364,7 @@ void ScFiltersTest::testSharedFormulaXLS()
     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pCell);
     ScFormulaCellGroupRef xGroup = pCell->GetCellGroup();
     CPPUNIT_ASSERT_MESSAGE("This cell should be a part of a cell group.", xGroup);
-    CPPUNIT_ASSERT_MESSAGE("Incorrect group geometry.", xGroup->mnStart == 1 && xGroup->mnLength == 18);
+    CPPUNIT_ASSERT_MESSAGE("Incorrect group geometry.", xGroup->mpTopCell->aPos.Row() == 1 && xGroup->mnLength == 18);
 
     xDocSh->DoClose();
 }
@@ -387,7 +387,7 @@ void ScFiltersTest::testSharedFormulaXLSX()
     CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pCell);
     ScFormulaCellGroupRef xGroup = pCell->GetCellGroup();
     CPPUNIT_ASSERT_MESSAGE("This cell should be a part of a cell group.", xGroup);
-    CPPUNIT_ASSERT_MESSAGE("Incorrect group geometry.", xGroup->mnStart == 1 && xGroup->mnLength == 18);
+    CPPUNIT_ASSERT_MESSAGE("Incorrect group geometry.", xGroup->mpTopCell->aPos.Row() == 1 && xGroup->mnLength == 18);
 
     xDocSh->DoClose();
 }
diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx
index a400935..141240f 100644
--- a/sc/source/core/data/column3.cxx
+++ b/sc/source/core/data/column3.cxx
@@ -2786,14 +2786,14 @@ public:
                 nRow += xCurGrp->mnLength;
                 std::advance(it, xCurGrp->mnLength);
                 pPrev->SetCellGroup(xCurGrp);
-                --xCurGrp->mnStart;
+                --xCurGrp->mpTopCell = pPrev;
                 ++xCurGrp->mnLength;
                 xPrevGrp = xCurGrp;
             }
             else
             {
                 // Both previous and current cells are regular cells.
-                xPrevGrp = pPrev->CreateCellGroup(nRow - 1, 2, eCompState == ScFormulaCell::EqualInvariant);
+                xPrevGrp = pPrev->CreateCellGroup(2, eCompState == ScFormulaCell::EqualInvariant);
                 pCur->SetCellGroup(xPrevGrp);
                 ++nRow;
                 ++it;
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 036005c..3ab19a6 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -407,7 +407,7 @@ ScFormulaCellGroup::ScFormulaCellGroup() :
     mnRefCount(0),
     mpCode(NULL),
     mpDynamicKernel(NULL),
-    mnStart(0),
+    mpTopCell(NULL),
     mnLength(0),
     mnFormatType(NUMBERFORMAT_NUMBER),
     mbInvariant(false),
@@ -2321,12 +2321,6 @@ bool ScFormulaCell::UpdatePosOnShift( const sc::RefUpdateContext& rCxt )
 
     // This formula cell itself is being shifted during cell range
     // insertion or deletion. Update its position.
-
-    if (mxGroup && (mxGroup->mnStart+mxGroup->mnLength-1) == aPos.Row())
-        // For a shared formula cell, update its group start position only
-        // when it's the last cell of the group.
-        mxGroup->mnStart += rCxt.mnRowDelta;
-
     aPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta);
 
     return true;
@@ -2751,7 +2745,7 @@ bool ScFormulaCell::UpdateReference(
     if (pDocument->IsClipOrUndo())
         return false;
 
-    if (mxGroup && mxGroup->mnStart != aPos.Row())
+    if (mxGroup && mxGroup->mpTopCell != this)
     {
         // This is not a top cell of a formula group. Don't update references.
 
@@ -3314,14 +3308,14 @@ ScFormulaCell*  ScFormulaCell::GetNextTrack() const                    { return
 void            ScFormulaCell::SetPreviousTrack( ScFormulaCell* pF )   { pPreviousTrack = pF; }
 void            ScFormulaCell::SetNextTrack( ScFormulaCell* pF )       { pNextTrack = pF; }
 
-ScFormulaCellGroupRef ScFormulaCell::CreateCellGroup( SCROW nStart, SCROW nLen, bool bInvariant )
+ScFormulaCellGroupRef ScFormulaCell::CreateCellGroup( SCROW nLen, bool bInvariant )
 {
     if (mxGroup)
         // You can't create a new group if the cell is already a part of a group.
         return ScFormulaCellGroupRef();
 
     mxGroup.reset(new ScFormulaCellGroup);
-    mxGroup->mnStart = nStart;
+    mxGroup->mpTopCell = this;
     mxGroup->mbInvariant = bInvariant;
     mxGroup->mnLength = nLen;
     mxGroup->mpCode = pCode; // Move this to the shared location.
@@ -3697,8 +3691,7 @@ bool ScFormulaCell::InterpretFormulaGroup()
         return InterpretInvariantFormulaGroup();
 
     ScTokenArray aCode;
-    ScAddress aTopPos = aPos;
-    aTopPos.SetRow(mxGroup->mnStart);
+    ScAddress aTopPos = mxGroup->mpTopCell->aPos;
     GroupTokenConverter aConverter(aCode, *pDocument, *this, aTopPos);
     if (!aConverter.convert(*pCode))
     {
@@ -3776,7 +3769,7 @@ bool ScFormulaCell::InterpretInvariantFormulaGroup()
     for ( sal_Int32 i = 0; i < mxGroup->mnLength; i++ )
     {
         ScAddress aTmpPos = aPos;
-        aTmpPos.SetRow(mxGroup->mnStart + i);
+        aTmpPos.SetRow(mxGroup->mpTopCell->aPos.Row() + i);
         ScFormulaCell* pCell = pDocument->GetFormulaCell(aTmpPos);
         assert( pCell != NULL );
 
@@ -4014,12 +4007,12 @@ bool ScFormulaCell::IsSharedTop() const
     if (!mxGroup)
         return false;
 
-    return mxGroup->mnStart == aPos.Row();
+    return mxGroup->mpTopCell == this;
 }
 
 SCROW ScFormulaCell::GetSharedTopRow() const
 {
-    return mxGroup ? mxGroup->mnStart : -1;
+    return mxGroup ? mxGroup->mpTopCell->aPos.Row() : -1;
 }
 
 SCROW ScFormulaCell::GetSharedLength() const
diff --git a/sc/source/core/tool/sharedformula.cxx b/sc/source/core/tool/sharedformula.cxx
index 6583ac9..20eef83 100644
--- a/sc/source/core/tool/sharedformula.cxx
+++ b/sc/source/core/tool/sharedformula.cxx
@@ -38,18 +38,18 @@ void SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type
 
     ScFormulaCellGroupRef xGroup = rTop.GetCellGroup();
 
-    SCROW nLength2 = xGroup->mnStart + xGroup->mnLength - nRow;
+    SCROW nLength2 = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - nRow;
     ScFormulaCellGroupRef xGroup2;
     if (nLength2 > 1)
     {
         xGroup2.reset(new ScFormulaCellGroup);
         xGroup2->mbInvariant = xGroup->mbInvariant;
-        xGroup2->mnStart = nRow;
+        xGroup2->mpTopCell = &rTop;
         xGroup2->mnLength = nLength2;
         xGroup2->mpCode = xGroup->mpCode->Clone();
     }
 
-    xGroup->mnLength = nRow - xGroup->mnStart;
+    xGroup->mnLength = nRow - xGroup->mpTopCell->aPos.Row();
     if (xGroup->mnLength == 1)
     {
         // The top group consists of only one cell. Ungroup this.
@@ -60,7 +60,7 @@ void SharedFormulaUtil::splitFormulaCellGroup(const CellStoreType::position_type
 
     // Apply the lower group object to the lower cells.
 #if DEBUG_COLUMN_STORAGE
-    if (xGroup2->mnStart + xGroup2->mnLength > aPos.first->position + aPos.first->size)
+    if (xGroup2->mpTopCell->aPos.Row() + xGroup2->mnLength > aPos.first->position + aPos.first->size)
     {
         cerr << "ScColumn::SplitFormulaCellGroup: Shared formula region goes beyond the formula block. Not good." << endl;
         cerr.flush();
@@ -111,8 +111,6 @@ void SharedFormulaUtil::joinFormulaCells(const CellStoreType::position_type& rPo
     if (eState == ScFormulaCell::NotEqual)
         return;
 
-    SCROW nRow = rPos.first->position + rPos.second;
-
     // Formula tokens equal those of the previous formula cell.
     ScFormulaCellGroupRef xGroup1 = rCell1.GetCellGroup();
     ScFormulaCellGroupRef xGroup2 = rCell2.GetCellGroup();
@@ -147,13 +145,13 @@ void SharedFormulaUtil::joinFormulaCells(const CellStoreType::position_type& rPo
         {
             // cell 1 is not shared, but cell 2 is already shared.
             rCell1.SetCellGroup(xGroup2);
-            xGroup2->mnStart = nRow;
+            xGroup2->mpTopCell = &rCell1;
             ++xGroup2->mnLength;
         }
         else
         {
             // neither cells are shared.
-            xGroup1 = rCell1.CreateCellGroup(nRow, 2, eState == ScFormulaCell::EqualInvariant);
+            xGroup1 = rCell1.CreateCellGroup(2, eState == ScFormulaCell::EqualInvariant);
             rCell2.SetCellGroup(xGroup1);
         }
     }
@@ -207,8 +205,9 @@ void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type& a
         else
         {
             // Move the top cell to the next formula cell down.
+            ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
             --xGroup->mnLength;
-            ++xGroup->mnStart;
+            xGroup->mpTopCell = &rNext;
         }
     }
     else if (rCell.aPos.Row() == rCell.GetSharedTopRow() + rCell.GetSharedLength() - 1)
@@ -239,8 +238,8 @@ void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type& a
     {
         // In the middle of the shared range. Split it into two groups.
         ScFormulaCellGroupRef xGroup = rCell.GetCellGroup();
-        SCROW nEndRow = xGroup->mnStart + xGroup->mnLength - 1;
-        xGroup->mnLength = rCell.aPos.Row() - xGroup->mnStart; // Shorten the top group.
+        SCROW nEndRow = xGroup->mpTopCell->aPos.Row() + xGroup->mnLength - 1;
+        xGroup->mnLength = rCell.aPos.Row() - xGroup->mpTopCell->aPos.Row(); // Shorten the top group.
         if (xGroup->mnLength == 1)
         {
             // Make the top cell non-shared.
@@ -261,12 +260,13 @@ void SharedFormulaUtil::unshareFormulaCell(const CellStoreType::position_type& a
         {
             ScFormulaCellGroupRef xGroup2;
             xGroup2.reset(new ScFormulaCellGroup);
-            xGroup2->mnStart = rCell.aPos.Row() + 1;
+            ScFormulaCell& rNext = *sc::formula_block::at(*it->data, aPos.second+1);
+            xGroup2->mpTopCell = &rNext;
             xGroup2->mnLength = nLength2;
             xGroup2->mbInvariant = xGroup->mbInvariant;
             xGroup2->mpCode = xGroup->mpCode->Clone();
 #if DEBUG_COLUMN_STORAGE
-            if (xGroup2->mnStart + xGroup2->mnLength > it->position + it->size)
+            if (xGroup2->mpTopCell->aPos.Row() + xGroup2->mnLength > it->position + it->size)
             {
                 cerr << "ScColumn::UnshareFormulaCell: Shared formula region goes beyond the formula block. Not good." << endl;
                 cerr.flush();
diff --git a/sc/source/filter/excel/excform.cxx b/sc/source/filter/excel/excform.cxx
index 5ce657e..06e14b2 100644
--- a/sc/source/filter/excel/excform.cxx
+++ b/sc/source/filter/excel/excform.cxx
@@ -135,7 +135,7 @@ void ImportExcel::Formula(
                     ScFormulaCell* pCell = new ScFormulaCell(pD, aScPos, xGroup);
                     rDoc.getDoc().EnsureTable(aScPos.Tab());
                     rDoc.setFormulaCell(aScPos, pCell);
-                    xGroup->mnLength = aScPos.Row() - xGroup->mnStart + 1;
+                    xGroup->mnLength = aScPos.Row() - xGroup->mpTopCell->aPos.Row() + 1;
                     pCell->SetNeedNumberFormat(false);
                     if (!rtl::math::isNan(fCurVal))
                         pCell->SetResultDouble(fCurVal);
diff --git a/sc/source/filter/excel/impop.cxx b/sc/source/filter/excel/impop.cxx
index 507a904..bda23cb 100644
--- a/sc/source/filter/excel/impop.cxx
+++ b/sc/source/filter/excel/impop.cxx
@@ -889,6 +889,7 @@ void ImportExcel::Shrfmla( void )
         xGroup->compileCode(rDoc.getDoc(), aPos, formula::FormulaGrammar::GRAM_DEFAULT);
 
         ScFormulaCell* pCell = new ScFormulaCell(pD, aPos, xGroup);
+        xGroup->mpTopCell = pCell;
         rDoc.getDoc().EnsureTable(aPos.Tab());
         rDoc.setFormulaCell(aPos, pCell);
         pCell->SetNeedNumberFormat(false);
diff --git a/sc/source/filter/excel/namebuff.cxx b/sc/source/filter/excel/namebuff.cxx
index 43a1adb..1456c89 100644
--- a/sc/source/filter/excel/namebuff.cxx
+++ b/sc/source/filter/excel/namebuff.cxx
@@ -86,7 +86,10 @@ void SharedFormulaBuffer::Store( const ScRange& rRange, const ScTokenArray& rArr
         aPos.SetCol(i);
 
         ScFormulaCellGroupRef xNewGroup(new ScFormulaCellGroup);
-        xNewGroup->mnStart = rRange.aStart.Row();
+        // We have no ScFormulaCell yet to point mpTopCell to!?
+        // Let's hope that the only called of this in ImportExcel::Shrfmla()
+        // fixes that up properly.
+        xNewGroup->mpTopCell = NULL;
         xNewGroup->mnLength = 1;
         xNewGroup->setCode(rArray);
         maFormulaGroups.insert(FormulaGroupsType::value_type(aPos, xNewGroup));
diff --git a/sc/source/filter/oox/formulabuffer.cxx b/sc/source/filter/oox/formulabuffer.cxx
index 4303b41..ca48eca 100644
--- a/sc/source/filter/oox/formulabuffer.cxx
+++ b/sc/source/filter/oox/formulabuffer.cxx
@@ -198,7 +198,7 @@ void applyCellFormulas(
                 ScFormulaCellGroupRef xGroup = rPrev.GetCellGroup();
                 if (!xGroup)
                     // Last cell is not grouped yet. Start a new group.
-                    xGroup = rPrev.CreateCellGroup(p->mnRow, 1, false);
+                    xGroup = rPrev.CreateCellGroup(p->mnRow, false);
 
                 ++xGroup->mnLength;
 
commit 94293870da07719c50f9281f6aeed8347fb92517
Author: Tor Lillqvist <tml at collabora.com>
Date:   Fri Nov 8 13:41:11 2013 +0200

    Add "sc.opencl" logging area
    
    Change-Id: Icd851c17bab1f55cd16d5a48cc71ff2ac91daa2c

diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 6663fa9..036005c 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -436,6 +436,7 @@ ScFormulaCellGroup::~ScFormulaCellGroup()
                 assert(mxCLKernelThread.is());
                 mxCLKernelThread->finish();
                 mxCLKernelThread->join();
+                SAL_INFO("sc.opencl", "OpenCL kernel compilation thread has finished");
                 mxCLKernelThread.clear();
             }
     }
diff --git a/sc/source/core/tool/clkernelthread.cxx b/sc/source/core/tool/clkernelthread.cxx
index 90edf8c..90c9496 100644
--- a/sc/source/core/tool/clkernelthread.cxx
+++ b/sc/source/core/tool/clkernelthread.cxx
@@ -26,9 +26,7 @@ CLBuildKernelThread::~CLBuildKernelThread()
 
 void CLBuildKernelThread::execute()
 {
-    SAL_INFO("sc", "opencl-buildkernel-thread running");
-
-    SAL_INFO("sc", "opencl-buildkernel-thread finishing");
+    SAL_INFO("sc.opencl", "opencl-buildkernel-thread running");
 }
 
 void CLBuildKernelThread::produce()
@@ -41,7 +39,7 @@ void CLBuildKernelThread::consume()
 
 void CLBuildKernelThread::finish()
 {
-    SAL_INFO("sc", "opencl-buildkernel-thread request to finish");
+    SAL_INFO("sc.opencl", "opencl-buildkernel-thread request to finish");
 }
 
 }
commit 723060d4b301e80bf6390422a0e82d56ab8e1fd3
Author: Tor Lillqvist <tml at collabora.com>
Date:   Fri Nov 8 10:16:39 2013 +0200

    WIP: Do OpenCL compilation in advance in a worker thread
    
    Change-Id: I9fbf848bd487e5ea49a383461f04e3c1678af607

diff --git a/sc/Library_sc.mk b/sc/Library_sc.mk
index c9f0b5f..3d57587 100644
--- a/sc/Library_sc.mk
+++ b/sc/Library_sc.mk
@@ -195,6 +195,7 @@ $(eval $(call gb_Library_add_exception_objects,sc,\
     sc/source/core/tool/chartpos \
     sc/source/core/tool/chgtrack \
     sc/source/core/tool/chgviset \
+    sc/source/core/tool/clkernelthread \
     sc/source/core/tool/compare \
     sc/source/core/tool/compiler \
     sc/source/core/tool/consoli  \
diff --git a/sc/Library_scfilt.mk b/sc/Library_scfilt.mk
index c23af3d..499f873 100644
--- a/sc/Library_scfilt.mk
+++ b/sc/Library_scfilt.mk
@@ -128,7 +128,6 @@ $(eval $(call gb_Library_add_exception_objects,scfilt,\
 	sc/source/filter/excel/xltools \
 	sc/source/filter/excel/xltracer \
 	sc/source/filter/excel/xlview \
-	sc/source/filter/ftools/clkernelthread \
 	sc/source/filter/ftools/fapihelper \
 	sc/source/filter/ftools/fprogressbar \
 	sc/source/filter/ftools/ftools \
diff --git a/sc/source/filter/inc/clkernelthread.hxx b/sc/inc/clkernelthread.hxx
similarity index 77%
rename from sc/source/filter/inc/clkernelthread.hxx
rename to sc/inc/clkernelthread.hxx
index 32586e7..c780c28 100644
--- a/sc/source/filter/inc/clkernelthread.hxx
+++ b/sc/inc/clkernelthread.hxx
@@ -7,7 +7,8 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
-#include "salhelper/thread.hxx"
+#include <osl/conditn.hxx>
+#include <salhelper/thread.hxx>
 
 namespace sc {
 
@@ -17,8 +18,15 @@ public:
     CLBuildKernelThread();
     virtual ~CLBuildKernelThread();
 
+    void finish();
+
 protected:
     virtual void execute();
+
+private:
+    osl::Condition maConsumeCondition;
+    void produce();
+    void consume();
 };
 
 }
diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx
index b32f250..c151e06 100644
--- a/sc/inc/formulacell.hxx
+++ b/sc/inc/formulacell.hxx
@@ -23,6 +23,7 @@
 #include "formularesult.hxx"
 
 #include "formula/tokenarray.hxx"
+#include <rtl/ref.hxx>
 #include "svl/listener.hxx"
 #include "types.hxx"
 
@@ -31,6 +32,7 @@
 
 namespace sc {
 
+class CLBuildKernelThread;
 class StartListeningContext;
 class EndListeningContext;
 struct RefUpdateContext;
@@ -69,6 +71,9 @@ struct SC_DLLPUBLIC ScFormulaCellGroup : boost::noncopyable
     void setCode( const ScTokenArray& rCode );
     void compileCode(
         ScDocument& rDoc, const ScAddress& rPos, formula::FormulaGrammar::Grammar eGram );
+
+    static int mnCount;
+    static rtl::Reference<sc::CLBuildKernelThread> mxCLKernelThread;
 };
 
 inline void intrusive_ptr_add_ref(const ScFormulaCellGroup *p)
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index 7c48a00..6663fa9 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -38,6 +38,7 @@
 #include "editutil.hxx"
 #include "chgtrack.hxx"
 #include "tokenarray.hxx"
+#include "clkernelthread.hxx"
 
 #include "formula/errorcodes.hxx"
 #include "formula/vectortoken.hxx"
@@ -382,6 +383,26 @@ void adjustDBRange(ScToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldD
 
 }
 
+//  The mutex to synchronize access to the OpenCL compilation thread.
+static osl::Mutex& getOpenCLCompilationThreadMutex()
+{
+    static osl::Mutex* pMutex = NULL;
+    if( !pMutex )
+    {
+        osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );
+        if( !pMutex )
+        {
+            static osl::Mutex aMutex;
+            pMutex = &aMutex;
+        }
+    }
+
+    return *pMutex;
+}
+
+int ScFormulaCellGroup::mnCount = 0;
+rtl::Reference<sc::CLBuildKernelThread> ScFormulaCellGroup::mxCLKernelThread;
+
 ScFormulaCellGroup::ScFormulaCellGroup() :
     mnRefCount(0),
     mpCode(NULL),
@@ -393,10 +414,31 @@ ScFormulaCellGroup::ScFormulaCellGroup() :
     mbSubTotal(false),
     meCalcState(sc::GroupCalcEnabled)
 {
+    if (ScInterpreter::GetGlobalConfig().mbOpenCLEnabled)
+    {
+        osl::MutexGuard aGuard(getOpenCLCompilationThreadMutex());
+        if (mnCount++ == 0)
+            {
+                assert(!mxCLKernelThread.is());
+                mxCLKernelThread.set(new sc::CLBuildKernelThread);
+                mxCLKernelThread->launch();
+            }
+    }
 }
 
 ScFormulaCellGroup::~ScFormulaCellGroup()
 {
+    if (ScInterpreter::GetGlobalConfig().mbOpenCLEnabled)
+    {
+        osl::MutexGuard aGuard(getOpenCLCompilationThreadMutex());
+        if (--mnCount == 0)
+            {
+                assert(mxCLKernelThread.is());
+                mxCLKernelThread->finish();
+                mxCLKernelThread->join();
+                mxCLKernelThread.clear();
+            }
+    }
     delete mpCode;
 }
 
diff --git a/sc/source/core/opencl/formulagroupcl.cxx b/sc/source/core/opencl/formulagroupcl.cxx
index 4c44402..e2a7460 100644
--- a/sc/source/core/opencl/formulagroupcl.cxx
+++ b/sc/source/core/opencl/formulagroupcl.cxx
@@ -2652,7 +2652,7 @@ CompiledFormula* FormulaGroupInterpreterOpenCL::createCompiledFormula(ScDocument
                                                                       const ScAddress& rTopPos,
                                                                       ScTokenArray& rCode)
 {
-    return sc::opencl::DynamicKernel::create(rDoc, rTopPos, rCode);
+    return DynamicKernel::create(rDoc, rTopPos, rCode);
 }
 
 bool FormulaGroupInterpreterOpenCL::interpret( ScDocument& rDoc,
diff --git a/sc/source/core/tool/clkernelthread.cxx b/sc/source/core/tool/clkernelthread.cxx
new file mode 100644
index 0000000..90edf8c
--- /dev/null
+++ b/sc/source/core/tool/clkernelthread.cxx
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <sal/log.hxx>
+
+#include "clkernelthread.hxx"
+
+using namespace std;
+
+namespace sc {
+
+CLBuildKernelThread::CLBuildKernelThread() :
+    salhelper::Thread("opencl-build-kernel-thread")
+{
+}
+
+CLBuildKernelThread::~CLBuildKernelThread()
+{
+}
+
+void CLBuildKernelThread::execute()
+{
+    SAL_INFO("sc", "opencl-buildkernel-thread running");
+
+    SAL_INFO("sc", "opencl-buildkernel-thread finishing");
+}
+
+void CLBuildKernelThread::produce()
+{
+}
+
+void CLBuildKernelThread::consume()
+{
+}
+
+void CLBuildKernelThread::finish()
+{
+    SAL_INFO("sc", "opencl-buildkernel-thread request to finish");
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/filter/ftools/clkernelthread.cxx b/sc/source/filter/ftools/clkernelthread.cxx
deleted file mode 100644
index c307793..0000000
--- a/sc/source/filter/ftools/clkernelthread.cxx
+++ /dev/null
@@ -1,26 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/*
- * This file is part of the LibreOffice project.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-#include "clkernelthread.hxx"
-
-using namespace std;
-
-namespace sc {
-
-CLBuildKernelThread::CLBuildKernelThread() : salhelper::Thread("opencl-build-kernel-thread") {}
-
-CLBuildKernelThread::~CLBuildKernelThread() {}
-
-void CLBuildKernelThread::execute()
-{
-}
-
-}
-
-/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
commit 89f8829a62479c6d6deb1ffaab1baf710c8cc856
Author: Tor Lillqvist <tml at collabora.com>
Date:   Thu Nov 7 18:44:29 2013 +0200

    Make it clear in comments what kind of "code generation" they mean
    
    Change-Id: I1a21ba33dd5c5c87938efaa9a779e2eab4f2778c

diff --git a/sc/source/core/inc/dynamickernel.hxx b/sc/source/core/inc/dynamickernel.hxx
index e926be5..a390873 100644
--- a/sc/source/core/inc/dynamickernel.hxx
+++ b/sc/source/core/inc/dynamickernel.hxx
@@ -67,7 +67,7 @@ public:
     static DynamicKernel *create(ScDocument& rDoc,
                                  const ScAddress& rTopPos,
                                  ScTokenArray& rCode);
-    /// Code generation in OpenCL
+    /// OpenCL code generation
     void CodeGen();
     /// Produce kernel hash
     std::string GetMD5(void);
diff --git a/sc/source/core/opencl/formulagroupcl.cxx b/sc/source/core/opencl/formulagroupcl.cxx
index 17536b6..4c44402 100644
--- a/sc/source/core/opencl/formulagroupcl.cxx
+++ b/sc/source/core/opencl/formulagroupcl.cxx
@@ -820,9 +820,7 @@ protected:
     cl_mem mpClmem2;
 };
 
-
-
-/// Abstract class for code generation
+/// Abstract class for OpenCL source code generation
 
 class Reduction: public SlidingFunctionBase
 {
@@ -2399,7 +2397,7 @@ void SymbolTable::Marshal(cl_kernel k, int nVectorWidth, cl_program pProgram)
     }
 }
 
-    /// Code generation in OpenCL
+/// OpenCL code generation
 void DynamicKernel::CodeGen()
 {
         // Travese the tree of expression and declare symbols used
@@ -2633,7 +2631,7 @@ DynamicKernel* DynamicKernel::create(ScDocument& rDoc,
     if (!pDynamicKernel)
         return NULL;
 
-    // Code generation
+    // OpenCL source code generation
     try {
         pDynamicKernel->CodeGen();
     }
commit 8764c9e91a27f9953be2a5cab54e08cc2eb35278
Author: Tor Lillqvist <tml at collabora.com>
Date:   Thu Nov 7 18:09:58 2013 +0200

    Refactoring in preparation for threaded OpenCL kernel compilation
    
    Introduce a new abstract class CompiledFormula, of which the
    (existing) class BinaryKernel, that represents one OpenCL kernel, is a
    concrete subclass.
    
    Change-Id: I70fd26fa4bd3de026276727bf9fa8ed01bfb62c9

diff --git a/sc/inc/formulacell.hxx b/sc/inc/formulacell.hxx
index c2e578b..b32f250 100644
--- a/sc/inc/formulacell.hxx
+++ b/sc/inc/formulacell.hxx
@@ -38,6 +38,12 @@ struct RefUpdateInsertTabContext;
 struct RefUpdateDeleteTabContext;
 struct RefUpdateMoveTabContext;
 
+namespace opencl {
+
+class DynamicKernel;
+
+}
+
 }
 
 class ScProgress;
@@ -49,6 +55,7 @@ struct SC_DLLPUBLIC ScFormulaCellGroup : boost::noncopyable
     mutable size_t mnRefCount;
 
     ScTokenArray* mpCode;
+    sc::opencl::DynamicKernel* mpDynamicKernel;
     SCROW mnStart;  // Start offset of that cell
     SCROW mnLength; // How many of these do we have ?
     short mnFormatType;
diff --git a/sc/inc/formulagroup.hxx b/sc/inc/formulagroup.hxx
index 7c16364..faccbfb 100644
--- a/sc/inc/formulagroup.hxx
+++ b/sc/inc/formulagroup.hxx
@@ -75,6 +75,13 @@ struct FormulaGroupContext : boost::noncopyable
 };
 
 /**
+ * Abstract base class for a "compiled" formula
+ */
+class SC_DLLPUBLIC CompiledFormula
+{
+};
+
+/**
  * Abstract base class for vectorised formula group interpreters,
  * plus a global instance factory.
  */
@@ -92,6 +99,9 @@ class SC_DLLPUBLIC FormulaGroupInterpreter
     static void enableOpenCL(bool bEnable);
 
     virtual ScMatrixRef inverseMatrix(const ScMatrix& rMat) = 0;
+    virtual CompiledFormula* createCompiledFormula(ScDocument& rDoc,
+                                                   const ScAddress& rTopPos,
+                                                   ScTokenArray& rCode) = 0;
     virtual bool interpret(ScDocument& rDoc, const ScAddress& rTopPos, const ScFormulaCellGroupRef& xGroup, ScTokenArray& rCode) = 0;
 };
 
@@ -103,6 +113,9 @@ public:
     virtual ~FormulaGroupInterpreterSoftware() {}
 
     virtual ScMatrixRef inverseMatrix(const ScMatrix& rMat);
+    virtual CompiledFormula* createCompiledFormula(ScDocument& rDoc,
+                                                   const ScAddress& rTopPos,
+                                                   ScTokenArray& rCode) SAL_OVERRIDE;
     virtual bool interpret(ScDocument& rDoc, const ScAddress& rTopPos, const ScFormulaCellGroupRef& xGroup, ScTokenArray& rCode);
 };
 
diff --git a/sc/inc/types.hxx b/sc/inc/types.hxx
index 045d6b1..1704341 100644
--- a/sc/inc/types.hxx
+++ b/sc/inc/types.hxx
@@ -59,6 +59,7 @@ const sal_uInt16 MatrixEdgeOpen    = 32;
 enum GroupCalcState
 {
     GroupCalcEnabled,
+    GroupCalcOpenCLKernelBinaryCreated,
     GroupCalcRunning,
     GroupCalcDisabled
 };
diff --git a/sc/source/core/data/formulacell.cxx b/sc/source/core/data/formulacell.cxx
index ab6dda9..7c48a00 100644
--- a/sc/source/core/data/formulacell.cxx
+++ b/sc/source/core/data/formulacell.cxx
@@ -385,6 +385,7 @@ void adjustDBRange(ScToken* pToken, ScDocument& rNewDoc, const ScDocument* pOldD
 ScFormulaCellGroup::ScFormulaCellGroup() :
     mnRefCount(0),
     mpCode(NULL),
+    mpDynamicKernel(NULL),
     mnStart(0),
     mnLength(0),
     mnFormatType(NUMBERFORMAT_NUMBER),
diff --git a/sc/source/core/inc/dynamickernel.hxx b/sc/source/core/inc/dynamickernel.hxx
new file mode 100644
index 0000000..e926be5
--- /dev/null
+++ b/sc/source/core/inc/dynamickernel.hxx
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef INCLUDED_SC_SOURCE_CORE_INC_DYNAMICKERNEL_HXX
+#define INCLUDED_SC_SOURCE_CORE_INC_DYNAMICKERNEL_HXX
+
+#include <config_features.h>
+
+#include "formulagroup.hxx"
+
+#if !HAVE_FEATURE_OPENCL
+
+namespace sc { namespace opencl {
+
+class DynamikcKernel : public CompiledFormula
+{
+}
+
+} // namespace opencl
+
+} // namespace sc
+
+#else
+
+#include "clew.h"
+
+#include "document.hxx"
+#include "opbase.hxx"
+
+namespace sc { namespace opencl {
+
+class DynamicKernelArgument;
+class SlidingFunctionBase;
+
+/// Holds the symbol table for a given dynamic kernel
+class SymbolTable {
+public:
+    typedef std::map<const formula::FormulaToken *,
+        boost::shared_ptr<DynamicKernelArgument> > ArgumentMap;
+    // This avoids instability caused by using pointer as the key type
+    typedef std::list< boost::shared_ptr<DynamicKernelArgument> > ArgumentList;
+    SymbolTable(void):mCurId(0) {}
+    template <class T>
+    const DynamicKernelArgument *DeclRefArg(FormulaTreeNodeRef, SlidingFunctionBase* pCodeGen);
+    /// Used to generate sliding window helpers
+    void DumpSlidingWindowFunctions(std::stringstream &ss);
+    /// Memory mapping from host to device and pass buffers to the given kernel as
+    /// arguments
+    void Marshal(cl_kernel, int, cl_program);
+private:
+    unsigned int mCurId;
+    ArgumentMap mSymbols;
+    ArgumentList mParams;
+};
+
+class DynamicKernel : public CompiledFormula
+{
+public:
+    DynamicKernel(FormulaTreeNodeRef r):mpRoot(r),
+        mpProgram(NULL), mpKernel(NULL), mpResClmem(NULL) {}
+    static DynamicKernel *create(ScDocument& rDoc,
+                                 const ScAddress& rTopPos,
+                                 ScTokenArray& rCode);
+    /// Code generation in OpenCL
+    void CodeGen();
+    /// Produce kernel hash
+    std::string GetMD5(void);
+    /// Create program, build, and create kerenl
+    /// TODO cache results based on kernel body hash
+    /// TODO: abstract OpenCL part out into OpenCL wrapper.
+    void CreateKernel(void);
+    /// Prepare buffers, marshal them to GPU, and launch the kernel
+    /// TODO: abstract OpenCL part out into OpenCL wrapper.
+    void Launch(size_t nr);
+    ~DynamicKernel();
+    cl_mem GetResultBuffer(void) const { return mpResClmem; }
+private:
+    void TraverseAST(FormulaTreeNodeRef);
+    FormulaTreeNodeRef mpRoot;
+    SymbolTable mSyms;
+    std::string mKernelSignature, mKernelHash;
+    std::string mFullProgramSrc;
+    cl_program mpProgram;
+    cl_kernel mpKernel;
+    cl_mem mpResClmem; // Results
+    std::set<std::string> inlineDecl;
+    std::set<std::string> inlineFun;
+};
+
+}
+
+}
+
+#endif // HAVE_FEATURE_OPENCL
+
+#endif // INCLUDED_SC_SOURCE_CORE_INC_DYNAMICKERNEL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/core/opencl/formulagroupcl.cxx b/sc/source/core/opencl/formulagroupcl.cxx
index 40df1ae..17536b6 100644
--- a/sc/source/core/opencl/formulagroupcl.cxx
+++ b/sc/source/core/opencl/formulagroupcl.cxx
@@ -16,6 +16,7 @@
 #include "formula/vectortoken.hxx"
 #include "scmatrix.hxx"
 
+#include "dynamickernel.hxx"
 #include "openclwrapper.hxx"
 
 #include "op_financial.hxx"
@@ -44,6 +45,7 @@
 
 #include <boost/scoped_ptr.hpp>
 
+#undef NO_FALLBACK_TO_SWINTERP /* undef this for non-TDD runs */
 
 using namespace formula;
 
@@ -2377,33 +2379,16 @@ DynamicKernelSoPArguments::DynamicKernelSoPArguments(
         };
     }
 }
-/// Holds the symbol table for a given dynamic kernel
-class SymbolTable {
-public:
-    typedef std::map<const FormulaToken *,
-        boost::shared_ptr<DynamicKernelArgument> > ArgumentMap;
-    // This avoids instability caused by using pointer as the key type

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list