packages feed

futhark-0.7.3: rts/csharp/opencl.cs

// Stub code for OpenCL setup.

private void OPENCL_SUCCEED(int return_code,
                        [CallerFilePath] string filePath = "",
                        [CallerLineNumber] int lineNumber = 0)
{
    OpenCLSucceed(return_code, "", filePath, lineNumber);
}

private void OPENCL_SUCCEED(ComputeErrorCode return_code,
                    [CallerFilePath] string filePath = "",
                    [CallerLineNumber] int lineNumber = 0)
{
    OpenCLSucceed((int) return_code, "", filePath, lineNumber);
}

private void OPENCL_SUCCEED(object return_code,
                    [CallerFilePath] string filePath = "",
                    [CallerLineNumber] int lineNumber = 0)
{
    OpenCLSucceed((int) return_code, "", filePath, lineNumber);
}

public struct OpenCLConfig
{
    public bool Debugging;
    public int PreferredDeviceNum;
    public string PreferredPlatform;
    public string PreferredDevice;

    public string DumpProgramTo;
    public string LoadProgramFrom;

    public int DefaultGroupSize;
    public int DefaultNumGroups;
    public int DefaultTileSize;
    public int DefaultThreshold;
    public int TransposeBlockDim;

    public int NumSizes;
    public string[] SizeNames;
    public int[] SizeValues;
    public string[] SizeClasses;
}

private void MemblockUnrefDevice(ref FutharkContext
 context, ref OpenCLMemblock block, string desc)
{
    if (!block.IsNull)
    {
        block.DecreaseRefs();
        if (context.DetailMemory)
        {
            Console.Error.WriteLine(String.Format(
                "Unreferencing block {0} (allocated as {1}) in {2}: {3} references remaining.",
                desc, block.Tag, "space 'device'", block.References));
        }

        if (block.References == 0)
        {
            context.CurrentMemUsageDevice -= block.Size;
            OPENCL_SUCCEED(OpenCLFree(ref context, block.Mem, block.Tag));
            block.IsNull = true;
        }

        if (context.DetailMemory)
        {
            Console.Error.WriteLine(String.Format(
                "{0} bytes freed (now allocated: {1} bytes)",
                block.Size, context.CurrentMemUsageDevice));
        }
    }
}

private void MemblockSetDevice(ref FutharkContext context,
    ref OpenCLMemblock lhs, ref OpenCLMemblock rhs, string lhs_desc)
{
    MemblockUnrefDevice(ref context, ref lhs, lhs_desc);
    rhs.IncreaseRefs();
    lhs = rhs;
}

private OpenCLMemblock MemblockAllocDevice(ref FutharkContext context, OpenCLMemblock block, long size, string desc)
{
    if (size < 0)
    {
        panic(1, String.Format("Negative allocation of {0} bytes attempted for {1} in {2}",
            size, desc));
    }

    MemblockUnrefDevice(ref context, ref block, desc);
    OPENCL_SUCCEED(OpenCLAlloc(ref context, size, desc, ref block.Mem));

    block.References = 1;
    block.IsNull = false;
    block.Size = size;
    block.Tag = desc;
    context.CurrentMemUsageDevice += size;

    if (context.DetailMemory)
    {
        Console.Error.Write(String.Format("Allocated {0} bytes for {1} in {2} (now allocated: {3} bytes)",
            size, desc, "space 'device'", Ctx.CurrentMemUsageDevice));
    }

    if (context.CurrentMemUsageDevice > context.PeakMemUsageDevice)
    {
        context.PeakMemUsageDevice = context.CurrentMemUsageDevice;
        if (context.DetailMemory)
        {
            Console.Error.Write(" (new peak).\n");
        }
    }
    else if (context.DetailMemory)
    {
        Console.Error.Write(".\n");
    }

    return block;
}


private bool FreeListFind(ref OpenCLFreeList free_list, string tag, ref long size_out, ref CLMemoryHandle mem_out)
{
    for (int i = 0; i < free_list.Capacity; i++)
    {
        if (free_list.Entries[i].Valid && free_list.Entries[i].Tag == tag)
        {
            free_list.Entries[i].Valid = false;
            size_out = free_list.Entries[i].Size;
            mem_out = free_list.Entries[i].Mem;
            free_list.Used--;
            return true;
        }
    }

    return false;
}

