1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
12 #include <clew/clew.h>
13 #include <formula/token.hxx>
14 #include <formula/types.hxx>
15 #include <formula/vectortoken.hxx>
16 #include <opencl/OpenCLZone.hxx>
17 #include <sal/log.hxx>
25 namespace sc::opencl
{
27 // FIXME: The idea that somebody would bother to (now and then? once a year? once a month?) manually
28 // edit a source file and change the value of some #defined constant and run some ill-defined
29 // "correctness test" is of course ludicrous. Either things are checked in normal unit tests, in
30 // every 'make check', or not at all. The below comments are ridiculous.
32 constexpr auto REDUCE_THRESHOLD
= 201; // set to 4 for correctness testing. priority 1
33 constexpr auto UNROLLING_FACTOR
= 16; // set to 4 for correctness testing (if no reduce)
36 class FormulaTreeNode
;
44 UnhandledToken( const char* m
, std::string fn
, int ln
);
51 /// Failed in marshaling
55 OpenCLError( std::string function
, cl_int error
, std::string file
, int line
);
57 std::string mFunction
;
63 /// Inconsistent state
67 Unhandled( std::string fn
, int ln
);
73 class InvalidParameterCount
76 InvalidParameterCount( int parameterCount
, std::string file
, int ln
);
80 int const mLineNumber
;
83 // Helper macros to be used in code emitting OpenCL code for Calc functions.
84 // Require the vSubArguments parameter.
85 #define CHECK_PARAMETER_COUNT(min, max) \
87 const int count = vSubArguments.size(); \
88 if( count < ( min ) || count > ( max )) \
89 throw InvalidParameterCount( count, __FILE__, __LINE__ ); \
91 #define CHECK_PARAMETER_COUNT_MIN(min) \
93 const int count = vSubArguments.size(); \
94 if( count < ( min )) \
95 throw InvalidParameterCount( count, __FILE__, __LINE__ ); \
97 #define CHECK_PARAMETER_DOUBLEVECTORREF(arg) \
99 formula::FormulaToken *token = vSubArguments[arg]->GetFormulaToken(); \
100 if (token == nullptr || token->GetType() != formula::svDoubleVectorRef) \
101 throw Unhandled(__FILE__, __LINE__); \
104 typedef std::shared_ptr
<FormulaTreeNode
> FormulaTreeNodeRef
;
106 class FormulaTreeNode
109 explicit FormulaTreeNode( const formula::FormulaToken
* ft
) : mpCurrentFormula(ft
)
113 std::vector
<FormulaTreeNodeRef
> Children
;
114 formula::FormulaToken
* GetFormulaToken() const
116 return const_cast<formula::FormulaToken
*>(mpCurrentFormula
.get());
120 formula::FormulaConstTokenRef mpCurrentFormula
;
123 /// (Partially) abstract base class for an operand
124 class DynamicKernelArgument
127 /// delete copy constructor
128 DynamicKernelArgument( const DynamicKernelArgument
& ) = delete;
130 /// delete copy-assignment operator
131 const DynamicKernelArgument
& operator=( const DynamicKernelArgument
& ) = delete;
133 DynamicKernelArgument( const ScCalcConfig
& config
, std::string s
, FormulaTreeNodeRef ft
);
134 virtual ~DynamicKernelArgument() {}
136 /// Generate declaration
137 virtual void GenDecl( outputstream
& ss
) const = 0;
139 /// When declared as input to a sliding window function
140 virtual void GenSlidingWindowDecl( outputstream
& ss
) const = 0;
142 /// When referenced in a sliding window function
143 virtual std::string
GenSlidingWindowDeclRef( bool = false ) const = 0;
145 /// Create buffer and pass the buffer to a given kernel
146 virtual size_t Marshal( cl_kernel
, int, int, cl_program
) = 0;
148 virtual size_t GetWindowSize() const = 0;
150 /// When Mix, it will be called
151 virtual std::string
GenDoubleSlidingWindowDeclRef( bool = false ) const;
153 /// When Mix, it will be called
154 virtual std::string
GenStringSlidingWindowDeclRef( bool = false ) const;
156 /// Will generate value saying whether the value is a string.
157 virtual std::string
GenIsString( bool = false ) const { return "false"; }
159 /// Generate use/references to the argument
160 virtual void GenDeclRef( outputstream
& ss
) const;
162 virtual void GenSlidingWindowFunction( outputstream
& );
163 formula::FormulaToken
* GetFormulaToken() const;
164 virtual std::string
DumpOpName() const;
165 virtual void DumpInlineFun( std::set
<std::string
>&, std::set
<std::string
>& ) const;
166 const std::string
& GetName() const;
167 virtual bool NeedParallelReduction() const;
168 /// If there's actually no argument, i.e. it expands to no code.
169 virtual bool IsEmpty() const { return false; }
171 static void ClearStringIds();
174 const ScCalcConfig
& mCalcConfig
;
175 std::string mSymName
;
176 FormulaTreeNodeRef mFormulaTree
;
177 static int GetStringId( const rtl_uString
* string
);
180 typedef std::shared_ptr
<DynamicKernelArgument
> DynamicKernelArgumentRef
;
182 /// Holds an input (read-only) argument reference to a SingleVectorRef.
183 /// or a DoubleVectorRef for non-sliding-window argument of complex functions
184 /// like SumOfProduct
185 /// In most of the cases the argument is introduced
186 /// by a Push operation in the given RPN.
187 class VectorRef
: public DynamicKernelArgument
190 VectorRef( const ScCalcConfig
& config
, const std::string
& s
, const FormulaTreeNodeRef
& ft
, int index
= 0 );
191 virtual ~VectorRef() override
;
193 /// Generate declaration
194 virtual void GenDecl( outputstream
& ss
) const override
;
195 /// When declared as input to a sliding window function
196 virtual void GenSlidingWindowDecl( outputstream
& ss
) const override
;
198 /// When referenced in a sliding window function
199 virtual std::string
GenSlidingWindowDeclRef( bool = false ) const override
;
201 /// Create buffer and pass the buffer to a given kernel
202 virtual size_t Marshal( cl_kernel
, int, int, cl_program
) override
;
204 virtual void GenSlidingWindowFunction( outputstream
& ) override
;
205 virtual size_t GetWindowSize() const override
;
206 virtual std::string
DumpOpName() const override
;
207 virtual void DumpInlineFun( std::set
<std::string
>&, std::set
<std::string
>& ) const override
;
208 const std::string
& GetName() const;
209 cl_mem
GetCLBuffer() const;
210 virtual bool NeedParallelReduction() const override
;
213 // Used by marshaling
215 // index in multiple double vector refs that have multiple ranges
217 // Makes Marshall convert strings to 0 values.
218 bool forceStringsToZero
;
219 // Used for storing when the data needs to be modified before sending to OpenCL.
220 std::vector
< double > dataBuffer
;
223 // Sets VectorRef::forceStringsToZero.
224 class VectorRefStringsToZero
: public VectorRef
227 VectorRefStringsToZero( const ScCalcConfig
& config
, const std::string
& s
, const FormulaTreeNodeRef
& ft
, int index
= 0 );
230 /// A vector of strings
231 class DynamicKernelStringArgument
: public VectorRef
234 DynamicKernelStringArgument( const ScCalcConfig
& config
, const std::string
& s
,
235 const FormulaTreeNodeRef
& ft
, int index
= 0 ) :
236 VectorRef(config
, s
, ft
, index
) { }
238 virtual void GenSlidingWindowFunction( outputstream
& ) override
{ }
239 /// Generate declaration
240 virtual void GenDecl( outputstream
& ss
) const override
242 ss
<< "__global double *" << mSymName
;
244 virtual void GenSlidingWindowDecl( outputstream
& ss
) const override
246 DynamicKernelStringArgument::GenDecl(ss
);
248 virtual std::string
GenIsString( bool = false ) const override
;
249 virtual size_t Marshal( cl_kernel
, int, int, cl_program
) override
;
252 /// Arguments that are actually compile-time constants
253 class DynamicKernelConstantArgument
: public DynamicKernelArgument
256 DynamicKernelConstantArgument( const ScCalcConfig
& config
, const std::string
& s
,
257 const FormulaTreeNodeRef
& ft
) :
258 DynamicKernelArgument(config
, s
, ft
) { }
259 /// Generate declaration
260 virtual void GenDecl( outputstream
& ss
) const override
262 ss
<< "double " << mSymName
;
264 virtual void GenDeclRef( outputstream
& ss
) const override
268 virtual void GenSlidingWindowDecl( outputstream
& ss
) const override
272 virtual std::string
GenSlidingWindowDeclRef( bool = false ) const override
274 if (GetFormulaToken()->GetType() != formula::svDouble
)
275 throw Unhandled(__FILE__
, __LINE__
);
278 virtual size_t GetWindowSize() const override
282 virtual double GetDouble() const
284 formula::FormulaToken
* Tok
= GetFormulaToken();
285 if (Tok
->GetType() != formula::svDouble
)
286 throw Unhandled(__FILE__
, __LINE__
);
287 return Tok
->GetDouble();
289 /// Create buffer and pass the buffer to a given kernel
290 virtual size_t Marshal( cl_kernel k
, int argno
, int, cl_program
) override
293 double tmp
= GetDouble();
294 // Pass the scalar result back to the rest of the formula kernel
295 SAL_INFO("sc.opencl", "Kernel " << k
<< " arg " << argno
<< ": double: " << preciseFloat( tmp
));
296 cl_int err
= clSetKernelArg(k
, argno
, sizeof(double), static_cast<void*>(&tmp
));
297 if (CL_SUCCESS
!= err
)
298 throw OpenCLError("clSetKernelArg", err
, __FILE__
, __LINE__
);
303 // Constant 0 argument when a string is forced to zero.
304 class DynamicKernelStringToZeroArgument
: public DynamicKernelConstantArgument
307 DynamicKernelStringToZeroArgument( const ScCalcConfig
& config
, const std::string
& s
,
308 const FormulaTreeNodeRef
& ft
) :
309 DynamicKernelConstantArgument(config
, s
, ft
) { }
310 virtual std::string
GenSlidingWindowDeclRef( bool = false ) const override
314 virtual double GetDouble() const override
{ return 0; }
318 /// Abstract class for code generation
322 // FIXME: What exactly is this? It seems to be a starting value for some calculations
323 // (1 for OpMul, MAXFLOAT for OpMin), but it's often used pointlessly and sometimes
324 // even incorrectly (default value for when the cell is empty).
325 virtual std::string
GetBottom() { return "";};
326 virtual std::string
Gen2( const std::string
&/*lhs*/,
327 const std::string
&/*rhs*/ ) const { return "";}
328 static std::string
Gen( std::vector
<std::string
>& /*argVector*/ ) { return "";};
329 virtual std::string
BinFuncName() const { return "";};
330 virtual void BinInlineFun( std::set
<std::string
>&,
331 std::set
<std::string
>& ) { }
332 virtual bool takeString() const = 0;
333 virtual bool takeNumeric() const = 0;
334 // Whether DoubleRef containing more than one column is handled properly.
335 virtual bool canHandleMultiVector() const { return false; }
336 //Continue process 'Zero' or Not(like OpMul, not continue process when meet
338 virtual bool ZeroReturnZero() { return false;}
339 // For use with COUNTA() etc, input strings will be converted to 0 in data.
340 virtual bool forceStringsToZero() const { return false; }
341 virtual ~OpBase() { }
344 class SlidingFunctionBase
: public OpBase
347 typedef std::vector
<DynamicKernelArgumentRef
> SubArguments
;
348 virtual void GenSlidingWindowFunction( outputstream
&,
349 const std::string
&, SubArguments
& ) = 0;
351 // This enum controls how the generated code will handle empty cells in ranges.
354 EmptyIsZero
, // empty cells become 0.0
355 EmptyIsNan
, // empty cells become NAN, use isnan() to check in code
356 SkipEmpty
// empty cells will be skipped
358 // This enum controls whether the generated code will also include variable
359 // <name>_is_string that will be set depending on the value type.
360 enum GenerateArgTypeType
362 DoNotGenerateArgType
,
365 void GenerateFunctionDeclaration( const std::string
& sSymName
,
366 SubArguments
& vSubArguments
, outputstream
& ss
);
367 // Generate code for "double <name> = <value>;" from vSubArguments, svDoubleVectorRef is not supported.
368 void GenerateArg( const char* name
, int arg
, SubArguments
& vSubArguments
, outputstream
& ss
,
369 EmptyArgType empty
= EmptyIsZero
, GenerateArgTypeType generateType
= DoNotGenerateArgType
);
370 // overload, variable will be named "arg<arg>"
371 void GenerateArg( int arg
, SubArguments
& vSubArguments
, outputstream
& ss
,
372 EmptyArgType empty
= EmptyIsZero
, GenerateArgTypeType generateType
= DoNotGenerateArgType
);
373 // generate code for "double <name> = <value>;" from vSubArguments, if it exists,
374 // otherwise set to <def>
375 void GenerateArgWithDefault( const char* name
, int arg
, double def
, SubArguments
& vSubArguments
,
376 outputstream
& ss
, EmptyArgType empty
= EmptyIsZero
);
377 // Generate code that will handle all arguments firstArg-lastArg (zero-based, inclusive),
378 // including range arguments (svDoubleVectorRef) and each value will be processed by 'code',
379 // value will be named "arg".
380 static void GenerateRangeArgs( int firstArg
, int lastArg
, SubArguments
& vSubArguments
,
381 outputstream
& ss
, EmptyArgType empty
, const char* code
);
382 // overload, handle all arguments
383 static void GenerateRangeArgs( SubArguments
& vSubArguments
, outputstream
& ss
,
384 EmptyArgType empty
, const char* code
);
385 // overload, handle the given argument
386 static void GenerateRangeArg( int arg
, SubArguments
& vSubArguments
, outputstream
& ss
,
387 EmptyArgType empty
, const char* code
);
389 // Both arguments must be svDoubleRef of the same size.
390 // If 'firstElementDiff' is set, the loop start will be offset by '+ firstElementDiff'.
391 void GenerateRangeArg( int arg1
, int arg2
, SubArguments
& vSubArguments
,
392 outputstream
& ss
, EmptyArgType empty
, const char* code
, const char* firstElementDiff
= nullptr );
393 // Generate code that will handle the given two arguments in one loop where n-th element of arg1 and arg2
394 // will be handled at the same time, named 'arg1' and 'arg2'.
395 // Both arguments must be svDoubleRef of the same size.
396 // If 'firstElementDiff' is set, the loop start will be offset by '+ firstElementDiff'.
397 static void GenerateRangeArgPair( int arg1
, int arg2
, SubArguments
& vSubArguments
,
398 outputstream
& ss
, EmptyArgType empty
, const char* code
, const char* firstElementDiff
= nullptr );
399 // Generate code for "double <name> = range[<element>]" from vSubArguments.
400 // The argument must be svDoubleRef.
401 static void GenerateRangeArgElement( const char* name
, int arg
, const char* element
,
402 SubArguments
& vSubArguments
, outputstream
& ss
, EmptyArgType empty
);
403 static void GenerateDoubleVectorLoopHeader( outputstream
& ss
,
404 const formula::DoubleVectorRefToken
* pDVR
, const char* firstElementDiff
);
407 class Normal
: public SlidingFunctionBase
410 virtual void GenSlidingWindowFunction( outputstream
& ss
,
411 const std::string
& sSymName
, SubArguments
& vSubArguments
) override
;
412 virtual bool takeString() const override
{ return false; }
413 virtual bool takeNumeric() const override
{ return true; }
416 class CheckVariables
: public Normal
419 static void GenTmpVariables( outputstream
& ss
, const SubArguments
& vSubArguments
);
420 static void CheckSubArgumentIsNan( outputstream
& ss
,
421 SubArguments
& vSubArguments
, int argumentNum
);
422 static void CheckAllSubArgumentIsNan( outputstream
& ss
,
423 SubArguments
& vSubArguments
);
425 static void CheckSubArgumentIsNan2( outputstream
& ss
,
426 SubArguments
& vSubArguments
, int argumentNum
, const std::string
& p
);
427 static void UnrollDoubleVector( outputstream
& ss
,
428 const outputstream
& unrollstr
, const formula::DoubleVectorRefToken
* pCurDVR
,
429 int nCurWindowSize
);
435 /// Handling a Double Vector that is used as a sliding window input
436 /// to either a sliding window average or sum-of-products
437 /// Generate a sequential loop for reductions
439 class DynamicKernelSlidingArgument
: public Base
442 DynamicKernelSlidingArgument(const ScCalcConfig
& config
, const std::string
& s
,
443 const FormulaTreeNodeRef
& ft
,
444 std::shared_ptr
<SlidingFunctionBase
> CodeGen
, int index
);
445 // Should only be called by SumIfs. Yikes!
446 virtual bool NeedParallelReduction() const;
447 virtual void GenSlidingWindowFunction( outputstream
& ) { }
449 std::string
GenSlidingWindowDeclRef( bool nested
= false ) const;
450 /// Controls how the elements in the DoubleVectorRef are traversed
451 size_t GenReductionLoopHeader( outputstream
& ss
, bool& needBody
);
453 size_t GetArrayLength() const { return mpDVR
->GetArrayLength(); }
455 size_t GetWindowSize() const { return mpDVR
->GetRefRowSize(); }
457 bool GetStartFixed() const { return bIsStartFixed
; }
459 bool GetEndFixed() const { return bIsEndFixed
; }
462 bool bIsStartFixed
, bIsEndFixed
;
463 const formula::DoubleVectorRefToken
* mpDVR
;
465 std::shared_ptr
<SlidingFunctionBase
> mpCodeGen
;
468 /// Handling a Double Vector that is used as a sliding window input
469 /// Performs parallel reduction based on given operator
471 class ParallelReductionVectorRef
: public Base
474 ParallelReductionVectorRef(const ScCalcConfig
& config
, const std::string
& s
,
475 const FormulaTreeNodeRef
& ft
,
476 std::shared_ptr
<SlidingFunctionBase
> CodeGen
, int index
);
477 ~ParallelReductionVectorRef();
479 /// Emit the definition for the auxiliary reduction kernel
480 virtual void GenSlidingWindowFunction( outputstream
& ss
);
481 virtual std::string
GenSlidingWindowDeclRef( bool ) const;
482 /// Controls how the elements in the DoubleVectorRef are traversed
483 size_t GenReductionLoopHeader( outputstream
& ss
, int nResultSize
, bool& needBody
);
484 virtual size_t Marshal( cl_kernel k
, int argno
, int w
, cl_program mpProgram
);
485 size_t GetArrayLength() const { return mpDVR
->GetArrayLength(); }
486 size_t GetWindowSize() const { return mpDVR
->GetRefRowSize(); }
487 bool GetStartFixed() const { return bIsStartFixed
; }
488 bool GetEndFixed() const { return bIsEndFixed
; }
491 bool bIsStartFixed
, bIsEndFixed
;
492 const formula::DoubleVectorRefToken
* mpDVR
;
494 std::shared_ptr
<SlidingFunctionBase
> mpCodeGen
;
495 // controls whether to invoke the reduction kernel during marshaling or not
499 class Reduction
: public SlidingFunctionBase
501 int const mnResultSize
;
503 explicit Reduction(int nResultSize
) : mnResultSize(nResultSize
) {}
505 typedef DynamicKernelSlidingArgument
<VectorRef
> NumericRange
;
506 typedef DynamicKernelSlidingArgument
<VectorRefStringsToZero
> NumericRangeStringsToZero
;
507 typedef DynamicKernelSlidingArgument
<DynamicKernelStringArgument
> StringRange
;
508 typedef ParallelReductionVectorRef
<VectorRef
> ParallelNumericRange
;
510 virtual bool HandleNaNArgument( outputstream
&, unsigned, SubArguments
& ) const
515 virtual void GenSlidingWindowFunction( outputstream
& ss
,
516 const std::string
& sSymName
, SubArguments
& vSubArguments
) override
;
517 virtual bool isAverage() const { return false; }
518 virtual bool isMinOrMax() const { return false; }
519 virtual bool takeString() const override
{ return false; }
520 virtual bool takeNumeric() const override
{ return true; }
525 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */