Fixed some limit pointers to point to correct address space

__constant float4 baseFactor = (float4)(1.0f,2.0f,3.0f,4.0f);

void init_scratch(
    size_t gid, size_t wgid,
    __global float4* input, __constant float4* factors, __local float4* scratch) {
    scratch[wgid] = input[gid]*factors[gid];
}

__local float4* flip_to_awesomeness(size_t wgid, size_t wgsize, __local float4* scratch) {
    float4 index_vec = scratch[wgid];
    float index_float = index_vec.x + index_vec.y + index_vec.z + index_vec.w;
    int index = (int)(index_float) % wgsize;
    return &scratch[index];
}

__kernel void awesomize(
    __global float4* input,
    __global float4* output,
    __constant float4* factors,
    __local float4* scratch) {

    size_t gid = get_global_id(0);
    size_t wgid = get_local_id(0);
    size_t wgsize = get_local_size (0);

    __local size_t lotteryWinner;

    init_scratch(gid, wgid, wgsize, input, factors, scratch);
    lotteryWinner = gid;
    barrier(CLK_LOCAL_MEM_FENCE);

    if (gid == 0) {
        output[0] = lotteryWinner;
    } else {
        output[gid] = (*flip_to_awesomeness(wgid, wgsize, scratch))*baseFactor;
    }
}

// FULL Translated ---------->

typedef struct {
    float4 baseFactor;
} ConstantAllocations;

typedef struct {
    size_t lotteryWinner;
} LocalAllocations;

typedef struct {
    size_t awesomize__gid;
    size_t awesomize__wgid;
    size_t awesomize__wgsize;
    float4 flip_to_awesomeness__index_vec;
    float  flip_to_awesomeness__index_float;
    int    flip_to_awesomeness__index;
} PrivateAllocations;

// TODO: fix representation to be correct
typedef struct {
    // probably this is not needed here.. we can refer directly to struct
    // where ever it is used
    __constant ConstantAllocations *constantAllocations_min, *constantAllocations_max;
    __constant float4 *factors_min, *factors_max;
} ConstantLimits;

typedef struct {
    __global float4 *input_min, *input_max;
    __global float4 *output_min, *output_max;
} GlobalLimits;

typedef struct {
    __local LocalAllocations *localAllocations_min, *localAllocations_max;
    __local float4 *scratch_min, *scratch_max;
} LocalLimits;

typedef struct {
    ConstantLimits     constantLimits;
    GlobalLimits       globalLimits;
    LocalLimits        localLimits;
    // this is used also to actually store private memory since
    // that is always unfragmented
    PrivateAllocations privateAllocations;
} ProgramAllocations;

// all initialized values in global scope must be in constant address space
__constant ConstantAllocations constantAllocations = {
    (float4)(1.0f,2.0f,3.0f,4.0f)
};

// local address space is here because in LLVM IR variables in local address
// space are promoted to global scope
__local LocalAllocations localAllocations = {0};

// startup code to collect all limits and allocate private memory
__kernel void awesomize(
    __global float4* input, size_t input_count,
    __global float4* output, size_t output_count,
    __constant float4* factors, size_t factors_count,
    __local float4* scratch, size_t scratch_count) {

    ProgramAllocations pa;
    pa.constantLimits.constantAllocations_min = &constantAllocations;
    pa.constantLimits.constantAllocations_max = &constantAllocations + 1;
    pa.constantLimits.factors_min = &factors[0];
    pa.constantLimits.factors_max = &factors[factors_count];

    pa.globalLimits.input_min = &input[0];
    pa.globalLimits.input_max = &input[input_count];
    pa.globalLimits.output_min = &output[0];
    pa.globalLimits.output_max = &output[output_count];

    pa.localLimits.localAllocations_min = &localAllocations;
    pa.localLimits.localAllocations_max = &localAllocations + 1;
    pa.localLimits.scratch_min = &scratch[0];
    pa.localLimits.scratch_max = &scratch[scratch_count];

    // syntax simplified to prevent too verbose struct definition and creation
    awesomize_original_kernel(
        &pa,
        {input, pa.globalLimits.input_min, pa.globalLimits.input_max},
        {output, pa.globalLimits.output_min, pa.globalLimits.output_max},
        {factors, pa.constantLimits.factors_min, pa.constantLimits.factors_max},
        {scratch, pa.localLimits.scratch_min, pa.localLimits.scratch_max}
    );
}