private bool FreeListFirst(ref OpenCLFreeList free_list, ref CLMemoryHandle mem_out)
{
    for (int i = 0; i < free_list.Capacity; i++)
    {
        if (free_list.Entries[i].Valid)
        {
            free_list.Entries[i].Valid = false;
            mem_out = free_list.Entries[i].Mem;
            free_list.Used--;
            return true;
        }
    }
    return false;
}

private ComputeErrorCode OpenCLAllocActual(ref FutharkContext context, long min_size, ref CLMemoryHandle mem)
{
    ComputeErrorCode error;
    mem = CL10.CreateBuffer(context.OpenCL.Context, ComputeMemoryFlags.ReadWrite
        , new IntPtr(min_size), IntPtr.Zero, out error);

    if (error != ComputeErrorCode.Success)
    {
        return error;
    }

    int x = 2;
    unsafe
    {
        error = CL10.EnqueueWriteBuffer(Ctx.OpenCL.Queue, mem, true, IntPtr.Zero, new IntPtr(sizeof(int)), new IntPtr(&x), 0, null, null);
    }

    return error;
}

private ComputeErrorCode OpenCLAlloc(ref FutharkContext context, long min_size, string tag, ref CLMemoryHandle mem_out)
{
    if (min_size < 0)
    {
        panic(1, "Tried to allocate a negative amount of bytes.");
    }

    min_size = (min_size < sizeof(int)) ? sizeof(int) : min_size;

    long size = 0;

    if (FreeListFind(ref context.FreeList, tag, ref size, ref mem_out))
    {
        if (size >= min_size && size <= min_size * 2)
        {
            return ComputeErrorCode.Success;
        }
        else
        {
            ComputeErrorCode code1 = CL10.ReleaseMemObject(mem_out);
            if (code1 != ComputeErrorCode.Success)
            {
                return code1;
            }
        }
    }

    ComputeErrorCode error = OpenCLAllocActual(ref context, min_size, ref mem_out);
    while (error == ComputeErrorCode.MemoryObjectAllocationFailure)
    {
        CLMemoryHandle mem = Ctx.EMPTY_MEM_HANDLE;
        if (FreeListFirst(ref context.FreeList, ref mem))
        {
            error = CL10.ReleaseMemObject(mem);
            if (error != ComputeErrorCode.Success)
            {
                return error;
            }
        }
        else
        {
            break;
        }

        error = OpenCLAllocActual(ref context, min_size, ref mem_out);
    }
    return error;
}


private ComputeErrorCode OpenCLFree(ref FutharkContext context, CLMemoryHandle mem, string tag)
{
    long size = 0;
    CLMemoryHandle existing_mem = Ctx.EMPTY_MEM_HANDLE;
    ComputeErrorCode error = ComputeErrorCode.Success;
    if (FreeListFind(ref context.FreeList, tag, ref size, ref existing_mem))
    {
        error = CL10.ReleaseMemObject(existing_mem);
        if (error != ComputeErrorCode.Success)
        {
            return error;
        }
    }

    if (existing_mem.Value == mem.Value)
    {
        return error;
    }

    var trash_null = new IntPtr(0);
    unsafe
    {
        error = CL10.GetMemObjectInfo(mem, ComputeMemoryInfo.Size,
            new IntPtr(sizeof(long)), new IntPtr(&size), out trash_null);
    }

    if (error == ComputeErrorCode.Success)
    {
        FreeListInsert(ref context, size, mem, tag);
    }
    return error;
}

private void FreeListInsert(ref FutharkContext context, long size, CLMemoryHandle mem, string tag)
{
    int i = FreeListFindInvalid(ref context);
    if (i == context.FreeList.Capacity)
    {
        var cap = context.FreeList.Capacity;
        int new_capacity = cap * 2;
        Array.Resize(ref context.FreeList.Entries, new_capacity);
        for (int j = 0; j < cap; j++)
        {
            var entry = new OpenCLFreeListEntry();
            entry.Valid = false;
            context.FreeList.Entries[cap + j] = entry;
        }

        context.FreeList.Capacity *= 2;
    }

    context.FreeList.Entries[i].Valid = true;
    context.FreeList.Entries[i].Size = size;
    context.FreeList.Entries[i].Tag = tag;
    context.FreeList.Entries[i].Mem = mem;
    context.FreeList.Used++;
}

