Fix duplication of lpp.cl process calls

Fix duplication of lpp.cl process calls

(defstruct param type name deref funcset)
(defstruct funcdef funcset funcname params returntype)

(setq preprocessor-file-extension "lpp")
(setq begin-marker-left "^^^BEGINREPLACE:")
(setq begin-marker-right "^^^")
(setq end-marker "^^^ENDREPLACE^^^")
(setq auto-header-marker "^^^AUTOGENHEADER^^^")
(setq token-marker #\^)
(setq token-arg-marker #\:)
(setq token-arg-delimiter #\,)

(defstruct lppmacro macro filter)

(setq macro-hash (make-hash-table :test 'equal))
(defun add-macro (&rest args)
  (let ((m (apply #'make-lppmacro args)))
    (setf (gethash (lppmacro-macro m) macro-hash) m)))
(defun get-macro (key) (gethash key macro-hash))

(setq void-param-hash (make-hash-table :test 'equal))
(defun add-void-param (&rest args)
  (let* ((p (apply #'make-param args))
         (funcset-name (param-funcset p)))
    (cond ((not (null funcset-name))
           (setf (gethash funcset-name void-param-hash) p))
          (t (error "Must provide :funcset for add-void-param")))))

(defun get-void-param (funcset-name) (gethash funcset-name void-param-hash))

(add-macro :macro "RETURN"      :filter (lambda (fd len args vp) (funcdef-returntype fd)))
(add-macro :macro "FUNC"        :filter (lambda (fd len args vp) (funcdef-funcname fd)))
(add-macro :macro "PARAMS"      :filter (lambda (fd len args vp) (format-params (funcdef-params fd) len args vp)))

;; PARAMS arguments: types, names, deref, ref, void, voidonly, nowrap
;;;;;;;;;;;;;;;;;;

(defun partial (func &rest args1)
  (lambda (&rest args2) (apply func (append args1 args2))))

(defun newline ()
  (write-string (format nil "~%")))

(defun concat-string-list (s)
  (format nil "~{~A~^~%~}" s))

(defun replace-all (string part replacement &key (test #'char=))
  "Returns a new string in which all the occurences of the part
is replaced with replacement."
  (with-output-to-string (out)
    (loop with part-length = (length part)
       for old-pos = 0 then (+ pos part-length)
       for pos = (search part string
                         :start2 old-pos
                         :test test)
       do (write-string string out
                        :start old-pos
                        :end (or pos (length string)))
       when pos do (write-string replacement out)
       while pos)))

(defun back-to-forward-slashes (str)
  (replace-all str "\\" "/")
)

(defun is-arg-set (target-arg args)
  (not (null (find target-arg args :test 'equal))))

(defun format-paramitem-type (args p)
  (format nil "~A" (param-type p)))

(defun format-paramitem-name (args p)
  ;;in name only format, we can dereference the argument (pointer)
  ;;but only if the macro argument requested it
  ;;and this parameter can be dereferenced
  (if (and (is-arg-set "deref" args) (param-deref p))
      ;;then
      (format nil "*~A" (param-name p))
      ;;else
      (format nil "~A" (param-name p))))

(defun format-paramitem-full (args p)
  (let* ((type-decl (param-type p))
         (last-index (1- (length type-decl))))
    (format nil "~A ~A"
            ;;if macro requested reference mode,
            ;; this parameter is dereferencable,
            ;; and the last char is a *
            (if (and (is-arg-set "ref" args) (param-deref p) (equal "*" (subseq type-decl last-index)))
                ;;then, replace the last * with a &
                (concatenate 'string (subseq type-decl 0 last-index) "&")
                ;;else
                type-decl)
            (param-name p))))

(defun error-if-null (var errmsg) (when (null var) (error errmsg)))

(defun format-params (params token-start-length args void-param)
  ;;full, types, names, deref, ref, void, voidonly, nowrap
  (let*  ((arg-types      (is-arg-set "types" args))
          (arg-names      (is-arg-set "names" args))
          (arg-deref      (is-arg-set "deref" args))
          (arg-ref        (is-arg-set "ref" args))
          (arg-void       (is-arg-set "void" args))
          (arg-voidonly   (is-arg-set "voidonly" args))
          (arg-wrap       (is-arg-set "wrap" args))
          (arg-nowrap     (is-arg-set "nowrap" args))
          (filtered-params (cond  (arg-voidonly
                                   (error-if-null void-param "Void param must be set to use void argument")
                                   (cons void-param nil)) ;;only the void-param
                                  (arg-void
                                   (error-if-null void-param "Void param must be set to use void argument")
                                   (cons void-param params)) ;;prepend the void-param
                                  (t params))) ;;default, just use params
          (types-only (and arg-types (not arg-names)))
          (names-only (and arg-names (not arg-types)))
          (full-decl  (not (xor arg-names arg-types)))
          (format-param-func (cond  (full-decl  #'format-paramitem-full)
                                    (types-only #'format-paramitem-type)
                                    (names-only #'format-paramitem-name)))
          (do-wrap    (or arg-wrap (not (or names-only arg-nowrap))))
          (format-line (if do-wrap "~{~A~^,~%~}" "~{~A~^, ~}")))
    (when filtered-params
      (format nil format-line
              (cons
               ;;first parameter => no indentation
               (apply format-param-func (list args (car filtered-params)))
               (mapcar
                (lambda (p)
                  (concatenate 'string
                               ;;second parameter, indent according to wrap policy
                               (when do-wrap (make-string token-start-length :initial-element #\ ))
                               (apply format-param-func (list args p))))
                (cdr filtered-params)))))))

(defun split-by-delimiter (string delimiter)
  (loop   for start = 0 then (1+ finish)
     for finish = (position delimiter string :start start)
     if (and (not (eq (length string) start))
             (not (eq start finish)))
     collect (subseq string start finish)
     until (null finish)))

(defun process-tokens (line funcdata void-param &optional (line-length 0))
  (let ((token-marker-left (position token-marker line)))
    (if (null token-marker-left)
        ;;then, no left token marker. return line
        line
        ;;else, process more
        (let* ((token-start (1+ token-marker-left))
               (token-stop (position token-marker line :start token-start)))
          (if (null token-stop)
              ;;then, no right token marker. return line
              line
              ;;else, process more
              (let* ((token-full (subseq line token-start token-stop))
                     (seperator-index (position token-arg-marker token-full))
                     (token (subseq token-full 0 seperator-index))
                     (token-args (if (not (null seperator-index))
                                     (split-by-delimiter
                                      (subseq token-full (1+ seperator-index) nil) token-arg-delimiter)
                                     '(nil)))
                     (line-begin (subseq line 0 token-marker-left))
                     (line-end (subseq line (1+ token-stop) nil))
                     (m (get-macro token)))
                ;;m (the macro struct) is nil if no macro found for the token
                (if (null m)
                    ;;then
                    line
                    ;;else
                    (let* ((token-start-length (+ line-length token-marker-left))
                           (expanded-token
                            (funcall (lppmacro-filter m)
                                     funcdata
                                     token-start-length
                                     token-args void-param)))
                      (concatenate 'string
                                   line-begin
                                   expanded-token
                                   (process-tokens
                                    line-end
                                    funcdata
                                    void-param
                                    (+ token-start-length (length expanded-token))))))))))))

(defun expand-template (template-list func-data void-param)
  (concat-string-list
   (mapcar
    (lambda (template-line) (process-tokens template-line func-data void-param))
    template-list)))

(defun expand-methods-with-template (template-list funcset-name)
  (remove nil ;;filter out the nil values (from wrong funcsets)
          (mapcar
           (lambda (func-data)
             (cond ;;only expand this function if it belongs to the target funcset
               ((equal funcset-name (funcdef-funcset func-data))
                (expand-template template-list func-data (get-void-param funcset-name)))
               ;;otherwise return nil
               (t nil)))
           funcs)))

(defun clear-funcs ()
  (setq funcs nil)
  (setq funcs-tail funcs))

(clear-funcs)

(defun add-func (&rest args)
  (let ((func-data (apply #'make-funcdef args)))
    (if (endp funcs)
        ;;then
        (progn
          (setq funcs (cons func-data nil))
          (setq funcs-tail funcs))
        ;;else
        (progn
          (setf (cdr funcs-tail) (cons func-data nil))
          (setq funcs-tail (cdr funcs-tail))))))

(defun outputfuncs (f)
  (write-line (concat-string-list f)))

(defun filter-line (line filename)
  (replace-all line
               auto-header-marker
               (format nil "THIS FILE AUTO-GENERATED FROM ~A. DO NOT EDIT." filename)))

(defmacro prepend-into (var value)
  `(setq ,var (cons ,value ,var)))

(defun strip-end-characters (str numChars)
  (let ((index (- (length str) numChars)))
    (if (>= index 1)
        (subseq str 0 index)
        nil)))

(defun get-funcset-from-begin-marker (line)
  (let ((start-index (search begin-marker-left line)))
    (cond ;;no begin marker, return nil
      ((null start-index) nil)

      ;;begin marker must be at start of line
      ((not (= start-index 0)) nil)

      ;;left side is good
      (t (let*  ((start-param-index (+ start-index (length begin-marker-left)))
                 (end-index (search begin-marker-right line :start2 start-param-index)))
           (cond ;;no right side marker, return nil
             ((null end-index) nil)

             ;;right marker must be at end of line
             ((not (= (length line) (+ end-index (length begin-marker-right)))) nil)

             ;;markers but no funcset parameter, can't process
             ((= start-param-index end-index) nil)
             ;;we have a begin-marker and funcset parameter!
             (t (values (subseq line start-param-index end-index) start-param-index end-index))))))))

(defun is-end-marker (line)
  (equal line end-marker))

(defun in-to-out-filename (fn)
  (strip-end-characters fn 4))

(defun process-file (infilename)
  (let ((infile (open infilename :if-does-not-exist nil))
        (outfile (open (in-to-out-filename infilename) :direction :output :if-exists :supersede))
        (filelines nil)
        (template-lines nil)
        (funcset-name nil))
    (when infile
      (loop
         for line = (read-line infile nil)
         while line
         do
           (cond   ;;outside of a replace block
             ((null funcset-name)
              ;;look for a begin-replace marker (funcset name becomes not nil)
              (setq funcset-name (get-funcset-from-begin-marker line))
              ;;if still no funcset-name
              (when (null funcset-name)
                ;;accumulate filtered lines to filelines
                (prepend-into filelines (filter-line line (file-namestring infilename)))))

             ;;all below: funcset-name is not nil -- inside of a replace block
             ;;check for end marker
             ((is-end-marker line)
              ;;since we have a full template, expand template it
              (mapcar
               (lambda (v) (prepend-into filelines v))
               (expand-methods-with-template (reverse template-lines) funcset-name))
              (setq funcset-name nil)  ;;clear funcset-name -- return to normal operation above
              (setq template-lines nil) ;;clear template so we can process another one in the same file
             )
             ;;inside replace block, accumulate template
             (t (prepend-into template-lines line))))
      (loop for line in (reverse filelines)
         do (write-line line outfile))
      (close infile)
      (close outfile))))

(defvar api-file "apidef.cl")
(defvar lpp-file "lpp.cl")

(load api-file :verbose nil)
(load "cl-fad/load.lisp" :verbose nil)

(defun mapc-directory-tree (fn directory)
  (dolist (entry (cl-fad:list-directory directory))
    (when (cl-fad:directory-pathname-p entry)
      (mapc-directory-tree fn entry))
    (funcall fn entry)))

(defun _ () (load lpp-file :verbose nil))

(defun file-modified-time (f)
  (cond ((probe-file f) (posix:file-stat-mtime (posix:file-stat f)))
        (t 0)
  )
)

(defun process (dir)
  (let* ((target-directory (if (null dir)
                               (ext:cd)
                               dir))
         (cache-file-path (cl-fad:merge-pathnames-as-file
                           (cl-fad:pathname-as-directory target-directory)
                           ".pp-modification-cache.cl"))
         (api-modify (file-modified-time api-file))
         (lpp-modify (file-modified-time lpp-file))
         (pp-cache (with-open-file (fsi cache-file-path :if-does-not-exist nil)
                      (cond ((null fsi) (make-hash-table :test 'equal) )
                            (t (let ((data (read fsi nil)))
                                  (if (hash-table-p data)
                                      data
                                      (make-hash-table :test 'equal)
                                   )
                               ))
                      )
                   ))
        )
    (write-line (format nil "=== LPP processing directory: ~A ===~%" target-directory))
    (mapc-directory-tree (lambda (x)
                           (when (equal (pathname-type x) preprocessor-file-extension)
                             (let* ((fwd-path (back-to-forward-slashes (namestring x)))
                                    (cache-modify (gethash fwd-path pp-cache 0))
                                    (last-modify (max lpp-modify api-modify (file-modified-time x)))
                                   )
                               (when (> last-modify cache-modify)
                                 (write-line (format nil "~A => ~A"
                                                     (enough-namestring x target-directory)
                                                     (in-to-out-filename (file-namestring x))
                                             ))

                                 (process-file (namestring x))
                                 (setf (gethash fwd-path pp-cache) last-modify)
                               )
                             )
                           )
                         )
                         target-directory)
    (with-open-file (fso cache-file-path :direction :output :if-does-not-exist :create :if-exists :supersede)
      (write pp-cache :stream fso)
    )
  )
)

(process (car *args*))
;(process "../include")
;(process "../src")

(defstruct param type name deref funcset)
(defstruct funcdef funcset funcname params returntype)

(setq preprocessor-file-extension "lpp")
(setq begin-marker-left "^^^BEGINREPLACE:")
(setq begin-marker-right "^^^")
(setq end-marker "^^^ENDREPLACE^^^")
(setq auto-header-marker "^^^AUTOGENHEADER^^^")
(setq token-marker #\^)
(setq token-arg-marker #\:)
(setq token-arg-delimiter #\,)

(defstruct lppmacro macro filter)

(setq macro-hash (make-hash-table :test 'equal))
(defun add-macro (&rest args)
  (let ((m (apply #'make-lppmacro args)))
    (setf (gethash (lppmacro-macro m) macro-hash) m)))
(defun get-macro (key) (gethash key macro-hash))

(setq void-param-hash (make-hash-table :test 'equal))
(defun add-void-param (&rest args)
  (let* ((p (apply #'make-param args))
         (funcset-name (param-funcset p)))
    (cond ((not (null funcset-name))
           (setf (gethash funcset-name void-param-hash) p))
          (t (error "Must provide :funcset for add-void-param")))))

(defun get-void-param (funcset-name) (gethash funcset-name void-param-hash))

(add-macro :macro "RETURN"      :filter (lambda (fd len args vp) (funcdef-returntype fd)))
(add-macro :macro "FUNC"        :filter (lambda (fd len args vp) (funcdef-funcname fd)))
(add-macro :macro "PARAMS"      :filter (lambda (fd len args vp) (format-params (funcdef-params fd) len args vp)))

;; PARAMS arguments: types, names, deref, ref, void, voidonly, nowrap
;;;;;;;;;;;;;;;;;;

(defun partial (func &rest args1)
  (lambda (&rest args2) (apply func (append args1 args2))))

(defun newline ()
  (write-string (format nil "~%")))

(defun concat-string-list (s)
  (format nil "~{~A~^~%~}" s))

(defun replace-all (string part replacement &key (test #'char=))
  "Returns a new string in which all the occurences of the part
is replaced with replacement."
  (with-output-to-string (out)
    (loop with part-length = (length part)
       for old-pos = 0 then (+ pos part-length)
       for pos = (search part string
                         :start2 old-pos
                         :test test)
       do (write-string string out
                        :start old-pos
                        :end (or pos (length string)))
       when pos do (write-string replacement out)
       while pos)))

(defun back-to-forward-slashes (str)
  (replace-all str "\\" "/")
)

(defun is-arg-set (target-arg args)
  (not (null (find target-arg args :test 'equal))))

(defun format-paramitem-type (args p)
  (format nil "~A" (param-type p)))

(defun format-paramitem-name (args p)
  ;;in name only format, we can dereference the argument (pointer)
  ;;but only if the macro argument requested it
  ;;and this parameter can be dereferenced
  (if (and (is-arg-set "deref" args) (param-deref p))
      ;;then
      (format nil "*~A" (param-name p))
      ;;else
      (format nil "~A" (param-name p))))

(defun format-paramitem-full (args p)
  (let* ((type-decl (param-type p))
         (last-index (1- (length type-decl))))
    (format nil "~A ~A"
            ;;if macro requested reference mode,
            ;; this parameter is dereferencable,
            ;; and the last char is a *
            (if (and (is-arg-set "ref" args) (param-deref p) (equal "*" (subseq type-decl last-index)))
                ;;then, replace the last * with a &
                (concatenate 'string (subseq type-decl 0 last-index) "&")
                ;;else
                type-decl)
            (param-name p))))

(defun error-if-null (var errmsg) (when (null var) (error errmsg)))

(defun format-params (params token-start-length args void-param)
  ;;full, types, names, deref, ref, void, voidonly, nowrap
  (let*  ((arg-types      (is-arg-set "types" args))
          (arg-names      (is-arg-set "names" args))
          (arg-deref      (is-arg-set "deref" args))
          (arg-ref        (is-arg-set "ref" args))
          (arg-void       (is-arg-set "void" args))
          (arg-voidonly   (is-arg-set "voidonly" args))
          (arg-wrap       (is-arg-set "wrap" args))
          (arg-nowrap     (is-arg-set "nowrap" args))
          (filtered-params (cond  (arg-voidonly
                                   (error-if-null void-param "Void param must be set to use void argument")
                                   (cons void-param nil)) ;;only the void-param
                                  (arg-void
                                   (error-if-null void-param "Void param must be set to use void argument")
                                   (cons void-param params)) ;;prepend the void-param
                                  (t params))) ;;default, just use params
          (types-only (and arg-types (not arg-names)))
          (names-only (and arg-names (not arg-types)))
          (full-decl  (not (xor arg-names arg-types)))
          (format-param-func (cond  (full-decl  #'format-paramitem-full)
                                    (types-only #'format-paramitem-type)
                                    (names-only #'format-paramitem-name)))
          (do-wrap    (or arg-wrap (not (or names-only arg-nowrap))))
          (format-line (if do-wrap "~{~A~^,~%~}" "~{~A~^, ~}")))
    (when filtered-params
      (format nil format-line
              (cons
               ;;first parameter => no indentation
               (apply format-param-func (list args (car filtered-params)))
               (mapcar
                (lambda (p)
                  (concatenate 'string
                               ;;second parameter, indent according to wrap policy
                               (when do-wrap (make-string token-start-length :initial-element #\ ))
                               (apply format-param-func (list args p))))
                (cdr filtered-params)))))))

(defun split-by-delimiter (string delimiter)
  (loop   for start = 0 then (1+ finish)
     for finish = (position delimiter string :start start)
     if (and (not (eq (length string) start))
             (not (eq start finish)))
     collect (subseq string start finish)
     until (null finish)))

(defun process-tokens (line funcdata void-param &optional (line-length 0))
  (let ((token-marker-left (position token-marker line)))
    (if (null token-marker-left)
        ;;then, no left token marker. return line
        line
        ;;else, process more
        (let* ((token-start (1+ token-marker-left))
               (token-stop (position token-marker line :start token-start)))
          (if (null token-stop)
              ;;then, no right token marker. return line
              line
              ;;else, process more
              (let* ((token-full (subseq line token-start token-stop))
                     (seperator-index (position token-arg-marker token-full))
                     (token (subseq token-full 0 seperator-index))
                     (token-args (if (not (null seperator-index))
                                     (split-by-delimiter
                                      (subseq token-full (1+ seperator-index) nil) token-arg-delimiter)
                                     '(nil)))
                     (line-begin (subseq line 0 token-marker-left))
                     (line-end (subseq line (1+ token-stop) nil))
                     (m (get-macro token)))
                ;;m (the macro struct) is nil if no macro found for the token
                (if (null m)
                    ;;then
                    line
                    ;;else
                    (let* ((token-start-length (+ line-length token-marker-left))
                           (expanded-token
                            (funcall (lppmacro-filter m)
                                     funcdata
                                     token-start-length
                                     token-args void-param)))
                      (concatenate 'string
                                   line-begin
                                   expanded-token
                                   (process-tokens
                                    line-end
                                    funcdata
                                    void-param
                                    (+ token-start-length (length expanded-token))))))))))))

(defun expand-template (template-list func-data void-param)
  (concat-string-list
   (mapcar
    (lambda (template-line) (process-tokens template-line func-data void-param))
    template-list)))

(defun expand-methods-with-template (template-list funcset-name)
  (remove nil ;;filter out the nil values (from wrong funcsets)
          (mapcar
           (lambda (func-data)
             (cond ;;only expand this function if it belongs to the target funcset
               ((equal funcset-name (funcdef-funcset func-data))
                (expand-template template-list func-data (get-void-param funcset-name)))
               ;;otherwise return nil
               (t nil)))
           funcs)))

(defun clear-funcs ()
  (setq funcs nil)
  (setq funcs-tail funcs))

(clear-funcs)

(defun add-func (&rest args)
  (let ((func-data (apply #'make-funcdef args)))
    (if (endp funcs)
        ;;then
        (progn
          (setq funcs (cons func-data nil))
          (setq funcs-tail funcs))
        ;;else
        (progn
          (setf (cdr funcs-tail) (cons func-data nil))
          (setq funcs-tail (cdr funcs-tail))))))

(defun outputfuncs (f)
  (write-line (concat-string-list f)))

(defun filter-line (line filename)
  (replace-all line
               auto-header-marker
               (format nil "THIS FILE AUTO-GENERATED FROM ~A. DO NOT EDIT." filename)))

(defmacro prepend-into (var value)
  `(setq ,var (cons ,value ,var)))

(defun strip-end-characters (str numChars)
  (let ((index (- (length str) numChars)))
    (if (>= index 1)
        (subseq str 0 index)
        nil)))

(defun get-funcset-from-begin-marker (line)
  (let ((start-index (search begin-marker-left line)))
    (cond ;;no begin marker, return nil
      ((null start-index) nil)

      ;;begin marker must be at start of line
      ((not (= start-index 0)) nil)

      ;;left side is good
      (t (let*  ((start-param-index (+ start-index (length begin-marker-left)))
                 (end-index (search begin-marker-right line :start2 start-param-index)))
           (cond ;;no right side marker, return nil
             ((null end-index) nil)

             ;;right marker must be at end of line
             ((not (= (length line) (+ end-index (length begin-marker-right)))) nil)

             ;;markers but no funcset parameter, can't process
             ((= start-param-index end-index) nil)
             ;;we have a begin-marker and funcset parameter!
             (t (values (subseq line start-param-index end-index) start-param-index end-index))))))))

(defun is-end-marker (line)
  (equal line end-marker))

(defun in-to-out-filename (fn)
  (strip-end-characters fn 4))

(defun process-file (infilename)
  (let ((infile (open infilename :if-does-not-exist nil))
        (outfile (open (in-to-out-filename infilename) :direction :output :if-exists :supersede))
        (filelines nil)
        (template-lines nil)
        (funcset-name nil))
    (when infile
      (loop
         for line = (read-line infile nil)
         while line
         do
           (cond   ;;outside of a replace block
             ((null funcset-name)
              ;;look for a begin-replace marker (funcset name becomes not nil)
              (setq funcset-name (get-funcset-from-begin-marker line))
              ;;if still no funcset-name
              (when (null funcset-name)
                ;;accumulate filtered lines to filelines
                (prepend-into filelines (filter-line line (file-namestring infilename)))))

             ;;all below: funcset-name is not nil -- inside of a replace block
             ;;check for end marker
             ((is-end-marker line)
              ;;since we have a full template, expand template it
              (mapcar
               (lambda (v) (prepend-into filelines v))
               (expand-methods-with-template (reverse template-lines) funcset-name))
              (setq funcset-name nil)  ;;clear funcset-name -- return to normal operation above
              (setq template-lines nil) ;;clear template so we can process another one in the same file
             )
             ;;inside replace block, accumulate template
             (t (prepend-into template-lines line))))
      (loop for line in (reverse filelines)
         do (write-line line outfile))
      (close infile)
      (close outfile))))

(defvar api-file "apidef.cl")
(defvar lpp-file "lpp.cl")

(load api-file :verbose nil)
(load "cl-fad/load.lisp" :verbose nil)

(defun mapc-directory-tree (fn directory)
  (dolist (entry (cl-fad:list-directory directory))
    (when (cl-fad:directory-pathname-p entry)
      (mapc-directory-tree fn entry))
    (funcall fn entry)))

(defun _ () (load lpp-file :verbose nil))

(defun file-modified-time (f)
  (cond ((probe-file f) (posix:file-stat-mtime (posix:file-stat f)))
        (t 0)
  )
)

(defun process (dir)
  (let* ((target-directory (if (null dir)
                               (ext:cd)
                               dir))
         (cache-file-path (cl-fad:merge-pathnames-as-file
                           (cl-fad:pathname-as-directory target-directory)
                           ".pp-modification-cache.cl"))
         (api-modify (file-modified-time api-file))
         (lpp-modify (file-modified-time lpp-file))
         (pp-cache (with-open-file (fsi cache-file-path :if-does-not-exist nil)
                      (cond ((null fsi) (make-hash-table :test 'equal) )
                            (t (let ((data (read fsi nil)))
                                  (if (hash-table-p data)
                                      data
                                      (make-hash-table :test 'equal)
                                   )
                               ))
                      )
                   ))
        )
    (write-line (format nil "=== LPP processing directory: ~A ===~%" target-directory))
    (mapc-directory-tree (lambda (x)
                           (when (equal (pathname-type x) preprocessor-file-extension)
                             (let* ((fwd-path (back-to-forward-slashes (namestring x)))
                                    (cache-modify (gethash fwd-path pp-cache 0))
                                    (last-modify (max lpp-modify api-modify (file-modified-time x)))
                                   )
                               (when (> last-modify cache-modify)
                                 (write-line (format nil "~A => ~A"
                                                     (enough-namestring x target-directory)
                                                     (in-to-out-filename (file-namestring x))
                                             ))

                                 (process-file (namestring x))
                                 (setf (gethash fwd-path pp-cache) last-modify)
                               )
                             )
                           )
                         )
                         target-directory)
    (with-open-file (fso cache-file-path :direction :output :if-does-not-exist :create :if-exists :supersede)
      (write pp-cache :stream fso)
    )
  )
)

;(process (car *args*))
(process "../include")
(process "../src")


Use row pointer instead of row index in matrix-vector multiplication kernel

// Basic linear algebra

typedef float Scalar;
typedef float4 Scalar4;

kernel void fill(global Scalar *dest, const Scalar value) {
    size_t i = get_global_id(0);
    dest[i] = value;
}

// dest = x * k
kernel void constantMul(global Scalar *x, const Scalar k, global Scalar *dest) {
    size_t i = get_global_id(0);
    dest[i] = x[i] * k;
}

// dest = x / k
kernel void constantDiv(global Scalar *x, const Scalar k, global Scalar *dest) {
    size_t i = get_global_id(0);
    dest[i] = x[i] / k;
}

// dest = x - y
kernel void elementSub(global Scalar *x, global Scalar *y, global Scalar *dest) {
    size_t i = get_global_id(0);
    dest[i] = x[i] - y[i];
}

// dest = x + y
kernel void elementAdd(global Scalar *x, global Scalar *y, global Scalar *dest) {
    size_t i = get_global_id(0);
    dest[i] = x[i] + y[i];
}

// dest = x * y
kernel void elementMul(global Scalar *x, global Scalar *y, global Scalar *dest) {
    size_t i = get_global_id(0);
    dest[i] = x[i] * y[i];
}

kernel void partialSum(global Scalar *x, const uint size, const uint partSize, global Scalar *dest) {
    uint part = get_global_id(0);

    Scalar sum = 0.0f;
    for (size_t i = part*partSize, end = min(part*partSize + partSize, size); i < end; ++i)
        sum += x[i];
    dest[part] = sum;
}

kernel void matrixIdentity(global Scalar *matrix, const uint columns) {
    size_t i = get_global_id(0);
    size_t j = get_global_id(1);
    matrix[i * columns + j] = i == j? 1.0 : 0.0;
}

kernel void matrixVectorMul(global Scalar *matrix, global Scalar *vector, const uint columns, const uint partSize, global Scalar *output, local Scalar *work) {
    // Compute partial dot product
    size_t i = get_global_id(0);
    size_t j = get_global_id(1);
    size_t parts = get_global_size(1);
    size_t k = j*partSize;

    Scalar partialSum = 0;
    const global Scalar *row = matrix + i*columns;
    for (size_t end = k + partSize; k < end; k++) {
        partialSum += row[k] * vector[k];
    }

    // Store the partial result in local work memory
    size_t ii = get_local_id(0);
    size_t jj = get_local_id(1);
    size_t workColumns = get_local_size(1);
    size_t workRowOffset = ii * workColumns;
    work[workRowOffset + jj] = partialSum;

    // Wait until all the threads in this group have computed their partial sums
    barrier(CLK_LOCAL_MEM_FENCE);

    // The first thread in a given row sums all the partial sums produced by itself
    // and the other threads in this row.
    if (jj == 0) {
        Scalar sum = 0;
        for (size_t k = workRowOffset, end = workRowOffset + workColumns; k < end; ++k) {
            sum += work[k];
        }
        output[i] = sum;
    }
}

kernel void matrixVectorMul4(global Scalar4 *matrix, global Scalar4 *vector, const uint columns, const uint partSize, global Scalar *output, local Scalar *work) {
    // Compute partial dot product
    size_t i = get_global_id(0);
    size_t j = get_global_id(1);
    size_t parts = get_global_size(1);
    size_t k = j*partSize;

    Scalar partialSum = 0;
    const global Scalar4 *row = matrix + i*columns;
    for (size_t end = k + partSize; k < end; k++) {
        partialSum += dot(row[k], vector[k]);
    }

    // Store the partial result in local work memory
    size_t ii = get_local_id(0);
    size_t jj = get_local_id(1);
    size_t workColumns = get_local_size(1);
    size_t workRowOffset = ii * workColumns;
    work[workRowOffset + jj] = partialSum;

    // Wait until all the threads in this group have computed their partial sums
    barrier(CLK_LOCAL_MEM_FENCE);

    // The first thread in a given row sums all the partial sums produced by itself
    // and the other threads in this row.
    if (jj == 0) {
        Scalar sum = 0;
        for (size_t k = workRowOffset, end = workRowOffset + workColumns; k < end; ++k) {
            sum += work[k];
        }
        output[i] = sum;
    }
}

// Basic linear algebra

typedef float Scalar;
typedef float4 Scalar4;

kernel void fill(global Scalar *dest, const Scalar value) {
    size_t i = get_global_id(0);
    dest[i] = value;
}

// dest = x * k
kernel void constantMul(global Scalar *x, const Scalar k, global Scalar *dest) {
    size_t i = get_global_id(0);
    dest[i] = x[i] * k;
}

// dest = x / k
kernel void constantDiv(global Scalar *x, const Scalar k, global Scalar *dest) {
    size_t i = get_global_id(0);
    dest[i] = x[i] / k;
}

// dest = x - y
kernel void elementSub(global Scalar *x, global Scalar *y, global Scalar *dest) {
    size_t i = get_global_id(0);
    dest[i] = x[i] - y[i];
}

// dest = x + y
kernel void elementAdd(global Scalar *x, global Scalar *y, global Scalar *dest) {
    size_t i = get_global_id(0);
    dest[i] = x[i] + y[i];
}

// dest = x * y
kernel void elementMul(global Scalar *x, global Scalar *y, global Scalar *dest) {
    size_t i = get_global_id(0);
    dest[i] = x[i] * y[i];
}

kernel void partialSum(global Scalar *x, const uint size, const uint partSize, global Scalar *dest) {
    uint part = get_global_id(0);

    Scalar sum = 0.0f;
    for (size_t i = part*partSize, end = min(part*partSize + partSize, size); i < end; ++i)
        sum += x[i];
    dest[part] = sum;
}

kernel void matrixIdentity(global Scalar *matrix, const uint columns) {
    size_t i = get_global_id(0);
    size_t j = get_global_id(1);
    matrix[i * columns + j] = i == j? 1.0 : 0.0;
}

kernel void matrixVectorMul(global Scalar *matrix, global Scalar *vector, const uint columns, const uint partSize, global Scalar *output, local Scalar *work) {
    // Compute partial dot product
    size_t i = get_global_id(0);
    size_t j = get_global_id(1);
    size_t parts = get_global_size(1);
    size_t k = j*partSize;

    size_t offset = i*columns + k;
    Scalar partialSum = 0;
    for (size_t end = k + partSize; k < end; k++, offset++) {
        partialSum += matrix[offset] * vector[k];
    }

    // Store the partial result in local work memory
    size_t ii = get_local_id(0);
    size_t jj = get_local_id(1);
    size_t workColumns = get_local_size(1);
    size_t workRowOffset = ii * workColumns;
    work[workRowOffset + jj] = partialSum;

    // Wait until all the threads in this group have computed their partial sums
    barrier(CLK_LOCAL_MEM_FENCE);

    // The first thread in a given row sums all the partial sums produced by itself
    // and the other threads in this row.
    if (jj == 0) {
        Scalar sum = 0;
        for (size_t k = workRowOffset, end = workRowOffset + workColumns; k < end; ++k) {
            sum += work[k];
        }
        output[i] = sum;
    }
}

kernel void matrixVectorMul4(global Scalar4 *matrix, global Scalar4 *vector, const uint columns, const uint partSize, global Scalar *output, local Scalar *work) {
    // Compute partial dot product
    size_t i = get_global_id(0);
    size_t j = get_global_id(1);
    size_t parts = get_global_size(1);
    size_t k = j*partSize;

    size_t offset = i*columns + k;
    Scalar partialSum = 0;
    for (size_t end = k + partSize; k < end; k++, offset++) {
        partialSum += dot(matrix[offset], vector[k]);
    }

    // Store the partial result in local work memory
    size_t ii = get_local_id(0);
    size_t jj = get_local_id(1);
    size_t workColumns = get_local_size(1);
    size_t workRowOffset = ii * workColumns;
    work[workRowOffset + jj] = partialSum;

    // Wait until all the threads in this group have computed their partial sums
    barrier(CLK_LOCAL_MEM_FENCE);

    // The first thread in a given row sums all the partial sums produced by itself
    // and the other threads in this row.
    if (jj == 0) {
        Scalar sum = 0;
        for (size_t k = workRowOffset, end = workRowOffset + workColumns; k < end; ++k) {
            sum += work[k];
        }
        output[i] = sum;
    }
}


OpenCL: Removed light.cl as it does not belong in module.

/*********************************************************************************
 *
 * Inviwo - Interactive Visualization Workshop
 *
 * Copyright (c) 2013-2015 Inviwo Foundation
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer in the documentation
 * and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *********************************************************************************/

#ifndef LIGHT_CL
#define LIGHT_CL

#include "datastructures/lightsource.cl"
#include "datastructures/bbox.cl"
#include "shading/shadingmath.cl"
#include "transformations.cl"

int getLightSourceId(const int2 nPhotonsPerLight, const int nLightSources) {
    return (get_global_id(1)*get_global_size(0)+get_global_id(0))/nPhotonsPerLight.y;
}

float2 getLightSourceUniformUV(const int2 nPhotonsPerLight, const int nLightSources) {
    int globalId = get_global_id(1)*get_global_size(0)+get_global_id(0);
    int lightSourceId =  getLightSourceId(nPhotonsPerLight, nLightSources);

    int lightSourceOffset = lightSourceId*nPhotonsPerLight.y;

    int lightSampleIndex = globalId-lightSourceOffset;
    int2 photonId = (int2)(lightSampleIndex%nPhotonsPerLight.x, lightSampleIndex/nPhotonsPerLight.x);

    return (float2)((convert_float2(photonId)+0.5f)/convert_float(nPhotonsPerLight.x));
}
float2 getLightSourceStratifiedUV(const int2 nPhotonsPerLight, const int nLightSources, const float2 randNum) {
    int globalId = get_global_id(1)*get_global_size(0)+get_global_id(0);

    int lightSourceId =  getLightSourceId(nPhotonsPerLight, nLightSources);

    int lightSourceOffset = lightSourceId*nPhotonsPerLight.y;

    int lightSampleIndex = globalId-lightSourceOffset;
    // Calculate integer xy coordinate on light
    int2 photonId = (int2)(lightSampleIndex%nPhotonsPerLight.x, lightSampleIndex/nPhotonsPerLight.x);

    return (float2)((convert_float2(photonId)+randNum)/convert_float(nPhotonsPerLight.x));
}

void sampleLight(LightSource lightSource, const float2 uv, float3* __restrict origin, float3* __restrict wi, float3* __restrict power, float* __restrict pdf, float3 rndNum, const BBox bbox) {

    if( lightSource.type == LIGHT_POINT ) {
        float3 localOrigin = (float3)(0.f);
        *origin = transformPoint(lightSource.tm, localOrigin);
        float3 sphereSample = uniformSampleSphere(uv);
        *wi = -sphereSample;

        *pdf = uniformSpherePdf();
        *power = lightSource.radiance / *pdf;
    }
    else if (lightSource.type == LIGHT_AREA ) {
        float3 localOrigin = (float3)(lightSource.size*(-0.5f+uv), 0.f);

        *origin = transformPoint(lightSource.tm, localOrigin);
        // Area light
        float3 pointOnPlane = bbox.pMin+ (bbox.pMax-bbox.pMin)*(float3)(rndNum.x, rndNum.y, rndNum.z);
        *wi = normalize(pointOnPlane-*origin);
        *pdf = lightSource.area;

        *power = lightSource.radiance / *pdf;
    }
    else if (lightSource.type == LIGHT_DIRECTIONAL) {

            float3 localOrigin = (float3)(lightSource.size*(-0.5f+uv), 0.f);

            *origin = transformPoint(lightSource.tm, localOrigin);

            // Directional light
            float3 pointOnPlane = transformPoint(lightSource.tm, (float3)(localOrigin.xy, 1.0f));
            *wi = normalize(pointOnPlane-*origin);
            *pdf = lightSource.area;
            *power = lightSource.radiance;

    } else { // if (lightSource.type == LIGHT_CONE )
        float3 localOrigin = (float3)(0.0f, 0.f, 0.f);
        *origin = transformPoint(lightSource.tm, localOrigin);

        float3 localDir = uniformSampleCone(uv, lightSource.cosFOV);
        float3 pointOnPlane = transformPoint(lightSource.tm, localDir);
        *wi = normalize(pointOnPlane-*origin);
        *pdf = uniformConePdf(lightSource.cosFOV);

        *power = localDir.z*localDir.z*localDir.z*localDir.z*lightSource.radiance*localDir.z/(*pdf);
    }

}

void sampleLights(__global LightSource const * __restrict lightSources, const int2 nPhotonsPerLight,
                  const int nLightSources, const float2 uv, float3* __restrict origin, float3* __restrict wi, float3* __restrict power, float* __restrict pdf, float3 rndNum, const BBox bbox) {

    uint globalId = get_global_id(1)*get_global_size(0)+get_global_id(0);
    int lightSourceId = getLightSourceId(nPhotonsPerLight, nLightSources);

    sampleLight(lightSources[lightSourceId], uv, origin, wi, power, pdf, rndNum, bbox);

}

#endif // LIGHT_CL


Refine unroll in MIOpenConvDirUniC

Refine unroll in MIOpenConvDirUniC

/*
 * Copyright (c) 2016 AMD Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Materials.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */

#define _FLOAT					float
#define _FLOAT2					float2
#define _FLOAT4					float4
#define _FLOAT8					float8

#ifndef FLT_MAX
#define FLT_MAX         3.402823466e+38F        /* max value */
#endif

#define UNUSED __attribute__((__unused__))

#define MLO_FILTER_SZ (MLO_FILTER_SIZE1*MLO_FILTER_SIZE0)

#define MLO_GRP_SZ0  (MLO_GRP_TILE0*MLO_GRP_TILE1)
#define MLO_GRP_SZ1 1
#define MLO_GRP_SZ2 1
#define MLO_GRP_SZ (MLO_GRP_SZ0*MLO_GRP_SZ1*MLO_GRP_SZ2)
#define MLO_N_PROC_WAVES ((MLO_GRP_SZ + MLO_N_READ_PROCS - 1)/MLO_N_READ_PROCS)
#define MLO_OUT_TILE_SZ (MLO_OUT_PIX_TILE1*MLO_OUT_PIX_TILE0)
#define MLO_ALU_TILE_SZ (MLO_ALU_VTILE1*MLO_ALU_VTILE0)

#if MLO_IN_TILE0 < MLO_OUT_WIDTH || MLO_IN_TILE1 < MLO_OUT_HEIGHT
#define MLO_LARGE_MAP 1
#else
#define MLO_LARGE_MAP 0
#endif

#if (MLO_IN_WIDTH == MLO_OUT_WIDTH && (MLO_IN_WIDTH / MLO_IN_TILE0 ) * MLO_IN_TILE0 == MLO_IN_WIDTH && MLO_IN_HEIGHT == MLO_OUT_HEIGHT && (MLO_IN_HEIGHT / MLO_IN_TILE1 ) * MLO_IN_TILE1 == MLO_IN_HEIGHT)
#define MLO_OUT_ALIGNED 1
#else
#define MLO_OUT_ALIGNED 0
#endif

#define MLO_ALUTILES_STACK_SZ (MLO_N_ALUTILES_PERSTACK*MLO_ALU_TILE_SZ)
#define MLO_N_IN_TILES_TOTAL (MLO_N_IN_TILES_PERSTACK*MLO_N_STACKS)

#define MLO_N_OUT_TILE_BLOCKS0 ((MLO_OUT_WIDTH+MLO_IN_TILE0-1)/MLO_IN_TILE0)
#define MLO_N_OUT_TILE_BLOCKS1 ((MLO_OUT_HEIGHT+MLO_IN_TILE1-1)/MLO_IN_TILE1)
#define MLO_N_IN_PACKS  (MLO_N_INPUTS/MLO_N_IN_TILES_PERSTACK)

#define MLO_N_IN_READ (MLO_N_IN_PACKS*MLO_N_IN_TILES_PERSTACK)
#if MLO_N_IN_READ == MLO_N_INPUTS
#define MLO_INPUTS_ALIGNED 1
#else
#define MLO_INPUTS_ALIGNED 0
#endif

#define MLO_N_OUT_PACKS  (MLO_N_OUTPUTS/MLO_N_OUT_TILES_PERSTACK)
#if MLO_N_OUT_PACKS*MLO_N_OUT_TILES_PERSTACK == MLO_N_OUTPUTS && MLO_N_OUT_TILES_PERSTACK != MLO_N_OUTPUTS
#define MLO_OUTPUTS_ALIGNED 1
#else
#define MLO_OUTPUTS_ALIGNED 0
#endif

#define MLO_N_BATCH_PACKS (MLO_BATCH_SZ/MLO_N_STACKS)
#if MLO_N_BATCH_PACKS*MLO_N_STACKS == MLO_BATCH_SZ && MLO_N_STACKS != MLO_BATCH_SZ
#define MLO_BATCH_ALIGNED 1
#else
#define MLO_BATCH_ALIGNED 0
#endif

#define MLO_IN_LCL_WIDTH (MLO_IN_TILE0 + MLO_FILTER_SIZE0 - 1)  // here we use kernel size. it's important when padding == 0
#define MLO_IN_LCL_HEIGHT (MLO_IN_TILE1 + MLO_FILTER_SIZE1 - 1)
#define MLO_IN_LCL_TILE_SZ (MLO_IN_LCL_WIDTH*MLO_IN_LCL_HEIGHT)
#define MLO_IN_LCL_PERSTACK_SZ (MLO_IN_LCL_TILE_SZ*MLO_N_IN_TILES_PERSTACK)
#define MLO_IN_LCL_SZ (MLO_IN_LCL_PERSTACK_SZ*MLO_N_STACKS)

#define MLO_WEIGHTS_SZ (MLO_N_OUT_TILES_PERSTACK*MLO_N_IN_TILES_PERSTACK*MLO_FILTER_SZ)

#define MLO_PVT_ACCUM_DATA_SZ (MLO_N_OUT_TILES * MLO_OUT_TILE_SZ)
#define MLO_PVT_IN_WIDTH (MLO_FILTER_SIZE0 + MLO_OUT_PIX_TILE0 - 1)
#define MLO_PVT_IN_HEIGHT (MLO_OUT_PIX_TILE1)

#define MLO_LCL_WEIGHTS 1

#if defined(__AMDGCN__)
extern uint __llvm_amdgcn_readfirstlane(uint) __asm("llvm.amdgcn.readfirstlane");
#define uniform(x) __llvm_amdgcn_readfirstlane(x)
#else
#define uniform(x) (x)
#endif

static inline void calculateXYPos(uint linPos, uint width, uint * __restrict x, uint * __restrict y)
{

	(*y) = (uint)((float)linPos * (1.0f / (float)width) + 0.00001f);

	(*x) = linPos - (*y) * width;
}

static inline uint calculateOffset(uint stride, uint x, uint y)
{
	uint ret = y * stride + x;
	return(ret);
}

static inline void readDataElem(uint linPos,__local _FLOAT *lcl_data, uint lcl_base, UNUSED uint lcl_height, uint lcl_width, uint lcl_stride, uint lcl_y, uint lcl_x,
					 const __global _FLOAT * gbl_data, uint gbl_base, uint gbl_height, uint gbl_width, uint gbl_stride, int gbl_y, int gbl_x,
					 bool vis,
					 UNUSED bool debug)
{
	uint x, y;
	calculateXYPos(linPos, lcl_width, &x, &y);
	int g_x = x + gbl_x;
	int g_y = y + gbl_y;
	uint gbl_off0 = calculateOffset(gbl_stride, g_x, g_y);
	uint gbl_off = gbl_off0 + gbl_base;

#if MLO_LARGE_MAP == 1
	uint lcl_off = lcl_base + linPos;
	(void)lcl_stride;
	(void)lcl_x;
	(void)lcl_y;
#else
	uint l_x = x + lcl_x;
	uint l_y = y + lcl_y;
	uint lcl_off = lcl_base + mad24(l_y, lcl_stride, l_x);
#endif

#if MLO_LARGE_MAP == 1
	 vis &= (g_x >= 0 && g_x < gbl_width && g_y >= 0 && g_y < gbl_height);
#else
	 (void)gbl_width;
	 (void)gbl_height;
#endif
	 gbl_off = (vis) ? gbl_off : 0;
	 _FLOAT gbl_val = gbl_data[gbl_off];
	 gbl_val = (vis) ? gbl_val : 0;

	lcl_data[lcl_off] = gbl_val;

}

static inline void readData(uint lcl_id, uint size, uint lcl_p_stride, __local _FLOAT *lcl_data, uint lcl_base, uint lcl_height, uint lcl_width, uint lcl_stride, uint lcl_y, uint lcl_x,
					 const __global _FLOAT * gbl_data, uint gbl_base, uint gbl_height, uint gbl_width, uint gbl_stride, int gbl_y, int gbl_x,
					 bool vis,
					 bool debug
					 )
{

	for(uint i = lcl_id; i < size; i+= lcl_p_stride)
	{
		readDataElem(i, lcl_data, lcl_base, lcl_height, lcl_width, lcl_stride, lcl_y, lcl_x,
					 gbl_data, gbl_base, gbl_height, gbl_width, gbl_stride, gbl_y, gbl_x,
					 vis,
					 debug);
	}

}

static inline void loadData(uint lcl_id, uint lcl_p_stride,
					__local _FLOAT * lcl_data,
					uint lcl_off, uint lcl_size, uint lcl_height, uint lcl_width, uint lcl_stride, uint lcl_bot_y, uint lcl_bot_x,
					const __global _FLOAT * gbl_data,
					uint gbl_off, uint gbl_size, uint gbl_height, uint glb_width, uint gbl_stride, int gbl_bot_y, int gbl_bot_x,
					uint buf_block_ind, uint max_n_bufs, uint lcl_n_bufs,
					bool debug)
{

	for(uint c = 0; c < lcl_n_bufs;
		 ++c, lcl_off += lcl_size, gbl_off += gbl_size )
	{
		bool vis = (buf_block_ind + c <  max_n_bufs);
		readData(lcl_id, lcl_size, lcl_p_stride,
				 lcl_data, lcl_off, lcl_height, lcl_width, lcl_stride, lcl_bot_y, lcl_bot_x,
				 gbl_data, gbl_off, gbl_height, glb_width, gbl_stride, gbl_bot_y, gbl_bot_x,
				 vis,
				 (debug));
	}

}

static inline void Conv(uint o_map_base,
				uint in_stg_off,
				 _FLOAT * __restrict pvt_in_stage, __local _FLOAT * __restrict lcl_indata,
				 _FLOAT * __restrict pvt_wei_stage, __local _FLOAT * __restrict lcl_wei,
				 _FLOAT * __restrict pvt_accum
				 )
{
// convolution

		// over all inputs in stack
		uint in_stg_off1 = in_stg_off;
		for(uint i_c = 0; i_c < MLO_N_IN_TILES_PERSTACK; ++i_c, in_stg_off1 += MLO_IN_LCL_TILE_SZ)
		{
        // preload input
			uint wei_stg_base_off = mad24(o_map_base, (uint)(MLO_N_IN_TILES_PERSTACK*MLO_FILTER_SZ), mul24(i_c,(uint)MLO_FILTER_SZ));
			uint in_stg_off2 = in_stg_off1;
			for(uint j = 0; j < MLO_PVT_IN_HEIGHT-1; ++j, in_stg_off2+=MLO_IN_LCL_WIDTH
					)
			{
				for(uint i = 0; i < MLO_PVT_IN_WIDTH; ++i)
				{
					pvt_in_stage[j*MLO_PVT_IN_WIDTH + i] = lcl_indata[in_stg_off2 + i];
				}
			}

		// over filter rows
#ifdef __AMDGCN__
#if (MLO_FILTER_SZ > 9) || (MLO_IN_CHANNEL_STRIDE <= 196) || (MLO_IN_CHANNEL_STRIDE > 784 && MLO_DIR_FORWARD != 1)
			#pragma unroll
#else
			#pragma unroll 2
#endif
#endif
			for(uint k = 0; k < MLO_FILTER_SIZE1; ++k, in_stg_off2+=MLO_IN_LCL_WIDTH
			)
			{
				uint k_act = 0;
#if MLO_DIR_FORWARD==1
				k_act = k;
#else
// load filter in reverse order
				k_act = MLO_FILTER_SIZE1 - 1 - k;
#endif
		// load next input row
				for(uint i_pvt = 0; i_pvt < MLO_PVT_IN_WIDTH; ++i_pvt)
				{
					pvt_in_stage[(MLO_PVT_IN_HEIGHT-1)*MLO_PVT_IN_WIDTH + i_pvt] = lcl_indata[in_stg_off2 + i_pvt];
				}

		// over all outputs
				for(uint o_c = 0; o_c < MLO_N_OUT_TILES; ++o_c)
				{
					uint wei_stg_off = wei_stg_base_off + o_c * MLO_N_IN_TILES_PERSTACK*MLO_FILTER_SZ + k_act * MLO_FILTER_SIZE0;
					for(uint i = 0; i < MLO_FILTER_SIZE0; ++i)
					{
						pvt_wei_stage[i] = lcl_wei[wei_stg_off + i];
					}

		// actual conv

					for( uint j = 0; j < MLO_OUT_PIX_TILE1; ++j)
					{
						for(uint i = 0; i < MLO_OUT_PIX_TILE0; ++i)
						{
							for(uint l = 0; l < MLO_FILTER_SIZE0; ++l)
							{

								uint l_act = 0;
#if MLO_DIR_FORWARD==1
								l_act = l;

#else
// in reverse horizontal and vertical orders
								l_act = MLO_FILTER_SIZE0 - 1 - l;

#endif

								pvt_accum[(o_c * MLO_OUT_PIX_TILE1 + j) * MLO_OUT_PIX_TILE0 + i]
									 += pvt_in_stage[j * MLO_PVT_IN_WIDTH + i + l] * pvt_wei_stage[l_act];

							}

						}
					}

				} // for(uint o_c = 0; o_c < MLO_N_OUT_TILES; ++o_c)

               // move data up
				for(uint j = 0; j < MLO_PVT_IN_HEIGHT-1; ++j)
				{
					for(uint i = 0; i < MLO_PVT_IN_WIDTH; ++i)
					{
						pvt_in_stage[j*MLO_PVT_IN_WIDTH + i] = pvt_in_stage[(j+1)*MLO_PVT_IN_WIDTH + i];
					}
				}

//				mem_fence(CLK_LOCAL_MEM_FENCE);

			} // for(uint k = 0; k < MLO_FILER_SIZE1; ++k,in_stg_off2+=MLO_IN_LCL_WIDTH)

		} // for(uint i_c = 0; i_c < MLO_N_IN_TILES_PERSTACK; ++i_c, in_stg_off1 += MLO_IN_LCL_PERSTACK_SZ)

}

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2)))
__kernel void MIOpenConvUniC(
       const __global _FLOAT * __restrict in,
       const __global _FLOAT * __restrict weights,
#if MLO_CONV_BIAS
       const __global _FLOAT * __restrict bias,
#endif
	__global _FLOAT * __restrict out,
	UNUSED _FLOAT padding_val
	   )
{
	__local _FLOAT lcl_indata[MLO_IN_LCL_SZ];
	__local _FLOAT lcl_wei[MLO_WEIGHTS_SZ];
	__private _FLOAT pvt_accum[MLO_PVT_ACCUM_DATA_SZ];
	__private _FLOAT pvt_in_stage[MLO_PVT_IN_HEIGHT * MLO_PVT_IN_WIDTH];
	__private _FLOAT pvt_wei_stage[MLO_FILTER_SIZE0];

	uint grp_id0 = get_group_id(0);
#if MLO_OUT_WIDTH == MLO_IN_TILE0
	uint y_tile_blk = grp_id0;
	uint x_tile_blk = 0;
#else
#if MLO_N_OUT_TILE_BLOCKS0 & (MLO_N_OUT_TILE_BLOCKS0 - 1)
	uint y_tile_blk = (uint)((float)grp_id0 * (1.0f / (float) MLO_N_OUT_TILE_BLOCKS0) + 0.00001f);
	int x_tile_blk = grp_id0 - mul24(y_tile_blk, (uint)MLO_N_OUT_TILE_BLOCKS0);
#else
	uint y_tile_blk = grp_id0 / MLO_N_OUT_TILE_BLOCKS0;
	int x_tile_blk = grp_id0 & (MLO_N_OUT_TILE_BLOCKS0 - 1);
#endif
#endif
	uint o_pack = get_group_id(1); // block of outputs
	uint b_pack = get_group_id(2); // batch block

	uint lcl_id = get_local_id(0);
#if MLO_ALUTILES_STACK_SZ & (MLO_ALUTILES_STACK_SZ - 1)
	uint stack = (uint)((float)lcl_id * (1.0f / (float)MLO_ALUTILES_STACK_SZ) + 0.00001f);  // stack
	uint alu_stack_id = lcl_id - mul24(stack, (uint)MLO_ALUTILES_STACK_SZ);  // alu index in stack
#else
	uint stack = lcl_id / MLO_ALUTILES_STACK_SZ;  // stack
	uint alu_stack_id = lcl_id & (MLO_ALUTILES_STACK_SZ - 1);  // alu index in stack
#if MLO_ALUTILES_STACK_SZ >= 64
	stack = uniform(stack);
#endif
#endif
// ALU plane inside stack
#if MLO_ALU_TILE_SZ & (MLO_ALU_TILE_SZ - 1)
	uint alu_out_plane_id = (uint)((float)alu_stack_id * (1.0f / (float)MLO_ALU_TILE_SZ) + 0.00001f);  // alu output plane index
	uint alu_out_id = alu_stack_id - mul24(alu_out_plane_id, (uint)MLO_ALU_TILE_SZ); // alu index inside an ALU output plane
#else
	uint alu_out_plane_id = alu_stack_id / MLO_ALU_TILE_SZ;  // alu output plane index
	uint alu_out_id = alu_stack_id & (MLO_ALU_TILE_SZ - 1); // alu index inside an ALU output plane
#endif
// pos inside ALU tile
#if MLO_ALU_VTILE0 & (MLO_ALU_VTILE0 - 1)
	uint alu_tl1 = (uint)((float)alu_out_id * (1.0f / (float)MLO_ALU_VTILE0) + 0.00001f);
	uint alu_tl0 = alu_out_id - mul24(alu_tl1, (uint)MLO_ALU_VTILE0);
#else
	uint alu_tl1 = alu_out_id / MLO_ALU_VTILE0;
	uint alu_tl0 = alu_out_id & (MLO_ALU_VTILE0 - 1);
#endif

	uint o_map_plane = o_pack * MLO_N_OUT_TILES_PERSTACK; // first output maps index per full ALU plane stack
	uint o_map_base = alu_out_plane_id*MLO_N_OUT_TILES;  // local output map offset
	uint o_map = o_map_plane + o_map_base; // output map index per ALU plane
	uint b_index = b_pack * MLO_N_STACKS;

#if MLO_LARGE_MAP != 1
#if MLO_GRP_SZ <= MLO_N_READ_PROCS
	uint wave_id = 0;
	uint wave_lcl_id = lcl_id;
#elif MLO_N_READ_PROCS & (MLO_N_READ_PROCS - 1)
	uint wave_id = (uint)((float)lcl_id * (1.0f / (float)MLO_N_READ_PROCS) + 0.00001f);
	uint wave_lcl_id = lcl_id - mul24(wave_id, (uint)MLO_N_READ_PROCS);
#else
	uint wave_id = (uint)((uint)lcl_id / MLO_N_READ_PROCS);
	uint wave_lcl_id = lcl_id & (MLO_N_READ_PROCS - 1);
#if MLO_N_READ_PROCS >= 64
        wave_id = uniform(wave_id);
#endif
#endif
#endif

	int x_grp = x_tile_blk * MLO_IN_TILE0;
	uint y_grp = y_tile_blk * MLO_IN_TILE1;

// TO DO: scale
#if MLO_LARGE_MAP == 1
	int x_in_grp = x_grp - MLO_FILTER_PAD0;
	int y_in_grp = y_grp - MLO_FILTER_PAD1;
#endif

	uint x_in_lcl = alu_tl0 * MLO_OUT_PIX_TILE0;
	uint y_in_lcl = alu_tl1 * MLO_OUT_PIX_TILE1;

// base offset to read data from local input data
	uint in_stg_off = stack*MLO_IN_LCL_PERSTACK_SZ + (y_in_lcl) * MLO_IN_LCL_WIDTH + x_in_lcl;

    uint in_off = b_index * MLO_IN_BATCH_STRIDE;

#if MLO_DIR_FORWARD==1
	uint wei_off = mul24(o_map_plane, (uint)(MLO_N_INPUTS * MLO_FILTER_SZ));
#else
	uint wei_off = mul24(o_map_plane,(uint)MLO_FILTER_SZ);
#endif

#if MLO_LARGE_MAP == 0
	for(uint i = lcl_id; i < MLO_IN_LCL_SZ;  i += MLO_GRP_SZ)
	{
		lcl_indata[i] = 0;
	}
#endif

	for(uint i = 0; i < MLO_PVT_ACCUM_DATA_SZ; ++i)
	{
		pvt_accum[i] = 0;
	}

	for(uint ic = 0; ic < MLO_N_INPUTS; ic += MLO_N_IN_TILES_PERSTACK, in_off += MLO_IN_CHANNEL_STRIDE*MLO_N_IN_TILES_PERSTACK,
				wei_off += MLO_N_IN_TILES_PERSTACK * MLO_FILTER_SZ
#if MLO_DIR_FORWARD==0
							* MLO_N_OUTPUTS
#endif
	)
	{
		barrier(CLK_LOCAL_MEM_FENCE);

// small map has been read in full continiously into the lDS buffer within padded rect,
// padding has been done on initilization.
// large map calculates padding on the fly and fills it with 0.

#if 1 // all inputs

#if MLO_LARGE_MAP == 1
		uint in_lcl_off1 = 0;
		uint in_off1 = in_off;
		for(uint i_b = 0; i_b < MLO_N_STACKS; ++i_b, in_off1 += MLO_IN_BATCH_STRIDE, in_lcl_off1 += MLO_IN_LCL_PERSTACK_SZ)
		{
			bool vis = true;
#if MLO_BATCH_ALIGNED == 0
			vis &= (b_index + i_b < MLO_BATCH_SZ);
#endif

// over all inputs in stack
			uint in_off2 = in_off1;
			uint in_lcl_off2 = in_lcl_off1;
			for(uint i_c = 0; i_c < MLO_N_IN_TILES_PERSTACK; ++i_c, in_off2 += MLO_IN_CHANNEL_STRIDE, in_lcl_off2 += MLO_IN_LCL_TILE_SZ)
			{
#if MLO_INPUTS_ALIGNED == 0
				vis &= (ic + i_c < MLO_N_INPUTS);
#endif

				uint elem_id = lcl_id;
				uint lcl_p_stride = MLO_GRP_SZ0;
				uint lcl_base = 0;
				uint lcl_y = 0;
				uint lcl_x = 0;
				uint gbl_base = 0;

				readData(elem_id, (MLO_IN_LCL_HEIGHT * MLO_IN_LCL_WIDTH), lcl_p_stride,
						&lcl_indata[in_lcl_off2], lcl_base, MLO_IN_LCL_HEIGHT, MLO_IN_LCL_WIDTH, MLO_IN_LCL_WIDTH, lcl_y, lcl_x,
						&in[in_off2], gbl_base, MLO_IN_HEIGHT, MLO_IN_WIDTH, MLO_IN_STRIDE, y_in_grp, x_in_grp,
						vis,
						true
					 );
			}

		}
#else
#ifdef __AMDGCN__
#if (MLO_FILTER_SZ <= 9) && (MLO_IN_CHANNEL_STRIDE <= 784)
		#pragma unroll
#endif
#endif
		for(uint i = wave_id; i < MLO_N_IN_TILES_TOTAL;  i += MLO_N_PROC_WAVES)
		{
		//(MLO_N_STACKS * MLO_N_OUT_TILES_PERSTACK)
#if MLO_N_IN_TILES_PERSTACK & (MLO_N_IN_TILES_PERSTACK - 1)
			uint i_b = (uint)((float)i * (1.0f / (float)MLO_N_IN_TILES_PERSTACK) + 0.00001f);
			uint i_c = i - mul24(i_b, (uint)MLO_N_IN_TILES_PERSTACK);
#else
			uint i_b = (uint)i / MLO_N_IN_TILES_PERSTACK;
			uint i_c = i & (MLO_N_IN_TILES_PERSTACK - 1);
#endif

			bool vis = true;

#if MLO_BATCH_ALIGNED == 0
			vis &= (b_index + i_b < MLO_BATCH_SZ);
#endif

#if MLO_INPUTS_ALIGNED == 0
			vis &= (ic + i_c < MLO_N_INPUTS);
#endif
			uint in_off2 = in_off + i_b * MLO_IN_BATCH_STRIDE + i_c * MLO_IN_CHANNEL_STRIDE;
			uint in_lcl_off2 = i_b * MLO_IN_LCL_PERSTACK_SZ + i_c * MLO_IN_LCL_TILE_SZ;

			uint elem_id = wave_lcl_id;
			uint lcl_p_stride = MLO_N_READ_PROCS;
			uint lcl_base = 0;
			uint lcl_y = MLO_FILTER_PAD1;
			uint lcl_x = MLO_FILTER_PAD0;
			uint gbl_base = 0;

			readData(elem_id, (MLO_IN_HEIGHT * MLO_IN_WIDTH), lcl_p_stride,
						&lcl_indata[in_lcl_off2], lcl_base, MLO_IN_HEIGHT, MLO_IN_WIDTH, MLO_IN_LCL_WIDTH, lcl_y, lcl_x,
						&in[in_off2], gbl_base, MLO_IN_HEIGHT, MLO_IN_WIDTH, MLO_IN_STRIDE, y_grp, x_grp,
						vis,
						true
					 );
		}
#endif

// read inputs and weights
// put weights into LDS

#if 1  // only weights

#if (MLO_WEIGHTS_SZ >= MLO_GRP_SZ) && defined(__AMDGCN__)
#if MLO_WEIGHTS_SZ/MLO_GRP_SZ > 4
#pragma unroll
#else
#pragma unroll (MLO_WEIGHTS_SZ/MLO_GRP_SZ)
#endif
#endif
		for(uint i = lcl_id; i < MLO_WEIGHTS_SZ; i += MLO_GRP_SZ)
		{
#if MLO_DIR_FORWARD==1
// here is [tops][bottoms]
#if (MLO_N_IN_TILES_PERSTACK * MLO_FILTER_SZ) & ((MLO_N_IN_TILES_PERSTACK * MLO_FILTER_SZ) - 1)
			uint lcl_o = (uint)((float)i * (1.0f / (float)(MLO_N_IN_TILES_PERSTACK * MLO_FILTER_SZ)) + 0.00001f);
			uint gbl_i = i - mul24(lcl_o, (uint)(MLO_N_IN_TILES_PERSTACK * MLO_FILTER_SZ));
#else
			uint lcl_o = i / (MLO_N_IN_TILES_PERSTACK * MLO_FILTER_SZ);
			uint gbl_i = i & ((MLO_N_IN_TILES_PERSTACK * MLO_FILTER_SZ) - 1);
#endif
			if((wei_off + lcl_o * MLO_N_INPUTS * MLO_FILTER_SZ + gbl_i) < (MLO_N_OUTPUTS*MLO_N_INPUTS*MLO_FILTER_SZ))
				lcl_wei[i] = weights[wei_off + lcl_o * MLO_N_INPUTS * MLO_FILTER_SZ + gbl_i];
			else
				lcl_wei[i] = weights[0];
#else
// outputs are botoms(inputs))
// inputs are tops(outputs)

#if (MLO_N_OUT_TILES_PERSTACK * MLO_FILTER_SZ) & ((MLO_N_OUT_TILES_PERSTACK * MLO_FILTER_SZ) - 1)
			uint lcl_o = (uint)((float)i * (1.0f / (float)(MLO_N_OUT_TILES_PERSTACK * MLO_FILTER_SZ)) + 0.00001f);
			uint gbl_i = i - mul24(lcl_o, (uint)(MLO_N_OUT_TILES_PERSTACK * MLO_FILTER_SZ));
#else
			uint lcl_o = i / (MLO_N_OUT_TILES_PERSTACK * MLO_FILTER_SZ);
			uint gbl_i = i & ((MLO_N_OUT_TILES_PERSTACK * MLO_FILTER_SZ) - 1);
#endif
#if MLO_FILTER_SZ & (MLO_FILTER_SZ - 1)
			uint lcl_c = (uint)((float)gbl_i * (1.0f / (float)MLO_FILTER_SZ) + 0.00001f);
			uint lcl_i = gbl_i - mul24(lcl_c, (uint)MLO_FILTER_SZ);
#else
			uint lcl_c = gbl_i / MLO_FILTER_SZ;
			uint lcl_i = gbl_i & (MLO_FILTER_SZ - 1);
#endif

			uint lcl_we_off = mad24(mad24(lcl_c, (uint)MLO_N_IN_TILES_PERSTACK, lcl_o), (uint)MLO_FILTER_SZ, lcl_i);
			uint gbl_we_off = mad24(mad24(lcl_o, (uint)MLO_N_OUTPUTS, lcl_c), (uint)MLO_FILTER_SZ, wei_off + lcl_i);
			bool within_range = gbl_we_off < (MLO_N_OUTPUTS*MLO_N_INPUTS*MLO_FILTER_SZ);
			gbl_we_off = (within_range) ? gbl_we_off : 0;
			_FLOAT wei = weights[gbl_we_off];
			wei = (within_range) ? wei : 0;
			lcl_wei[lcl_we_off]
				= wei;

#endif

		}

#endif

// over all batch stacks

#endif  // all input

		barrier(CLK_LOCAL_MEM_FENCE);

// convolution
#if MLO_GRP_SZ > MLO_ACTIVE_ALUS
		if (lcl_id < MLO_ACTIVE_ALUS)
#endif
		Conv(o_map_base,
			in_stg_off,
			pvt_in_stage, lcl_indata,
			pvt_wei_stage, lcl_wei,
			pvt_accum
			);

//		barrier(CLK_LOCAL_MEM_FENCE);
	}

#if MLO_GRP_SZ > MLO_ACTIVE_ALUS
	if (lcl_id >= MLO_ACTIVE_ALUS)
	{
		return;
	}
#endif
// write results out
	uint x_out_grp = x_grp;
	uint y_out_grp = y_grp;
	uint x_out_lcl = alu_tl0 * MLO_OUT_PIX_TILE0;
	uint y_out_lcl = alu_tl1 * MLO_OUT_PIX_TILE1;

    uint out_off = (b_index + stack) * MLO_OUT_BATCH_STRIDE + o_map * MLO_OUT_CHANNEL_STRIDE + (y_out_grp + y_out_lcl) * MLO_OUT_STRIDE + x_out_grp + x_out_lcl;
// over all local stacks
#if MLO_BATCH_ALIGNED == 0
	if (b_index + stack < MLO_BATCH_SZ)
#endif
	{

// over all local outputs
		uint out_off1 = out_off;
		for(uint o = 0; o < MLO_N_OUT_TILES
						; ++o, out_off1 += MLO_OUT_CHANNEL_STRIDE
						)
		{
// over output tile
			_FLOAT  bias_val = 0;
#if MLO_CONV_BIAS
			bias_val = bias[o_map + o];
#endif
			uint out_off2 = out_off1;
			for( uint j = 0; j < MLO_OUT_PIX_TILE1; ++j, out_off2 += MLO_OUT_STRIDE)
			{
				__global _FLOAT *out_p = &out[out_off2];
				for(uint i = 0; i < MLO_OUT_PIX_TILE0; ++i)
				{
					if ( true
#if 1 //MLO_OUT_ALIGNED == 0
					&& y_out_lcl + j < MLO_OUT_TILE1 && y_out_grp + y_out_lcl + j < MLO_OUT_HEIGHT &&  x_out_lcl + i < MLO_OUT_TILE0 && x_out_grp + x_out_lcl + i < MLO_OUT_WIDTH
#endif
#if MLO_OUTPUTS_ALIGNED == 0
						&& o_map + o < MLO_N_OUTPUTS
#endif
					)
					{
						out_p[i] = pvt_accum[o*MLO_OUT_TILE_SZ + j * MLO_OUT_PIX_TILE0 + i] + bias_val;
#if 0
						if ( out_off2 + i == 1 /*y_out_grp + y_out_lcl + j == 2 && x_out_grp + x_out_lcl + i == 0*/)
						{
							printf("K:out: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d    %f %f %f\n",
								MLO_OUT_TILE1,
								MLO_OUT_TILE0,
								grp_id0,
								lcl_id,
								alu_out_plane_id,
								alu_out_id,
								b_index + stack,
								o,
								o_map,
								out_off,
								out_off1,
								out_off2,
								y_out_grp,
								y_out_lcl,
								x_out_grp,
								x_out_lcl,
								j,
								i,
								pvt_accum[o*MLO_OUT_TILE_SZ + j * MLO_OUT_PIX_TILE0 + i],
								 bias_val,
								out[out_off2 + i]
								);
						}
#endif
					}

				}
			}

		}
	}

}

/*
 * Copyright (c) 2016 AMD Inc.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and/or associated documentation files (the
 * "Materials"), to deal in the Materials without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, sublicense, and/or sell copies of the Materials, and to
 * permit persons to whom the Materials are furnished to do so, subject to
 * the following conditions:
 *
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Materials.
 *
 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
 * MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
 */

#define _FLOAT					float
#define _FLOAT2					float2
#define _FLOAT4					float4
#define _FLOAT8					float8

#ifndef FLT_MAX
#define FLT_MAX         3.402823466e+38F        /* max value */
#endif

#define UNUSED __attribute__((__unused__))

#define MLO_FILTER_SZ (MLO_FILTER_SIZE1*MLO_FILTER_SIZE0)

#define MLO_GRP_SZ0  (MLO_GRP_TILE0*MLO_GRP_TILE1)
#define MLO_GRP_SZ1 1
#define MLO_GRP_SZ2 1
#define MLO_GRP_SZ (MLO_GRP_SZ0*MLO_GRP_SZ1*MLO_GRP_SZ2)
#define MLO_N_PROC_WAVES ((MLO_GRP_SZ + MLO_N_READ_PROCS - 1)/MLO_N_READ_PROCS)
#define MLO_OUT_TILE_SZ (MLO_OUT_PIX_TILE1*MLO_OUT_PIX_TILE0)
#define MLO_ALU_TILE_SZ (MLO_ALU_VTILE1*MLO_ALU_VTILE0)

#if MLO_IN_TILE0 < MLO_OUT_WIDTH || MLO_IN_TILE1 < MLO_OUT_HEIGHT
#define MLO_LARGE_MAP 1
#else
#define MLO_LARGE_MAP 0
#endif

#if (MLO_IN_WIDTH == MLO_OUT_WIDTH && (MLO_IN_WIDTH / MLO_IN_TILE0 ) * MLO_IN_TILE0 == MLO_IN_WIDTH && MLO_IN_HEIGHT == MLO_OUT_HEIGHT && (MLO_IN_HEIGHT / MLO_IN_TILE1 ) * MLO_IN_TILE1 == MLO_IN_HEIGHT)
#define MLO_OUT_ALIGNED 1
#else
#define MLO_OUT_ALIGNED 0
#endif

#define MLO_ALUTILES_STACK_SZ (MLO_N_ALUTILES_PERSTACK*MLO_ALU_TILE_SZ)
#define MLO_N_IN_TILES_TOTAL (MLO_N_IN_TILES_PERSTACK*MLO_N_STACKS)

#define MLO_N_OUT_TILE_BLOCKS0 ((MLO_OUT_WIDTH+MLO_IN_TILE0-1)/MLO_IN_TILE0)
#define MLO_N_OUT_TILE_BLOCKS1 ((MLO_OUT_HEIGHT+MLO_IN_TILE1-1)/MLO_IN_TILE1)
#define MLO_N_IN_PACKS  (MLO_N_INPUTS/MLO_N_IN_TILES_PERSTACK)

#define MLO_N_IN_READ (MLO_N_IN_PACKS*MLO_N_IN_TILES_PERSTACK)
#if MLO_N_IN_READ == MLO_N_INPUTS
#define MLO_INPUTS_ALIGNED 1
#else
#define MLO_INPUTS_ALIGNED 0
#endif

#define MLO_N_OUT_PACKS  (MLO_N_OUTPUTS/MLO_N_OUT_TILES_PERSTACK)
#if MLO_N_OUT_PACKS*MLO_N_OUT_TILES_PERSTACK == MLO_N_OUTPUTS && MLO_N_OUT_TILES_PERSTACK != MLO_N_OUTPUTS
#define MLO_OUTPUTS_ALIGNED 1
#else
#define MLO_OUTPUTS_ALIGNED 0
#endif

#define MLO_N_BATCH_PACKS (MLO_BATCH_SZ/MLO_N_STACKS)
#if MLO_N_BATCH_PACKS*MLO_N_STACKS == MLO_BATCH_SZ && MLO_N_STACKS != MLO_BATCH_SZ
#define MLO_BATCH_ALIGNED 1
#else
#define MLO_BATCH_ALIGNED 0
#endif

#define MLO_IN_LCL_WIDTH (MLO_IN_TILE0 + MLO_FILTER_SIZE0 - 1)  // here we use kernel size. it's important when padding == 0
#define MLO_IN_LCL_HEIGHT (MLO_IN_TILE1 + MLO_FILTER_SIZE1 - 1)
#define MLO_IN_LCL_TILE_SZ (MLO_IN_LCL_WIDTH*MLO_IN_LCL_HEIGHT)
#define MLO_IN_LCL_PERSTACK_SZ (MLO_IN_LCL_TILE_SZ*MLO_N_IN_TILES_PERSTACK)
#define MLO_IN_LCL_SZ (MLO_IN_LCL_PERSTACK_SZ*MLO_N_STACKS)

#define MLO_WEIGHTS_SZ (MLO_N_OUT_TILES_PERSTACK*MLO_N_IN_TILES_PERSTACK*MLO_FILTER_SZ)

#define MLO_PVT_ACCUM_DATA_SZ (MLO_N_OUT_TILES * MLO_OUT_TILE_SZ)
#define MLO_PVT_IN_WIDTH (MLO_FILTER_SIZE0 + MLO_OUT_PIX_TILE0 - 1)
#define MLO_PVT_IN_HEIGHT (MLO_OUT_PIX_TILE1)

#define MLO_LCL_WEIGHTS 1

#if defined(__AMDGCN__)
extern uint __llvm_amdgcn_readfirstlane(uint) __asm("llvm.amdgcn.readfirstlane");
#define uniform(x) __llvm_amdgcn_readfirstlane(x)
#else
#define uniform(x) (x)
#endif

static inline void calculateXYPos(uint linPos, uint width, uint * __restrict x, uint * __restrict y)
{

	(*y) = (uint)((float)linPos * (1.0f / (float)width) + 0.00001f);

	(*x) = linPos - (*y) * width;
}

static inline uint calculateOffset(uint stride, uint x, uint y)
{
	uint ret = y * stride + x;
	return(ret);
}

static inline void readDataElem(uint linPos,__local _FLOAT *lcl_data, uint lcl_base, UNUSED uint lcl_height, uint lcl_width, uint lcl_stride, uint lcl_y, uint lcl_x,
					 const __global _FLOAT * gbl_data, uint gbl_base, uint gbl_height, uint gbl_width, uint gbl_stride, int gbl_y, int gbl_x,
					 bool vis,
					 UNUSED bool debug)
{
	uint x, y;
	calculateXYPos(linPos, lcl_width, &x, &y);
	int g_x = x + gbl_x;
	int g_y = y + gbl_y;
	uint gbl_off0 = calculateOffset(gbl_stride, g_x, g_y);
	uint gbl_off = gbl_off0 + gbl_base;

#if MLO_LARGE_MAP == 1
	uint lcl_off = lcl_base + linPos;
	(void)lcl_stride;
	(void)lcl_x;
	(void)lcl_y;
#else
	uint l_x = x + lcl_x;
	uint l_y = y + lcl_y;
	uint lcl_off = lcl_base + mad24(l_y, lcl_stride, l_x);
#endif

#if MLO_LARGE_MAP == 1
	 vis &= (g_x >= 0 && g_x < gbl_width && g_y >= 0 && g_y < gbl_height);
#else
	 (void)gbl_width;
	 (void)gbl_height;
#endif
	 gbl_off = (vis) ? gbl_off : 0;
	 _FLOAT gbl_val = gbl_data[gbl_off];
	 gbl_val = (vis) ? gbl_val : 0;

	lcl_data[lcl_off] = gbl_val;

}

static inline void readData(uint lcl_id, uint size, uint lcl_p_stride, __local _FLOAT *lcl_data, uint lcl_base, uint lcl_height, uint lcl_width, uint lcl_stride, uint lcl_y, uint lcl_x,
					 const __global _FLOAT * gbl_data, uint gbl_base, uint gbl_height, uint gbl_width, uint gbl_stride, int gbl_y, int gbl_x,
					 bool vis,
					 bool debug
					 )
{

	for(uint i = lcl_id; i < size; i+= lcl_p_stride)
	{
		readDataElem(i, lcl_data, lcl_base, lcl_height, lcl_width, lcl_stride, lcl_y, lcl_x,
					 gbl_data, gbl_base, gbl_height, gbl_width, gbl_stride, gbl_y, gbl_x,
					 vis,
					 debug);
	}

}

static inline void loadData(uint lcl_id, uint lcl_p_stride,
					__local _FLOAT * lcl_data,
					uint lcl_off, uint lcl_size, uint lcl_height, uint lcl_width, uint lcl_stride, uint lcl_bot_y, uint lcl_bot_x,
					const __global _FLOAT * gbl_data,
					uint gbl_off, uint gbl_size, uint gbl_height, uint glb_width, uint gbl_stride, int gbl_bot_y, int gbl_bot_x,
					uint buf_block_ind, uint max_n_bufs, uint lcl_n_bufs,
					bool debug)
{

	for(uint c = 0; c < lcl_n_bufs;
		 ++c, lcl_off += lcl_size, gbl_off += gbl_size )
	{
		bool vis = (buf_block_ind + c <  max_n_bufs);
		readData(lcl_id, lcl_size, lcl_p_stride,
				 lcl_data, lcl_off, lcl_height, lcl_width, lcl_stride, lcl_bot_y, lcl_bot_x,
				 gbl_data, gbl_off, gbl_height, glb_width, gbl_stride, gbl_bot_y, gbl_bot_x,
				 vis,
				 (debug));
	}

}

static inline void Conv(uint o_map_base,
				uint in_stg_off,
				 _FLOAT * pvt_in_stage, __local _FLOAT * __restrict lcl_indata,
				 _FLOAT * pvt_wei_stage, __local _FLOAT * __restrict lcl_wei,
				 _FLOAT * pvt_accum
				 )
{
// convolution

		// over all inputs in stack
		uint in_stg_off1 = in_stg_off;
		for(uint i_c = 0; i_c < MLO_N_IN_TILES_PERSTACK; ++i_c, in_stg_off1 += MLO_IN_LCL_TILE_SZ)
		{
        // preload input
			uint wei_stg_base_off = mad24(o_map_base, (uint)(MLO_N_IN_TILES_PERSTACK*MLO_FILTER_SZ), mul24(i_c,(uint)MLO_FILTER_SZ));
			uint in_stg_off2 = in_stg_off1;
			for(uint j = 0; j < MLO_PVT_IN_HEIGHT-1; ++j, in_stg_off2+=MLO_IN_LCL_WIDTH
					)
			{
				for(uint i = 0; i < MLO_PVT_IN_WIDTH; ++i)
				{
					pvt_in_stage[j*MLO_PVT_IN_WIDTH + i] = lcl_indata[in_stg_off2 + i];
				}
			}

		// over filter rows
#ifdef __AMDGCN__
			#pragma unroll
#endif
			for(uint k = 0; k < MLO_FILTER_SIZE1; ++k, in_stg_off2+=MLO_IN_LCL_WIDTH
			)
			{
				uint k_act = 0;
#if MLO_DIR_FORWARD==1
				k_act = k;
#else
// load filter in reverse order
				k_act = MLO_FILTER_SIZE1 - 1 - k;
#endif
		// load next input row
				for(uint i_pvt = 0; i_pvt < MLO_PVT_IN_WIDTH; ++i_pvt)
				{
					pvt_in_stage[(MLO_PVT_IN_HEIGHT-1)*MLO_PVT_IN_WIDTH + i_pvt] = lcl_indata[in_stg_off2 + i_pvt];
				}

		// over all outputs
				for(uint o_c = 0; o_c < MLO_N_OUT_TILES; ++o_c)
				{
					uint wei_stg_off = wei_stg_base_off + o_c * MLO_N_IN_TILES_PERSTACK*MLO_FILTER_SZ + k_act * MLO_FILTER_SIZE0;
					for(uint i = 0; i < MLO_FILTER_SIZE0; ++i)
					{
						pvt_wei_stage[i] = lcl_wei[wei_stg_off + i];
					}

		// actual conv

					for( uint j = 0; j < MLO_OUT_PIX_TILE1; ++j)
					{
						for(uint i = 0; i < MLO_OUT_PIX_TILE0; ++i)
						{
							for(uint l = 0; l < MLO_FILTER_SIZE0; ++l)
							{

								uint l_act = 0;
#if MLO_DIR_FORWARD==1
								l_act = l;

#else
// in reverse horizontal and vertical orders
								l_act = MLO_FILTER_SIZE0 - 1 - l;

#endif

								pvt_accum[(o_c * MLO_OUT_PIX_TILE1 + j) * MLO_OUT_PIX_TILE0 + i]
									 += pvt_in_stage[j * MLO_PVT_IN_WIDTH + i + l] * pvt_wei_stage[l_act];

							}

						}
					}

				} // for(uint o_c = 0; o_c < MLO_N_OUT_TILES; ++o_c)

               // move data up
				for(uint j = 0; j < MLO_PVT_IN_HEIGHT-1; ++j)
				{
					for(uint i = 0; i < MLO_PVT_IN_WIDTH; ++i)
					{
						pvt_in_stage[j*MLO_PVT_IN_WIDTH + i] = pvt_in_stage[(j+1)*MLO_PVT_IN_WIDTH + i];
					}
				}

//				mem_fence(CLK_LOCAL_MEM_FENCE);

			} // for(uint k = 0; k < MLO_FILER_SIZE1; ++k,in_stg_off2+=MLO_IN_LCL_WIDTH)

		} // for(uint i_c = 0; i_c < MLO_N_IN_TILES_PERSTACK; ++i_c, in_stg_off1 += MLO_IN_LCL_PERSTACK_SZ)

}

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2)))
__kernel void MIOpenConvUniC(
       const __global _FLOAT * __restrict in,
       const __global _FLOAT * __restrict weights,
#if MLO_CONV_BIAS
       const __global _FLOAT * __restrict bias,
#endif
	__global _FLOAT * __restrict out,
	UNUSED _FLOAT padding_val
	   )
{
	__local _FLOAT lcl_indata[MLO_IN_LCL_SZ];
	__local _FLOAT lcl_wei[MLO_WEIGHTS_SZ];
	__private _FLOAT pvt_accum[MLO_PVT_ACCUM_DATA_SZ];
	__private _FLOAT pvt_in_stage[MLO_PVT_IN_HEIGHT * MLO_PVT_IN_WIDTH];
	__private _FLOAT pvt_wei_stage[MLO_FILTER_SIZE0];

	uint grp_id0 = get_group_id(0);
#if MLO_OUT_WIDTH == MLO_IN_TILE0
	uint y_tile_blk = grp_id0;
	uint x_tile_blk = 0;
#else
#if MLO_N_OUT_TILE_BLOCKS0 & (MLO_N_OUT_TILE_BLOCKS0 - 1)
	uint y_tile_blk = (uint)((float)grp_id0 * (1.0f / (float) MLO_N_OUT_TILE_BLOCKS0) + 0.00001f);
	int x_tile_blk = grp_id0 - mul24(y_tile_blk, (uint)MLO_N_OUT_TILE_BLOCKS0);
#else
	uint y_tile_blk = grp_id0 / MLO_N_OUT_TILE_BLOCKS0;
	int x_tile_blk = grp_id0 & (MLO_N_OUT_TILE_BLOCKS0 - 1);
#endif
#endif
	uint o_pack = get_group_id(1); // block of outputs
	uint b_pack = get_group_id(2); // batch block

	uint lcl_id = get_local_id(0);
#if MLO_ALUTILES_STACK_SZ & (MLO_ALUTILES_STACK_SZ - 1)
	uint stack = (uint)((float)lcl_id * (1.0f / (float)MLO_ALUTILES_STACK_SZ) + 0.00001f);  // stack
	uint alu_stack_id = lcl_id - mul24(stack, (uint)MLO_ALUTILES_STACK_SZ);  // alu index in stack
#else
	uint stack = lcl_id / MLO_ALUTILES_STACK_SZ;  // stack
	uint alu_stack_id = lcl_id & (MLO_ALUTILES_STACK_SZ - 1);  // alu index in stack
#if MLO_ALUTILES_STACK_SZ >= 64
	stack = uniform(stack);
#endif
#endif
// ALU plane inside stack
#if MLO_ALU_TILE_SZ & (MLO_ALU_TILE_SZ - 1)
	uint alu_out_plane_id = (uint)((float)alu_stack_id * (1.0f / (float)MLO_ALU_TILE_SZ) + 0.00001f);  // alu output plane index
	uint alu_out_id = alu_stack_id - mul24(alu_out_plane_id, (uint)MLO_ALU_TILE_SZ); // alu index inside an ALU output plane
#else
	uint alu_out_plane_id = alu_stack_id / MLO_ALU_TILE_SZ;  // alu output plane index
	uint alu_out_id = alu_stack_id & (MLO_ALU_TILE_SZ - 1); // alu index inside an ALU output plane
#endif
// pos inside ALU tile
#if MLO_ALU_VTILE0 & (MLO_ALU_VTILE0 - 1)
	uint alu_tl1 = (uint)((float)alu_out_id * (1.0f / (float)MLO_ALU_VTILE0) + 0.00001f);
	uint alu_tl0 = alu_out_id - mul24(alu_tl1, (uint)MLO_ALU_VTILE0);
#else
	uint alu_tl1 = alu_out_id / MLO_ALU_VTILE0;
	uint alu_tl0 = alu_out_id & (MLO_ALU_VTILE0 - 1);
#endif

	uint o_map_plane = o_pack * MLO_N_OUT_TILES_PERSTACK; // first output maps index per full ALU plane stack
	uint o_map_base = alu_out_plane_id*MLO_N_OUT_TILES;  // local output map offset
	uint o_map = o_map_plane + o_map_base; // output map index per ALU plane
	uint b_index = b_pack * MLO_N_STACKS;

#if MLO_LARGE_MAP != 1
#if MLO_GRP_SZ <= MLO_N_READ_PROCS
	uint wave_id = 0;
	uint wave_lcl_id = lcl_id;
#elif MLO_N_READ_PROCS & (MLO_N_READ_PROCS - 1)
	uint wave_id = (uint)((float)lcl_id * (1.0f / (float)MLO_N_READ_PROCS) + 0.00001f);
	uint wave_lcl_id = lcl_id - mul24(wave_id, (uint)MLO_N_READ_PROCS);
#else
	uint wave_id = (uint)((uint)lcl_id / MLO_N_READ_PROCS);
	uint wave_lcl_id = lcl_id & (MLO_N_READ_PROCS - 1);
#if MLO_N_READ_PROCS >= 64
        wave_id = uniform(wave_id);
#endif
#endif
#endif

	int x_grp = x_tile_blk * MLO_IN_TILE0;
	uint y_grp = y_tile_blk * MLO_IN_TILE1;

// TO DO: scale
#if MLO_LARGE_MAP == 1
	int x_in_grp = x_grp - MLO_FILTER_PAD0;
	int y_in_grp = y_grp - MLO_FILTER_PAD1;
#endif

	uint x_in_lcl = alu_tl0 * MLO_OUT_PIX_TILE0;
	uint y_in_lcl = alu_tl1 * MLO_OUT_PIX_TILE1;

// base offset to read data from local input data
	uint in_stg_off = stack*MLO_IN_LCL_PERSTACK_SZ + (y_in_lcl) * MLO_IN_LCL_WIDTH + x_in_lcl;

    uint in_off = b_index * MLO_IN_BATCH_STRIDE;

#if MLO_DIR_FORWARD==1
	uint wei_off = mul24(o_map_plane, (uint)(MLO_N_INPUTS * MLO_FILTER_SZ));
#else
	uint wei_off = mul24(o_map_plane,(uint)MLO_FILTER_SZ);
#endif

#if MLO_LARGE_MAP == 0
	for(uint i = lcl_id; i < MLO_IN_LCL_SZ;  i += MLO_GRP_SZ)
	{
		lcl_indata[i] = 0;
	}
#endif

	for(uint i = 0; i < MLO_PVT_ACCUM_DATA_SZ; ++i)
	{
		pvt_accum[i] = 0;
	}

	for(uint ic = 0; ic < MLO_N_INPUTS; ic += MLO_N_IN_TILES_PERSTACK, in_off += MLO_IN_CHANNEL_STRIDE*MLO_N_IN_TILES_PERSTACK,
				wei_off += MLO_N_IN_TILES_PERSTACK * MLO_FILTER_SZ
#if MLO_DIR_FORWARD==0
							* MLO_N_OUTPUTS
#endif
	)
	{
		barrier(CLK_LOCAL_MEM_FENCE);

// small map has been read in full continiously into the lDS buffer within padded rect,
// padding has been done on initilization.
// large map calculates padding on the fly and fills it with 0.

#if 1 // all inputs

#if MLO_LARGE_MAP == 1
		uint in_lcl_off1 = 0;
		uint in_off1 = in_off;
		for(uint i_b = 0; i_b < MLO_N_STACKS; ++i_b, in_off1 += MLO_IN_BATCH_STRIDE, in_lcl_off1 += MLO_IN_LCL_PERSTACK_SZ)
		{
			bool vis = true;
#if MLO_BATCH_ALIGNED == 0
			vis &= (b_index + i_b < MLO_BATCH_SZ);
#endif

// over all inputs in stack
			uint in_off2 = in_off1;
			uint in_lcl_off2 = in_lcl_off1;
			for(uint i_c = 0; i_c < MLO_N_IN_TILES_PERSTACK; ++i_c, in_off2 += MLO_IN_CHANNEL_STRIDE, in_lcl_off2 += MLO_IN_LCL_TILE_SZ)
			{
#if MLO_INPUTS_ALIGNED == 0
				vis &= (ic + i_c < MLO_N_INPUTS);
#endif

				uint elem_id = lcl_id;
				uint lcl_p_stride = MLO_GRP_SZ0;
				uint lcl_base = 0;
				uint lcl_y = 0;
				uint lcl_x = 0;
				uint gbl_base = 0;

				readData(elem_id, (MLO_IN_LCL_HEIGHT * MLO_IN_LCL_WIDTH), lcl_p_stride,
						&lcl_indata[in_lcl_off2], lcl_base, MLO_IN_LCL_HEIGHT, MLO_IN_LCL_WIDTH, MLO_IN_LCL_WIDTH, lcl_y, lcl_x,
						&in[in_off2], gbl_base, MLO_IN_HEIGHT, MLO_IN_WIDTH, MLO_IN_STRIDE, y_in_grp, x_in_grp,
						vis,
						true
					 );
			}

		}
#else
		for(uint i = wave_id; i < MLO_N_IN_TILES_TOTAL;  i += MLO_N_PROC_WAVES)
		{
		//(MLO_N_STACKS * MLO_N_OUT_TILES_PERSTACK)
#if MLO_N_IN_TILES_PERSTACK & (MLO_N_IN_TILES_PERSTACK - 1)
			uint i_b = (uint)((float)i * (1.0f / (float)MLO_N_IN_TILES_PERSTACK) + 0.00001f);
			uint i_c = i - mul24(i_b, (uint)MLO_N_IN_TILES_PERSTACK);
#else
			uint i_b = (uint)i / MLO_N_IN_TILES_PERSTACK;
			uint i_c = i & (MLO_N_IN_TILES_PERSTACK - 1);
#endif

			bool vis = true;

#if MLO_BATCH_ALIGNED == 0
			vis &= (b_index + i_b < MLO_BATCH_SZ);
#endif

#if MLO_INPUTS_ALIGNED == 0
			vis &= (ic + i_c < MLO_N_INPUTS);
#endif
			uint in_off2 = in_off + i_b * MLO_IN_BATCH_STRIDE + i_c * MLO_IN_CHANNEL_STRIDE;
			uint in_lcl_off2 = i_b * MLO_IN_LCL_PERSTACK_SZ + i_c * MLO_IN_LCL_TILE_SZ;

			uint elem_id = wave_lcl_id;
			uint lcl_p_stride = MLO_N_READ_PROCS;
			uint lcl_base = 0;
			uint lcl_y = MLO_FILTER_PAD1;
			uint lcl_x = MLO_FILTER_PAD0;
			uint gbl_base = 0;

			readData(elem_id, (MLO_IN_HEIGHT * MLO_IN_WIDTH), lcl_p_stride,
						&lcl_indata[in_lcl_off2], lcl_base, MLO_IN_HEIGHT, MLO_IN_WIDTH, MLO_IN_LCL_WIDTH, lcl_y, lcl_x,
						&in[in_off2], gbl_base, MLO_IN_HEIGHT, MLO_IN_WIDTH, MLO_IN_STRIDE, y_grp, x_grp,
						vis,
						true
					 );
		}
#endif

// read inputs and weights
// put weights into LDS

#if 1  // only weights

#if (MLO_WEIGHTS_SZ >= MLO_GRP_SZ) && defined(__AMDGCN__)
#if MLO_WEIGHTS_SZ/MLO_GRP_SZ > 4
#pragma unroll
#else
#pragma unroll (MLO_WEIGHTS_SZ/MLO_GRP_SZ)
#endif
#endif
		for(uint i = lcl_id; i < MLO_WEIGHTS_SZ; i += MLO_GRP_SZ)
		{
#if MLO_DIR_FORWARD==1
// here is [tops][bottoms]
#if (MLO_N_IN_TILES_PERSTACK * MLO_FILTER_SZ) & ((MLO_N_IN_TILES_PERSTACK * MLO_FILTER_SZ) - 1)
			uint lcl_o = (uint)((float)i * (1.0f / (float)(MLO_N_IN_TILES_PERSTACK * MLO_FILTER_SZ)) + 0.00001f);
			uint gbl_i = i - mul24(lcl_o, (uint)(MLO_N_IN_TILES_PERSTACK * MLO_FILTER_SZ));
#else
			uint lcl_o = i / (MLO_N_IN_TILES_PERSTACK * MLO_FILTER_SZ);
			uint gbl_i = i & ((MLO_N_IN_TILES_PERSTACK * MLO_FILTER_SZ) - 1);
#endif
			if((wei_off + lcl_o * MLO_N_INPUTS * MLO_FILTER_SZ + gbl_i) < (MLO_N_OUTPUTS*MLO_N_INPUTS*MLO_FILTER_SZ))
				lcl_wei[i] = weights[wei_off + lcl_o * MLO_N_INPUTS * MLO_FILTER_SZ + gbl_i];
			else
				lcl_wei[i] = weights[0];
#else
// outputs are botoms(inputs))
// inputs are tops(outputs)

#if (MLO_N_OUT_TILES_PERSTACK * MLO_FILTER_SZ) & ((MLO_N_OUT_TILES_PERSTACK * MLO_FILTER_SZ) - 1)
			uint lcl_o = (uint)((float)i * (1.0f / (float)(MLO_N_OUT_TILES_PERSTACK * MLO_FILTER_SZ)) + 0.00001f);
			uint gbl_i = i - mul24(lcl_o, (uint)(MLO_N_OUT_TILES_PERSTACK * MLO_FILTER_SZ));
#else
			uint lcl_o = i / (MLO_N_OUT_TILES_PERSTACK * MLO_FILTER_SZ);
			uint gbl_i = i & ((MLO_N_OUT_TILES_PERSTACK * MLO_FILTER_SZ) - 1);
#endif
#if MLO_FILTER_SZ & (MLO_FILTER_SZ - 1)
			uint lcl_c = (uint)((float)gbl_i * (1.0f / (float)MLO_FILTER_SZ) + 0.00001f);
			uint lcl_i = gbl_i - mul24(lcl_c, (uint)MLO_FILTER_SZ);
#else
			uint lcl_c = gbl_i / MLO_FILTER_SZ;
			uint lcl_i = gbl_i & (MLO_FILTER_SZ - 1);
#endif

			uint lcl_we_off = mad24(mad24(lcl_c, (uint)MLO_N_IN_TILES_PERSTACK, lcl_o), (uint)MLO_FILTER_SZ, lcl_i);
			uint gbl_we_off = mad24(mad24(lcl_o, (uint)MLO_N_OUTPUTS, lcl_c), (uint)MLO_FILTER_SZ, wei_off + lcl_i);
			bool within_range = gbl_we_off < (MLO_N_OUTPUTS*MLO_N_INPUTS*MLO_FILTER_SZ);
			gbl_we_off = (within_range) ? gbl_we_off : 0;
			_FLOAT wei = weights[gbl_we_off];
			wei = (within_range) ? wei : 0;
			lcl_wei[lcl_we_off]
				= wei;

#endif

		}

#endif

// over all batch stacks

#endif  // all input

		barrier(CLK_LOCAL_MEM_FENCE);

// convolution
#if MLO_GRP_SZ > MLO_ACTIVE_ALUS
		if (lcl_id < MLO_ACTIVE_ALUS)
#endif
		Conv(o_map_base,
			in_stg_off,
			pvt_in_stage, lcl_indata,
			pvt_wei_stage, lcl_wei,
			pvt_accum
			);

//		barrier(CLK_LOCAL_MEM_FENCE);
	}

#if MLO_GRP_SZ > MLO_ACTIVE_ALUS
	if (lcl_id >= MLO_ACTIVE_ALUS)
	{
		return;
	}
#endif
// write results out
	uint x_out_grp = x_grp;
	uint y_out_grp = y_grp;
	uint x_out_lcl = alu_tl0 * MLO_OUT_PIX_TILE0;
	uint y_out_lcl = alu_tl1 * MLO_OUT_PIX_TILE1;

    uint out_off = (b_index + stack) * MLO_OUT_BATCH_STRIDE + o_map * MLO_OUT_CHANNEL_STRIDE + (y_out_grp + y_out_lcl) * MLO_OUT_STRIDE + x_out_grp + x_out_lcl;
// over all local stacks
#if MLO_BATCH_ALIGNED == 0
	if (b_index + stack < MLO_BATCH_SZ)
#endif
	{

// over all local outputs
		uint out_off1 = out_off;
		for(uint o = 0; o < MLO_N_OUT_TILES
						; ++o, out_off1 += MLO_OUT_CHANNEL_STRIDE
						)
		{
// over output tile
			_FLOAT  bias_val = 0;
#if MLO_CONV_BIAS
			bias_val = bias[o_map + o];
#endif
			uint out_off2 = out_off1;
			for( uint j = 0; j < MLO_OUT_PIX_TILE1; ++j, out_off2 += MLO_OUT_STRIDE)
			{
				__global _FLOAT *out_p = &out[out_off2];
				for(uint i = 0; i < MLO_OUT_PIX_TILE0; ++i)
				{
					if ( true
#if 1 //MLO_OUT_ALIGNED == 0
					&& y_out_lcl + j < MLO_OUT_TILE1 && y_out_grp + y_out_lcl + j < MLO_OUT_HEIGHT &&  x_out_lcl + i < MLO_OUT_TILE0 && x_out_grp + x_out_lcl + i < MLO_OUT_WIDTH
#endif
#if MLO_OUTPUTS_ALIGNED == 0
						&& o_map + o < MLO_N_OUTPUTS
#endif
					)
					{
						out_p[i] = pvt_accum[o*MLO_OUT_TILE_SZ + j * MLO_OUT_PIX_TILE0 + i] + bias_val;
#if 0
						if ( out_off2 + i == 1 /*y_out_grp + y_out_lcl + j == 2 && x_out_grp + x_out_lcl + i == 0*/)
						{
							printf("K:out: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d    %f %f %f\n",
								MLO_OUT_TILE1,
								MLO_OUT_TILE0,
								grp_id0,
								lcl_id,
								alu_out_plane_id,
								alu_out_id,
								b_index + stack,
								o,
								o_map,
								out_off,
								out_off1,
								out_off2,
								y_out_grp,
								y_out_lcl,
								x_out_grp,
								x_out_lcl,
								j,
								i,
								pvt_accum[o*MLO_OUT_TILE_SZ + j * MLO_OUT_PIX_TILE0 + i],
								 bias_val,
								out[out_off2 + i]
								);
						}
#endif
					}

				}
			}

		}
	}

}


tests/sixtracklib/opencl: bugfixing in testlib OpenCL kernel for copying particles

#ifndef TESTS_SIXTRACKLIB_OPENCL_TEST_PARTICLES_KERNEL_OPENCL_CL__
#define TESTS_SIXTRACKLIB_OPENCL_TEST_PARTICLES_KERNEL_OPENCL_CL__

#if !defined( SIXTRL_NO_INCLUDES )
    #include "sixtracklib/opencl/internal/default_compile_options.h"

    #include "sixtracklib/common/definitions.h"
    #include "sixtracklib/common/internal/buffer_main_defines.h"
    #include "sixtracklib/common/buffer/managed_buffer_minimal.h"
    #include "sixtracklib/common/buffer/managed_buffer_remap.h"
    #include "sixtracklib/common/particles.h"
#endif /* !defined( SIXTRL_NO_INCLUDES ) */

#pragma OPENCL_EXTENSION cl_khr_int32_extended_atomics

__kernel void NS(Particles_copy_buffer_opencl)(
    SIXTRL_BUFFER_DATAPTR_DEC unsigned char const* SIXTRL_RESTRICT in_buffer_begin,
    SIXTRL_BUFFER_DATAPTR_DEC unsigned char* SIXTRL_RESTRICT out_buffer_begin,
    SIXTRL_BUFFER_DATAPTR_DEC SIXTRL_INT32_T* SIXTRL_RESTRICT ptr_success_flag )
{
    typedef NS(buffer_size_t) buf_size_t;
    typedef SIXTRL_BUFFER_OBJ_ARGPTR_DEC NS(Object)*       obj_iter_t;
    typedef SIXTRL_BUFFER_OBJ_ARGPTR_DEC NS(Object) const* obj_const_iter_t;

    typedef SIXTRL_PARTICLE_ARGPTR_DEC NS(Particles) const* ptr_const_particles_t;
    typedef SIXTRL_PARTICLE_ARGPTR_DEC NS(Particles)*       ptr_particles_t;

    SIXTRL_INT32_T success_flag = ( SIXTRL_INT32_T )0u;
    buf_size_t const  slot_size = ( buf_size_t )8u;

    if( ( !NS(ManagedBuffer_needs_remapping)(  in_buffer_begin,  slot_size ) ) &&
        ( !NS(ManagedBuffer_needs_remapping)( out_buffer_begin,  slot_size ) ) &&
        (  NS(ManagedBuffer_get_num_objects)( out_buffer_begin, slot_size ) ==
           NS(ManagedBuffer_get_num_objects)(  in_buffer_begin, slot_size ) ) )
    {
        obj_const_iter_t in_obj_it  = NS(ManagedBuffer_get_const_objects_index_begin)(
            in_buffer_begin, slot_size );

        obj_const_iter_t in_obj_end = NS(ManagedBuffer_get_const_objects_index_end)(
            in_buffer_begin, slot_size );

        obj_iter_t out_obj_it = NS(ManagedBuffer_get_objects_index_begin)(
            out_buffer_begin, slot_size );

        buf_size_t obj_begin_index = ( buf_size_t )0u;

        size_t const global_id = get_global_id( 0 );

        for( ; in_obj_it != in_obj_end ; ++in_obj_it, ++out_obj_it )
        {
            ptr_const_particles_t in_particles = ( ptr_const_particles_t )(
                uintptr_t )NS(Object_get_begin_addr)( in_obj_it );

            ptr_particles_t out_particles = ( ptr_particles_t )(
                uintptr_t )NS(Object_get_begin_addr)( out_obj_it );

            buf_size_t const nn =
                NS(Particles_get_num_of_particles)( in_particles );

            buf_size_t const obj_end_index = obj_begin_index + nn;

            if( nn == NS(Particles_get_num_of_particles)( out_particles ) )
            {
                if( ( global_id >= obj_begin_index ) &&
                    ( global_id <  obj_end_index ) )
                {
                    buf_size_t const particle_id = global_id - obj_begin_index;

                    if( particle_id < nn )
                    {
                        NS(Particles_copy_single)( out_particles, particle_id,
                            in_particles, particle_id );

                        if( NS(Particles_get_state_value)( out_particles, particle_id ) !=
                            NS(Particles_get_state_value)( in_particles,  particle_id ) )
                        {
                            printf( "ERROR %d\r\n", ( int )particle_id );
                        }
                    }
                    else
                    {
                        success_flag |= -1;
                    }

                    break;
                }
            }
            else
            {
                success_flag |= -2;
            }

            obj_begin_index = obj_end_index;
        }
    }
    else
    {
        if( NS(ManagedBuffer_needs_remapping)(  in_buffer_begin, slot_size ) )
        {
            success_flag |= -4;
        }

        if( NS(ManagedBuffer_needs_remapping)( out_buffer_begin, slot_size ) )
        {
            success_flag |= -8;
        }

        if(  NS(ManagedBuffer_get_num_objects)( out_buffer_begin, slot_size ) !=
             NS(ManagedBuffer_get_num_objects)(  in_buffer_begin, slot_size ) )
        {
            success_flag |= -16;
        }
    }

    if( ( success_flag     != ( SIXTRL_INT32_T )0 ) &&
        ( ptr_success_flag != SIXTRL_NULLPTR ) )
    {
        atomic_or( ptr_success_flag, success_flag );
    }

    return;
}

#endif /* TESTS_SIXTRACKLIB_OPENCL_TEST_PARTICLES_KERNEL_OPENCL_CL__ */

/* end: tests/sixtracklib/opencl/test_particles_kernel.cl */

#ifndef TESTS_SIXTRACKLIB_OPENCL_TEST_PARTICLES_KERNEL_OPENCL_CL__
#define TESTS_SIXTRACKLIB_OPENCL_TEST_PARTICLES_KERNEL_OPENCL_CL__

#if !defined( SIXTRL_NO_INCLUDES )
    #include "sixtracklib/opencl/internal/default_compile_options.h"

    #include "sixtracklib/common/definitions.h"
    #include "sixtracklib/common/internal/buffer_main_defines.h"
    #include "sixtracklib/common/buffer/managed_buffer_minimal.h"
    #include "sixtracklib/common/buffer/managed_buffer_remap.h"
    #include "sixtracklib/common/particles.h"
#endif /* !defined( SIXTRL_NO_INCLUDES ) */

#pragma OPENCL_EXTENSION cl_khr_int32_extended_atomics

__kernel void NS(Particles_copy_buffer_opencl)(
    SIXTRL_BUFFER_DATAPTR_DEC unsigned char const* SIXTRL_RESTRICT in_buffer_begin,
    SIXTRL_BUFFER_DATAPTR_DEC unsigned char* SIXTRL_RESTRICT out_buffer_begin,
    SIXTRL_BUFFER_DATAPTR_DEC int* SIXTRL_RESTRICT ptr_success_flag )
{
    typedef NS(buffer_size_t) buf_size_t;
    typedef SIXTRL_BUFFER_OBJ_ARGPTR_DEC NS(Object)*       obj_iter_t;
    typedef SIXTRL_BUFFER_OBJ_ARGPTR_DEC NS(Object) const* obj_const_iter_t;

    typedef SIXTRL_PARTICLE_ARGPTR_DEC NS(Particles) const* ptr_const_particles_t;
    typedef SIXTRL_PARTICLE_ARGPTR_DEC NS(Particles)*       ptr_particles_t;

    int success_flag = ( int )0u;
    buf_size_t const slot_size = ( buf_size_t )8u;

    if( ( !NS(ManagedBuffer_needs_remapping)(  in_buffer_begin,  slot_size ) ) &&
        ( !NS(ManagedBuffer_needs_remapping)( out_buffer_begin,  slot_size ) ) &&
        (  NS(ManagedBuffer_get_num_objects)( out_buffer_begin, slot_size ) ==
           NS(ManagedBuffer_get_num_objects)(  in_buffer_begin, slot_size ) ) )
    {
        obj_const_iter_t in_obj_it  = NS(ManagedBuffer_get_const_objects_index_begin)(
            in_buffer_begin, slot_size );

        obj_const_iter_t in_obj_end = NS(ManagedBuffer_get_const_objects_index_end)(
            in_buffer_begin, slot_size );

        obj_iter_t out_obj_it = NS(ManagedBuffer_get_objects_index_begin)(
            out_buffer_begin, slot_size );

        buf_size_t obj_begin_index = ( buf_size_t )0u;

        size_t const global_id = get_global_id( 0 );

        for( ; in_obj_it != in_obj_end ; ++in_obj_it, ++out_obj_it )
        {
            ptr_const_particles_t in_particles = ( ptr_const_particles_t )(
                uintptr_t )NS(Object_get_begin_addr)( in_obj_it );

            ptr_particles_t out_particles = ( ptr_particles_t )(
                uintptr_t )NS(Object_get_begin_addr)( out_obj_it );

            buf_size_t const nn =
                NS(Particles_get_num_of_particles)( in_particles );

            buf_size_t const obj_end_index = obj_begin_index + nn;

            if( nn == NS(Particles_get_num_of_particles)( out_particles ) )
            {
                if( ( global_id >= obj_begin_index ) &&
                    ( global_id <  obj_end_index ) )
                {
                    buf_size_t const particle_id = global_id - obj_begin_index;

                    if( particle_id < nn )
                    {
                        NS(Particles_copy_single)( out_particles, particle_id,
                            in_particles, particle_id );

                        if( NS(Particles_get_state_value)( out_particles, particle_id ) !=
                            NS(Particles_get_state_value)( in_particles,  particle_id ) )
                        {
                            printf( "ERROR %d\r\n", ( int )particle_id );
                        }
                    }
                    else
                    {
                        success_flag |= -1;
                    }

                    break;
                }
            }
            else
            {
                success_flag |= -2;
            }
        }
    }
    else
    {
        if( NS(ManagedBuffer_needs_remapping)(  in_buffer_begin, slot_size ) )
        {
            success_flag |= -4;
        }

        if( NS(ManagedBuffer_needs_remapping)( out_buffer_begin, slot_size ) )
        {
            success_flag |= -8;
        }

        if(  NS(ManagedBuffer_get_num_objects)( out_buffer_begin, slot_size ) !=
             NS(ManagedBuffer_get_num_objects)(  in_buffer_begin, slot_size ) )
        {
            success_flag |= -16;
        }
    }

    if( ( success_flag != 0 ) && ( ptr_success_flag != SIXTRL_NULLPTR ) )
    {
        atomic_or( ptr_success_flag, success_flag );
    }

    return;
}

#endif /* TESTS_SIXTRACKLIB_OPENCL_TEST_PARTICLES_KERNEL_OPENCL_CL__ */

/* end: tests/sixtracklib/opencl/test_particles_kernel.cl */