{-# LANGUAGE QuasiQuotes #-} {-# LANGUAGE TemplateHaskell #-} module Futhark.CodeGen.Backends.CCUDA.Boilerplate ( generateBoilerplate ) where import qualified Language.C.Quote.OpenCL as C import qualified Futhark.CodeGen.Backends.GenericC as GC import Futhark.Representation.ExplicitMemory hiding (GetSize, CmpSizeLe, GetSizeMax) import Futhark.CodeGen.ImpCode.OpenCL import Futhark.Util (chunk, zEncodeString) import qualified Data.Map as M import Data.FileEmbed (embedStringFile) generateBoilerplate :: String -> String -> [String] -> M.Map Name SizeClass -> GC.CompilerM OpenCL () () generateBoilerplate cuda_program cuda_prelude kernel_names sizes = do GC.earlyDecls [C.cunit| $esc:("#include ") $esc:("#include ") $esc:("typedef CUdeviceptr fl_mem_t;") $esc:free_list_h $esc:cuda_h const char *cuda_program[] = {$inits:fragments, NULL}; |] generateSizeFuns sizes cfg <- generateConfigFuns sizes generateContextFuns cfg kernel_names sizes where cuda_h = $(embedStringFile "rts/c/cuda.h") free_list_h = $(embedStringFile "rts/c/free_list.h") fragments = map (\s -> [C.cinit|$string:s|]) $ chunk 2000 (cuda_prelude ++ cuda_program) generateSizeFuns :: M.Map Name SizeClass -> GC.CompilerM OpenCL () () generateSizeFuns sizes = do let size_name_inits = map (\k -> [C.cinit|$string:(pretty k)|]) $ M.keys sizes size_var_inits = map (\k -> [C.cinit|$string:(zEncodeString (pretty k))|]) $ M.keys sizes size_class_inits = map (\c -> [C.cinit|$string:(pretty c)|]) $ M.elems sizes num_sizes = M.size sizes GC.libDecl [C.cedecl|static const char *size_names[] = { $inits:size_name_inits };|] GC.libDecl [C.cedecl|static const char *size_vars[] = { $inits:size_var_inits };|] GC.libDecl [C.cedecl|static const char *size_classes[] = { $inits:size_class_inits };|] GC.publicDef_ "get_num_sizes" GC.InitDecl $ \s -> ([C.cedecl|int $id:s(void);|], [C.cedecl|int $id:s(void) { return $int:num_sizes; }|]) GC.publicDef_ "get_size_name" GC.InitDecl $ \s -> ([C.cedecl|const char* $id:s(int);|], [C.cedecl|const char* $id:s(int i) { return size_names[i]; }|]) GC.publicDef_ "get_size_class" GC.InitDecl $ \s -> ([C.cedecl|const char* $id:s(int);|], [C.cedecl|const char* $id:s(int i) { return size_classes[i]; }|]) generateConfigFuns :: M.Map Name SizeClass -> GC.CompilerM OpenCL () String generateConfigFuns sizes = do let size_decls = map (\k -> [C.csdecl|size_t $id:k;|]) $ M.keys sizes num_sizes = M.size sizes GC.libDecl [C.cedecl|struct sizes { $sdecls:size_decls };|] cfg <- GC.publicDef "context_config" GC.InitDecl $ \s -> ([C.cedecl|struct $id:s;|], [C.cedecl|struct $id:s { struct cuda_config cu_cfg; size_t sizes[$int:num_sizes]; int num_nvrtc_opts; const char **nvrtc_opts; };|]) let size_value_inits = map (\i -> [C.cstm|cfg->sizes[$int:i] = 0;|]) [0..M.size sizes-1] GC.publicDef_ "context_config_new" GC.InitDecl $ \s -> ([C.cedecl|struct $id:cfg* $id:s(void);|], [C.cedecl|struct $id:cfg* $id:s(void) { struct $id:cfg *cfg = malloc(sizeof(struct $id:cfg)); if (cfg == NULL) { return NULL; } cfg->num_nvrtc_opts = 0; cfg->nvrtc_opts = malloc(sizeof(const char*)); cfg->nvrtc_opts[0] = NULL; $stms:size_value_inits cuda_config_init(&cfg->cu_cfg, $int:num_sizes, size_names, size_vars, cfg->sizes, size_classes); return cfg; }|]) GC.publicDef_ "context_config_free" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg);|], [C.cedecl|void $id:s(struct $id:cfg* cfg) { free(cfg->nvrtc_opts); free(cfg); }|]) GC.publicDef_ "context_config_add_nvrtc_option" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *opt);|], [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *opt) { cfg->nvrtc_opts[cfg->num_nvrtc_opts] = opt; cfg->num_nvrtc_opts++; cfg->nvrtc_opts = realloc(cfg->nvrtc_opts, (cfg->num_nvrtc_opts+1) * sizeof(const char*)); cfg->nvrtc_opts[cfg->num_nvrtc_opts] = NULL; }|]) GC.publicDef_ "context_config_set_debugging" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|], [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) { cfg->cu_cfg.logging = cfg->cu_cfg.debugging = flag; }|]) GC.publicDef_ "context_config_set_logging" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg, int flag);|], [C.cedecl|void $id:s(struct $id:cfg* cfg, int flag) { cfg->cu_cfg.logging = flag; }|]) GC.publicDef_ "context_config_set_device" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *s);|], [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *s) { set_preferred_device(&cfg->cu_cfg, s); }|]) GC.publicDef_ "context_config_dump_program_to" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|], [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) { cfg->cu_cfg.dump_program_to = path; }|]) GC.publicDef_ "context_config_load_program_from" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|], [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) { cfg->cu_cfg.load_program_from = path; }|]) GC.publicDef_ "context_config_dump_ptx_to" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|], [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) { cfg->cu_cfg.dump_ptx_to = path; }|]) GC.publicDef_ "context_config_load_ptx_from" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path);|], [C.cedecl|void $id:s(struct $id:cfg* cfg, const char *path) { cfg->cu_cfg.load_ptx_from = path; }|]) GC.publicDef_ "context_config_set_default_block_size" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg, int size);|], [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) { cfg->cu_cfg.default_block_size = size; cfg->cu_cfg.default_block_size_changed = 1; }|]) GC.publicDef_ "context_config_set_default_grid_size" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|], [C.cedecl|void $id:s(struct $id:cfg* cfg, int num) { cfg->cu_cfg.default_grid_size = num; }|]) GC.publicDef_ "context_config_set_default_tile_size" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|], [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) { cfg->cu_cfg.default_tile_size = size; cfg->cu_cfg.default_tile_size_changed = 1; }|]) GC.publicDef_ "context_config_set_default_threshold" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:cfg* cfg, int num);|], [C.cedecl|void $id:s(struct $id:cfg* cfg, int size) { cfg->cu_cfg.default_threshold = size; }|]) GC.publicDef_ "context_config_set_size" GC.InitDecl $ \s -> ([C.cedecl|int $id:s(struct $id:cfg* cfg, const char *size_name, size_t size_value);|], [C.cedecl|int $id:s(struct $id:cfg* cfg, const char *size_name, size_t size_value) { for (int i = 0; i < $int:num_sizes; i++) { if (strcmp(size_name, size_names[i]) == 0) { cfg->sizes[i] = size_value; return 0; } } return 1; }|]) return cfg generateContextFuns :: String -> [String] -> M.Map Name SizeClass -> GC.CompilerM OpenCL () () generateContextFuns cfg kernel_names sizes = do final_inits <- GC.contextFinalInits (fields, init_fields) <- GC.contextContents let kernel_fields = map (\k -> [C.csdecl|typename CUfunction $id:k;|]) kernel_names ctx <- GC.publicDef "context" GC.InitDecl $ \s -> ([C.cedecl|struct $id:s;|], [C.cedecl|struct $id:s { int detail_memory; int debugging; typename lock_t lock; char *error; $sdecls:fields $sdecls:kernel_fields struct cuda_context cuda; struct sizes sizes; };|]) let set_sizes = zipWith (\i k -> [C.cstm|ctx->sizes.$id:k = cfg->sizes[$int:i];|]) [(0::Int)..] $ M.keys sizes GC.publicDef_ "context_new" GC.InitDecl $ \s -> ([C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg);|], [C.cedecl|struct $id:ctx* $id:s(struct $id:cfg* cfg) { struct $id:ctx* ctx = malloc(sizeof(struct $id:ctx)); if (ctx == NULL) { return NULL; } ctx->debugging = ctx->detail_memory = cfg->cu_cfg.debugging; ctx->cuda.cfg = cfg->cu_cfg; create_lock(&ctx->lock); $stms:init_fields cuda_setup(&ctx->cuda, cuda_program, cfg->nvrtc_opts); $stms:(map (loadKernelByName) kernel_names) $stms:final_inits $stms:set_sizes return ctx; }|]) GC.publicDef_ "context_free" GC.InitDecl $ \s -> ([C.cedecl|void $id:s(struct $id:ctx* ctx);|], [C.cedecl|void $id:s(struct $id:ctx* ctx) { cuda_cleanup(&ctx->cuda); free_lock(&ctx->lock); free(ctx); }|]) GC.publicDef_ "context_sync" GC.InitDecl $ \s -> ([C.cedecl|int $id:s(struct $id:ctx* ctx);|], [C.cedecl|int $id:s(struct $id:ctx* ctx) { CUDA_SUCCEED(cuCtxSynchronize()); return 0; }|]) GC.publicDef_ "context_get_error" GC.InitDecl $ \s -> ([C.cedecl|char* $id:s(struct $id:ctx* ctx);|], [C.cedecl|char* $id:s(struct $id:ctx* ctx) { return ctx->error; }|]) where loadKernelByName name = [C.cstm|CUDA_SUCCEED(cuModuleGetFunction(&ctx->$id:name, ctx->cuda.module, $string:name));|]