private int FreeListFindInvalid(ref FutharkContext context)
{
    int i;
    for (i = 0; i < context.FreeList.Capacity; i++)
    {
        if (!context.FreeList.Entries[i].Valid)
        {
            break;
        }
    }

    return i;
}

private class OpenCLMemblock
{
    public int References;
    public CLMemoryHandle Mem;
    public long Size;
    public string Tag;
    public bool IsNull;

    public void IncreaseRefs()
    {
        this.References += 1;
    }

    public void DecreaseRefs()
    {
        this.References -= 1;
    }
}

private OpenCLMemblock EmptyMemblock(CLMemoryHandle mem)
{
    var block = new OpenCLMemblock();
    block.Mem = mem;
    block.References = 0;
    block.Tag = "";
    block.Size = 0;
    block.IsNull = true;

    return block;
}

public struct OpenCLFreeListEntry
{
    public bool Valid;
    public CLMemoryHandle Mem;
    public long Size;
    public string Tag;
}

public struct OpenCLFreeList
{
    public OpenCLFreeListEntry[] Entries;
    public int Capacity;
    public int Used;
}


private OpenCLFreeList OpenCLFreeListInit()
{
    int CAPACITY = 30; // arbitrarily chosen
    var free_list = new OpenCLFreeList();
    free_list.Entries = Enumerable.Range(0, CAPACITY)
        .Select<int, OpenCLFreeListEntry>(_ =>
                {
                    var entry = new OpenCLFreeListEntry();
                    entry.Valid = false;
                    return entry;
                }).ToArray();

    free_list.Capacity = CAPACITY;
    free_list.Used = 0;

    return free_list;
}


private void OpenCLConfigInit(out OpenCLConfig cfg,
                      int num_sizes,
                      string[] size_names,
                      int[] size_values,
                      string[] size_classes)
{
    cfg.Debugging = false;
    cfg.PreferredDeviceNum = 0;
    cfg.PreferredPlatform = "";
    cfg.PreferredDevice = "";
    cfg.DumpProgramTo = null;
    cfg.LoadProgramFrom = null;

    cfg.DefaultGroupSize = 256;
    cfg.DefaultNumGroups = 128;
    cfg.DefaultTileSize = 32;
    cfg.DefaultThreshold = 32*1024;
    cfg.TransposeBlockDim = 16;

    cfg.NumSizes = num_sizes;
    cfg.SizeNames = size_names;
    cfg.SizeValues = size_values;
    cfg.SizeClasses = size_classes;
}

public struct OpenCLContext {
   public CLPlatformHandle Platform;
   public CLDeviceHandle Device;
   public CLContextHandle Context;
   public CLCommandQueueHandle Queue;

   public OpenCLConfig Cfg;

   public int MaxGroupSize;
   public int MaxNumGroups;
   public int MaxTileSize;
   public int MaxThreshold;

   public int LockstepWidth;
}

public struct OpenCLDeviceOption {
    public CLPlatformHandle Platform;
    public CLDeviceHandle Device;
    public ComputeDeviceTypes DeviceType;
    public string PlatformName;
    public string DeviceName;
};

/* This function must be defined by the user.  It is invoked by
   setup_opencl() after the platform and device has been found, but
   before the program is loaded.  Its intended use is to tune
   constants based on the selected platform and device. */