void init_scratch(
    ProgramAllocations *pa,
    size_t gid, size_t wgid,
    SafeGlobalFloat4 input, SafeConstantFloat4 factors, SafeLocalFloat4 scratch) {

    // original scratch[wgid] = input[gid]*factors[gid];

    write_safely(scratch, wgid, scratch.min, scratch.max,
        read_safely(input.ptr, gid, input.min, input.max) *
        read_safely(factors.ptr, gid, factors.min, factors.max);
}

__local float4* flip_to_awesomeness(
    ProgramAllocations *pa,
    size_t wgid, size_t wgsize, SafeLocalFloat4 scratch) {

    pa->privateAllocations.flip_to_awesomeness__index_vec =
        read_safely(scratch.ptr, wgid, scratch.min, scratch.max);

    pa->privateAllocations.flip_to_awesomeness__index_float =
        pa->privateAllocations.flip_to_awesomeness__index_vec.x +
        pa->privateAllocations.flip_to_awesomeness__index_vec.y +
        pa->privateAllocations.flip_to_awesomeness__index_vec.z +
        pa->privateAllocations.flip_to_awesomeness__index_vec.w;

    pa->privateAllocations.flip_to_awesomeness__index =
        (int)(pa->privateAllocations.flip_to_awesomeness__index_float) % wgsize;

    // no check needed because we are not accessing memory yet, just getting address
    return &scratch.ptr[index];
}

void awesomize_original_kernel(
    ProgramAllocations *pa,
    SafeGlobalFloat4   input,
    SafeGlobalFloat4   output,
    SafeConstantFloat4 factors,
    SafeLocalFloat4    scratch) {

    pa->privateAllocations.awesomize__gid = get_global_id(0);
    pa->privateAllocations.awesomize__wgid = get_local_id(0);
    pa->privateAllocations.awesomize__wgsize = get_local_size(0);

    init_scratch(
        pa->privateAllocations.awesomize__gid,
        pa->privateAllocations.awesomize__wgid,
        pa->privateAllocations.awesomize__wgsize,
        input, factors, scratch);

    // for local memory replacement is done directly against struct in global scope
    localAllocations.lotteryWinner = pa->privateAllocations.awesomize__gid;
    barrier(CLK_LOCAL_MEM_FENCE);

    if (pa->privateAllocations.awesomize__gid == 0) {
        write_safely(output.ptr, 0, output.min, output.max, localAllocations.lotteryWinner);
    } else {
        // here we were not able to know exact limit to which fragment pointer belongs, so for read
        // we need to compare all limits
        write_safely(output.ptr, gid, output.min, output.max,
            read_safely(flip_to_awesomeness(wgid, wgsize, scratch), 0, pa->localLimits) *
            constantAllocations.baseFactor);
    }
}

// OPTIMIZED translation -------->

typedef struct {
    __constant float4 *factors_min, *factors_max;
} ConstantLimits;

typedef struct {
    __global float4 *input_min, *input_max;
    __global float4 *output_min, *output_max;
} GlobalLimits;

typedef struct {
    __local float4 *scratch_min, *scratch_max;
} LocalLimits;

typedef struct {
    ConstantLimits     constantLimits;
    GlobalLimits       globalLimits;
    LocalLimits        localLimits;
} ProgramAllocations;

__constant float4 baseFactor = (float4)(1.0f,2.0f,3.0f,4.0f);

// startup code to collect all limits and allocate private memory
__kernel void awesomize(
    __global float4* input, size_t input_count,
    __global float4* output, size_t output_count,
    __constant float4* factors, size_t factors_count,
    __local float4* scratch, size_t scratch_count) {

    ProgramAllocations pa;
    pa.constantLimits.factors_min = &factors[0];
    pa.constantLimits.factors_max = &factors[factors_count];
    pa.globalLimits.input_min = &input[0];
    pa.globalLimits.input_max = &input[input_count];
    pa.globalLimits.output_min = &output[0];
    pa.globalLimits.output_max = &output[output_count];
    pa.localLimits.scratch_min = &scratch[0];
    pa.localLimits.scratch_max = &scratch[scratch_count];

    // syntax simplified to prevent too verbose struct definition and creation
    awesomize_original_kernel(
        &pa,
        {input, pa.globalLimits.input_min, pa.globalLimits.input_max},
        {output, pa.globalLimits.output_min, pa.globalLimits.output_max},
        {factors, pa.constantLimits.factors_min, pa.constantLimits.factors_max},
        {scratch, pa.localLimits.scratch_min, pa.localLimits.scratch_max}
    );
}

void init_scratch(
    ProgramAllocations *pa,
    size_t gid, size_t wgid,
    SafeGlobalFloat4 input, SafeConstantFloat4 factors, SafeLocalFloat4 scratch) {

    // original: scratch[wgid] = input[gid]*factors[gid];
    write_safely(scratch, wgid, scratch.min, scratch.max,
        read_safely(input.ptr, gid, input.min, input.max) *
        read_safely(factors.ptr, gid, factors.min, factors.max);
}

__local float4* flip_to_awesomeness(
    ProgramAllocations *pa,
    size_t wgid, size_t wgsize, SafeLocalFloat4 scratch) {

    float4 index_vec = read_safely(scratch.ptr, wgid, scratch.min, scratch.max);
    float index_float = index_vec.x + index_vec.y + index_vec.z + index_vec.w;
    int index = (int)(index_float) % wgsize;

    // no check needed because we are not accessing memory yet, just getting address
    return &scratch.ptr[index];
}

void awesomize_original_kernel(
    ProgramAllocations *pa,
    SafeGlobalFloat4   input,
    SafeGlobalFloat4   output,
    SafeConstantFloat4 factors,
    SafeLocalFloat4    scratch) {

    size_t gid = get_global_id(0);
    size_t wgid = get_local_id(0);
    size_t wgsize = get_local_size(0);

    init_scratch(gid, wgid, wgsize, input, factors, scratch);

    __local size_t lotteryWinner = gid;
    barrier(CLK_LOCAL_MEM_FENCE);

    if (gid == 0) {
        write_safely(output.ptr, 0, output.min, output.max, lotteryWinner);
    } else {
        write_safely(output.ptr, gid, output.min, output.max,
            read_safely(flip_to_awesomeness(wgid, wgsize, scratch), 0,
                        pa->localLimits.scratch_min, pa->LocalLimits.scratch_max) * baseFactor);
    }
}

__constant float4 baseFactor = (float4)(1.0f,2.0f,3.0f,4.0f);

void init_scratch(
    size_t gid, size_t wgid,
    __global float4* input, __constant float4* factors, __local float4* scratch) {
    scratch[wgid] = input[gid]*factors[gid];
}

__local float4* flip_to_awesomeness(size_t wgid, size_t wgsize, __local float4* scratch) {
    float4 index_vec = scratch[wgid];
    float index_float = index_vec.x + index_vec.y + index_vec.z + index_vec.w;
    int index = (int)(index_float) % wgsize;
    return &scratch[index];
}

__kernel void awesomize(
    __global float4* input,
    __global float4* output,
    __constant float4* factors,
    __local float4* scratch) {

    size_t gid = get_global_id(0);
    size_t wgid = get_local_id(0);
    size_t wgsize = get_local_size (0);

    __local size_t lotteryWinner;

    init_scratch(gid, wgid, wgsize, input, factors, scratch);
    lotteryWinner = gid;
    barrier(CLK_LOCAL_MEM_FENCE);

    if (gid == 0) {
        output[0] = lotteryWinner;
    } else {
        output[gid] = (*flip_to_awesomeness(wgid, wgsize, scratch))*baseFactor;
    }
}

// FULL Translated ---------->

typedef struct {
    float4 baseFactor;
} ConstantAllocations;

typedef struct {
    size_t lotteryWinner;
} LocalAllocations;

typedef struct {
    size_t awesomize__gid;
    size_t awesomize__wgid;
    size_t awesomize__wgsize;
    float4 flip_to_awesomeness__index_vec;
    float  flip_to_awesomeness__index_float;
    int    flip_to_awesomeness__index;
} PrivateAllocations;

// TODO: fix representation to be correct
typedef struct {
    // probably this is not needed here.. we can refer directly to struct
    // where ever it is used
    __constant ConstantAllocations *constantAllocations_min, *constantAllocations_max;
    __constant float4 *factors_min, *factors_max;
} ConstantLimits;

typedef struct {
    __global float4 *input_min, *input_max;
    __global float4 *output_min, *output_max;
} GlobalLimits;

typedef struct {
    __constant LocalAllocations *localAllocations_min, *localAllocations_max;
    __constant float4 *scratch_min, *scratch_max;
} LocalLimits;

typedef struct {
    ConstantLimits     constantLimits;
    GlobalLimits       globalLimits;
    LocalLimits        localLimits;
    // this is used also to actually store private memory since
    // that is always unfragmented
    PrivateAllocations privateAllocations;
} ProgramAllocations;

// all initialized values in global scope must be in constant address space
__constant ConstantAllocations constantAllocations = {
    (float4)(1.0f,2.0f,3.0f,4.0f)
};

// local address space is here because in LLVM IR variables in local address
// space are promoted to global scope
__local LocalAllocations localAllocations = {0};

// startup code to collect all limits and allocate private memory
__kernel void awesomize(
    __global float4* input, size_t input_count,
    __global float4* output, size_t output_count,
    __constant float4* factors, size_t factors_count,
    __local float4* scratch, size_t scratch_count) {

    ProgramAllocations pa;
    pa.constantLimits.constantAllocations_min = &constantAllocations;
    pa.constantLimits.constantAllocations_max = &constantAllocations + 1;
    pa.constantLimits.factors_min = &factors[0];
    pa.constantLimits.factors_max = &factors[factors_count];

    pa.globalLimits.input_min = &input[0];
    pa.globalLimits.input_max = &input[input_count];
    pa.globalLimits.output_min = &output[0];
    pa.globalLimits.output_max = &output[output_count];

    pa.localLimits.localAllocations_min = &localAllocations;
    pa.localLimits.localAllocations_max = &localAllocations + 1;
    pa.localLimits.scratch_min = &scratch[0];
    pa.localLimits.scratch_max = &scratch[scratch_count];

    // syntax simplified to prevent too verbose struct definition and creation
    awesomize_original_kernel(
        &pa,
        {input, pa.globalLimits.input_min, pa.globalLimits.input_max},
        {output, pa.globalLimits.output_min, pa.globalLimits.output_max},
        {factors, pa.constantLimits.factors_min, pa.constantLimits.factors_max},
        {scratch, pa.localLimits.scratch_min, pa.localLimits.scratch_max}
    );
}

void init_scratch(
    ProgramAllocations *pa,
    size_t gid, size_t wgid,
    SafeGlobalFloat4 input, SafeConstantFloat4 factors, SafeLocalFloat4 scratch) {

    // original scratch[wgid] = input[gid]*factors[gid];

    write_safely(scratch, wgid, scratch.min, scratch.max,
        read_safely(input.ptr, gid, input.min, input.max) *
        read_safely(factors.ptr, gid, factors.min, factors.max);
}

__local float4* flip_to_awesomeness(
    ProgramAllocations *pa,
    size_t wgid, size_t wgsize, SafeLocalFloat4 scratch) {

    pa->privateAllocations.flip_to_awesomeness__index_vec =
        read_safely(scratch.ptr, wgid, scratch.min, scratch.max);

    pa->privateAllocations.flip_to_awesomeness__index_float =
        pa->privateAllocations.flip_to_awesomeness__index_vec.x +
        pa->privateAllocations.flip_to_awesomeness__index_vec.y +
        pa->privateAllocations.flip_to_awesomeness__index_vec.z +
        pa->privateAllocations.flip_to_awesomeness__index_vec.w;

    pa->privateAllocations.flip_to_awesomeness__index =
        (int)(pa->privateAllocations.flip_to_awesomeness__index_float) % wgsize;

    // no check needed because we are not accessing memory yet, just getting address
    return &scratch.ptr[index];
}

void awesomize_original_kernel(
    ProgramAllocations *pa,
    SafeGlobalFloat4   input,
    SafeGlobalFloat4   output,
    SafeConstantFloat4 factors,
    SafeLocalFloat4    scratch) {

    pa->privateAllocations.awesomize__gid = get_global_id(0);
    pa->privateAllocations.awesomize__wgid = get_local_id(0);
    pa->privateAllocations.awesomize__wgsize = get_local_size(0);

    init_scratch(
        pa->privateAllocations.awesomize__gid,
        pa->privateAllocations.awesomize__wgid,
        pa->privateAllocations.awesomize__wgsize,
        input, factors, scratch);

    // for local memory replacement is done directly against struct in global scope
    localAllocations.lotteryWinner = pa->privateAllocations.awesomize__gid;
    barrier(CLK_LOCAL_MEM_FENCE);

    if (pa->privateAllocations.awesomize__gid == 0) {
        write_safely(output.ptr, 0, output.min, output.max, localAllocations.lotteryWinner);
    } else {
        // here we were not able to know exact limit to which fragment pointer belongs, so for read
        // we need to compare all limits
        write_safely(output.ptr, gid, output.min, output.max,
            read_safely(flip_to_awesomeness(wgid, wgsize, scratch), 0, pa->localLimits) *
            constantAllocations.baseFactor);
    }
}

// OPTIMIZED translation -------->

typedef struct {
    __constant float4 *factors_min, *factors_max;
} ConstantLimits;

typedef struct {
    __global float4 *input_min, *input_max;
    __global float4 *output_min, *output_max;
} GlobalLimits;

typedef struct {
    __constant float4 *scratch_min, *scratch_max;
} LocalLimits;

typedef struct {
    ConstantLimits     constantLimits;
    GlobalLimits       globalLimits;
    LocalLimits        localLimits;
} ProgramAllocations;

__constant float4 baseFactor = (float4)(1.0f,2.0f,3.0f,4.0f);

// startup code to collect all limits and allocate private memory
__kernel void awesomize(
    __global float4* input, size_t input_count,
    __global float4* output, size_t output_count,
    __constant float4* factors, size_t factors_count,
    __local float4* scratch, size_t scratch_count) {

    ProgramAllocations pa;
    pa.constantLimits.factors_min = &factors[0];
    pa.constantLimits.factors_max = &factors[factors_count];
    pa.globalLimits.input_min = &input[0];
    pa.globalLimits.input_max = &input[input_count];
    pa.globalLimits.output_min = &output[0];
    pa.globalLimits.output_max = &output[output_count];
    pa.localLimits.scratch_min = &scratch[0];
    pa.localLimits.scratch_max = &scratch[scratch_count];

    // syntax simplified to prevent too verbose struct definition and creation
    awesomize_original_kernel(
        &pa,
        {input, pa.globalLimits.input_min, pa.globalLimits.input_max},
        {output, pa.globalLimits.output_min, pa.globalLimits.output_max},
        {factors, pa.constantLimits.factors_min, pa.constantLimits.factors_max},
        {scratch, pa.localLimits.scratch_min, pa.localLimits.scratch_max}
    );
}

void init_scratch(
    ProgramAllocations *pa,
    size_t gid, size_t wgid,
    SafeGlobalFloat4 input, SafeConstantFloat4 factors, SafeLocalFloat4 scratch) {

    // original: scratch[wgid] = input[gid]*factors[gid];
    write_safely(scratch, wgid, scratch.min, scratch.max,
        read_safely(input.ptr, gid, input.min, input.max) *
        read_safely(factors.ptr, gid, factors.min, factors.max);
}

__local float4* flip_to_awesomeness(
    ProgramAllocations *pa,
    size_t wgid, size_t wgsize, SafeLocalFloat4 scratch) {

    float4 index_vec = read_safely(scratch.ptr, wgid, scratch.min, scratch.max);
    float index_float = index_vec.x + index_vec.y + index_vec.z + index_vec.w;
    int index = (int)(index_float) % wgsize;

    // no check needed because we are not accessing memory yet, just getting address
    return &scratch.ptr[index];
}

void awesomize_original_kernel(
    ProgramAllocations *pa,
    SafeGlobalFloat4   input,
    SafeGlobalFloat4   output,
    SafeConstantFloat4 factors,
    SafeLocalFloat4    scratch) {

    size_t gid = get_global_id(0);
    size_t wgid = get_local_id(0);
    size_t wgsize = get_local_size(0);

    init_scratch(gid, wgid, wgsize, input, factors, scratch);

    __local size_t lotteryWinner = gid;
    barrier(CLK_LOCAL_MEM_FENCE);

    if (gid == 0) {
        write_safely(output.ptr, 0, output.min, output.max, lotteryWinner);
    } else {
        write_safely(output.ptr, gid, output.min, output.max,
            read_safely(flip_to_awesomeness(wgid, wgsize, scratch), 0,
                        pa->localLimits.scratch_min, pa->LocalLimits.scratch_max) * baseFactor);
    }
}


1 canline is verified

1 canline is verified

/*
 * 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 MLO_N_OUT_HORIZ_READS (MLO_IN_WIDTH)
#define MLO_PER_WAVE_READ ((MLO_IN_WIDTH + MLO_N_WAVES - 1) / MLO_N_WAVES)
#define MLO_N_OUT_HORIZ_PIX_READS (MLO_PER_WAVE_READ * MLO_N_WAVES)
#define MLO_N_OUT_VERTICAL_READS (MLO_FILTER_SIZE1)

#define MLO_IN_VERT_READS (1)
// there is an assumption that the scanline fits into LDS
#define MLO_N_IN_HORIZ_PIX_READS (MLO_IN_WIDTH)
#define MLO_N_IN_HORIZ_READS ((MLO_N_IN_HORIZ_PIX_READS + MLO_READ_UNIT - 1) / MLO_READ_UNIT)
#define MLO_IN_N_PIXS_OFF  (MLO_N_IN_HORIZ_READS*MLO_READ_UNIT - MLO_N_IN_HORIZ_PIX_READS)
#define MLO_IN_LCL_WIDTH (MLO_N_IN_HORIZ_READS * MLO_READ_UNIT + 2* MLO_FILTER_PAD0)
#define MLO_IN_LCL_HEIGHT MLO_IN_VERT_READS
#define MLO_IN_LCL_SZ (MLO_IN_LCL_WIDTH * MLO_IN_LCL_HEIGHT)
#define MLO_TOTAL_IN_LCL_SZ (MLO_N_LCL_BATCHS*MLO_N_LCL_IN_MAPS*MLO_IN_LCL_SZ)

#define MLO_WEI_LCL_SZ ((MLO_N_WAVES - 1) * MLO_HW_WAVE_SZ * MLO_FILTER_SIZE0)
#if MLO_TOTAL_IN_LCL_SZ > MLO_WEI_LCL_SZ
#define MLO_LCL_SZ (MLO_TOTAL_IN_LCL_SZ)
#else
#define MLO_LCL_SZ (MLO_WEI_LCL_SZ)
#endif

#define MLO_HW_WAVE_ID_SETTING 0

#if MLO_HW_WAVE_ID_SETTING
extern __attribute__((const)) uint __hsail_get_dynwave_id(void);
inline int getWaveId()
{
	int wave_id = 0;

	wave_id = __hsail_get_dynwave_id();
	wave_id = wave_id & MLO_N_PHYS_WAVES_MASK;
	return(wave_id);
}
#else
inline int getWaveId()
{
	int wave_id = 0;

	wave_id = (get_local_id(0) >> MLO_LG2_PHYS_WAVE_SZ);

	return(wave_id);
}
#endif

inline int gePhysLocalId()
{
	int lcl_wave_id = get_local_id(0) - ((get_local_id(0) >> MLO_LG2_PHYS_WAVE_SZ) << MLO_LG2_PHYS_WAVE_SZ);
	return(lcl_wave_id);
}

inline int iDiv(int v, int d)
{
	int r = (int)((float)v / d + 0.00001f);
	return(r);
}

inline int iMod(int v, int u, int d)
{
	int r = v - mul24((int)u, (int)d);
	return(r);
}

inline void ReduceKernel(__local _FLOAT * lcl_blob, _FLOAT *weights_accum, int lcl_id, int scan_lcl, int sum_stride, int unit_len, bool debug)
{
// read first half
	if (scan_lcl < (sum_stride >> 1))
	{
		for (int i = 0; i < unit_len; ++i)
		{
			weights_accum[i] = lcl_blob[(lcl_id + scan_lcl) * unit_len + i];

		}

	}
// add second half
// appload accumulated value so far
	for (int j = (sum_stride >> 1); j > 0; j >>= 1)
	{
		barrier(CLK_LOCAL_MEM_FENCE);
		if (scan_lcl < j)
		{
			for (int i = 0; i < unit_len; ++i)
			{
				weights_accum[i] += lcl_blob[(lcl_id + j) * unit_len + i];

				lcl_blob[lcl_id * unit_len + i] = weights_accum[i];
			}

		}
	}
}

inline void ReduceKernel64(__local _FLOAT * lcl_blob, _FLOAT *weights_accum, int lcl_id, int scan_lcl, int sum_stride, int unit_len, bool debug)
{
	// read first half
	if (scan_lcl < (sum_stride >> 1))
	{
		for (int i = 0; i < unit_len; ++i)
		{
			weights_accum[i] = lcl_blob[(lcl_id + scan_lcl) * unit_len + i];

		}

	}
	// add second half
	// appload accumulated value so far
	for (int j = (sum_stride >> 1); j > 0; j >>= 1)
	{
		barrier(CLK_LOCAL_MEM_FENCE);
		if (scan_lcl < j)
		{
			for (int i = 0; i < unit_len; ++i)
			{
				weights_accum[i] += lcl_blob[(lcl_id + j) * unit_len + i];

				lcl_blob[lcl_id * unit_len + i] = weights_accum[i];
			}

		}
	}
}

inline void  Kahan_summation(_FLOAT *sum, _FLOAT * c, _FLOAT v)
{
	_FLOAT y = v - *c;    //So far, so good: c is zero.
	_FLOAT t = *sum + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
	*c = (t - *sum) - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
	*sum = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
}

inline void  Kahan_summation_tricked(_FLOAT *sum, _FLOAT * c, _FLOAT v, _FLOAT mod)
{
	_FLOAT y = v - *c;    //So far, so good: c is zero.
	_FLOAT t = *sum + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
	*c = (t - *sum) * mod - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
	*sum = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
}

inline void Kahan_summation2(_FLOAT *sum, _FLOAT *c, _FLOAT *v, int n)
{
	for (int i = 0; i < n; ++i)
	{
		_FLOAT y = v[i] - c[i];    //So far, so good: c is zero.
		_FLOAT t = sum[i] + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
		c[i] = (t - sum[i]) - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
		sum[i] = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
	}
}

inline void readInput(int lcl_id, int c_idx, int sc, int gbl_in_scan_off, __global _FLOAT * bot, __local _FLOAT *lcl_bot)
{
	for (int p4 = lcl_id; p4 < MLO_N_LCL_IN_MAPS * MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS;
		p4 += MLO_GRP_SZ)
	{
		__private _FLOAT in_rd_data[MLO_READ_UNIT];

		int c_scan = sc;

		int c = iDiv(p4, (MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS));
		int c_pix4 = iMod(p4, c, (MLO_N_IN_HORIZ_READS*MLO_IN_VERT_READS));

		if (c_idx + c < MLO_N_INPUTS)

		{

#if MLO_IN_N_PIXS_OFF > 0

			if (c_pix4 == MLO_N_IN_HORIZ_READS - 1)
			{
				for (int i = 0; i < MLO_IN_N_PIXS_OFF; ++i)
				{

					in_rd_data[i] = bot[gbl_in_scan_off + c*MLO_IN_CHANNEL_STRIDE + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT + i];
				}

				for (int i = MLO_READ_UNIT - 1; i >= MLO_READ_UNIT - MLO_IN_N_PIXS_OFF; --i)
				{
					in_rd_data[i] = 0;
				}

			}
			else
#endif
			{
				*(MLO_READ_TYPE*)in_rd_data = *(MLO_READ_TYPE*)&bot[gbl_in_scan_off + c*MLO_IN_CHANNEL_STRIDE + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT];
			}

			for (int i = 0; i < MLO_READ_UNIT; ++i)
			{
				int lcl_in_off = c*MLO_IN_LCL_SZ + c_scan*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i;
				lcl_bot[lcl_in_off] = in_rd_data[i];
#if 1
				if (c == 38 && c_pix4 == 0)
				{
					printf("K:g: %d %d %d %d %f\n",
						lcl_id,
						c_scan,
						i,
						lcl_in_off,
						lcl_bot[lcl_in_off]
					);
				}
#endif
			}
		}

	} // for (int p4 = lcl_id; p4 < MLO_N_LCL_IN_MAPS * MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS;

	barrier(CLK_LOCAL_MEM_FENCE);

}
/*********************************************************************************************************
// wrw algorithm for large filters
// idea:
// split output line line on number of spans by number of waves
// read MLO_FILTER_SIZE1 number of such spans into SGPS (for example 5 *7 = 35)
// read 1 input line for 64 maps into LDS
//
// alg
//
// accumulate 1 scan of data per wave per each input map per wk-item

**********************************************************************************************************/

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2)))
__kernel void MLOpenCvBwdWrW(
	const __global _FLOAT * top_df,
	const __global _FLOAT * bot,
	__global _FLOAT * weights_df,
#if MLO_CONV_BIAS
	__global _FLOAT * bias_df,
#endif
	_FLOAT padding_val
)
{

	// input/output tiles + reduce buffer

	__local _FLOAT lcl[(MLO_LCL_SZ)];
	__local _FLOAT * lcl_bot = lcl;

	// guarnteeing an uniformity over a wave
	int wave_id = getWaveId();
	int lcl_id = get_local_id(0);
	int lcl_wv_id = gePhysLocalId();

	int c_idx_base = get_group_id(1); // input map index base

	int o_idx_base = iDiv(get_group_id(2), (MLO_BATCH_SZ / (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS))); // output map index base
	int ib_base = iMod(get_group_id(2), o_idx_base, (MLO_BATCH_SZ / (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS)));

	int ib = ib_base*MLO_N_LCL_BATCHS;

	int c_idx = c_idx_base * MLO_N_LCL_IN_MAPS; // input map index

	int o_idx = o_idx_base * (MLO_N_LCL_OUT_MAPS * MLO_OUT_STACKS); // output map index

	int gbl_in_off = c_idx * MLO_IN_CHANNEL_STRIDE + ib * MLO_IN_BATCH_STRIDE;
	int gbl_out_off = o_idx * MLO_OUT_CHANNEL_STRIDE + ib * MLO_OUT_BATCH_STRIDE;

	__private int top_wave_base[MLO_N_WAVES];

	for (int i = 0; i < MLO_N_WAVES; ++i)
	{
		top_wave_base[i] = i * MLO_PER_WAVE_READ;
	}

	__private _FLOAT bot_dat[MLO_PER_WAVE_READ*MLO_FILTER_SIZE1];

	for (int i = 0; i < MLO_PER_WAVE_READ*MLO_FILTER_SIZE1; ++i)
	{
		bot_dat[i] = 0;
	}

	__private _FLOAT pvt_accum[(MLO_FILTER_SIZE1*MLO_FILTER_SIZE0)];

	for (int i = 0; i < (MLO_FILTER_SIZE1*MLO_FILTER_SIZE0); ++i)
	{
		pvt_accum[i] = 0;
	}

	// zero out LDS
	for (int i = lcl_id; i < (MLO_LCL_SZ); i += MLO_GRP_SZ)
	{
		lcl[i] = 0;
	}

//	barrier(CLK_LOCAL_MEM_FENCE);

#if 0
	if (wei_tl_idx == 4 && o_idx == 0 && c_idx == 0 && (dat_tl_idx == 0 || dat_tl_idx == 1))
	{
		printf("G:p: %d %d %d %d %d\n",
			lcl_id,
			wei_tl_idx1,
			wei_tl_idx0,
			wei_tl1,
			wei_tl0
		);
	}

#endif

	// over all batches

	for (int b = 0;
		b < MLO_N_BATCH_LOOPS;
		++b,
		gbl_in_off += MLO_N_LCL_BATCHS*MLO_IN_BATCH_STRIDE,
		gbl_out_off += MLO_N_LCL_BATCHS*MLO_OUT_BATCH_STRIDE
		)
	{
		int in_y = 0;
		int out_y = 0;

		int gbl_in_scan_off = gbl_in_off;
		int gbl_out_scan_off = gbl_out_off;
		// over all out blocks
		// processing per MLO_N_ALIGNED_OUT_SCAN_BLK output scans

//		barrier(CLK_LOCAL_MEM_FENCE);

		// prefetch output
		for (int j = 0; j < MLO_FILTER_SIZE1 - 1; ++j, ++out_y)
		{
			int top_df_off = (out_y < MLO_OUT_HEIGHT) ? gbl_out_scan_off + j * MLO_IN_STRIDE + top_wave_base[wave_id] : 0;
			_FLOAT mask = (out_y < MLO_OUT_HEIGHT) ? 1 : 0;
			for (int i = 0; i < MLO_PER_WAVE_READ; ++i)
			{
				_FLOAT top_val = top_df[top_df_off + i] * mask;
				bot_dat[j*MLO_PER_WAVE_READ + i] = top_val;
			}
		}

		// prolog
		// handling padding

		int sc = 0;
// pad0
		for (; sc < 1; ++sc, gbl_out_scan_off += MLO_OUT_CHANNEL_STRIDE, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)
		{
			barrier(CLK_LOCAL_MEM_FENCE);
// read input line
			readInput(lcl_id, c_idx, sc, gbl_in_scan_off, bot, lcl_bot);
// processing
			for (int l = MLO_FILTER_PAD1; l >= 0; --l)
			{

				for (int m = 0; m < MLO_PER_WAVE_READ; ++m)
				{
					for (int n = 0; n < MLO_FILTER_SIZE0; ++n)
					{
						pvt_accum[l*MLO_FILTER_SIZE0 + n]
							// each wk-item process an input
							= lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
							* bot_dat[(MLO_FILTER_PAD1 - l) * MLO_PER_WAVE_READ + m];
#if 1
						if (lcl_wv_id == 38 && l == 2 && n == 2)
						{
							printf("G:ap0: %d %d %d  %f %f %f %f\n",
								wave_id,
								l,
								n,
								pvt_accum[l*MLO_FILTER_SIZE0 + n],
								lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
									* bot_dat[l * MLO_PER_WAVE_READ + m],
								lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m],
								bot_dat[(MLO_FILTER_PAD1 - l) * MLO_PER_WAVE_READ + m]
								);
						}
#endif

					}

				}

			}

		}

// pad1
		for (; sc < 2 && sc < MLO_OUT_HEIGHT; ++sc, gbl_out_scan_off += MLO_OUT_CHANNEL_STRIDE, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)
		{
			barrier(CLK_LOCAL_MEM_FENCE);
			// read input line
			readInput(lcl_id, c_idx, sc, gbl_in_scan_off, bot, lcl_bot);

			  // processing
			for (int l = MLO_FILTER_PAD1 + 1; l >= 0; --l)
			{

				for (int m = 0; m < MLO_PER_WAVE_READ; ++m)
				{
					for (int n = 0; n < MLO_FILTER_SIZE0; ++n)
					{
						pvt_accum[l*MLO_FILTER_SIZE0 + n]
							// each wk-item process an input
							= lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
							* bot_dat[(MLO_FILTER_PAD1 - l + 1) * MLO_PER_WAVE_READ + m];
#if 0
						if (lcl_wv_id == 0 && l == 1 && n == 1)
						{
							printf("G:ap1: %d %d %d  %f %f %f %f\n",
								wave_id,
								l,
								n,
								pvt_accum[l*MLO_FILTER_SIZE0 + n],
								lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
								* bot_dat[l * MLO_PER_WAVE_READ + m],
								lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m],
								bot_dat[(MLO_FILTER_PAD1 - l) * MLO_PER_WAVE_READ + m]
							);
						}
#endif

					}

				}

			}

		}

// generic

		for (; sc < MLO_OUT_HEIGHT - MLO_FILTER_PAD1; ++sc, gbl_out_scan_off += MLO_OUT_CHANNEL_STRIDE, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE, ++out_y)
		{

			int top_df_off = (out_y < MLO_OUT_HEIGHT) ? gbl_out_scan_off + (sc + MLO_FILTER_PAD1) * MLO_IN_STRIDE + top_wave_base[wave_id] : 0;
			_FLOAT mask = (out_y < MLO_OUT_HEIGHT) ? 1 : 0;
			// move in the last output scan
			for (int i = 0; i < MLO_PER_WAVE_READ; ++i)
			{
				bot_dat[(MLO_FILTER_SIZE1 - 1) *MLO_PER_WAVE_READ + i] = top_df[top_df_off + i] * mask;
			}

			barrier(CLK_LOCAL_MEM_FENCE);

			// read input line
			readInput(lcl_id, c_idx, sc, gbl_in_scan_off, bot, lcl_bot);

			  // processing
			for (int l = MLO_FILTER_SIZE1 - 1; l >= 0; --l)
			{

				for (int m = 0; m < MLO_PER_WAVE_READ; ++m)
				{
					for (int n = 0; n < MLO_FILTER_SIZE0; ++n)
					{
						pvt_accum[l*MLO_FILTER_SIZE0 + n]
							// each wk-item process an input
							= lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
							* bot_dat[l * MLO_PER_WAVE_READ + m];
#if 0
						if (lcl_wv_id == 0 && l == 1 && n == 1)
						{
							printf("G:a: %d %d %d  %f %f %f %f\n",
								wave_id,
								l,
								n,
								pvt_accum[l*MLO_FILTER_SIZE0 + n],
								lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
								* bot_dat[l * MLO_PER_WAVE_READ + m],
								lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m],
								bot_dat[(MLO_FILTER_PAD1 - l) * MLO_PER_WAVE_READ + m]
							);
						}
#endif
					}

				}

			}
// move up output
			for (int j = 0; j < MLO_FILTER_SIZE1 - 1; ++j)
			{
				for (int i = 0; i < MLO_PER_WAVE_READ; ++i)
				{
					bot_dat[j*MLO_PER_WAVE_READ + i] = bot_dat[(j+1)*MLO_PER_WAVE_READ + i];
				}
			}

			barrier(CLK_LOCAL_MEM_FENCE);

		}