private string OpenCLErrorString(int err)
{
    switch ((ComputeErrorCode) err) {
        case ComputeErrorCode.Success:                                        return "Success!";
        case ComputeErrorCode.DeviceNotFound:                                 return "Device not found.";
        case ComputeErrorCode.DeviceNotAvailable:                             return "Device not available";
        case ComputeErrorCode.CompilerNotAvailable:                           return "Compiler not available";
        case ComputeErrorCode.MemoryObjectAllocationFailure:                  return "Memory object allocation failure";
        case ComputeErrorCode.OutOfResources:                                 return "Out of resources";
        case ComputeErrorCode.OutOfHostMemory:                                return "Out of host memory";
        case ComputeErrorCode.ProfilingInfoNotAvailable:                      return "Profiling information not available";
        case ComputeErrorCode.MemoryCopyOverlap:                              return "Memory copy overlap";
        case ComputeErrorCode.ImageFormatMismatch:                            return "Image format mismatch";
        case ComputeErrorCode.ImageFormatNotSupported:                        return "Image format not supported";
        case ComputeErrorCode.BuildProgramFailure:                            return "Program build failure";
        case ComputeErrorCode.MapFailure:                                     return "Map failure";
        case ComputeErrorCode.InvalidValue:                                   return "Invalid value";
        case ComputeErrorCode.InvalidDeviceType:                              return "Invalid device type";
        case ComputeErrorCode.InvalidPlatform:                                return "Invalid platform";
        case ComputeErrorCode.InvalidDevice:                                  return "Invalid device";
        case ComputeErrorCode.InvalidContext:                                 return "Invalid context";
        case ComputeErrorCode.InvalidCommandQueueFlags:                       return "Invalid queue properties";
        case ComputeErrorCode.InvalidCommandQueue:                            return "Invalid command queue";
        case ComputeErrorCode.InvalidHostPointer:                             return "Invalid host pointer";
        case ComputeErrorCode.InvalidMemoryObject:                            return "Invalid memory object";
        case ComputeErrorCode.InvalidImageFormatDescriptor:                   return "Invalid image format descriptor";
        case ComputeErrorCode.InvalidImageSize:                               return "Invalid image size";
        case ComputeErrorCode.InvalidSampler:                                 return "Invalid sampler";
        case ComputeErrorCode.InvalidBinary:                                  return "Invalid binary";
        case ComputeErrorCode.InvalidBuildOptions:                            return "Invalid build options";
        case ComputeErrorCode.InvalidProgram:                                 return "Invalid program";
        case ComputeErrorCode.InvalidProgramExecutable:                       return "Invalid program executable";
        case ComputeErrorCode.InvalidKernelName:                              return "Invalid kernel name";
        case ComputeErrorCode.InvalidKernelDefinition:                        return "Invalid kernel definition";
        case ComputeErrorCode.InvalidKernel:                                  return "Invalid kernel";
        case ComputeErrorCode.InvalidArgumentIndex:                           return "Invalid argument index";
        case ComputeErrorCode.InvalidArgumentValue:                           return "Invalid argument value";
        case ComputeErrorCode.InvalidArgumentSize:                            return "Invalid argument size";
        case ComputeErrorCode.InvalidKernelArguments:                         return "Invalid kernel arguments";
        case ComputeErrorCode.InvalidWorkDimension:                           return "Invalid work dimension";
        case ComputeErrorCode.InvalidWorkGroupSize:                           return "Invalid work group size";
        case ComputeErrorCode.InvalidWorkItemSize:                            return "Invalid work item size";
        case ComputeErrorCode.InvalidGlobalOffset:                            return "Invalid global offset";
        case ComputeErrorCode.InvalidEventWaitList:                           return "Invalid event wait list";
        case ComputeErrorCode.InvalidEvent:                                   return "Invalid event";
        case ComputeErrorCode.InvalidOperation:                               return "Invalid operation";
        case ComputeErrorCode.InvalidGLObject:                                return "Invalid OpenGL object";
        case ComputeErrorCode.InvalidBufferSize:                              return "Invalid buffer size";
        case ComputeErrorCode.InvalidMipLevel:                                return "Invalid mip-map level";
        default:                                             return "Unknown";
    }
}

private void OpenCLSucceed(int ret,
                   string call,
                   string file,
                   int line)
{
    if (ret != (int) ComputeErrorCode.Success)
    {
        panic(-1, "{0}:{1}: OpenCL call\n  {2}\nfailed with error code {3} ({4})\n",
              file, line, call, ret, OpenCLErrorString(ret));
    }
}

private void SetPreferredPlatform(ref OpenCLConfig cfg, string s) {
    cfg.PreferredPlatform = s;
}

private void SetPreferredDevice(ref OpenCLConfig cfg, string s)
{
    int x = 0;
    int i = 0;
    if (s[0] == '#') {
        i = 1;
        while (i < s.Length && char.IsDigit(s[i])) {
            x = x * 10 + (int) (s[i])-'0';
            i++;
        }
        // Skip trailing spaces.
        while (i < s.Length && char.IsWhiteSpace(s[i])) {
            i++;
        }
    }
    cfg.PreferredDevice = s.Substring(i);
    cfg.PreferredDeviceNum = x;
}

private string OpenCLPlatformInfo(CLPlatformHandle platform,
                         ComputePlatformInfo param) {
    IntPtr req_bytes;
    IntPtr _null = new IntPtr();
    OPENCL_SUCCEED(CL10.GetPlatformInfo(platform, param, _null, _null, out req_bytes));

    byte[] info = new byte[(int) req_bytes];
    unsafe
    {
        fixed (byte* ptr = &info[0])
        {
            OPENCL_SUCCEED(CL10.GetPlatformInfo(platform, param, req_bytes, new IntPtr(ptr), out _null));
        }
    }

    return System.Text.Encoding.Default.GetString(info);
}

private string OpenCLDeviceInfo(CLDeviceHandle device,
                        ComputeDeviceInfo param) {
    IntPtr req_bytes;
    IntPtr _null = new IntPtr();
    OPENCL_SUCCEED(CL10.GetDeviceInfo(device, param, _null, _null, out req_bytes));

    byte[] info = new byte[(int) req_bytes];
    unsafe
    {
        fixed (byte* ptr = &info[0])
        {
            OPENCL_SUCCEED(CL10.GetDeviceInfo(device, param, req_bytes, new IntPtr(ptr), out _null));
        }
    }
    return System.Text.Encoding.Default.GetString(info);

}

private void OpenCLAllDeviceOptions(out OpenCLDeviceOption[] devices_out,
                            out int num_devices_out)
{
    int num_devices = 0, num_devices_added = 0;

    CLPlatformHandle[] all_platforms;
    int[] platform_num_devices;

    int num_platforms;

    // Find the number of platforms.
    OPENCL_SUCCEED(CL10.GetPlatformIDs(0, null, out num_platforms));

    // Make room for them.
    all_platforms = new CLPlatformHandle[num_platforms];
    platform_num_devices = new int[num_platforms];

    int tmp;
    // Fetch all the platforms.
    OPENCL_SUCCEED(CL10.GetPlatformIDs(num_platforms, all_platforms, out tmp));

    // Count the number of devices for each platform, as well as the
    // total number of devices.
    for (int i = 0; i < num_platforms; i++)
    {
        if (CL10.GetDeviceIDs(all_platforms[i], ComputeDeviceTypes.All,
                              0, null, out platform_num_devices[i]) == ComputeErrorCode.Success)
        {
            num_devices += platform_num_devices[i];
        }
        else
        {
            platform_num_devices[i] = 0;
        }
    }

    // Make room for all the device options.
    OpenCLDeviceOption[] devices = new OpenCLDeviceOption[num_devices];

    // Loop through the platforms, getting information about their devices.
    for (int i = 0; i < num_platforms; i++) {
        CLPlatformHandle platform = all_platforms[i];
        int num_platform_devices = platform_num_devices[i];

        if (num_platform_devices == 0) {
            continue;
        }

        string platform_name = OpenCLPlatformInfo(platform, ComputePlatformInfo.Name);
        CLDeviceHandle[] platform_devices = new CLDeviceHandle[num_platform_devices];

        // Fetch all the devices.
        OPENCL_SUCCEED(CL10.GetDeviceIDs(platform, ComputeDeviceTypes.All,
                                         num_platform_devices, platform_devices, out tmp));

        IntPtr tmpptr;
        // Loop through the devices, adding them to the devices array.
        unsafe
        {
            for (int j = 0; j < num_platform_devices; j++) {
                string device_name = OpenCLDeviceInfo(platform_devices[j], ComputeDeviceInfo.Name);
                devices[num_devices_added].Platform = platform;
                devices[num_devices_added].Device = platform_devices[j];
                fixed (void* ptr = &devices[num_devices_added].DeviceType)
                {
                    OPENCL_SUCCEED(CL10.GetDeviceInfo(platform_devices[j],
                                                      ComputeDeviceInfo.Type,
                                                      new IntPtr(sizeof(ComputeDeviceTypes)),
                                                      new IntPtr(ptr),
                                                      out tmpptr));
                }
                // We don't want the structs to share memory, so copy the platform name.
                // Each device name is already unique.
                devices[num_devices_added].PlatformName = platform_name;
                devices[num_devices_added].DeviceName = device_name;
                num_devices_added++;
            }
        }
    }

    devices_out = devices;
    num_devices_out = num_devices;
}