// epilog
// handling padding
// pad1
		for (; sc < MLO_OUT_HEIGHT - MLO_FILTER_PAD1 + 1; ++sc, gbl_out_scan_off += MLO_OUT_CHANNEL_STRIDE, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)
		{

			barrier(CLK_LOCAL_MEM_FENCE);
			// read input line
			readInput(lcl_id, c_idx, sc, gbl_in_scan_off, bot, lcl_bot);

			  // processing
			for (int l = MLO_FILTER_SIZE1 - 1; l >= MLO_FILTER_PAD1 - 1; --l)
			{

				for (int m = 0; m < MLO_PER_WAVE_READ; ++m)
				{
					for (int n = 0; n < MLO_FILTER_SIZE0; ++n)
					{
						pvt_accum[l*MLO_FILTER_SIZE0 + n]
							// each wk-item process an input
							= lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
							* bot_dat[l * MLO_PER_WAVE_READ + m];
#if 0
						if (lcl_wv_id == 0 && l == 1 && n == 1)
						{
							printf("G:a0p: %d %d %d  %f %f %f %f\n",
								wave_id,
								l,
								n,
								pvt_accum[l*MLO_FILTER_SIZE0 + n],
								lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
								* bot_dat[l * MLO_PER_WAVE_READ + m],
								lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m],
								bot_dat[(MLO_FILTER_PAD1 - l) * MLO_PER_WAVE_READ + m]
							);
						}
#endif

					}

				}

			} // for (int l = MLO_FILTER_SIZE1 - 1; l >= MLO_FILTER_PAD1 - 1; --l)

			barrier(CLK_LOCAL_MEM_FENCE);

		} // for (; sc < MLO_OUT_HEIGHT - MLO_FILTER_PAD1 + 1; ++sc, gbl_out_scan_off += MLO_OUT_CHANNEL_STRIDE, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)