private bool IsBlacklisted(string platform_name, string device_name)
{
    return (platform_name.Contains("Apple") &&
            device_name.Contains("Intel(R) Core(TM)"));
}

private OpenCLDeviceOption GetPreferredDevice(OpenCLConfig cfg) {
    OpenCLDeviceOption[] devices;
    int num_devices;

    OpenCLAllDeviceOptions(out devices, out num_devices);

    int num_device_matches = 0;

    for (int i = 0; i < num_devices; i++)
    {
        OpenCLDeviceOption device = devices[i];
        if (!IsBlacklisted(device.PlatformName, device.DeviceName) &&
            device.PlatformName.Contains(cfg.PreferredPlatform) &&
            device.DeviceName.Contains(cfg.PreferredDevice) &&
            num_device_matches++ == cfg.PreferredDeviceNum)
        {
            return device;
        }
    }

    panic(1, "Could not find acceptable OpenCL device.\n");
    // this is never reached
    throw new Exception();

}

private void DescribeDeviceOption(OpenCLDeviceOption device) {
    Console.Error.WriteLine("Using platform: {0}", device.PlatformName);
    Console.Error.WriteLine("Using device: {0}", device.DeviceName);
}

private ComputeProgramBuildStatus BuildOpenCLProgram(ref CLProgramHandle program, CLDeviceHandle device, string options) {
    ComputeErrorCode ret_val = CL10.BuildProgram(program, 1, new []{device}, options, null, IntPtr.Zero);

    // Avoid termination due to CL_BUILD_PROGRAM_FAILURE
    if (ret_val != ComputeErrorCode.Success && ret_val != ComputeErrorCode.BuildProgramFailure) {
        Debug.Assert((int) ret_val == 0);
    }

    ComputeProgramBuildStatus build_status;
    unsafe
    {
        IntPtr _null = new IntPtr();
        ret_val = CL10.GetProgramBuildInfo(program,
                                           device,
                                           ComputeProgramBuildInfo.Status,
                                           new IntPtr(sizeof(int)),
                                           new IntPtr(&build_status),
                                           out _null);
    }
    Debug.Assert(ret_val == 0);

    if (build_status != ComputeProgramBuildStatus.Success) {
        char[] build_log;
        IntPtr ret_val_size;
        unsafe
        {
        ret_val = CL10.GetProgramBuildInfo(program,
                                           device,
                                           ComputeProgramBuildInfo.BuildLog,
                                           IntPtr.Zero,
                                           IntPtr.Zero,
                                           out ret_val_size);
        }
        Debug.Assert(ret_val == 0);

        build_log = new char[((int)ret_val_size)+1];
        unsafe
        {
            IntPtr _null = new IntPtr();
            fixed (char* ptr = &build_log[0])
            {
                CL10.GetProgramBuildInfo(program,
                                         device,
                                         ComputeProgramBuildInfo.BuildLog,
                                         ret_val_size,
                                         new IntPtr(ptr),
                                         out _null);
            }
        }
        Debug.Assert(ret_val == 0);

        // The spec technically does not say whether the build log is zero-terminated, so let's be careful.
        build_log[(int)ret_val_size] = '\0';
        Console.Error.Write("Build log:\n{0}\n", new string(build_log));
    }

    return build_status;
}


// We take as input several strings representing the program, because
// C does not guarantee that the compiler supports particularly large
// literals.  Notably, Visual C has a limit of 2048 characters.  The
// array must be NULL-terminated.
private CLProgramHandle SetupOpenCL(ref FutharkContext ctx,
                            string[] srcs,
                            bool required_types) {

    ComputeErrorCode error;
    CLPlatformHandle platform;
    CLDeviceHandle device;
    int MaxGroupSize;

    ctx.OpenCL.LockstepWidth = 0;

    OpenCLDeviceOption device_option = GetPreferredDevice(ctx.OpenCL.Cfg);

    if (ctx.Debugging) {
        DescribeDeviceOption(device_option);
    }

    device = device = device_option.Device;
    platform = platform = device_option.Platform;

    if (required_types){
        int supported;
        unsafe
        {
            IntPtr throwaway0 = new IntPtr();
            OPENCL_SUCCEED(CL10.GetDeviceInfo(device,
                                              ComputeDeviceInfo.PreferredVectorWidthDouble,
                                              new IntPtr(sizeof(IntPtr)),
                                              new IntPtr(&supported),
                                              out throwaway0));
        }
        if (supported == 0) {
            panic(1,
                  "Program uses double-precision floats, but this is not supported on chosen device: {0}\n",
                  device_option.DeviceName);
        }
    }

    unsafe
    {
        IntPtr throwaway1 = new IntPtr();
        OPENCL_SUCCEED(CL10.GetDeviceInfo(device,
                                          ComputeDeviceInfo.MaxWorkGroupSize,
                                          new IntPtr(sizeof(IntPtr)),
                                          new IntPtr(&MaxGroupSize),
                                          out throwaway1));
    }

    int MaxTileSize = (int) Math.Sqrt(MaxGroupSize);

    if (MaxGroupSize < ctx.OpenCL.Cfg.DefaultGroupSize) {
        Console.Error.WriteLine("Note: Device limits default group size to {0} (down from {1}).\n",
                                MaxGroupSize, ctx.OpenCL.Cfg.DefaultGroupSize);
        ctx.OpenCL.Cfg.DefaultGroupSize = MaxGroupSize;
    }

    if (MaxTileSize < ctx.OpenCL.Cfg.DefaultTileSize) {
        Console.Error.WriteLine("Note: Device limits default tile size to {0} (down from {1}).\n",
                                MaxTileSize, ctx.OpenCL.Cfg.DefaultTileSize);
        ctx.OpenCL.Cfg.DefaultTileSize = MaxTileSize;
    }

    ctx.OpenCL.MaxGroupSize = MaxGroupSize;
    ctx.OpenCL.MaxTileSize = MaxTileSize; // No limit.
    ctx.OpenCL.MaxThreshold = ctx.OpenCL.MaxNumGroups; // No limit.

    // Now we go through all the sizes, clamp them to the valid range,
    // or set them to the default.
    for (int i = 0; i < ctx.OpenCL.Cfg.NumSizes; i++) {
        string size_class = ctx.OpenCL.Cfg.SizeClasses[i];
        int size_value = ctx.OpenCL.Cfg.SizeValues[i];
        string size_name = ctx.OpenCL.Cfg.SizeNames[i];
        int max_value, default_value;
        max_value = default_value = 0;
        if (size_class == "group_size") {
            max_value = MaxGroupSize;
            default_value = ctx.OpenCL.Cfg.DefaultGroupSize;
        } else if (size_class == "num_groups") {
            max_value = MaxGroupSize; // Futhark assumes this constraint.
            default_value = ctx.OpenCL.Cfg.DefaultNumGroups;
        } else if (size_class == "tile_size"){
            max_value = (int) Math.Sqrt(MaxGroupSize);
            default_value = ctx.OpenCL.Cfg.DefaultTileSize;
        } else if (size_class == "threshold") {
            max_value = 0; // No limit.
            default_value = ctx.OpenCL.Cfg.DefaultThreshold;
        } else {
            panic(1, "Unknown size class for size '{0}': {1}\n", size_name, size_class);
        }
        if (size_value == 0) {
            ctx.OpenCL.Cfg.SizeValues[i] = default_value;
        } else if (max_value > 0 && size_value > max_value) {
            Console.Error.WriteLine("Note: Device limits {0} to {1} (down from {2})",
                                    size_name, max_value, size_value);
            ctx.OpenCL.Cfg.SizeValues[i] = default_value;
        }
    }

    IntPtr[] properties = new []{
        new IntPtr((int) ComputeContextInfo.Platform),
        platform.Value,
        IntPtr.Zero
    };
    // Note that nVidia's OpenCL requires the platform property
    IntPtr _null;
    ctx.OpenCL.Context = CL10.CreateContext(properties, 1, new []{device}, null, ctx.NULL, out error);
    Debug.Assert(error == 0);

    ctx.OpenCL.Queue = CL10.CreateCommandQueue(ctx.OpenCL.Context, device, 0, out error);
    Debug.Assert(error == 0);

    // Make sure this function is defined.
    PostOpenCLSetup(ref ctx, ref device_option);

    if (ctx.Debugging) {
        Console.Error.WriteLine("Lockstep width: {0}\n", (int)ctx.OpenCL.LockstepWidth);
        Console.Error.WriteLine("Default group size: {0}\n", (int)ctx.OpenCL.Cfg.DefaultGroupSize);
        Console.Error.WriteLine("Default number of groups: {0}\n", (int)ctx.OpenCL.Cfg.DefaultNumGroups);
    }

    string fut_opencl_src;

    // Maybe we have to read OpenCL source from somewhere else (used for debugging).
    if (ctx.OpenCL.Cfg.LoadProgramFrom != null) {
        fut_opencl_src = File.ReadAllText(ctx.OpenCL.Cfg.LoadProgramFrom);
    } else {
        // Build the OpenCL program.  First we have to concatenate all the fragments.
        fut_opencl_src = string.Join("\n", srcs);
    }

    CLProgramHandle prog;
    error = 0;
    string[] src_ptr = new[]{fut_opencl_src};
    IntPtr[] src_size = new []{IntPtr.Zero};

    if (ctx.OpenCL.Cfg.DumpProgramTo != null) {
        File.WriteAllText(ctx.OpenCL.Cfg.DumpProgramTo, fut_opencl_src);
    }

    unsafe
    {
        prog = CL10.CreateProgramWithSource(ctx.OpenCL.Context, 1, src_ptr, src_size, out error);
    }
    Debug.Assert(error == 0);

    int compile_opts_size = 1024;

    string compile_opts = String.Format("-DFUT_BLOCK_DIM={0} -DLOCKSTEP_WIDTH={1} ",
                                        ctx.OpenCL.Cfg.TransposeBlockDim,
                                        ctx.OpenCL.LockstepWidth);

    for (int i = 0; i < ctx.OpenCL.Cfg.NumSizes; i++) {
        compile_opts += String.Format("-D{0}={1} ",
                                      ctx.OpenCL.Cfg.SizeNames[i],
                                      ctx.OpenCL.Cfg.SizeValues[i]);
    }

    OPENCL_SUCCEED(BuildOpenCLProgram(ref prog, device, compile_opts));

    return prog;
}

private CLMemoryHandle EmptyMemHandle(CLContextHandle context)
{
    ComputeErrorCode tmp;
    var cl_mem = CL10.CreateBuffer(context, ComputeMemoryFlags.ReadWrite,
                                   IntPtr.Zero, IntPtr.Zero,
                                   out tmp);
    return cl_mem;

}

private void FutharkConfigPrintSizes()
{
    int n = FutharkGetNumSizes();
    for (int i = 0; i < n; i++)
    {
        if (FutharkGetSizeEntry(i) ==  EntryPoint)
        {
            Console.WriteLine("{0} ({1})", FutharkGetSizeName(i),
                              FutharkGetSizeClass(i));
        }
    }
    Environment.Exit(0);
}

private void FutharkConfigSetSize(ref FutharkContextConfig config, string optarg)
{
    var name_and_value = optarg.Split('=');
    if (name_and_value.Length != 2)
    {
        panic(1, "Invalid argument for size option: {0}", optarg);
    }

    var name = name_and_value[0];
    var value = Convert.ToInt32(name_and_value[1]);
    if (!FutharkContextConfigSetSize(ref config, name, value))
    {
        panic(1, "Unknown size: {0}", name);
    }
}