// pad0
		for (; sc < MLO_OUT_HEIGHT; ++sc, gbl_out_scan_off += MLO_OUT_CHANNEL_STRIDE, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)
		{

			barrier(CLK_LOCAL_MEM_FENCE);

			// read input line
			readInput(lcl_id, c_idx, sc, gbl_in_scan_off, bot, lcl_bot);

			// processing
			for (int l = MLO_FILTER_SIZE1 - 1; l >= MLO_FILTER_PAD1; --l)
			{

				for (int m = 0; m < MLO_PER_WAVE_READ; ++m)
				{
					for (int n = 0; n < MLO_FILTER_SIZE0; ++n)
					{
						pvt_accum[l*MLO_FILTER_SIZE0 + n]
							// each wk-item process an input
							= lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
							* bot_dat[l * MLO_PER_WAVE_READ + m];
#if 0
						if (lcl_wv_id == 0 && l == 1 && n == 1)
						{
							printf("G:a1p: %d %d %d  %f %f %f %f\n",
								wave_id,
								l,
								n,
								pvt_accum[l*MLO_FILTER_SIZE0 + n],
								lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
								* bot_dat[l * MLO_PER_WAVE_READ + m],
								lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m],
								bot_dat[(MLO_FILTER_PAD1 - l) * MLO_PER_WAVE_READ + m]
							);
						}
#endif
					}

				}

			}

			barrier(CLK_LOCAL_MEM_FENCE);

		} // for (; sc < MLO_OUT_HEIGHT - MLO_FILTER_PAD1 + 2; ++sc, gbl_out_scan_off += MLO_OUT_CHANNEL_STRIDE, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)

	} // 	for (int b = 0;

// final summation
	for (int l = 0; l < MLO_FILTER_SIZE1; ++l)
	{
		for (int n = 0; n < MLO_FILTER_SIZE0 && wave_id > 0; ++n)
		{
			lcl[((wave_id - 1) * MLO_HW_WAVE_SZ + lcl_wv_id) * MLO_FILTER_SIZE0 + n] =
				pvt_accum[l*MLO_FILTER_SIZE0 + n];
#if 1
			if (lcl_wv_id == 38 && l == 2 && n == 2)
			{
				printf("G:s1: %f\n",
					pvt_accum[l*MLO_FILTER_SIZE0 + n]
				);
			}
#endif

		}

		barrier(CLK_LOCAL_MEM_FENCE);

		for(int w = 0; w < MLO_N_WAVES-1 && wave_id == 0; ++w)
		{

			for (int n = 0; n < MLO_FILTER_SIZE0; ++n)
			{
				pvt_accum[l*MLO_FILTER_SIZE0 + n]
					+= lcl[(w* MLO_HW_WAVE_SZ + lcl_wv_id) * MLO_FILTER_SIZE0 + n];
#if 1
				if (lcl_wv_id == 38 && l == 2 && n == 2)
				{
					printf("G:s2: %f %f\n",
						pvt_accum[l*MLO_FILTER_SIZE0 + n],
						lcl[(w* MLO_HW_WAVE_SZ + lcl_wv_id) * MLO_FILTER_SIZE0 + n]
					);
				}
#endif

			}

		}

		barrier(CLK_LOCAL_MEM_FENCE);
	}

// output
// inputs are outputs
	int c = lcl_wv_id;

	int wei_df_off = ((ib * MLO_N_OUTPUTS + o_idx) * (int)MLO_WEI_BATCH_STRIDE)
		// this input channel
		+ mul24((c_idx + c), (int)MLO_WEI_CHANNEL_STRIDE);
	if (wave_id == 0)
	{
		for (int i = 0; i < (MLO_FILTER_SIZE1 * MLO_FILTER_SIZE0); ++i)
		{
			weights_df[wei_df_off + i] = pvt_accum[i];
		}

	}

}

// final reduction kernel
// add filters over batches
__attribute__((reqd_work_group_size(MLO_UT_GRP_SZ0, 1, 1)))
__kernel void MLOpenCvBwdWrW_rdc(
	const __global _FLOAT * weight_df_tmp,
	__global _FLOAT * weights_df
)
{
	int gbl_id = get_global_id(0);
	int wei_idx0 = gbl_id * MLO_UT_READ_UNIT;

	int wei_blk_idx = iDiv(wei_idx0, MLO_WEI_CHANNEL_STRIDE);
	int wei_idx = iMod(wei_idx0, wei_blk_idx, MLO_WEI_CHANNEL_STRIDE);

	_FLOAT pvt_accum_wei[MLO_UT_READ_UNIT];
	for (int i = 0; i < MLO_UT_READ_UNIT; ++i)
	{
		pvt_accum_wei[i] = 0;
	}

	int batch_loop = (MLO_BATCH_SZ + (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS) - 1) / (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS);
	for (int i = 0; i < batch_loop; ++i)
	{
		*(MLO_UT_READ_TYPE*)pvt_accum_wei
			+= *(MLO_UT_READ_TYPE*)&weight_df_tmp[(wei_blk_idx * MLO_WEI_CHANNEL_STRIDE + i* MLO_N_OUTPUTS*MLO_WEI_BATCH_STRIDE)  + wei_idx];
	}

	*(MLO_UT_READ_TYPE*)&weights_df[wei_idx0] = *(MLO_UT_READ_TYPE*)pvt_accum_wei;

}

/*
 * 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 MLO_N_OUT_HORIZ_READS (MLO_IN_WIDTH)
#define MLO_PER_WAVE_READ ((MLO_IN_WIDTH + MLO_N_WAVES - 1) / MLO_N_WAVES)
#define MLO_N_OUT_HORIZ_PIX_READS (MLO_PER_WAVE_READ * MLO_N_WAVES)
#define MLO_N_OUT_VERTICAL_READS (MLO_FILTER_SIZE1)

#define MLO_IN_VERT_READS (1)
// there is an assumption that the scanline fits into LDS
#define MLO_N_IN_HORIZ_PIX_READS (MLO_IN_WIDTH)
#define MLO_N_IN_HORIZ_READS ((MLO_N_IN_HORIZ_PIX_READS + MLO_READ_UNIT - 1) / MLO_READ_UNIT)
#define MLO_IN_N_PIXS_OFF  (MLO_N_IN_HORIZ_READS*MLO_READ_UNIT - MLO_N_IN_HORIZ_PIX_READS)
#define MLO_IN_LCL_WIDTH (MLO_N_IN_HORIZ_READS * MLO_READ_UNIT)
#define MLO_IN_LCL_HEIGHT MLO_IN_VERT_READS
#define MLO_IN_LCL_SZ (MLO_IN_LCL_WIDTH * MLO_IN_LCL_HEIGHT)
#define MLO_TOTAL_IN_LCL_SZ (MLO_N_LCL_BATCHS*MLO_N_LCL_IN_MAPS*MLO_IN_LCL_SZ)

#define MLO_LCL_SZ (MLO_TOTAL_IN_LCL_SZ)

#define MLO_HW_WAVE_ID_SETTING 0

#if MLO_HW_WAVE_ID_SETTING
extern __attribute__((const)) uint __hsail_get_dynwave_id(void);
inline int getWaveId()
{
	int wave_id = 0;

	wave_id = __hsail_get_dynwave_id();
	wave_id = wave_id & MLO_N_PHYS_WAVES_MASK;
	return(wave_id);
}
#else
inline int getWaveId()
{
	int wave_id = 0;

	wave_id = (get_local_id(0) >> MLO_LG2_PHYS_WAVE_SZ);

	return(wave_id);
}
#endif

inline int gePhysLocalId()
{
	int lcl_wave_id = get_local_id(0) - ((get_local_id(0) >> MLO_LG2_PHYS_WAVE_SZ) << MLO_LG2_PHYS_WAVE_SZ);
	return(lcl_wave_id);
}

inline int iDiv(int v, int d)
{
	int r = (int)((float)v / d + 0.00001f);
	return(r);
}

inline int iMod(int v, int u, int d)
{
	int r = v - mul24((int)u, (int)d);
	return(r);
}

inline void ReduceKernel(__local _FLOAT * lcl_blob, _FLOAT *weights_accum, int lcl_id, int scan_lcl, int sum_stride, int unit_len, bool debug)
{
// read first half
	if (scan_lcl < (sum_stride >> 1))
	{
		for (int i = 0; i < unit_len; ++i)
		{
			weights_accum[i] = lcl_blob[(lcl_id + scan_lcl) * unit_len + i];

		}

	}
// add second half
// appload accumulated value so far
	for (int j = (sum_stride >> 1); j > 0; j >>= 1)
	{
		barrier(CLK_LOCAL_MEM_FENCE);
		if (scan_lcl < j)
		{
			for (int i = 0; i < unit_len; ++i)
			{
				weights_accum[i] += lcl_blob[(lcl_id + j) * unit_len + i];

				lcl_blob[lcl_id * unit_len + i] = weights_accum[i];
			}

		}
	}
}

inline void ReduceKernel64(__local _FLOAT * lcl_blob, _FLOAT *weights_accum, int lcl_id, int scan_lcl, int sum_stride, int unit_len, bool debug)
{
	// read first half
	if (scan_lcl < (sum_stride >> 1))
	{
		for (int i = 0; i < unit_len; ++i)
		{
			weights_accum[i] = lcl_blob[(lcl_id + scan_lcl) * unit_len + i];

		}

	}
	// add second half
	// appload accumulated value so far
	for (int j = (sum_stride >> 1); j > 0; j >>= 1)
	{
		barrier(CLK_LOCAL_MEM_FENCE);
		if (scan_lcl < j)
		{
			for (int i = 0; i < unit_len; ++i)
			{
				weights_accum[i] += lcl_blob[(lcl_id + j) * unit_len + i];

				lcl_blob[lcl_id * unit_len + i] = weights_accum[i];
			}

		}
	}
}

inline void  Kahan_summation(_FLOAT *sum, _FLOAT * c, _FLOAT v)
{
	_FLOAT y = v - *c;    //So far, so good: c is zero.
	_FLOAT t = *sum + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
	*c = (t - *sum) - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
	*sum = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
}

inline void  Kahan_summation_tricked(_FLOAT *sum, _FLOAT * c, _FLOAT v, _FLOAT mod)
{
	_FLOAT y = v - *c;    //So far, so good: c is zero.
	_FLOAT t = *sum + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
	*c = (t - *sum) * mod - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
	*sum = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
}

inline void Kahan_summation2(_FLOAT *sum, _FLOAT *c, _FLOAT *v, int n)
{
	for (int i = 0; i < n; ++i)
	{
		_FLOAT y = v[i] - c[i];    //So far, so good: c is zero.
		_FLOAT t = sum[i] + y;         //Alas, sum is big, y small, so low-order digits of y are lost.
		c[i] = (t - sum[i]) - y;   //(t - sum) recovers the high-order part of y; subtracting y recovers -(low part of y)
		sum[i] = t;             //Algebraically, c should always be zero. Beware eagerly optimising compilers!
	}
}

/*********************************************************************************************************
// wrw algorithm for large filters
// idea:
// split output line line on number of spans by number of waves
// read MLO_FILTER_SIZE1 number of such spans into SGPS (for example 5 *7 = 35)
// read 1 input line for 64 maps into LDS
//
// alg
//
// accumulate 1 scan of data per wave per each input map per wk-item

**********************************************************************************************************/

__attribute__((reqd_work_group_size(MLO_GRP_SZ0, MLO_GRP_SZ1, MLO_GRP_SZ2)))
__kernel void MLOpenCvBwdWrW(
	const __global _FLOAT * top_df,
	const __global _FLOAT * bot,
	__global _FLOAT * weights_df,
#if MLO_CONV_BIAS
	__global _FLOAT * bias_df,
#endif
	_FLOAT padding_val
)
{

	// input/output tiles + reduce buffer

	__local _FLOAT lcl[(MLO_LCL_SZ)];
	__local _FLOAT * lcl_bot = lcl;

	// guarnteeing an uniformity over a wave
	int wave_id = getWaveId();
	int lcl_id = get_local_id(0);
	int lcl_wv_id = gePhysLocalId();

	int c_idx_base = get_group_id(1); // input map index base

	int o_idx_base = iDiv(get_group_id(2), (MLO_BATCH_SZ / (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS))); // output map index base
	int ib_base = iMod(get_group_id(2), o_idx_base, (MLO_BATCH_SZ / (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS)));

	int ib = ib_base*MLO_N_LCL_BATCHS;

	int c_idx = c_idx_base * MLO_N_LCL_IN_MAPS; // input map index

	int o_idx = o_idx_base * (MLO_N_LCL_OUT_MAPS * MLO_OUT_STACKS); // output map index

	int gbl_in_off = c_idx * MLO_IN_CHANNEL_STRIDE + ib * MLO_IN_BATCH_STRIDE;
	int gbl_out_off = o_idx * MLO_OUT_CHANNEL_STRIDE + ib * MLO_OUT_BATCH_STRIDE;

	__private int top_wave_base[MLO_N_WAVES];

	for (int i = 0; i < MLO_N_WAVES; ++i)
	{
		top_wave_base[i] = i * MLO_PER_WAVE_READ;
	}

	__private _FLOAT bot_dat[MLO_PER_WAVE_READ*MLO_FILTER_SIZE1];

	for (int i = 0; i < MLO_PER_WAVE_READ*MLO_FILTER_SIZE1; ++i)
	{
		bot_dat[i] = 0;
	}

	__private _FLOAT pvt_accum[(MLO_FILTER_SIZE1*MLO_FILTER_SIZE0)];

	for (int i = 0; i < (MLO_FILTER_SIZE1*MLO_FILTER_SIZE0); ++i)
	{
		pvt_accum[i] = 0;
	}

	// zero out LDS
	for (int i = lcl_id; i < (MLO_LCL_SZ); i += MLO_GRP_SZ)
	{
		lcl[i] = 0;
	}

//	barrier(CLK_LOCAL_MEM_FENCE);

#if 0
	if (wei_tl_idx == 4 && o_idx == 0 && c_idx == 0 && (dat_tl_idx == 0 || dat_tl_idx == 1))
	{
		printf("G:p: %d %d %d %d %d\n",
			lcl_id,
			wei_tl_idx1,
			wei_tl_idx0,
			wei_tl1,
			wei_tl0
		);
	}

#endif

	// over all batches

	for (int b = 0;
		b < MLO_N_BATCH_LOOPS;
		++b,
		gbl_in_off += MLO_N_LCL_BATCHS*MLO_IN_BATCH_STRIDE,
		gbl_out_off += MLO_N_LCL_BATCHS*MLO_OUT_BATCH_STRIDE
		)
	{
		int in_y = 0;
		int out_y = 0;

		int gbl_in_scan_off = gbl_in_off;
		int gbl_out_scan_off = gbl_out_off;
		// over all out blocks
		// processing per MLO_N_ALIGNED_OUT_SCAN_BLK output scans

//		barrier(CLK_LOCAL_MEM_FENCE);

		// prefetch output
		for (int j = 0; j < MLO_FILTER_SIZE1 - 1; ++j)
		{
			for (int i = 0; i < MLO_PER_WAVE_READ; ++i)
			{
				bot_dat[j*MLO_PER_WAVE_READ + i] = top_df[gbl_out_scan_off + j * MLO_IN_STRIDE + top_wave_base[wave_id] + i];
			}
		}

		// prolog
		// handling padding

		int sc = 0;
// pad0
		for (; sc < 1; ++sc, gbl_out_scan_off += MLO_OUT_CHANNEL_STRIDE, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)
		{
			barrier(CLK_LOCAL_MEM_FENCE);

			for (int p4 = lcl_id; p4 < MLO_N_LCL_IN_MAPS * MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS;
				p4 += MLO_GRP_SZ)
			{
				__private _FLOAT in_rd_data[MLO_READ_UNIT];

				int c_scan = sc;

				int c = iDiv(p4, (MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS));
				int c_pix4 = iMod(p4, c, (MLO_N_IN_HORIZ_READS*MLO_IN_VERT_READS));

//				if (c_idx + c < MLO_N_INPUTS)

				{
//					c_scan = iDiv(p4_t, MLO_N_IN_HORIZ_READS);

//					int c_pix4 = iMod(p4_t, c_scan, MLO_N_IN_HORIZ_READS);

#if MLO_IN_N_PIXS_OFF > 0

					if (c_pix4 == MLO_N_IN_HORIZ_READS - 1)
					{
						for (int i = 0; i < MLO_IN_N_PIXS_OFF; ++i)
						{

							in_rd_data[i] = bot[gbl_in_scan_off + c*MLO_IN_CHANNEL_STRIDE + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT + i];
						}

						for (int i = MLO_READ_UNIT - 1; i >= MLO_READ_UNIT - MLO_IN_N_PIXS_OFF; --i)
						{
							in_rd_data[i] = 0;
						}

					}
					else
#endif
					{
						*(MLO_READ_TYPE*)in_rd_data = *(MLO_READ_TYPE*)&bot[gbl_in_scan_off + c*MLO_IN_CHANNEL_STRIDE + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT];
					}

					for (int i = 0; i < MLO_READ_UNIT; ++i)
					{
						int lcl_in_off = c*MLO_IN_LCL_SZ + (c_scan + MLO_FILTER_PAD1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i;
						lcl_bot[lcl_in_off] = in_rd_data[i];
#if 0
						if (c_idx + c == 1 && p4_t == 0)
						{
							printf("K:g: %d %f\n",
								lcl_in_off,
								lcl_bot[lcl_in_off]
							);
						}
#endif
					}
				}

			} // for (int p4 = lcl_id; p4 < MLO_N_LCL_IN_MAPS * MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS;

			barrier(CLK_LOCAL_MEM_FENCE);

// processing
			for (int l = MLO_FILTER_PAD1; l >= 0; --l)
			{

				for (int m = 0; m < MLO_PER_WAVE_READ; ++m)
				{
					for (int n = 0; n < MLO_FILTER_SIZE0; ++n)
					{
						pvt_accum[l*MLO_FILTER_SIZE0 + n]
							// each wk-item process an input
							= lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
							* bot_dat[l * MLO_PER_WAVE_READ + m];
					}

				}

			}

		}

// pad1
		for (; sc < 2; ++sc, gbl_out_scan_off += MLO_OUT_CHANNEL_STRIDE, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)
		{
			barrier(CLK_LOCAL_MEM_FENCE);

			for (int p4 = lcl_id; p4 < MLO_N_LCL_IN_MAPS * MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS;
				p4 += MLO_GRP_SZ)
			{
				__private _FLOAT in_rd_data[MLO_READ_UNIT];

				int c_scan = sc;
				int c = iDiv(p4, (MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS));
				int c_pix4 = iMod(p4, c, (MLO_N_IN_HORIZ_READS*MLO_IN_VERT_READS));

				//if (c_idx + c < MLO_N_INPUTS)

				{
					//					c_scan = iDiv(p4_t, MLO_N_IN_HORIZ_READS);

					//					int c_pix4 = iMod(p4_t, c_scan, MLO_N_IN_HORIZ_READS);

#if MLO_IN_N_PIXS_OFF > 0

					if (c_pix4 == MLO_N_IN_HORIZ_READS - 1)
					{
						for (int i = 0; i < MLO_IN_N_PIXS_OFF; ++i)
						{

							in_rd_data[i] = bot[gbl_in_scan_off + c*MLO_IN_CHANNEL_STRIDE + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT + i];
						}

						for (int i = MLO_READ_UNIT - 1; i >= MLO_READ_UNIT - MLO_IN_N_PIXS_OFF; --i)
						{
							in_rd_data[i] = 0;
						}

					}
					else
#endif
					{
						*(MLO_READ_TYPE*)in_rd_data = *(MLO_READ_TYPE*)&bot[gbl_in_scan_off + c*MLO_IN_CHANNEL_STRIDE + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT];
					}

					for (int i = 0; i < MLO_READ_UNIT; ++i)
					{
						int lcl_in_off = c*MLO_IN_LCL_SZ + (c_scan + MLO_FILTER_PAD1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i;
						lcl_bot[lcl_in_off] = in_rd_data[i];
#if 0
						if (c_idx + c == 1 && p4_t == 0)
						{
							printf("K:g: %d %f\n",
								lcl_in_off,
								lcl_bot[lcl_in_off]
							);
						}
#endif
					}
				}

			} // for (int p4 = lcl_id; p4 < MLO_N_LCL_IN_MAPS * MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS;

			barrier(CLK_LOCAL_MEM_FENCE);

			  // processing
			for (int l = MLO_FILTER_PAD1 + 1; l >= 0; --l)
			{

				for (int m = 0; m < MLO_PER_WAVE_READ; ++m)
				{
					for (int n = 0; n < MLO_FILTER_SIZE0; ++n)
					{
						pvt_accum[l*MLO_FILTER_SIZE0 + n]
							// each wk-item process an input
							= lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
							* bot_dat[l * MLO_PER_WAVE_READ + m];
					}

				}

			}

		}

// generic

		// move in the last output scan

		for (; sc < MLO_OUT_HEIGHT - MLO_FILTER_PAD1; ++sc, gbl_out_scan_off += MLO_OUT_CHANNEL_STRIDE, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)
		{

			for (int i = 0; i < MLO_PER_WAVE_READ; ++i)
			{
				bot_dat[(MLO_FILTER_SIZE1 - 1) *MLO_PER_WAVE_READ + i] = top_df[gbl_out_scan_off + (MLO_FILTER_SIZE1 - 1) * MLO_IN_STRIDE + top_wave_base[wave_id] + i];
			}

			barrier(CLK_LOCAL_MEM_FENCE);

			for (int p4 = lcl_id; p4 < MLO_N_LCL_IN_MAPS * MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS;
				p4 += MLO_GRP_SZ)
			{
				__private _FLOAT in_rd_data[MLO_READ_UNIT];

				int c_scan = sc;
				int c = iDiv(p4, (MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS));
				int c_pix4 = iMod(p4, c, (MLO_N_IN_HORIZ_READS*MLO_IN_VERT_READS));

				//if (c_idx + c < MLO_N_INPUTS)

				{
					//					c_scan = iDiv(p4_t, MLO_N_IN_HORIZ_READS);

					//					int c_pix4 = iMod(p4_t, c_scan, MLO_N_IN_HORIZ_READS);

#if MLO_IN_N_PIXS_OFF > 0

					if (c_pix4 == MLO_N_IN_HORIZ_READS - 1)
					{
						for (int i = 0; i < MLO_IN_N_PIXS_OFF; ++i)
						{

							in_rd_data[i] = bot[gbl_in_scan_off + c*MLO_IN_CHANNEL_STRIDE + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT + i];
						}

						for (int i = MLO_READ_UNIT - 1; i >= MLO_READ_UNIT - MLO_IN_N_PIXS_OFF; --i)
						{
							in_rd_data[i] = 0;
						}

					}
					else
#endif
					{
						*(MLO_READ_TYPE*)in_rd_data = *(MLO_READ_TYPE*)&bot[gbl_in_scan_off + c*MLO_IN_CHANNEL_STRIDE + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT];
					}

					for (int i = 0; i < MLO_READ_UNIT; ++i)
					{
						int lcl_in_off = c*MLO_IN_LCL_SZ + (c_scan + MLO_FILTER_PAD1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i;
						lcl_bot[lcl_in_off] = in_rd_data[i];
#if 0
						if (c_idx + c == 1 && p4_t == 0)
						{
							printf("K:g: %d %f\n",
								lcl_in_off,
								lcl_bot[lcl_in_off]
							);
						}
#endif
					}
				}

			} // for (int p4 = lcl_id; p4 < MLO_N_LCL_IN_MAPS * MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS;

			  // processing
			for (int l = MLO_FILTER_SIZE1 - 1; l >= 0; --l)
			{

				for (int m = 0; m < MLO_PER_WAVE_READ; ++m)
				{
					for (int n = 0; n < MLO_FILTER_SIZE0; ++n)
					{
						pvt_accum[l*MLO_FILTER_SIZE0 + n]
							// each wk-item process an input
							= lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
							* bot_dat[l * MLO_PER_WAVE_READ + m];
					}

				}

			}
// move up output
			for (int j = 0; j < MLO_FILTER_SIZE1 - 1; ++j)
			{
				for (int i = 0; i < MLO_PER_WAVE_READ; ++i)
				{
					bot_dat[j*MLO_PER_WAVE_READ + i] = bot_dat[(j+1)*MLO_PER_WAVE_READ + i];
				}
			}

			barrier(CLK_LOCAL_MEM_FENCE);

		}

// epilog
// handling padding
// pad1
		for (; sc < MLO_OUT_HEIGHT - MLO_FILTER_PAD1 + 1; ++sc, gbl_out_scan_off += MLO_OUT_CHANNEL_STRIDE, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)
		{

			barrier(CLK_LOCAL_MEM_FENCE);

			for (int p4 = lcl_id; p4 < MLO_N_LCL_IN_MAPS * MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS;
				p4 += MLO_GRP_SZ)
			{
				__private _FLOAT in_rd_data[MLO_READ_UNIT];

				int c_scan = sc;
				int c = iDiv(p4, (MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS));
				int c_pix4 = iMod(p4, c, (MLO_N_IN_HORIZ_READS*MLO_IN_VERT_READS));

				//if (c_idx + c < MLO_N_INPUTS)

				{
					//					c_scan = iDiv(p4_t, MLO_N_IN_HORIZ_READS);

					//					int c_pix4 = iMod(p4_t, c_scan, MLO_N_IN_HORIZ_READS);

#if MLO_IN_N_PIXS_OFF > 0

					if (c_pix4 == MLO_N_IN_HORIZ_READS - 1)
					{
						for (int i = 0; i < MLO_IN_N_PIXS_OFF; ++i)
						{

							in_rd_data[i] = bot[gbl_in_scan_off + c*MLO_IN_CHANNEL_STRIDE + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT + i];
						}

						for (int i = MLO_READ_UNIT - 1; i >= MLO_READ_UNIT - MLO_IN_N_PIXS_OFF; --i)
						{
							in_rd_data[i] = 0;
						}

					}
					else
#endif
					{
						*(MLO_READ_TYPE*)in_rd_data = *(MLO_READ_TYPE*)&bot[gbl_in_scan_off + c*MLO_IN_CHANNEL_STRIDE + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT];
					}

					for (int i = 0; i < MLO_READ_UNIT; ++i)
					{
						int lcl_in_off = c*MLO_IN_LCL_SZ + (c_scan + MLO_FILTER_PAD1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i;
						lcl_bot[lcl_in_off] = in_rd_data[i];
#if 0
						if (c_idx + c == 1 && p4_t == 0)
						{
							printf("K:g: %d %f\n",
								lcl_in_off,
								lcl_bot[lcl_in_off]
							);
						}
#endif
					}
				}

			} // for (int p4 = lcl_id; p4 < MLO_N_LCL_IN_MAPS * MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS;

			  // processing
			for (int l = MLO_FILTER_SIZE1 - 1; l >= MLO_FILTER_PAD1 - 1; --l)
			{

				for (int m = 0; m < MLO_PER_WAVE_READ; ++m)
				{
					for (int n = 0; n < MLO_FILTER_SIZE0; ++n)
					{
						pvt_accum[l*MLO_FILTER_SIZE0 + n]
							// each wk-item process an input
							= lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
							* bot_dat[l * MLO_PER_WAVE_READ + m];
					}

				}

			} // for (int l = MLO_FILTER_SIZE1 - 1; l >= MLO_FILTER_PAD1 - 1; --l)

			barrier(CLK_LOCAL_MEM_FENCE);

		} // for (; sc < MLO_OUT_HEIGHT - MLO_FILTER_PAD1 + 1; ++sc, gbl_out_scan_off += MLO_OUT_CHANNEL_STRIDE, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)

// pad0
		for (; sc < MLO_OUT_HEIGHT; ++sc, gbl_out_scan_off += MLO_OUT_CHANNEL_STRIDE, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)
		{

			barrier(CLK_LOCAL_MEM_FENCE);

			for (int p4 = lcl_id; p4 < MLO_N_LCL_IN_MAPS * MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS;
				p4 += MLO_GRP_SZ)
			{
				__private _FLOAT in_rd_data[MLO_READ_UNIT];

				int c_scan = sc;
				int c = iDiv(p4, (MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS));
				int c_pix4 = iMod(p4, c, (MLO_N_IN_HORIZ_READS*MLO_IN_VERT_READS));

				//if (c_idx + c < MLO_N_INPUTS)

				{
					//					c_scan = iDiv(p4_t, MLO_N_IN_HORIZ_READS);

					//					int c_pix4 = iMod(p4_t, c_scan, MLO_N_IN_HORIZ_READS);

#if MLO_IN_N_PIXS_OFF > 0

					if (c_pix4 == MLO_N_IN_HORIZ_READS - 1)
					{
						for (int i = 0; i < MLO_IN_N_PIXS_OFF; ++i)
						{

							in_rd_data[i] = bot[gbl_in_scan_off + c*MLO_IN_CHANNEL_STRIDE + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT + i];
						}

						for (int i = MLO_READ_UNIT - 1; i >= MLO_READ_UNIT - MLO_IN_N_PIXS_OFF; --i)
						{
							in_rd_data[i] = 0;
						}

					}
					else
#endif
					{
						*(MLO_READ_TYPE*)in_rd_data = *(MLO_READ_TYPE*)&bot[gbl_in_scan_off + c*MLO_IN_CHANNEL_STRIDE + c_scan * MLO_IN_STRIDE + c_pix4*MLO_READ_UNIT];
					}

					for (int i = 0; i < MLO_READ_UNIT; ++i)
					{
						int lcl_in_off = c*MLO_IN_LCL_SZ + (c_scan + MLO_FILTER_PAD1)*MLO_IN_LCL_WIDTH + MLO_FILTER_PAD0 + c_pix4*MLO_READ_UNIT + i;
						lcl_bot[lcl_in_off] = in_rd_data[i];
#if 0
						if (c_idx + c == 1 && p4_t == 0)
						{
							printf("K:g: %d %f\n",
								lcl_in_off,
								lcl_bot[lcl_in_off]
							);
						}
#endif
					}
				}

			} // for (int p4 = lcl_id; p4 < MLO_N_LCL_IN_MAPS * MLO_N_IN_HORIZ_READS * MLO_IN_VERT_READS;

			  // processing
			for (int l = MLO_FILTER_SIZE1 - 1; l >= MLO_FILTER_PAD1; --l)
			{

				for (int m = 0; m < MLO_PER_WAVE_READ; ++m)
				{
					for (int n = 0; n < MLO_FILTER_SIZE0; ++n)
					{
						pvt_accum[l*MLO_FILTER_SIZE0 + n]
							// each wk-item process an input
							= lcl_bot[lcl_wv_id * MLO_IN_LCL_WIDTH + wave_id * MLO_PER_WAVE_READ + n + m]
							* bot_dat[l * MLO_PER_WAVE_READ + m];
					}

				}

			}

			barrier(CLK_LOCAL_MEM_FENCE);

		} // for (; sc < MLO_OUT_HEIGHT - MLO_FILTER_PAD1 + 2; ++sc, gbl_out_scan_off += MLO_OUT_CHANNEL_STRIDE, gbl_in_scan_off += MLO_IN_CHANNEL_STRIDE)

	} // 	for (int b = 0;

// final summation
	for (int l = 0; l < MLO_FILTER_SIZE1; ++l)
	{
		for (int n = 0; n < MLO_FILTER_SIZE0 && wave_id > 0; ++n)
		{
			lcl[((wave_id - 1) * MLO_HW_WAVE_SZ + lcl_wv_id) * MLO_FILTER_SIZE0 + n] =
				pvt_accum[l*MLO_FILTER_SIZE0 + n];
		}

		barrier(CLK_LOCAL_MEM_FENCE);

		for(int w = 0; w < MLO_N_WAVES && wave_id == 0; ++w)
		{

			for (int n = 0; n < MLO_FILTER_SIZE0; ++n)
			{
				pvt_accum[l*MLO_FILTER_SIZE0 + n]
					+= lcl[(w* MLO_HW_WAVE_SZ + lcl_wv_id) * MLO_FILTER_SIZE0 + n];

			}

		}
	}

// output
// inputs are outputs
	int c = lcl_wv_id;

	int wei_df_off = ((ib * MLO_N_OUTPUTS + o_idx) * (int)MLO_WEI_BATCH_STRIDE)
		// this input channel
		+ mul24((c_idx + c), (int)MLO_WEI_CHANNEL_STRIDE);
	if (wave_id == 0)
	{
		for (int i = 0; i < (MLO_FILTER_SIZE1 * MLO_FILTER_SIZE0); ++i)
		{
			weights_df[wei_df_off + i] = pvt_accum[i];
		}

	}

}

// final reduction kernel
// add filters over batches
__attribute__((reqd_work_group_size(MLO_UT_GRP_SZ0, 1, 1)))
__kernel void MLOpenCvBwdWrW_rdc(
	const __global _FLOAT * weight_df_tmp,
	__global _FLOAT * weights_df
)
{
	int gbl_id = get_global_id(0);
	int wei_idx0 = gbl_id * MLO_UT_READ_UNIT;

	int wei_blk_idx = iDiv(wei_idx0, MLO_WEI_CHANNEL_STRIDE);
	int wei_idx = iMod(wei_idx0, wei_blk_idx, MLO_WEI_CHANNEL_STRIDE);

	_FLOAT pvt_accum_wei[MLO_UT_READ_UNIT];
	for (int i = 0; i < MLO_UT_READ_UNIT; ++i)
	{
		pvt_accum_wei[i] = 0;
	}

	int batch_loop = (MLO_BATCH_SZ + (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS) - 1) / (MLO_N_BATCH_LOOPS*MLO_N_LCL_BATCHS);
	for (int i = 0; i < batch_loop; ++i)
	{
		*(MLO_UT_READ_TYPE*)pvt_accum_wei
			+= *(MLO_UT_READ_TYPE*)&weight_df_tmp[(wei_blk_idx * MLO_WEI_CHANNEL_STRIDE + i* MLO_N_OUTPUTS*MLO_WEI_BATCH_STRIDE)  + wei_idx];
	}

	*(MLO_UT_READ_TYPE*)&weights_df[wei_idx0] = *(MLO_UT_READ_TYPE*)pvt_accum_wei;

}


migrate prop4 to use function...

migrate prop4 to use function...

// Copyright Hugh Perkins 2014,2015 hughperkins at gmail
//
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file, You can
// obtain one at http://mozilla.org/MPL/2.0/.

// expected defines:
// BIASED (or not)

// not specifci to this kernel, but basically we have to convolve one plane of the propagate output
// with one plane of each propagate input, to get one filter plane change output plane
// (and do this for each example, sum them together)

// eg plane f from each output: 128 * 28 * 28 * 4 = 401KB
// plane i from each input: 128 * 28 * 28 * 4 = 401KB
// plane i from filter f: 5 * 5 * 4 = 100 bytes...
// plane i from all filters: 5 * 5 * 4 * 8 = 800 bytes (ok :-) )
// all planes from all filters eg: 5 * 5 * 4 * 8 * 1 = 800 bytes (ok :-) )
//
// in propagate, filter plane i of filter f:
// convolves with plane i from each input cube
// to contribute to plane f of each output
// so, for backprop, need to take plane i from each input cube, and plane f
// from each output cube, and convolve together to get changes to plane i of filter f

// concept this kernel:
// we process blocks of several input and output plane pairs together
// we structure the blocks to maximize sharing between the pairs, whilst keeping the number
// of pairs down
// see prototyping/blocking.cpp for an example of calculating this cost (we could also
// including boardsize too of course)

// workgroupId: [outBlockId][inBlockId]
// localId: [filterId][inputPlane][filterRow][filterCol]
// per-thread iteration: [n][outputRow][outputCol]
// local: errorboard: outputBoardSize * outputBoardSize
//        imageboard: inputBoardSize * inputBoardSize
void kernel backprop_floats_withscratch_dobias(
        const float learningRateMultiplier, const int batchSize,
         global const float *errors, global const float *images,
        global float *weights,
        #ifdef BIASED
             global float *biasWeights,
        #endif
        local float *_errorBoard, local float *_imageBoard
 ) {
    const int globalId = get_global_id(0);
    const int localId = get_local_id(0);
    const int workgroupId = get_group_id(0);
    const int workgroupSize = get_local_size(0);

    const int outBlockId = workgroupId / gInPerBlock;
    const int inBlockId = workgroupId % gInPerBlock;

    const int localBlockId = localId / gFilterSizeSquared;
    const int localInBlock = localBlockId % gInPerBlock;
    const int localOutBlock = localBlockId / gInPerBlock;

    const int inputPlane = inBlockId * gInPerBlock + localInBlock;
    const int outputPlane = outBlockId * gOutPerBlock + localOutBlock;

    const int localLinearPos = localId % gFilterSizeSquared;
    const int filterRow = localLinearPos / gFilterSize;
    const int filterCol = localLinearPos % gFilterSize;

    for( int outPlane =
    // weights:     [outPlane][upstreamPlane][filterRow][filterCol]
    //       aggregate over:  [outRow][outCol][n]
    float thiswchange = 0;
#ifdef BIASED
    float thisbiaschange = 0;
#endif
    for( int n = 0; n < batchSize; n++ ) {
        int upstreamBoardGlobalOffset = ( n * gInputPlanes + upstreamPlane ) * gInputBoardSizeSquared;
        // need to fetch the board, but it's bigger than us, so will need to loop...
        int numLoopsForUpstream = ( gInputBoardSizeSquared + workgroupSize - 1 ) / workgroupSize;
        barrier(CLK_LOCAL_MEM_FENCE);
        for( int i = 0; i < numLoopsForUpstream; i++ ) {
            int thisOffset = i * workgroupSize + localId;
            if( thisOffset < gInputBoardSizeSquared ) {
                _imageBoard[thisOffset] = images[ upstreamBoardGlobalOffset + thisOffset ];
            }
        }
        int resultBoardGlobalOffset = ( n * gNumFilters + outPlane ) * gOutputBoardSizeSquared;
        int numLoopsForResults = ( gOutputBoardSizeSquared + workgroupSize - 1 ) / workgroupSize;
        for( int i = 0; i < numLoopsForResults; i++ ) {
            int thisOffset = i * workgroupSize + localId;
            if( thisOffset < gOutputBoardSizeSquared ) {
                _errorBoard[thisOffset ] = errors[resultBoardGlobalOffset + thisOffset];
            }
        }
        barrier(CLK_LOCAL_MEM_FENCE);
        if( localId < gFilterSizeSquared ) {
            for( int outRow = 0; outRow < gOutputBoardSize; outRow++ ) {
                int upstreamRow = outRow - gMargin + filterRow;
                for( int outCol = 0; outCol < gOutputBoardSize; outCol++ ) {
                    int upstreamCol = outCol - gMargin + filterCol;
                    bool proceed = upstreamRow >= 0 && upstreamCol >= 0 && upstreamRow < gInputBoardSize
                        && upstreamCol < gInputBoardSize;
                    if( proceed ) {
                        int resultIndex = outRow * gOutputBoardSize + outCol;
                        float error = _errorBoard[resultIndex];
                        int upstreamDataIndex = upstreamRow * gInputBoardSize + upstreamCol;
                        float upstreamResult = _imageBoard[upstreamDataIndex];
                        thiswchange += upstreamResult * error;
    #ifdef BIASED
                        thisbiaschange += error;
    #endif
                    }
                }
            }
        }
    }
    if( localId < gFilterSizeSquared ) {
        weights[ workgroupId * gFilterSizeSquared + localId ] -= learningRateMultiplier * thiswchange;
    }
#ifdef BIASED
    bool writeBias = upstreamPlane == 0 && localId == 0;
    if( writeBias ) {
        biasWeights[outPlane] -= learningRateMultiplier * thisbiaschange;
    }
#endif
    // weights:     [outPlane][upstreamPlane][filterRow][filterCol]
    //       aggregate over:  [outRow][outCol][n]
}


r1707: mysql-sql.cl

r1707: mysql-sql.cl

;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
;;;; *************************************************************************
;;;; FILE IDENTIFICATION
;;;;
;;;; Name:          mysql-sql.cl
;;;; Purpose:       High-level MySQL interface using UFFI
;;;; Programmers:   Kevin M. Rosenberg based on
;;;;                Original code by Pierre R. Mai
;;;; Date Started:  Feb 2002
;;;;
;;;; $Id: mysql-sql.cl,v 1.18 2002/03/30 05:07:02 kevin Exp $
;;;;
;;;; This file, part of CLSQL, is Copyright (c) 2002 by Kevin M. Rosenberg
;;;; and Copyright (c) 1999-2001 by Pierre R. Mai
;;;;
;;;; CLSQL users are granted the rights to distribute and use this software
;;;; as governed by the terms of the Lisp Lesser GNU Public License
;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
;;;; *************************************************************************

(declaim (optimize (debug 3) (speed 3) (safety 1) (compilation-speed 0)))

;;;; Modified by Kevin Rosenberg, Feb 20002
;;;; -- Added support for Allegro CL and Lispworks using UFFI layer
;;;; -- Changed database-connect to use mysql-real-connect. This way,
;;;;    can avoid using double (unwind-protect)
;;;; -- Changed database-connect to have MySQL library allocate space
;;;;    for MYSQL structure. This will make the code more robust in
;;;;    the event that MySQL library changes the size of the mysql-mysql
;;;;    structure.
;;;;
;;;; Mar 2002
;;;; Added field types

(defpackage :clsql-mysql
    (:use :common-lisp :clsql-sys :mysql :clsql-uffi)
    (:export #:mysql-database)
    (:documentation "This is the CLSQL interface to MySQL."))

(in-package :clsql-mysql)

;;; Field conversion functions

(defun make-type-list-for-auto (num-fields res-ptr)
  (let ((new-types '())
	#+ignore (field-vec (mysql-fetch-fields res-ptr)))
    (dotimes (i num-fields)
      (declare (fixnum i))
      (let* ( (field (mysql-fetch-field-direct res-ptr i))
	     #+ignore (field (uffi:deref-array field-vec 'mysql-field-vector i))
	      (type (uffi:get-slot-value field 'mysql-field 'type)))
	(push
	 (case type
	   ((#.mysql-field-types#tiny
	     #.mysql-field-types#short
	     #.mysql-field-types#int24
	     #.mysql-field-types#long)
	    :int32)
	   (#.mysql-field-types#longlong
	    :int64)
	   ((#.mysql-field-types#double
	     #.mysql-field-types#float
	     #.mysql-field-types#decimal)
	    :double)
	   (otherwise
	    t))
	 new-types)))
    (nreverse new-types)))

(defun canonicalize-types (types num-fields res-ptr)
  (if (null types)
      nil
      (let ((auto-list (make-type-list-for-auto num-fields res-ptr)))
	(cond
	  ((listp types)
	   (canonicalize-type-list types auto-list))
	  ((eq types :auto)
	   auto-list)
	  (t
	   nil)))))

(defmethod database-initialize-database-type ((database-type (eql :mysql)))
  t)

(uffi:def-type mysql-mysql-ptr-def (* mysql-mysql))
(uffi:def-type mysql-row-def mysql-row)
(uffi:def-type mysql-mysql-res-ptr-def (* mysql-mysql-res))

(defclass mysql-database (database)
  ((mysql-ptr :accessor database-mysql-ptr :initarg :mysql-ptr
	      :type mysql-mysql-ptr-def)))

(defmethod database-name-from-spec (connection-spec (database-type (eql :mysql)))
  (check-connection-spec connection-spec database-type (host db user password))
  (destructuring-bind (host db user password) connection-spec
    (declare (ignore password))
    (concatenate 'string host "/" db "/" user)))

(defmethod database-connect (connection-spec (database-type (eql :mysql)))
  (check-connection-spec connection-spec database-type (host db user password))
  (destructuring-bind (host db user password) connection-spec
    (let ((mysql-ptr (mysql-init (uffi:make-null-pointer 'mysql-mysql)))
	  (socket nil))
      (if (uffi:null-pointer-p mysql-ptr)
	  (error 'clsql-connect-error
		 :database-type database-type
		 :connection-spec connection-spec
		 :errno (mysql-errno mysql-ptr)
		 :error (mysql-error-string mysql-ptr))
	(uffi:with-cstrings ((host-native host)
			    (user-native user)
			    (password-native password)
			    (db-native db)
			    (socket-native socket))
	  (let ((error-occurred nil))
	    (unwind-protect
		(if (uffi:null-pointer-p
		     (mysql-real-connect
		      mysql-ptr host-native user-native password-native
		      db-native 0 socket-native 0))
		    (progn
		      (setq error-occurred t)
		      (error 'clsql-connect-error
			     :database-type database-type
			     :connection-spec connection-spec
			     :errno (mysql-errno mysql-ptr)
			     :error (mysql-error-string mysql-ptr)))
		  (make-instance 'mysql-database
		    :name (database-name-from-spec connection-spec
						   database-type)
		    :mysql-ptr mysql-ptr))
	      (when error-occurred (mysql-close mysql-ptr)))))))))

(defmethod database-disconnect ((database mysql-database))
  (mysql-close (database-mysql-ptr database))
  (setf (database-mysql-ptr database) nil)
  t)

(defmethod database-query (query-expression (database mysql-database)
			   types)
  (with-slots (mysql-ptr) database
    (uffi:with-cstring (query-native query-expression)
       (if (zerop (mysql-query mysql-ptr query-native))
	   (let ((res-ptr (mysql-use-result mysql-ptr)))
	     (if res-ptr
		 (let ((num-fields (mysql-num-fields res-ptr)))
		   (setq types (canonicalize-types
				      types num-fields
				      res-ptr))
		   (unwind-protect
			(loop for row = (mysql-fetch-row res-ptr)
			      until (uffi:null-pointer-p row)
			      collect
			      (loop for i from 0 below num-fields
				    collect
				    (convert-raw-field
				     (uffi:deref-array row 'mysql-row i)
				     types i)))
		     (mysql-free-result res-ptr)))
	       (error 'clsql-sql-error
		      :database database
		      :expression query-expression
		      :errno (mysql-errno mysql-ptr)
		      :error (mysql-error-string mysql-ptr))))
	 (error 'clsql-sql-error
		:database database
		:expression query-expression
		:errno (mysql-errno mysql-ptr)
		:error (mysql-error-string mysql-ptr))))))

(defmethod database-execute-command (sql-expression (database mysql-database))
  (uffi:with-cstring (sql-native sql-expression)
    (let ((mysql-ptr (database-mysql-ptr database)))
      (declare (type mysql-mysql-ptr-def mysql-ptr))
      (if (zerop (mysql-query mysql-ptr sql-native))
	  t
	(error 'clsql-sql-error
	       :database database
	       :expression sql-expression
	       :errno (mysql-errno mysql-ptr)
	       :error (mysql-error-string mysql-ptr))))))

(defstruct mysql-result-set
  (res-ptr (uffi:make-null-pointer 'mysql-mysql-res)
	   :type mysql-mysql-res-ptr-def)
  (types nil)
  (num-fields nil :type fixnum)
  (full-set nil :type boolean))

(defmethod database-query-result-set (query-expression
				      (database mysql-database)
				      &key full-set types)
  (uffi:with-cstring (query-native query-expression)
    (let ((mysql-ptr (database-mysql-ptr database)))
     (declare (type mysql-mysql-ptr-def mysql-ptr))
      (if (zerop (mysql-query mysql-ptr query-native))
	  (let ((res-ptr (if full-set
			     (mysql-store-result mysql-ptr)
			   (mysql-use-result mysql-ptr))))
	    (declare (type mysql-mysql-res-ptr-def res-ptr))
	    (if (not (uffi:null-pointer-p res-ptr))
		(let* ((num-fields (mysql-num-fields res-ptr))
		       (result-set (make-mysql-result-set
				    :res-ptr res-ptr
				    :num-fields num-fields
				    :full-set full-set
				    :types
				    (canonicalize-types
				     types num-fields
				     res-ptr))))
		  (if full-set
		      (values result-set
			      num-fields
			      (mysql-num-rows res-ptr))
		      (values result-set
			      num-fields)))
		(error 'clsql-sql-error
		     :database database
		     :expression query-expression
		     :errno (mysql-errno mysql-ptr)
		     :error (mysql-error-string mysql-ptr))))
	(error 'clsql-sql-error
	       :database database
	       :expression query-expression
	       :errno (mysql-errno mysql-ptr)
	       :error (mysql-error-string mysql-ptr))))))

(defmethod database-dump-result-set (result-set (database mysql-database))
  (mysql-free-result (mysql-result-set-res-ptr result-set))
  t)

(defmethod database-store-next-row (result-set (database mysql-database) list)
  (let* ((res-ptr (mysql-result-set-res-ptr result-set))
	 (row (mysql-fetch-row res-ptr))
	 (types (mysql-result-set-types result-set)))
    (declare (type mysql-mysql-res-ptr-def res-ptr)
	     (type mysql-row-def row))
    (unless (uffi:null-pointer-p row)
      (loop for i from 0 below (mysql-result-set-num-fields result-set)
	    for rest on list
	    do
	    (setf (car rest)
		  (convert-raw-field
		   (uffi:deref-array row 'mysql-row i)
		   types
		   i)))
      list)))

;;;; -*- Mode: LISP; Syntax: ANSI-Common-Lisp; Base: 10 -*-
;;;; *************************************************************************
;;;; FILE IDENTIFICATION
;;;;
;;;; Name:          mysql-sql.cl
;;;; Purpose:       High-level MySQL interface using UFFI
;;;; Programmers:   Kevin M. Rosenberg based on
;;;;                Original code by Pierre R. Mai
;;;; Date Started:  Feb 2002
;;;;
;;;; $Id: mysql-sql.cl,v 1.17 2002/03/29 14:03:27 kevin Exp $
;;;;
;;;; This file, part of CLSQL, is Copyright (c) 2002 by Kevin M. Rosenberg
;;;; and Copyright (c) 1999-2001 by Pierre R. Mai
;;;;
;;;; CLSQL users are granted the rights to distribute and use this software
;;;; as governed by the terms of the Lisp Lesser GNU Public License
;;;; (http://opensource.franz.com/preamble.html), also known as the LLGPL.
;;;; *************************************************************************

(declaim (optimize (debug 3) (speed 3) (safety 1) (compilation-speed 0)))

;;;; Modified by Kevin Rosenberg, Feb 20002
;;;; -- Added support for Allegro CL and Lispworks using UFFI layer
;;;; -- Changed database-connect to use mysql-real-connect. This way,
;;;;    can avoid using double (unwind-protect)
;;;; -- Changed database-connect to have MySQL library allocate space
;;;;    for MYSQL structure. This will make the code more robust in
;;;;    the event that MySQL library changes the size of the mysql-mysql
;;;;    structure.
;;;;
;;;; Mar 2002
;;;; Added field types

(defpackage :clsql-mysql
    (:use :common-lisp :clsql-sys :mysql :clsql-uffi)
    (:export #:mysql-database)
    (:documentation "This is the CLSQL interface to MySQL."))

(in-package :clsql-mysql)

;;; Field conversion functions

(defun make-type-list-for-auto (num-fields res-ptr)
  (let ((new-types '())
	#+ignore (field-vec (mysql-fetch-fields res-ptr)))
    (dotimes (i num-fields)
      (declare (fixnum i))
      (let* ( (field (mysql-fetch-field-direct res-ptr i))
	     #+ignore (field (uffi:deref-array field-vec 'mysql-field-vector i))
	      (type (uffi:get-slot-value field 'mysql-field 'type)))
	(push
	 (case type
	   ((#.mysql-field-types#tiny
	     #.mysql-field-types#short
	     #.mysql-field-types#int24
	     #.mysql-field-types#long)
	    :int32)
	   (#.mysql-field-types#longlong
	    :int64)
	   ((#.mysql-field-types#double
	     #.mysql-field-types#float
	     #.mysql-field-types#decimal)
	    :double)
	   (otherwise
	    t))
	 new-types)))
    (nreverse new-types)))

(defun canonicalize-types (types num-fields res-ptr)
  (if (null types)
      nil
      (let ((auto-list (make-type-list-for-auto num-fields res-ptr)))
	(cond
	 ((listp types)
	  (canonicalize-type-list types auto-list))
	  types)
	  ((eq types :auto)
	   auto-list)
	  (t
	   nil))))

(defmethod database-initialize-database-type ((database-type (eql :mysql)))
  t)

(uffi:def-type mysql-mysql-ptr-def (* mysql-mysql))
(uffi:def-type mysql-row-def mysql-row)
(uffi:def-type mysql-mysql-res-ptr-def (* mysql-mysql-res))

(defclass mysql-database (database)
  ((mysql-ptr :accessor database-mysql-ptr :initarg :mysql-ptr
	      :type mysql-mysql-ptr-def)))

(defmethod database-name-from-spec (connection-spec (database-type (eql :mysql)))
  (check-connection-spec connection-spec database-type (host db user password))
  (destructuring-bind (host db user password) connection-spec
    (declare (ignore password))
    (concatenate 'string host "/" db "/" user)))

(defmethod database-connect (connection-spec (database-type (eql :mysql)))
  (check-connection-spec connection-spec database-type (host db user password))
  (destructuring-bind (host db user password) connection-spec
    (let ((mysql-ptr (mysql-init (uffi:make-null-pointer 'mysql-mysql)))
	  (socket nil))
      (if (uffi:null-pointer-p mysql-ptr)
	  (error 'clsql-connect-error
		 :database-type database-type
		 :connection-spec connection-spec
		 :errno (mysql-errno mysql-ptr)
		 :error (mysql-error-string mysql-ptr))
	(uffi:with-cstrings ((host-native host)
			    (user-native user)
			    (password-native password)
			    (db-native db)
			    (socket-native socket))
	  (let ((error-occurred nil))
	    (unwind-protect
		(if (uffi:null-pointer-p
		     (mysql-real-connect
		      mysql-ptr host-native user-native password-native
		      db-native 0 socket-native 0))
		    (progn
		      (setq error-occurred t)
		      (error 'clsql-connect-error
			     :database-type database-type
			     :connection-spec connection-spec
			     :errno (mysql-errno mysql-ptr)
			     :error (mysql-error-string mysql-ptr)))
		  (make-instance 'mysql-database
		    :name (database-name-from-spec connection-spec
						   database-type)
		    :mysql-ptr mysql-ptr))
	      (when error-occurred (mysql-close mysql-ptr)))))))))

(defmethod database-disconnect ((database mysql-database))
  (mysql-close (database-mysql-ptr database))
  (setf (database-mysql-ptr database) nil)
  t)

(defmethod database-query (query-expression (database mysql-database)
			   types)
  (with-slots (mysql-ptr) database
    (uffi:with-cstring (query-native query-expression)
       (if (zerop (mysql-query mysql-ptr query-native))
	   (let ((res-ptr (mysql-use-result mysql-ptr)))
	     (if res-ptr
		 (let ((num-fields (mysql-num-fields res-ptr)))
		   (setq types (canonicalize-types
				      types num-fields
				      res-ptr))
		   (unwind-protect
			(loop for row = (mysql-fetch-row res-ptr)
			      until (uffi:null-pointer-p row)
			      collect
			      (loop for i from 0 below num-fields
				    collect
				    (convert-raw-field
				     (uffi:deref-array row 'mysql-row i)
				     types i)))
		     (mysql-free-result res-ptr)))
	       (error 'clsql-sql-error
		      :database database
		      :expression query-expression
		      :errno (mysql-errno mysql-ptr)
		      :error (mysql-error-string mysql-ptr))))
	 (error 'clsql-sql-error
		:database database
		:expression query-expression
		:errno (mysql-errno mysql-ptr)
		:error (mysql-error-string mysql-ptr))))))

(defmethod database-execute-command (sql-expression (database mysql-database))
  (uffi:with-cstring (sql-native sql-expression)
    (let ((mysql-ptr (database-mysql-ptr database)))
      (declare (type mysql-mysql-ptr-def mysql-ptr))
      (if (zerop (mysql-query mysql-ptr sql-native))
	  t
	(error 'clsql-sql-error
	       :database database
	       :expression sql-expression
	       :errno (mysql-errno mysql-ptr)
	       :error (mysql-error-string mysql-ptr))))))

(defstruct mysql-result-set
  (res-ptr (uffi:make-null-pointer 'mysql-mysql-res)
	   :type mysql-mysql-res-ptr-def)
  (types nil)
  (num-fields nil :type fixnum)
  (full-set nil :type boolean))

(defmethod database-query-result-set (query-expression
				      (database mysql-database)
				      &key full-set types)
  (uffi:with-cstring (query-native query-expression)
    (let ((mysql-ptr (database-mysql-ptr database)))
     (declare (type mysql-mysql-ptr-def mysql-ptr))
      (if (zerop (mysql-query mysql-ptr query-native))
	  (let ((res-ptr (if full-set
			     (mysql-store-result mysql-ptr)
			   (mysql-use-result mysql-ptr))))
	    (declare (type mysql-mysql-res-ptr-def res-ptr))
	    (if (not (uffi:null-pointer-p res-ptr))
		(let* ((num-fields (mysql-num-fields res-ptr))
		       (result-set (make-mysql-result-set
				    :res-ptr res-ptr
				    :num-fields num-fields
				    :full-set full-set
				    :types
				    (canonicalize-types
				     types num-fields
				     res-ptr))))
		  (if full-set
		      (values result-set
			      num-fields
			      (mysql-num-rows res-ptr))
		      (values result-set
			      num-fields)))
		(error 'clsql-sql-error
		     :database database
		     :expression query-expression
		     :errno (mysql-errno mysql-ptr)
		     :error (mysql-error-string mysql-ptr))))
	(error 'clsql-sql-error
	       :database database
	       :expression query-expression
	       :errno (mysql-errno mysql-ptr)
	       :error (mysql-error-string mysql-ptr))))))

(defmethod database-dump-result-set (result-set (database mysql-database))
  (mysql-free-result (mysql-result-set-res-ptr result-set))
  t)

(defmethod database-store-next-row (result-set (database mysql-database) list)
  (let* ((res-ptr (mysql-result-set-res-ptr result-set))
	 (row (mysql-fetch-row res-ptr))
	 (types (mysql-result-set-types result-set)))
    (declare (type mysql-mysql-res-ptr-def res-ptr)
	     (type mysql-row-def row))
    (unless (uffi:null-pointer-p row)
      (loop for i from 0 below (mysql-result-set-num-fields result-set)
	    for rest on list
	    do
	    (setf (car rest)
		  (convert-raw-field
		   (uffi:deref-array row 'mysql-row i)
		   types
		   i)))
      list)))


fix typo and add some notes

fix typo and add some notes

;; @file generics-and-classes.cl
;;
;; @breif Generics and classes
;;

; For auto-incrementing account-number, used internally only.
(defvar *account-number* 0)
; User-defined class which subclasses `STANDARD-OBJECT`
(defclass bank-account ()
  ((customer-name
    :initarg :customer-name
    :initform (error "Customer name must be supplied.")
    :accessor customer-name
    :documentation "Name of the customer.")
   (balance
    :initarg :balance
    :initform 0
    :reader balance
    :documentation "Current balance.")
   (account-number
    :initform (incf *account-number*)
    :reader account-number
    :documentation "Internal account number, unique in the bank.")
   (account-type
    :reader account-type
    :documentation "Account type, one of :glod :silver :bronze and :iron .")))
; Determine account type according to initial balance
; @note the use of '&key' to make 'opening-bonus-percentage' a valid keyword
; argument of the `MAKE-INSTANCE` function
; @note that the use of `:after` auxiliary method to access existing slot
; values while creating the instance.
(defmethod initialize-instance :after ((account bank-account)
                                       &key opening-bonus-percentage)
  (let ((balance (slot-value account 'balance)))
    (setf (slot-value account 'account-type)
      (cond
        ((> balance 100000) :glod)
        ((> balance 50000) :silver)
        ((> balance 5000) :bronze)
        (t :iron)))
    (when opening-bonus-percentage
      (incf (slot-value account 'balance)
        (* balance (/ opening-bonus-percentage 100))))))
;;
; Slot inheritance and merge
; Basically, `:initarg`s are combined, others are set to the most specific
; options respectively. Besides, `:allocation` option could be changed while
; inheriting.
(defclass foo ()
  ((a :initarg :a :initform "A" :accessor a)
   (b :initarg :b :initform "B" :accessor b)))
(defclass bar (foo)
  ((a :initform (error "Must supply a value for a"))
   (b :initarg :the-b :accessor the-b :allocation :class)))
;;
; Multiple inheritance
; Saving account with interest
(defclass savings-account (bank-account)
  ((interest
    :initarg :interest
    :reader interest
    :documentation "Current accumulated interest.")))
; Give bonus interest on acount created
(defmethod initialize-instance :after ((account savings-account) &key)
  (setf (slot-value account 'interest) (* (balance account) 0.001)))
; Checking account with checking functionality and record checks number
(defclass checking-account (bank-account)
  ((checks
    :initform 0
    :reader checks
    :documentation "Number of total check operations.")))
; Check operation on checking-account, defined as generic
(defgeneric check (account)
  (:documentation "Perform a check operation"))
(defmethod check ((account checking-account))
  (format t "#~4d checking now" (incf (slot-value account 'checks))))
; Now is the interesting part
; money-market-account inherits both savings-account and checking-account
(defclass money-market-account (checking-account savings-account) ())
; Suppose a operation named 'print-statement' can be performed all kinds of
; bank-account, and specializations on savings-account and checking-account
; are different.
; As money-market-account inherits both savings-account and checking-account,
; it is natural that its specialization implements both specializations of its
; direct-superclasses.
; Well, frankly speaking, the solution is not obvious. To solve this problem,
; the trick of using auxiliary method could be applied as follows:
;
; Define generic function 'print-statement'
(defgeneric print-statement (account)
  (:documentation "Print (monthly) general statement of the account."))
; Base implementations in primary method of the common superclass
(defmethod print-statement ((account bank-account))
  (with-slots (customer-name balance account-type) account
    (format t "[bank-account]~15tcustomer-name: ~a~%~15tbalance: ~d~%~15taccount-type: ~a~%"
      customer-name balance account-type)))
; Detailed implementations in :after auxiliary method of the two subclasses
; @note choosing between :before and :after affects the order of
; implementations of in the class's precedence class list. With :before, the
; order is most-to-least, while with :after, the order is least-to-most.
;
; savings-account: print the current interest
(defmethod print-statement :after ((account savings-account))
  (with-slots (interest) account
    (format t "[savings-account]~18tinterest: ~d~%" interest)))
; checkings-account: print the number of total check operations
(defmethod print-statement :after ((account checking-account))
  (with-slots (checks) account
    (format t "[checking-account]~19tchecks: ~d~%" checks)))
; If needed, detailed implementations of money-market-account must also be
; defined as auxiliary method
(defmethod print-statement :after ((account money-market-account))
  (format t "[money-market-account]~%"))
; At last, it is obvious that the `:after` auxiliary method is Common Lisp's
; approach to initialization, just like constructors in C++/Java and `__int__`
; in Python.
; Together with `:before` and `:around`, Common Lisp's class system is more
; flexible and powerful compared to C++/Java/Python.

;; @file generics-and-classes.cl
;;
;; @breif Generics and classes
;;

; For auto-incrementing account-number, used internally only.
(defvar *account-number* 0)
; User-defined class which subclasses `STANDARD-OBJECT`
(defclass bank-account ()
  ((customer-name
    :initarg :customer-name
    :initform (error "Customer name must be supplied.")
    :accessor customer-name
    :documentation "Name of the customer.")
   (balance
    :initarg :balance
    :initform 0
    :reader balance
    :documentation "Current balance.")
   (account-number
    :initform (incf *account-number*)
    :reader account-number
    :documentation "Internal account number, unique in the bank.")
   (account-type
    :reader account-type
    :documentation "Account type, one of :glod :silver :bronze and :iron .")))
; Determine account type according to initial balance
; @note the use of '&key' to make 'opening-bonus-percentage' a valid keyword
; argument of the `MAKE-INSTANCE` function
; @note that the use of `:afte` auxiliary method to access existing slot
; values while creating the instance.
(defmethod initialize-instance :after ((account bank-account)
                                       &key opening-bonus-percentage)
  (let ((balance (slot-value account 'balance)))
    (setf (slot-value account 'account-type)
      (cond
        ((> balance 100000) :glod)
        ((> balance 50000) :silver)
        ((> balance 5000) :bronze)
        (t :iron)))
    (when opening-bonus-percentage
      (incf (slot-value account 'balance)
        (* balance (/ opening-bonus-percentage 100))))))
;;
; Slot inheritance and merge
; Basically, `:initarg`s are combined, others are set to the most specific
; options respectively. Besides, `:allocation` option could be changed while
; inheriting.
(defclass foo ()
  ((a :initarg :a :initform "A" :accessor a)
   (b :initarg :b :initform "B" :accessor b)))
(defclass bar (foo)
  ((a :initform (error "Must supply a value for a"))
   (b :initarg :the-b :accessor the-b :allocation :class)))
;;
; Multiple inheritance
; Saving account with interest
(defclass savings-account (bank-account)
  ((interest
    :initarg :interest
    :reader interest
    :documentation "Current accumulated interest.")))
; Give bonus interest on acount created
(defmethod initialize-instance :after ((account savings-account) &key)
  (setf (slot-value account 'interest) (* (balance account) 0.001)))
; Checking account with checking functionality and record checks number
(defclass checking-account (bank-account)
  ((checks
    :initform 0
    :reader checks
    :documentation "Number of total check operations.")))
; Check operation on checking-account, defined as generic
(defgeneric check (account)
  (:documentation "Perform a check operation"))
(defmethod check ((account checking-account))
  (format t "#~4d checking now" (incf (slot-value account 'checks))))
; Now is the interesting part
; money-market-account inherits both savings-account and checking-account
(defclass money-market-account (checking-account savings-account) ())
; Suppose a operation named 'print-statement' can be performed all kinds of
; bank-account, and specializations on savings-account and checking-account
; are different.
; As money-market-account inherits both savings-account and checking-account,
; it is natural that its specialization implements both specializations of its
; direct-superclasses.
; Well, frankly speaking, the solution is not obvious. To solve this problem,
; the trick of using auxiliary method could be applied as follows:
;
; Define generic function 'print-statement'
(defgeneric print-statement (account)
  (:documentation "Print (monthly) general statement of the account."))
; Base implementations in primary method of the common superclass
(defmethod print-statement ((account bank-account))
  (with-slots (customer-name balance account-type) account
    (format t "[bank-account]~15tcustomer-name: ~a~%~15tbalance: ~d~%~15taccount-type: ~a~%"
      customer-name balance account-type)))
; Detailed implementations in :after auxiliary method of the two subclasses
; @note choosing between :before and :after affects the order of
; implementations of in the class's precedence class list. With :before, the
; order is most-to-least, while with :after, the order is least-to-most.
;
; savings-account: print the current interest
(defmethod print-statement :after ((account savings-account))
  (with-slots (interest) account
    (format t "[savings-account]~18tinterest: ~d~%" interest)))
; checkings-account: print the number of total check operations
(defmethod print-statement :after ((account checking-account))
  (with-slots (checks) account
    (format t "[checking-account]~19tchecks: ~d~%" checks)))
; If needed, detailed implementations of money-market-account must also be
; defined as auxiliary method
(defmethod print-statement :after ((account money-market-account))
  (format t "[money-market-account]~%"))