packages feed

futhark 0.18.1 → 0.18.2

raw patch · 58 files changed

+1853/−1578 lines, 58 filesdep ~versionsPVP: major bump suggested

API removals or changes: PVP suggests a major version bump

Dependency ranges changed: versions

API changes (from Hackage documentation)

- Futhark.CodeGen.ImpGen: toInt32Exp :: ToExp a => a -> TExp Int32
- Futhark.Pkg.Types: semver :: Text -> Either ParsingError SemVer
- Language.Futhark.TypeChecker.Monad: instance Control.Monad.State.Class.MonadState Futhark.FreshNames.VNameSource Language.Futhark.TypeChecker.Monad.TypeM
- Language.Futhark.TypeChecker.Monad: instance Control.Monad.Writer.Class.MonadWriter Language.Futhark.Warnings.Warnings Language.Futhark.TypeChecker.Monad.TypeM
- Language.Futhark.TypeChecker.Terms: instance GHC.Classes.Eq Language.Futhark.TypeChecker.Terms.ConstrPat
- Language.Futhark.TypeChecker.Terms: instance GHC.Classes.Ord Language.Futhark.TypeChecker.Terms.ConstrPat
- Language.Futhark.Warnings: instance GHC.Classes.Eq Language.Futhark.Warnings.Warnings
- Language.Futhark.Warnings: instance GHC.Show.Show Language.Futhark.Warnings.Warnings
+ Futhark.Util: nubOrd :: Ord a => [a] -> [a]
+ Language.Futhark.Pretty: instance Text.PrettyPrint.Mainland.Class.Pretty Language.Futhark.Syntax.PatLit
+ Language.Futhark.Syntax: PatLitFloat :: Double -> PatLit
+ Language.Futhark.Syntax: PatLitInt :: Integer -> PatLit
+ Language.Futhark.Syntax: PatLitPrim :: PrimValue -> PatLit
+ Language.Futhark.Syntax: data PatLit
+ Language.Futhark.Syntax: instance GHC.Classes.Eq Language.Futhark.Syntax.PatLit
+ Language.Futhark.Syntax: instance GHC.Classes.Ord Language.Futhark.Syntax.PatLit
+ Language.Futhark.Syntax: instance GHC.Show.Show Language.Futhark.Syntax.PatLit
+ Language.Futhark.TypeChecker.Match: data Match
+ Language.Futhark.TypeChecker.Match: instance GHC.Classes.Eq Language.Futhark.TypeChecker.Match.Constr
+ Language.Futhark.TypeChecker.Match: instance GHC.Classes.Eq Language.Futhark.TypeChecker.Match.Match
+ Language.Futhark.TypeChecker.Match: instance GHC.Classes.Ord Language.Futhark.TypeChecker.Match.Constr
+ Language.Futhark.TypeChecker.Match: instance GHC.Classes.Ord Language.Futhark.TypeChecker.Match.Match
+ Language.Futhark.TypeChecker.Match: instance GHC.Show.Show Language.Futhark.TypeChecker.Match.Constr
+ Language.Futhark.TypeChecker.Match: instance GHC.Show.Show Language.Futhark.TypeChecker.Match.Match
+ Language.Futhark.TypeChecker.Match: instance Text.PrettyPrint.Mainland.Class.Pretty Language.Futhark.TypeChecker.Match.Match
+ Language.Futhark.TypeChecker.Match: unmatched :: [Pattern] -> [Match]
+ Language.Futhark.TypeChecker.Monad: instance Control.Monad.State.Class.MonadState Language.Futhark.TypeChecker.Monad.TypeState Language.Futhark.TypeChecker.Monad.TypeM
+ Language.Futhark.Warnings: anyWarnings :: Warnings -> Bool
+ Language.Futhark.Warnings: instance Text.PrettyPrint.Mainland.Class.Pretty Language.Futhark.Warnings.Warnings
- Futhark.Pkg.Types: SemVer :: Word -> Word -> Word -> [VChunk] -> [VChunk] -> SemVer
+ Futhark.Pkg.Types: SemVer :: !Word -> !Word -> !Word -> ![VChunk] -> ![VChunk] -> SemVer
- Futhark.Pkg.Types: [_svMajor] :: SemVer -> Word
+ Futhark.Pkg.Types: [_svMajor] :: SemVer -> !Word
- Futhark.Pkg.Types: [_svMeta] :: SemVer -> [VChunk]
+ Futhark.Pkg.Types: [_svMeta] :: SemVer -> ![VChunk]
- Futhark.Pkg.Types: [_svMinor] :: SemVer -> Word
+ Futhark.Pkg.Types: [_svMinor] :: SemVer -> !Word
- Futhark.Pkg.Types: [_svPatch] :: SemVer -> Word
+ Futhark.Pkg.Types: [_svPatch] :: SemVer -> !Word
- Futhark.Pkg.Types: [_svPreRel] :: SemVer -> [VChunk]
+ Futhark.Pkg.Types: [_svPreRel] :: SemVer -> ![VChunk]
- Futhark.Pkg.Types: errorBundlePretty :: (Stream s, ShowErrorComponent e) => ParseErrorBundle s e -> String
+ Futhark.Pkg.Types: errorBundlePretty :: (VisualStream s, TraversableStream s, ShowErrorComponent e) => ParseErrorBundle s e -> String
- Language.Futhark.Syntax: PatternLit :: ExpBase f vn -> f PatternType -> SrcLoc -> PatternBase f vn
+ Language.Futhark.Syntax: PatternLit :: PatLit -> f PatternType -> SrcLoc -> PatternBase f vn
- Language.Futhark.TypeChecker: checkDec :: Imports -> VNameSource -> Env -> ImportName -> UncheckedDec -> Either TypeError (Env, Dec, VNameSource)
+ Language.Futhark.TypeChecker: checkDec :: Imports -> VNameSource -> Env -> ImportName -> UncheckedDec -> (Warnings, Either TypeError (Env, Dec, VNameSource))
- Language.Futhark.TypeChecker: checkExp :: Imports -> VNameSource -> Env -> UncheckedExp -> Either TypeError ([TypeParam], Exp)
+ Language.Futhark.TypeChecker: checkExp :: Imports -> VNameSource -> Env -> UncheckedExp -> (Warnings, Either TypeError ([TypeParam], Exp))
- Language.Futhark.TypeChecker: checkModExp :: Imports -> VNameSource -> Env -> ModExpBase NoInfo Name -> Either TypeError (MTy, ModExpBase Info VName)
+ Language.Futhark.TypeChecker: checkModExp :: Imports -> VNameSource -> Env -> ModExpBase NoInfo Name -> (Warnings, Either TypeError (MTy, ModExpBase Info VName))
- Language.Futhark.TypeChecker: checkProg :: Imports -> VNameSource -> ImportName -> UncheckedProg -> Either TypeError (FileModule, Warnings, VNameSource)
+ Language.Futhark.TypeChecker: checkProg :: Imports -> VNameSource -> ImportName -> UncheckedProg -> (Warnings, Either TypeError (FileModule, VNameSource))
- Language.Futhark.TypeChecker.Monad: runTypeM :: Env -> ImportTable -> ImportName -> VNameSource -> TypeM a -> Either TypeError (a, Warnings, VNameSource)
+ Language.Futhark.TypeChecker.Monad: runTypeM :: Env -> ImportTable -> ImportName -> VNameSource -> TypeM a -> (Warnings, Either TypeError (a, VNameSource))
- Language.Futhark.TypeChecker.Monad: warn :: (MonadTypeChecker m, Located loc) => loc -> String -> m ()
+ Language.Futhark.TypeChecker.Monad: warn :: (MonadTypeChecker m, Located loc) => loc -> Doc -> m ()
- Language.Futhark.Warnings: singleWarning :: SrcLoc -> String -> Warnings
+ Language.Futhark.Warnings: singleWarning :: SrcLoc -> Doc -> Warnings
- Language.Futhark.Warnings: singleWarning' :: SrcLoc -> [SrcLoc] -> String -> Warnings
+ Language.Futhark.Warnings: singleWarning' :: SrcLoc -> [SrcLoc] -> Doc -> Warnings

Files

docs/c-api.rst view
@@ -344,6 +344,19 @@    During :c:func:`futhark_context_new`, read PTX code from the given    file instead of using the embedded program. +Multicore+---------++The following API functions are available when using the ``multicore``+backend.++.. c:function:: void context_config_set_num_threads(struct futhark_context_config *cfg, int n)++   The number of threads used to run parallel operations.  If set to a+   value less than ``1``, then the runtime system will use one thread+   per detected core.++ General guarantees ------------------ 
docs/language-reference.rst view
@@ -991,6 +991,12 @@ to a single type.  Further, unique types (see `In-place updates`_) must be explicitly annotated. +Type inference processes top-level declared in top-down order, and the+type of a top-level function must be completely inferred at its+definition site.  Specifically, if a top-level function uses+overloaded arithmetic operators, the resolution of those overloads+cannot be influenced by later uses of the function.+ .. _size-types:  Size Types
docs/man/futhark-dataset.rst view
@@ -27,6 +27,8 @@ format.  The input format (whether textual or binary) is automatically detected. +Returns a nonzero exit code if it fails to write the full output.+ OPTIONS ======= 
docs/usage.rst view
@@ -264,6 +264,18 @@ OpenCL backend uses.  These then refer to grid size and thread block size, respectively. +Multicore options+~~~~~~~~~~~~~~~~~++The following options are supported by executables generated by the+``multicore`` backend:++  ``--num-threads=INT``++    The number of threads used to run parallel operations.  If set to+    a value less than ``1``, then the runtime system will use one+    thread per detected core.+ Compiling to Library -------------------- 
futhark.cabal view
@@ -1,7 +1,7 @@ cabal-version: 2.4  name:           futhark-version:        0.18.1+version:        0.18.2 synopsis:       An optimising compiler for a functional, array-oriented language.  description:    Futhark is a small programming language designed to be compiled to@@ -255,6 +255,7 @@       Language.Futhark.Syntax       Language.Futhark.Traversals       Language.Futhark.TypeChecker+      Language.Futhark.TypeChecker.Match       Language.Futhark.TypeChecker.Modules       Language.Futhark.TypeChecker.Monad       Language.Futhark.TypeChecker.Terms@@ -313,7 +314,7 @@     , utf8-string >=1     , vector >=0.12     , vector-binary-instances >=0.2.2.0-    , versions >=3.3.1+    , versions >=4.0.1     , zip-archive >=0.3.1.1     , zlib >=0.6.1.2   default-language: Haskell2010
rts/c/chaselev.h view
@@ -1,8 +1,5 @@ // Start of chaselev.h -#ifndef _CHASELEV_H_-#define _CHASELEV_H_- /* Implementation of Chase-lev's concurrent lock-free deque    from ``Dynamic Circular Work-Stealing Deque`` (2005)    This implementation was ported from@@ -175,6 +172,5 @@   return nb_subtasks(q) < 1; } -#endif #endif // end of chaselev.h
− rts/c/multicore_defs.h
@@ -1,108 +0,0 @@-// start of multicore_defs.h--#ifndef MULTICORE_DEFS-#define MULTICORE_DEFS--#include <signal.h>--/* #define MCPROFILE */--// Which queue implementation to use-#define MCJOBQUEUE-// NOTE! MCCHASELEV has been removed from multicore branch-// Switch to multicore_deque branch to use chase-lev deque-/* #define MCCHASELEV */---#if defined(_WIN32)-#include <windows.h>-#elif defined(__APPLE__)-#include <sys/sysctl.h>-// For getting cpu usage of threads-#include <mach/mach.h>-#include <sys/resource.h>-#elif defined(__linux__)-#include <sys/sysinfo.h>-#include <sys/resource.h>-#include <signal.h>-#endif---// Forward declarations-// Scheduler definitions-struct scheduler;-struct scheduler_info;-struct scheduler_subtask;-struct scheduler_task;---struct subtask_queue {-  int capacity;             // Size of the buffer.-  int first;                // Index of the start of the ring buffer.-  int num_used;             // Number of used elements in the buffer.-  struct subtask **buffer;--  pthread_mutex_t mutex;    // Mutex used for synchronisation.-  pthread_cond_t cond;      // Condition variable used for synchronisation.-  int dead;--#if defined(MCPROFILE)-  /* Profiling fields */-  uint64_t time_enqueue;-  uint64_t time_dequeue;-  uint64_t n_dequeues;-  uint64_t n_enqueues;-#endif-};----// Function definitions-typedef int (*segop_fn)(void* args, int64_t iterations, int tid, struct scheduler_info info);-typedef int (*parloop_fn)(void* args, int64_t start, int64_t end, int subtask_id, int tid);---/* A subtask that can be executed by a worker */-struct subtask {-  /* The parloop function */-  parloop_fn fn;-  /* Execution parameters */-  void* args;-  int64_t start, end;-  int id;--  /* Dynamic scheduling parameters */-  int chunkable;-  int64_t chunk_size;--  /* Shared variables across subtasks */-  volatile int *counter; // Counter for ongoing subtasks-  // Shared task timers and iterators-  int64_t *task_time;-  int64_t *task_iter;--  /* For debugging */-  const char *name;-};---struct worker {-  pthread_t thread;-  struct scheduler *scheduler;  /* Reference to the scheduler struct the worker belongs to*/-  struct subtask_queue q;-  int dead;-  int tid;                      /* Just a thread id */--  /* "thread local" time fields used for online algorithm */-  uint64_t timer;-  uint64_t total;-  int nested; /* How nested the current computation is */--  // Profiling fields-  int output_usage;            /* Whether to dump thread usage */-  uint64_t time_spent_working; /* Time spent in parloop functions */-};--#endif--// end of multicore_defs.h
− rts/c/multicore_util.h
@@ -1,103 +0,0 @@-// start of multicore_util.h--/* Multicore Utility functions */--#ifndef _MULTICORE_UTIL_H_-#define _MULTICORE_UTIL_H_--/* A wrapper for getting rusage on Linux and MacOS */-/* TODO maybe figure out this for windows */-static inline int getrusage_thread(struct rusage *rusage)-{-  int err = -1;-#if  defined(__APPLE__)-    thread_basic_info_data_t info = { 0 };-    mach_msg_type_number_t info_count = THREAD_BASIC_INFO_COUNT;-    kern_return_t kern_err;--    kern_err = thread_info(mach_thread_self(),-                           THREAD_BASIC_INFO,-                           (thread_info_t)&info,-                           &info_count);-    if (kern_err == KERN_SUCCESS) {-        memset(rusage, 0, sizeof(struct rusage));-        rusage->ru_utime.tv_sec = info.user_time.seconds;-        rusage->ru_utime.tv_usec = info.user_time.microseconds;-        rusage->ru_stime.tv_sec = info.system_time.seconds;-        rusage->ru_stime.tv_usec = info.system_time.microseconds;-        err = 0;-    } else {-        errno = EINVAL;-    }-#elif defined(__linux__)-    err = getrusage(RUSAGE_THREAD, rusage);-#endif-    return err;-}--/* returns the number of logical cores */-static int num_processors()-{-#if  defined(_WIN32)-/* https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info */-    SYSTEM_INFO sysinfo;-    GetSystemInfo(&sysinfo);-    int ncores = sysinfo.dwNumberOfProcessors;-    fprintf(stderr, "Found %d cores on your Windows machine\n Is that correct?\n", ncores);-    return ncores;-#elif defined(__APPLE__)-    int ncores;-    size_t ncores_size = sizeof(ncores);-    CHECK_ERRNO(sysctlbyname("hw.logicalcpu", &ncores, &ncores_size, NULL, 0),-                "sysctlbyname (hw.logicalcpu)");-    return ncores;-#elif defined(__linux__)-  return get_nprocs();-#else-  fprintf(stderr, "operating system not recognised\n");-  return -1;-#endif-}--static inline void output_thread_usage(struct worker *worker)-{-  struct rusage usage;-  CHECK_ERRNO(getrusage_thread(&usage), "getrusage_thread");-  struct timeval user_cpu_time = usage.ru_utime;-  struct timeval sys_cpu_time = usage.ru_stime;-  fprintf(stderr, "tid: %2d - work time %10llu us - user time: %10llu us - sys: %10llu us\n",-          worker->tid,-          (long long unsigned)worker->time_spent_working / 1000,-          (long long unsigned)(user_cpu_time.tv_sec * 1000000 + user_cpu_time.tv_usec),-          (long long unsigned)(sys_cpu_time.tv_sec * 1000000 + sys_cpu_time.tv_usec));-}---static unsigned int g_seed;--// Used to seed the generator.-static inline void fast_srand(unsigned int seed) {-    g_seed = seed;-}--// Compute a pseudorandom integer.-// Output value in range [0, 32767]-static inline unsigned int fast_rand(void) {-    g_seed = (214013*g_seed+2531011);-    return (g_seed>>16)&0x7FFF;-}---int64_t min_int64(int64_t x, int64_t y)-{-  return x < y ? x : y;-}--int64_t max_int64(int64_t x, int64_t y)-{-  return x > y ? x : y;-}---#endif-// end of multicore_util.h
rts/c/opencl.h view
@@ -342,7 +342,7 @@ }  // Returns 0 on success.-static int list_devices(struct opencl_config *cfg) {+static int list_devices(void) {   struct opencl_device_option *devices;   size_t num_devices; 
rts/c/scheduler.h view
@@ -1,8 +1,675 @@ // start of scheduler.h-#ifndef _SCHEDULER_H_-#define _SCHEDULER_H_ +// First, the API that the generated code will access.  In principle,+// we could then compile the scheduler separately and link an object+// file with the generated code.  In practice, we will embed all of+// this in the generated code. +// Scheduler handle.+struct scheduler;++// Initialise a scheduler (and start worker threads).+static int scheduler_init(struct scheduler *scheduler,+                          int num_workers,+                          double kappa);++// Shut down a scheduler (and destroy worker threads).+static int scheduler_destroy(struct scheduler *scheduler);++// Figure out the smallest amount of work that amortises task+// creation.+static int determine_kappa(double *kappa);++// How a segop should be scheduled.+enum scheduling {+  DYNAMIC,+  STATIC+};++// How a given task should be executed.  Filled out by the scheduler+// and passed to the segop function+struct scheduler_info {+  int64_t iter_pr_subtask;+  int64_t remainder;+  int nsubtasks;+  enum scheduling sched;+  int wake_up_threads;++  int64_t *task_time;+  int64_t *task_iter;+};++// A segop function.  This is what you hand the scheduler for+// execution.+typedef int (*segop_fn)(void* args,+                        int64_t iterations,+                        int tid,+                        struct scheduler_info info);++// A task for the scheduler to execute.+struct scheduler_segop {+  void *args;+  segop_fn top_level_fn;+  segop_fn nested_fn;+  int64_t iterations;+  enum scheduling sched;++  // Pointers to timer and iter associated with the task+  int64_t *task_time;+  int64_t *task_iter;++  // For debugging+  const char* name;+};++static inline int scheduler_prepare_task(struct scheduler *scheduler,+                                         struct scheduler_segop *task);++typedef int (*parloop_fn)(void* args,+                          int64_t start,+                          int64_t end,+                          int subtask_id,+                          int tid);++// A parallel parloop task.+struct scheduler_parloop {+  void* args;+  parloop_fn fn;+  int64_t iterations;+  struct scheduler_info info;++  // For debugging+  const char* name;+};++static inline int scheduler_execute_task(struct scheduler *scheduler,+                                         struct scheduler_parloop *task);++// Then the API implementation.++#include <signal.h>++#if defined(_WIN32)+#include <windows.h>+#elif defined(__APPLE__)+#include <sys/sysctl.h>+// For getting cpu usage of threads+#include <mach/mach.h>+#include <sys/resource.h>+#elif defined(__linux__)+#include <sys/sysinfo.h>+#include <sys/resource.h>+#include <signal.h>+#endif++/* Multicore Utility functions */++/* A wrapper for getting rusage on Linux and MacOS */+/* TODO maybe figure out this for windows */+static inline int getrusage_thread(struct rusage *rusage)+{+  int err = -1;+#if  defined(__APPLE__)+    thread_basic_info_data_t info = { 0 };+    mach_msg_type_number_t info_count = THREAD_BASIC_INFO_COUNT;+    kern_return_t kern_err;++    kern_err = thread_info(mach_thread_self(),+                           THREAD_BASIC_INFO,+                           (thread_info_t)&info,+                           &info_count);+    if (kern_err == KERN_SUCCESS) {+        memset(rusage, 0, sizeof(struct rusage));+        rusage->ru_utime.tv_sec = info.user_time.seconds;+        rusage->ru_utime.tv_usec = info.user_time.microseconds;+        rusage->ru_stime.tv_sec = info.system_time.seconds;+        rusage->ru_stime.tv_usec = info.system_time.microseconds;+        err = 0;+    } else {+        errno = EINVAL;+    }+#elif defined(__linux__)+    err = getrusage(RUSAGE_THREAD, rusage);+#endif+    return err;+}++/* returns the number of logical cores */+static int num_processors()+{+#if  defined(_WIN32)+/* https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info */+    SYSTEM_INFO sysinfo;+    GetSystemInfo(&sysinfo);+    int ncores = sysinfo.dwNumberOfProcessors;+    fprintf(stderr, "Found %d cores on your Windows machine\n Is that correct?\n", ncores);+    return ncores;+#elif defined(__APPLE__)+    int ncores;+    size_t ncores_size = sizeof(ncores);+    CHECK_ERRNO(sysctlbyname("hw.logicalcpu", &ncores, &ncores_size, NULL, 0),+                "sysctlbyname (hw.logicalcpu)");+    return ncores;+#elif defined(__linux__)+  return get_nprocs();+#else+  fprintf(stderr, "operating system not recognised\n");+  return -1;+#endif+}++static unsigned int g_seed;++// Used to seed the generator.+static inline void fast_srand(unsigned int seed) {+    g_seed = seed;+}++// Compute a pseudorandom integer.+// Output value in range [0, 32767]+static inline unsigned int fast_rand(void) {+    g_seed = (214013*g_seed+2531011);+    return (g_seed>>16)&0x7FFF;+}++struct subtask_queue {+  int capacity;             // Size of the buffer.+  int first;                // Index of the start of the ring buffer.+  int num_used;             // Number of used elements in the buffer.+  struct subtask **buffer;++  pthread_mutex_t mutex;    // Mutex used for synchronisation.+  pthread_cond_t cond;      // Condition variable used for synchronisation.+  int dead;++#if defined(MCPROFILE)+  /* Profiling fields */+  uint64_t time_enqueue;+  uint64_t time_dequeue;+  uint64_t n_dequeues;+  uint64_t n_enqueues;+#endif+};++/* A subtask that can be executed by a worker */+struct subtask {+  /* The parloop function */+  parloop_fn fn;+  /* Execution parameters */+  void* args;+  int64_t start, end;+  int id;++  /* Dynamic scheduling parameters */+  int chunkable;+  int64_t chunk_size;++  /* Shared variables across subtasks */+  volatile int *counter; // Counter for ongoing subtasks+  // Shared task timers and iterators+  int64_t *task_time;+  int64_t *task_iter;++  /* For debugging */+  const char *name;+};+++struct worker {+  pthread_t thread;+  struct scheduler *scheduler;  /* Reference to the scheduler struct the worker belongs to*/+  struct subtask_queue q;+  int dead;+  int tid;                      /* Just a thread id */++  /* "thread local" time fields used for online algorithm */+  uint64_t timer;+  uint64_t total;+  int nested; /* How nested the current computation is */++  // Profiling fields+  int output_usage;            /* Whether to dump thread usage */+  uint64_t time_spent_working; /* Time spent in parloop functions */+};++static inline void output_worker_usage(struct worker *worker)+{+  struct rusage usage;+  CHECK_ERRNO(getrusage_thread(&usage), "getrusage_thread");+  struct timeval user_cpu_time = usage.ru_utime;+  struct timeval sys_cpu_time = usage.ru_stime;+  fprintf(stderr, "tid: %2d - work time %10llu us - user time: %10llu us - sys: %10llu us\n",+          worker->tid,+          (long long unsigned)worker->time_spent_working / 1000,+          (long long unsigned)(user_cpu_time.tv_sec * 1000000 + user_cpu_time.tv_usec),+          (long long unsigned)(sys_cpu_time.tv_sec * 1000000 + sys_cpu_time.tv_usec));+}++/* Doubles the size of the queue */+static inline int subtask_queue_grow_queue(struct subtask_queue *subtask_queue) {++  int new_capacity = 2 * subtask_queue->capacity;+#ifdef MCDEBUG+  fprintf(stderr, "Growing queue to %d\n", subtask_queue->capacity * 2);+#endif++  struct subtask **new_buffer = calloc(new_capacity, sizeof(struct subtask*));+  for (int i = 0; i < subtask_queue->num_used; i++) {+    new_buffer[i] = subtask_queue->buffer[(subtask_queue->first + i) % subtask_queue->capacity];+  }++  free(subtask_queue->buffer);+  subtask_queue->buffer = new_buffer;+  subtask_queue->capacity = new_capacity;+  subtask_queue->first = 0;++  return 0;+}++// Initialise a job queue with the given capacity.  The queue starts out+// empty.  Returns non-zero on error.+static inline int subtask_queue_init(struct subtask_queue *subtask_queue, int capacity)+{+  assert(subtask_queue != NULL);+  memset(subtask_queue, 0, sizeof(struct subtask_queue));++  subtask_queue->capacity = capacity;+  subtask_queue->buffer = calloc(capacity, sizeof(struct subtask*));+  if (subtask_queue->buffer == NULL) {+    return -1;+  }++  CHECK_ERRNO(pthread_mutex_init(&subtask_queue->mutex, NULL), "pthread_mutex_init");+  CHECK_ERRNO(pthread_cond_init(&subtask_queue->cond, NULL), "pthread_cond_init");++  return 0;+}++// Destroy the job queue.  Blocks until the queue is empty before it+// is destroyed.+static inline int subtask_queue_destroy(struct subtask_queue *subtask_queue)+{+  assert(subtask_queue != NULL);++  CHECK_ERR(pthread_mutex_lock(&subtask_queue->mutex), "pthread_mutex_lock");++  while (subtask_queue->num_used != 0) {+    CHECK_ERR(pthread_cond_wait(&subtask_queue->cond, &subtask_queue->mutex), "pthread_cond_wait");+  }++  // Queue is now empty.  Let's kill it!+  subtask_queue->dead = 1;+  free(subtask_queue->buffer);+  CHECK_ERR(pthread_cond_broadcast(&subtask_queue->cond), "pthread_cond_broadcast");+  CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");++  return 0;+}++static inline void dump_queue(struct worker *worker)+{+  struct subtask_queue *subtask_queue = &worker->q;+  CHECK_ERR(pthread_mutex_lock(&subtask_queue->mutex), "pthread_mutex_lock");+  for (int i = 0; i < subtask_queue->num_used; i++) {+    struct subtask * subtask = subtask_queue->buffer[(subtask_queue->first + i) % subtask_queue->capacity];+    printf("queue tid %d with %d task %s\n", worker->tid, i, subtask->name);+  }+  CHECK_ERR(pthread_cond_broadcast(&subtask_queue->cond), "pthread_cond_broadcast");+  CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");+}++// Push an element onto the end of the job queue.  Blocks if the+// subtask_queue is full (its size is equal to its capacity).  Returns+// non-zero on error.  It is an error to push a job onto a queue that+// has been destroyed.+static inline int subtask_queue_enqueue(struct worker *worker, struct subtask *subtask )+{+  assert(worker != NULL);+  struct subtask_queue *subtask_queue = &worker->q;++#ifdef MCPROFILE+  uint64_t start = get_wall_time();+#endif++  CHECK_ERR(pthread_mutex_lock(&subtask_queue->mutex), "pthread_mutex_lock");+  // Wait until there is room in the subtask_queue.+  while (subtask_queue->num_used == subtask_queue->capacity && !subtask_queue->dead) {+    if (subtask_queue->num_used == subtask_queue->capacity) {+      CHECK_ERR(subtask_queue_grow_queue(subtask_queue), "subtask_queue_grow_queue");+      continue;+    }+    CHECK_ERR(pthread_cond_wait(&subtask_queue->cond, &subtask_queue->mutex), "pthread_cond_wait");+  }++  if (subtask_queue->dead) {+    CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");+    return -1;+  }++  // If we made it past the loop, there is room in the subtask_queue.+  subtask_queue->buffer[(subtask_queue->first + subtask_queue->num_used) % subtask_queue->capacity] = subtask;+  subtask_queue->num_used++;++#ifdef MCPROFILE+  uint64_t end = get_wall_time();+  subtask_queue->time_enqueue += (end - start);+  subtask_queue->n_enqueues++;+#endif+  // Broadcast a reader (if any) that there is now an element.+  CHECK_ERR(pthread_cond_broadcast(&subtask_queue->cond), "pthread_cond_broadcast");+  CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");++  return 0;+}+++/* Like subtask_queue_dequeue, but with two differences:+   1) the subtask is stolen from the __front__ of the queue+   2) returns immediately if there is no subtasks queued,+      as we dont' want to block on another workers queue and+*/+static inline int subtask_queue_steal(struct worker *worker,+                                      struct subtask **subtask)+{+  struct subtask_queue *subtask_queue = &worker->q;+  assert(subtask_queue != NULL);++#ifdef MCPROFILE+  uint64_t start = get_wall_time();+#endif+  CHECK_ERR(pthread_mutex_lock(&subtask_queue->mutex), "pthread_mutex_lock");++  if (subtask_queue->num_used == 0) {+    CHECK_ERR(pthread_cond_broadcast(&subtask_queue->cond), "pthread_cond_broadcast");+    CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");+    return 1;+  }++  if (subtask_queue->dead) {+    CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");+    return -1;+  }++  // Tasks gets stolen from the "front"+  struct subtask *cur_back = subtask_queue->buffer[subtask_queue->first];+  struct subtask *new_subtask = NULL;+  int remaining_iter = cur_back->end - cur_back->start;+  // If subtask is chunkable, we steal half of the iterations+  if (cur_back->chunkable && remaining_iter > 1) {+      int64_t half = remaining_iter / 2;+      new_subtask = malloc(sizeof(struct subtask));+      *new_subtask = *cur_back;+      new_subtask->start = cur_back->end - half;+      cur_back->end = new_subtask->start;+      __atomic_fetch_add(cur_back->counter, 1, __ATOMIC_RELAXED);+  } else {+    new_subtask = cur_back;+    subtask_queue->num_used--;+    subtask_queue->first = (subtask_queue->first + 1) % subtask_queue->capacity;+  }+  *subtask = new_subtask;++  if (*subtask == NULL) {+    CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthred_mutex_unlock");+    return 1;+  }++#ifdef MCPROFILE+  uint64_t end = get_wall_time();+  subtask_queue->time_dequeue += (end - start);+  subtask_queue->n_dequeues++;+#endif++  // Broadcast a writer (if any) that there is now room for more.+  CHECK_ERR(pthread_cond_broadcast(&subtask_queue->cond), "pthread_cond_broadcast");+  CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");++  return 0;+}+++// Pop an element from the back of the job queue.+// Optional argument can be provided to block or not+static inline int subtask_queue_dequeue(struct worker *worker,+                                        struct subtask **subtask, int blocking)+{+  assert(worker != NULL);+  struct subtask_queue *subtask_queue = &worker->q;++#ifdef MCPROFILE+  uint64_t start = get_wall_time();+#endif++  CHECK_ERR(pthread_mutex_lock(&subtask_queue->mutex), "pthread_mutex_lock");+  if (subtask_queue->num_used == 0 && !blocking) {+    CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");+    return 1;+  }+  // Try to steal some work while the subtask_queue is empty+  while (subtask_queue->num_used == 0 && !subtask_queue->dead) {+    pthread_cond_wait(&subtask_queue->cond, &subtask_queue->mutex);+  }++  if (subtask_queue->dead) {+    CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");+    return -1;+  }++  // dequeue pops from the back+  *subtask = subtask_queue->buffer[(subtask_queue->first + subtask_queue->num_used - 1) % subtask_queue->capacity];+  subtask_queue->num_used--;++  if (*subtask == NULL) {+    assert(!"got NULL ptr");+    CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthred_mutex_unlock");+    return -1;+  }++#ifdef MCPROFILE+  uint64_t end = get_wall_time();+  subtask_queue->time_dequeue += (end - start);+  subtask_queue->n_dequeues++;+#endif++  // Broadcast a writer (if any) that there is now room for more.+  CHECK_ERR(pthread_cond_broadcast(&subtask_queue->cond), "pthread_cond_broadcast");+  CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");++  return 0;+}++static inline int subtask_queue_is_empty(struct subtask_queue *subtask_queue)+{+  return subtask_queue->num_used == 0;+}++/* Scheduler definitions */++struct scheduler {+  struct worker *workers;+  int num_threads;++  // If there is work to steal => active_work > 0+  volatile int active_work;++  // Only one error can be returned at the time now.  Maybe we can+  // provide a stack like structure for pushing errors onto if we wish+  // to backpropagte multiple errors+  volatile int error;++  // kappa time unit in nanoseconds+  double kappa;+};+++// Thread local variable worker struct+// Note that, accesses to tls variables are expensive+// Minimize direct references to this variable+__thread struct worker* worker_local = NULL;++static int64_t total_now(int64_t total, int64_t time) {+  return total + (get_wall_time_ns() - time);+}++static int random_other_worker(struct scheduler *scheduler, int my_id) {+  int my_num_workers = scheduler->num_threads;+  assert(my_num_workers != 1);+  int i = fast_rand() % (my_num_workers - 1);+  if (i >= my_id) {+    i++;+  }+#ifdef MCDEBUG+  assert(i >= 0);+  assert(i < my_num_workers);+  assert(i != my_id);+#endif++  return i;+}+++static inline int64_t compute_chunk_size(double kappa, struct subtask* subtask)+{+  double C = (double)*subtask->task_time / (double)*subtask->task_iter;+  if (C == 0.0F) C += DBL_EPSILON;+  return smax64((int64_t)(kappa / C), 1);+}++/* Takes a chunk from subtask and enqueues the remaining iterations onto the worker's queue */+/* A no-op if the subtask is not chunkable */+static inline struct subtask* chunk_subtask(struct worker* worker, struct subtask *subtask)+{+  if (subtask->chunkable) {+    // Do we have information from previous runs avaliable+    if (*subtask->task_iter > 0) {+      subtask->chunk_size = compute_chunk_size(worker->scheduler->kappa, subtask);+      assert(subtask->chunk_size > 0);+    }+    int64_t remaining_iter = subtask->end - subtask->start;+    assert(remaining_iter > 0);+    if (remaining_iter > subtask->chunk_size) {+      struct subtask *new_subtask = malloc(sizeof(struct subtask));+      *new_subtask = *subtask;+      // increment the subtask join counter to account for new subtask+      __atomic_fetch_add(subtask->counter, 1, __ATOMIC_RELAXED);+      // Update range parameters+      subtask->end = subtask->start + subtask->chunk_size;+      new_subtask->start = subtask->end;+      subtask_queue_enqueue(worker, new_subtask);+    }+  }+  return subtask;+}++static inline int run_subtask(struct worker* worker, struct subtask* subtask)+{+  assert(subtask != NULL);+  assert(worker != NULL);++  subtask = chunk_subtask(worker, subtask);+  worker->total = 0;+  worker->timer = get_wall_time_ns();+#if defined(MCPROFILE)+  int64_t start = worker->timer;+#endif+  worker->nested++;+  int err = subtask->fn(subtask->args, subtask->start, subtask->end,+                        subtask->chunkable ? worker->tid : subtask->id,+                        worker->tid);+  worker->nested--;+  // Some error occured during some other subtask+  // so we just clean-up and return+  if (worker->scheduler->error != 0) {+    // Even a failed task counts as finished.+    __atomic_fetch_sub(subtask->counter, 1, __ATOMIC_RELAXED);+    free(subtask);+    return 0;+  }+  if (err != 0) {+    __atomic_store_n(&worker->scheduler->error, err, __ATOMIC_RELAXED);+  }+  // Total sequential time spent+  int64_t time_elapsed = total_now(worker->total, worker->timer);+#if defined(MCPROFILE)+  worker->time_spent_working += get_wall_time_ns() - start;+#endif+  int64_t iter = subtask->end - subtask->start;+  // report measurements+  // These updates should really be done using a single atomic CAS operation+  __atomic_fetch_add(subtask->task_time, time_elapsed, __ATOMIC_RELAXED);+  __atomic_fetch_add(subtask->task_iter, iter, __ATOMIC_RELAXED);+  // We need a fence here, since if the counter is decremented before either+  // of the two above are updated bad things can happen, e.g. if they are stack-allocated+  __atomic_thread_fence(__ATOMIC_SEQ_CST);+  __atomic_fetch_sub(subtask->counter, 1, __ATOMIC_RELAXED);+  free(subtask);+  return 0;+}+++static inline int is_small(struct scheduler_segop *task, struct scheduler *scheduler, int *nsubtasks)+{+  int64_t time = *task->task_time;+  int64_t iter = *task->task_iter;++  if (task->sched == DYNAMIC || iter == 0) {+    *nsubtasks = scheduler->num_threads;+    return 0;+  }++  // Estimate the constant C+  double C = (double)time / (double)iter;+  double cur_task_iter = (double) task->iterations;++  // Returns true if the task is small i.e.+  // if the number of iterations times C is smaller+  // than the overhead of subtask creation+  if (C == 0.0F || C * cur_task_iter < scheduler->kappa) {+    *nsubtasks = 1;+    return 1;+  }++  // Else compute how many subtasks this tasks should create+  int64_t min_iter_pr_subtask = smax64(scheduler->kappa / C, 1);+  *nsubtasks = smin64(smax64(task->iterations / min_iter_pr_subtask, 1), scheduler->num_threads);++  return 0;+}++// TODO make this prettier+static inline struct subtask* create_subtask(parloop_fn fn,+                                             void* args,+                                             const char* name,+                                             volatile int* counter,+                                             int64_t *timer,+                                             int64_t *iter,+                                             int64_t start, int64_t end,+                                             int chunkable,+                                             int64_t chunk_size,+                                             int id)+{+  struct subtask* subtask = malloc(sizeof(struct subtask));+  if (subtask == NULL) {+    assert(!"malloc failed in create_subtask");+    return NULL;+  }+  subtask->fn         = fn;+  subtask->args       = args;++  subtask->counter    = counter;+  subtask->task_time  = timer;+  subtask->task_iter  = iter;++  subtask->start      = start;+  subtask->end        = end;+  subtask->id         = id;+  subtask->chunkable  = chunkable;+  subtask->chunk_size = chunk_size;++  subtask->name       = name;+  return subtask;+}+ static int dummy_counter = 0; static int64_t dummy_timer = 0; static int64_t dummy_iter = 0;@@ -59,8 +726,10 @@ static inline void *scheduler_worker(void* args) {   struct worker *worker = (struct worker*) args;+  struct scheduler *scheduler = worker->scheduler;   worker_local = worker;-  struct subtask * subtask = NULL;+  struct subtask *subtask = NULL;+   while(!is_finished(worker)) {     if (!subtask_queue_is_empty(&worker->q)) {       int retval = subtask_queue_dequeue(worker, &subtask, 0);@@ -69,8 +738,8 @@         CHECK_ERR(run_subtask(worker, subtask), "run_subtask");       } // else someone stole our work -    } else if (active_work) { /* steal */-      while (!is_finished(worker) && active_work) {+    } else if (scheduler->active_work) { /* steal */+      while (!is_finished(worker) && scheduler->active_work) {         if (steal_from_random_worker(worker)) {           break;         }@@ -85,10 +754,9 @@   }    assert(subtask_queue_is_empty(&worker->q));-  __atomic_fetch_sub(&num_workers, 1, __ATOMIC_RELAXED); #if defined(MCPROFILE)   if (worker->output_usage)-    output_thread_usage(worker);+    output_worker_usage(worker); #endif   return NULL; }@@ -99,7 +767,7 @@                                             int64_t *timer) { -  struct worker * worker = worker_local;+  struct worker *worker = worker_local;    struct scheduler_info info = task->info;   int64_t iter_pr_subtask = info.iter_pr_subtask;@@ -119,7 +787,7 @@     if (info.wake_up_threads || sched == DYNAMIC)-    __atomic_add_fetch(&active_work, nsubtasks, __ATOMIC_RELAXED);+    __atomic_add_fetch(&scheduler->active_work, nsubtasks, __ATOMIC_RELAXED);    int64_t start = 0;   int64_t end = iter_pr_subtask + (int64_t)(remainder != 0);@@ -131,13 +799,14 @@                                               chunkable, chunk_size,                                               subtask_id);     assert(subtask != NULL);-    if (worker->nested){-      CHECK_ERR(subtask_queue_enqueue(&scheduler->workers[worker->tid], subtask),-                "subtask_queue_enqueue");-    } else {-      CHECK_ERR(subtask_queue_enqueue(&scheduler->workers[subtask_id], subtask),-                "subtask_queue_enqueue");-    }+    // In most cases we will never have more subtasks than workers,+    // but there can be exceptions (e.g. the kappa tuning function).+    struct worker *subtask_worker =+      worker->nested+      ? &scheduler->workers[worker->tid]+      : &scheduler->workers[subtask_id % scheduler->num_threads];+    CHECK_ERR(subtask_queue_enqueue(subtask_worker, subtask),+              "subtask_queue_enqueue");     // Update range params     start = end;     end += iter_pr_subtask + ((subtask_id + 1) < remainder);@@ -168,12 +837,12 @@     if (info.wake_up_threads || sched == DYNAMIC) {-    __atomic_sub_fetch(&active_work, nsubtasks, __ATOMIC_RELAXED);+    __atomic_sub_fetch(&scheduler->active_work, nsubtasks, __ATOMIC_RELAXED);   }    // Write back timing results of all sequential work   (*timer) += task_timer;-  return scheduler_error;+  return scheduler->error; }  @@ -239,7 +908,7 @@    int nsubtasks;   // Decide if task should be scheduled sequentially-  if (is_small(task, scheduler->num_threads, &nsubtasks)) {+  if (is_small(task, scheduler, &nsubtasks)) {     info.iter_pr_subtask = task->iterations;     info.remainder = 0;     info.nsubtasks = nsubtasks;@@ -274,5 +943,194 @@   return task->top_level_fn(task->args, task->iterations, worker->tid, info); } -#endif+// Now some code for finding the proper value of kappa on a given+// machine (the smallest amount of work that amortises the cost of+// task creation).++struct tuning_struct {+  int32_t *free_tuning_res;+  int32_t *array;+};++// Reduction function over an integer array+static int tuning_loop(void *args, int64_t start, int64_t end,+                                     int flat_tid, int tid) {+  (void)flat_tid;+  (void)tid;++  int err = 0;+  struct tuning_struct *tuning_struct = (struct tuning_struct *) args;+  int32_t *array = tuning_struct->array;+  int32_t *tuning_res = tuning_struct->free_tuning_res;++  int32_t sum = 0;+  for (int i = start; i < end; i++) {+    int32_t y = array[i];+    sum = add32(sum, y);+  }+  *tuning_res = sum;+  return err;+}++// The main entry point for the tuning process.  Sets the provided+// variable ``kappa``.+static int determine_kappa(double *kappa) {+  int err = 0;++  int64_t iterations = 100000000;+  int64_t tuning_time = 0;+  int64_t tuning_iter = 0;++  int32_t *array = malloc(sizeof(int32_t) * iterations);+  for (int64_t i = 0; i < iterations; i++) {+    array[i] = fast_rand();+  }++  int64_t start_tuning = get_wall_time_ns();+  /* **************************** */+  /* Run sequential reduce first' */+  /* **************************** */+  int64_t tuning_sequentiual_start = get_wall_time_ns();+  struct tuning_struct tuning_struct;+  int32_t tuning_res;+  tuning_struct.free_tuning_res = &tuning_res;+  tuning_struct.array = array;++  err = tuning_loop(&tuning_struct, 0, iterations, 0, 0);+  int64_t tuning_sequentiual_end = get_wall_time_ns();+  int64_t sequential_elapsed = tuning_sequentiual_end - tuning_sequentiual_start;++  double C = (double)sequential_elapsed / (double)iterations;+  fprintf(stderr, " Time for sequential run is %lld - Found C %f\n", (long long)sequential_elapsed, C);++  /* ********************** */+  /* Now run tuning process */+  /* ********************** */+  // Setup a scheduler with a single worker+  struct scheduler scheduler;+  scheduler.num_threads = 1;+  scheduler.workers = malloc(sizeof(struct worker));+  worker_local = &scheduler.workers[0];+  worker_local->tid = 0;+  CHECK_ERR(subtask_queue_init(&scheduler.workers[0].q, 1024),+            "failed to init queue for worker %d\n", 0);++  // Start tuning for kappa+  double kappa_tune = 1000; // Initial kappa is 1 us+  double ratio;+  int64_t time_elapsed;+  while(1) {+    int64_t min_iter_pr_subtask = (int64_t) (kappa_tune / C) == 0 ? 1 : (kappa_tune / C);+    int nsubtasks = iterations / min_iter_pr_subtask;+    struct scheduler_info info;+    info.iter_pr_subtask = min_iter_pr_subtask;++    info.nsubtasks = iterations / min_iter_pr_subtask;+    info.remainder = iterations % min_iter_pr_subtask;+    info.task_time = &tuning_time;+    info.task_iter = &tuning_iter;+    info.sched = STATIC;++    struct scheduler_parloop parloop;+    parloop.name = "tuning_loop";+    parloop.fn = tuning_loop;+    parloop.args = &tuning_struct;+    parloop.iterations = iterations;+    parloop.info = info;++    int64_t tuning_chunked_start = get_wall_time_ns();+    int determine_kappa_err =+      scheduler_execute_task(&scheduler,+                             &parloop);+    assert(determine_kappa_err == 0);+    int64_t tuning_chunked_end = get_wall_time_ns();+    time_elapsed =  tuning_chunked_end - tuning_chunked_start;++    ratio = (double)time_elapsed / (double)sequential_elapsed;+    if (ratio < 1.055) {+      break;+    }+    kappa_tune += 100; // Increase by 100 ns at the time+    fprintf(stderr, "nsubtask %d - kappa %f - ratio %f\n", nsubtasks, kappa_tune, ratio);+  }++  int64_t end_tuning = get_wall_time_ns();+  fprintf(stderr, "tuning took %lld ns and found kappa %f - time %lld - ratio %f\n",+          (long long)end_tuning - start_tuning,+          kappa_tune,+          (long long)time_elapsed,+          ratio);+  *kappa = kappa_tune;++  // Clean-up+  CHECK_ERR(subtask_queue_destroy(&scheduler.workers[0].q), "failed to destroy queue");+  free(array);+  free(scheduler.workers);+  return err;+}++static int scheduler_init(struct scheduler *scheduler,+                          int num_workers,+                          double kappa) {+  assert(num_workers > 0);++  scheduler->kappa = kappa;+  scheduler->num_threads = num_workers;+  scheduler->active_work = 0;+  scheduler->error = 0;++  scheduler->workers = calloc(num_workers, sizeof(struct worker));++  const int queue_capacity = 1024;++  worker_local = &scheduler->workers[0];+  worker_local->tid = 0;+  worker_local->scheduler = scheduler;+  CHECK_ERR(subtask_queue_init(&worker_local->q, queue_capacity),+            "failed to init queue for worker %d\n", 0);++  for (int i = 1; i < num_workers; i++) {+    struct worker *cur_worker = &scheduler->workers[i];+    memset(cur_worker, 0, sizeof(struct worker));+    cur_worker->tid = i;+    cur_worker->output_usage = 0;+    cur_worker->scheduler = scheduler;+    CHECK_ERR(subtask_queue_init(&cur_worker->q, queue_capacity),+              "failed to init queue for worker %d\n", i);++    CHECK_ERR(pthread_create(&cur_worker->thread,+                             NULL,+                             &scheduler_worker,+                             cur_worker),+              "Failed to create worker %d\n", i);+  }++  return 0;+}++static int scheduler_destroy(struct scheduler *scheduler) {+  // First mark them all as dead.+  for (int i = 1; i < scheduler->num_threads; i++) {+    struct worker *cur_worker = &scheduler->workers[i];+    cur_worker->dead = 1;+  }++  // Then destroy their task queues (this will wake up the threads and+  // make them do their shutdown).+  for (int i = 1; i < scheduler->num_threads; i++) {+    struct worker *cur_worker = &scheduler->workers[i];+    subtask_queue_destroy(&cur_worker->q);+  }++  // Then actually wait for them to stop.+  for (int i = 1; i < scheduler->num_threads; i++) {+    struct worker *cur_worker = &scheduler->workers[i];+    CHECK_ERR(pthread_join(scheduler->workers[i].thread, NULL), "pthread_join");+  }++  free(scheduler->workers);++  return 0;+}+ // End of scheduler.h
− rts/c/scheduler_common.h
@@ -1,244 +0,0 @@-// start of scheduler_common.h--#ifndef _SCHEDULER_COMMON_H_-#define _SCHEDULER_COMMON_H_--#include <float.h>--/* Scheduler definitions */-enum scheduling {-  DYNAMIC,-  STATIC-};--/* How a given task should be executed */-/* Filled out by the scheduler-   and passed to the segop function-*/-struct scheduler_info {-  int64_t iter_pr_subtask;-  int64_t remainder;-  int nsubtasks;-  enum scheduling sched;-  int wake_up_threads;--  int64_t *task_time;-  int64_t *task_iter;-};--struct scheduler {-  struct worker *workers;-  int num_threads;-};--/* A parallel parloop task  */-struct scheduler_parloop {-  const char* name;-  parloop_fn fn;-  void* args;-  int64_t iterations;-  struct scheduler_info info;-};---/* A task for the scheduler to execute */-struct scheduler_segop {-  void *args;-  segop_fn top_level_fn;-  segop_fn nested_fn;-  int64_t iterations;-  enum scheduling sched;--  // Pointers to timer and iter associated with the task-  int64_t *task_time;-  int64_t *task_iter;--  // For debugging-  const char* name;-};--// If there is work to steal => active_work > 0-static volatile int active_work = 0;-// Number of alive workers-static volatile sig_atomic_t num_workers;--// Thread local variable worker struct-// Note that, accesses to tls variables are expensive-// Minimize direct references to this variable-__thread struct worker* worker_local = NULL;--/* Only one error can be returned at the time now-   Maybe we can provide a stack like structure for pushing errors onto-   if we wish to backpropagte multiple errors */-static volatile sig_atomic_t scheduler_error = 0;--// kappa time unit in nanoseconds-static double kappa = 5.1f * 1000;--int64_t total_now(int64_t total, int64_t time) {-  return total + (get_wall_time_ns() - time);-}--int random_other_worker(struct scheduler *scheduler, int my_id) {-  (void)scheduler;-  int my_num_workers = __atomic_load_n(&num_workers, __ATOMIC_RELAXED);-  assert(my_num_workers != 1);-  int i = fast_rand() % (my_num_workers - 1);-  if (i >= my_id) {-    i++;-  }-#ifdef MCDEBUG-  assert(i >= 0);-  assert(i < my_num_workers);-  assert(i != my_id);-#endif--  return i;-}---static inline int64_t compute_chunk_size(struct subtask* subtask)-{-  double C = (double)*subtask->task_time / (double)*subtask->task_iter;-  if (C == 0.0F) C += DBL_EPSILON;-  return max_int64((int64_t)(kappa / C), 1);-}--/* Takes a chunk from subtask and enqueues the remaining iterations onto the worker's queue */-/* A no-op if the subtask is not chunkable */-static inline struct subtask* chunk_subtask(struct worker* worker, struct subtask *subtask)-{-  if (subtask->chunkable) {-    // Do we have information from previous runs avaliable-    if (*subtask->task_iter > 0) {-      subtask->chunk_size = compute_chunk_size(subtask);-      assert(subtask->chunk_size > 0);-    }-    int64_t remaining_iter = subtask->end - subtask->start;-    assert(remaining_iter > 0);-    if (remaining_iter > subtask->chunk_size) {-      struct subtask *new_subtask = malloc(sizeof(struct subtask));-      *new_subtask = *subtask;-      // increment the subtask join counter to account for new subtask-      __atomic_fetch_add(subtask->counter, 1, __ATOMIC_RELAXED);-      // Update range parameters-      subtask->end = subtask->start + subtask->chunk_size;-      new_subtask->start = subtask->end;-      subtask_queue_enqueue(worker, new_subtask);-    }-  }-  return subtask;-}--static inline int run_subtask(struct worker* worker, struct subtask* subtask)-{-  assert(subtask != NULL);-  assert(worker != NULL);--  subtask = chunk_subtask(worker, subtask);-  worker->total = 0;-  worker->timer = get_wall_time_ns();-#if defined(MCPROFILE)-  int64_t start = worker->timer;-#endif-  worker->nested++;-  int err = subtask->fn(subtask->args, subtask->start, subtask->end,-                        subtask->chunkable ? worker->tid : subtask->id,-                        worker->tid);-  worker->nested--;-  // Some error occured during some other subtask-  // so we just clean-up and return-  if (scheduler_error != 0) {-    // Even a failed task counts as finished.-    __atomic_fetch_sub(subtask->counter, 1, __ATOMIC_RELAXED);-    free(subtask);-    return 0;-  }-  if (err != 0) {-    __atomic_store_n(&scheduler_error, err, __ATOMIC_RELAXED);-  }-  // Total sequential time spent-  int64_t time_elapsed = total_now(worker->total, worker->timer);-#if defined(MCPROFILE)-  worker->time_spent_working += get_wall_time_ns() - start;-#endif-  int64_t iter = subtask->end - subtask->start;-  // report measurements-  // These updates should really be done using a single atomic CAS operation-  __atomic_fetch_add(subtask->task_time, time_elapsed, __ATOMIC_RELAXED);-  __atomic_fetch_add(subtask->task_iter, iter, __ATOMIC_RELAXED);-  // We need a fence here, since if the counter is decremented before either-  // of the two above are updated bad things can happen, e.g. if they are stack-allocated-  __atomic_thread_fence(__ATOMIC_SEQ_CST);-  __atomic_fetch_sub(subtask->counter, 1, __ATOMIC_RELAXED);-  free(subtask);-  return 0;-}---static inline int is_small(struct scheduler_segop *task, int nthreads, int *nsubtasks)-{-  int64_t time = *task->task_time;-  int64_t iter = *task->task_iter;--  if (task->sched == DYNAMIC || iter == 0) {-    *nsubtasks = nthreads;-    return 0;-  }--  // Estimate the constant C-  double C = (double)time / (double)iter;-  double cur_task_iter = (double) task->iterations;--  // Returns true if the task is small i.e.-  // if the number of iterations times C is smaller-  // than the overhead of subtask creation-  if (C == 0.0F || C * cur_task_iter < kappa) {-    *nsubtasks = 1;-    return 1;-  }--  // Else compute how many subtasks this tasks should create-  int64_t min_iter_pr_subtask = max_int64((int64_t)(kappa / C), 1);-  *nsubtasks = (int)min_int64(max_int64(task->iterations / min_iter_pr_subtask, 1), nthreads);--  return 0;-}--// TODO make this prettier-static inline struct subtask* create_subtask(parloop_fn fn,-                                             void* args,-                                             const char* name,-                                             volatile int* counter,-                                             int64_t *timer,-                                             int64_t *iter,-                                             int64_t start, int64_t end,-                                             int chunkable,-                                             int64_t chunk_size,-                                             int id)-{-  struct subtask* subtask = malloc(sizeof(struct subtask));-  if (subtask == NULL) {-    assert(!"malloc failed in create_subtask");-    return NULL;-  }-  subtask->fn         = fn;-  subtask->args       = args;--  subtask->counter    = counter;-  subtask->task_time  = timer;-  subtask->task_iter  = iter;--  subtask->start      = start;-  subtask->end        = end;-  subtask->id         = id;-  subtask->chunkable  = chunkable;-  subtask->chunk_size = chunk_size;--  subtask->name       = name;-  return subtask;-}---#endif-// end of scheduler_common.h
− rts/c/scheduler_tune.h
@@ -1,127 +0,0 @@-/* The self-tuning program to estimate $\kappa$ */--struct futhark_mc_segred_stage_1_struct {-  struct futhark_context *ctx;-  int32_t *free_tuning_res;-  int32_t *array;-};--/* Reduction function over an integer array */-int futhark_mc_tuning_segred_stage_1(void *args, int64_t start, int64_t end,-                                     int flat_tid, int tid) {-  (void)flat_tid;-  (void)tid;--  int err = 0;-  struct futhark_mc_segred_stage_1_struct *futhark_mc_segred_stage_1_struct = (struct futhark_mc_segred_stage_1_struct *) args;-  struct futhark_context *ctx = futhark_mc_segred_stage_1_struct->ctx;-  int32_t *array = futhark_mc_segred_stage_1_struct->array;-  int32_t *tuning_res = futhark_mc_segred_stage_1_struct->free_tuning_res;--  int32_t sum = 0;-  for (int i = start; i < end; i++) {-    int32_t y = array[i];-    sum = add32(sum, y);-  }-  *tuning_res = sum;-  return err;-}--/* The main entry point for the tuning process */-/* Sets the global variable ``kappa`` */-int futhark_segred_tuning_program(struct futhark_context *ctx)-{-  int err = 0;--  int64_t iterations = 100000000;-  int64_t tuning_time = 0;-  int64_t tuning_iter = 0;--  int32_t *array = malloc(sizeof(int32_t) * iterations);-  for (int64_t i = 0; i < iterations; i++) {-    array[i] = fast_rand();-  }--  int64_t start_tuning = get_wall_time_ns();-  /* **************************** */-  /* Run sequential reduce first' */-  /* **************************** */-  int64_t tuning_sequentiual_start = get_wall_time_ns();-  struct futhark_mc_segred_stage_1_struct futhark_mc_segred_stage_1_struct;-  int32_t tuning_res;-  futhark_mc_segred_stage_1_struct.ctx = ctx;-  futhark_mc_segred_stage_1_struct.free_tuning_res = &tuning_res;-  futhark_mc_segred_stage_1_struct.array = array;--  err = futhark_mc_tuning_segred_stage_1(&futhark_mc_segred_stage_1_struct, 0, iterations, 0, 0);-  int64_t tuning_sequentiual_end = get_wall_time_ns();-  int64_t sequential_elapsed = tuning_sequentiual_end - tuning_sequentiual_start;--  double C = (double)sequential_elapsed / (double)iterations;-  fprintf(stderr, " Time for sequential run is %lld - Found C %f\n", (long long)sequential_elapsed, C);--  /* ********************** */-  /* Now run tuning process */-  /* ********************** */-  // Setup a scheduler with a single worker-  int num_threads = ctx->scheduler.num_threads;-  ctx->scheduler.num_threads = 1;-  ctx->scheduler.workers = malloc(sizeof(struct worker));-  worker_local = &ctx->scheduler.workers[0];-  worker_local->tid = 0;-  CHECK_ERR(subtask_queue_init(&ctx->scheduler.workers[0].q, 1024), "failed to init queue for worker %d\n", 0);--  // Start tuning for kappa-  double kappa_tune = 1000; // Initial kappa is 1 us-  double ratio;-  int64_t time_elapsed;-  while(1) {-    int64_t min_iter_pr_subtask = (int64_t) (kappa_tune / C) == 0 ? 1 : (kappa_tune / C);-    int nsubtasks = iterations / min_iter_pr_subtask;-    struct scheduler_info info;-    info.iter_pr_subtask = min_iter_pr_subtask;--    info.nsubtasks = iterations / min_iter_pr_subtask;-    info.remainder = iterations % min_iter_pr_subtask;-    info.task_time = &tuning_time;-    info.task_iter = &tuning_iter;-    info.sched = STATIC;--    struct scheduler_parloop futhark_segred_tuning_scheduler_parloop;-    futhark_segred_tuning_scheduler_parloop.name = "futhark_mc_tuning_segred_stage_1";-    futhark_segred_tuning_scheduler_parloop.fn = futhark_mc_tuning_segred_stage_1;-    futhark_segred_tuning_scheduler_parloop.args = &futhark_mc_segred_stage_1_struct;-    futhark_segred_tuning_scheduler_parloop.iterations = iterations;-    futhark_segred_tuning_scheduler_parloop.info = info;--    int64_t tuning_chunked_start = get_wall_time_ns();-    int futhark_segred_tuning_program_err =-      scheduler_execute_task(&ctx->scheduler,-                             &futhark_segred_tuning_scheduler_parloop);-    assert(futhark_segred_tuning_program_err == 0);-    int64_t tuning_chunked_end = get_wall_time_ns();-    time_elapsed =  tuning_chunked_end - tuning_chunked_start;--    ratio = (double)time_elapsed / (double)sequential_elapsed;-    if (ratio < 1.055) {-      break;-    }-    kappa_tune += 100; // Increase by 100 ns at the time-    fprintf(stderr, "nsubtask %d - kappa %f - ratio %f\n", nsubtasks, kappa_tune, ratio);-  }--  int64_t end_tuning = get_wall_time_ns();-  fprintf(stderr, "tuning took %lld ns and found kappa %f - time %lld - ratio %f\n",-          (long long)end_tuning - start_tuning,-          kappa_tune,-          (long long)time_elapsed,-          ratio);-  kappa = kappa_tune;--  // Clean-up-  CHECK_ERR(subtask_queue_destroy(&ctx->scheduler.workers[0].q), "failed to destroy queue");-  free(array);-  free(ctx->scheduler.workers);-  ctx->scheduler.num_threads = num_threads;-  return err;-}
− rts/c/subtask_queue.h
@@ -1,247 +0,0 @@-// start of subtask_queue.h--#ifndef SUBTASK_QUEUE_H-#define SUBTASK_QUEUE_H--/* Doubles the size of the queue */-static inline int subtask_queue_grow_queue(struct subtask_queue *subtask_queue) {--  int new_capacity = 2 * subtask_queue->capacity;-#ifdef MCDEBUG-  fprintf(stderr, "Growing queue to %d\n", subtask_queue->capacity * 2);-#endif--  struct subtask **new_buffer = calloc(new_capacity, sizeof(struct subtask*));-  for (int i = 0; i < subtask_queue->num_used; i++) {-    new_buffer[i] = subtask_queue->buffer[(subtask_queue->first + i) % subtask_queue->capacity];-  }--  free(subtask_queue->buffer);-  subtask_queue->buffer = new_buffer;-  subtask_queue->capacity = new_capacity;-  subtask_queue->first = 0;--  return 0;-}--// Initialise a job queue with the given capacity.  The queue starts out-// empty.  Returns non-zero on error.-static inline int subtask_queue_init(struct subtask_queue *subtask_queue, int capacity)-{-  assert(subtask_queue != NULL);-  memset(subtask_queue, 0, sizeof(struct subtask_queue));--  subtask_queue->capacity = capacity;-  subtask_queue->buffer = calloc(capacity, sizeof(struct subtask*));-  if (subtask_queue->buffer == NULL) {-    return -1;-  }--  CHECK_ERRNO(pthread_mutex_init(&subtask_queue->mutex, NULL), "pthread_mutex_init");-  CHECK_ERRNO(pthread_cond_init(&subtask_queue->cond, NULL), "pthread_cond_init");--  return 0;-}--// Destroy the job queue.  Blocks until the queue is empty before it-// is destroyed.-static inline int subtask_queue_destroy(struct subtask_queue *subtask_queue)-{-  assert(subtask_queue != NULL);--  CHECK_ERR(pthread_mutex_lock(&subtask_queue->mutex), "pthread_mutex_lock");--  while (subtask_queue->num_used != 0) {-    CHECK_ERR(pthread_cond_wait(&subtask_queue->cond, &subtask_queue->mutex), "pthread_cond_wait");-  }--  // Queue is now empty.  Let's kill it!-  subtask_queue->dead = 1;-  free(subtask_queue->buffer);-  CHECK_ERR(pthread_cond_broadcast(&subtask_queue->cond), "pthread_cond_broadcast");-  CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");--  return 0;-}--static inline void dump_queue(struct worker *worker)-{-  struct subtask_queue *subtask_queue = &worker->q;-  CHECK_ERR(pthread_mutex_lock(&subtask_queue->mutex), "pthread_mutex_lock");-  for (int i = 0; i < subtask_queue->num_used; i++) {-    struct subtask * subtask = subtask_queue->buffer[(subtask_queue->first + i) % subtask_queue->capacity];-    printf("queue tid %d with %d task %s\n", worker->tid, i, subtask->name);-  }-  CHECK_ERR(pthread_cond_broadcast(&subtask_queue->cond), "pthread_cond_broadcast");-  CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");-}--// Push an element onto the end of the job queue.  Blocks if the-// subtask_queue is full (its size is equal to its capacity).  Returns-// non-zero on error.  It is an error to push a job onto a queue that-// has been destroyed.-static inline int subtask_queue_enqueue(struct worker *worker, struct subtask *subtask )-{-  assert(worker != NULL);-  struct subtask_queue *subtask_queue = &worker->q;--#ifdef MCPROFILE-  uint64_t start = get_wall_time();-#endif--  CHECK_ERR(pthread_mutex_lock(&subtask_queue->mutex), "pthread_mutex_lock");-  // Wait until there is room in the subtask_queue.-  while (subtask_queue->num_used == subtask_queue->capacity && !subtask_queue->dead) {-    if (subtask_queue->num_used == subtask_queue->capacity) {-      CHECK_ERR(subtask_queue_grow_queue(subtask_queue), "subtask_queue_grow_queue");-      continue;-    }-    CHECK_ERR(pthread_cond_wait(&subtask_queue->cond, &subtask_queue->mutex), "pthread_cond_wait");-  }--  if (subtask_queue->dead) {-    CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");-    return -1;-  }--  // If we made it past the loop, there is room in the subtask_queue.-  subtask_queue->buffer[(subtask_queue->first + subtask_queue->num_used) % subtask_queue->capacity] = subtask;-  subtask_queue->num_used++;--#ifdef MCPROFILE-  uint64_t end = get_wall_time();-  subtask_queue->time_enqueue += (end - start);-  subtask_queue->n_enqueues++;-#endif-  // Broadcast a reader (if any) that there is now an element.-  CHECK_ERR(pthread_cond_broadcast(&subtask_queue->cond), "pthread_cond_broadcast");-  CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");--  return 0;-}---/* Like subtask_queue_dequeue, but with two differences:-   1) the subtask is stolen from the __front__ of the queue-   2) returns immediately if there is no subtasks queued,-      as we dont' want to block on another workers queue and-*/-static inline int subtask_queue_steal(struct worker *worker,-                                      struct subtask **subtask)-{-  struct subtask_queue *subtask_queue = &worker->q;-  assert(subtask_queue != NULL);--#ifdef MCPROFILE-  uint64_t start = get_wall_time();-#endif-  CHECK_ERR(pthread_mutex_lock(&subtask_queue->mutex), "pthread_mutex_lock");--  if (subtask_queue->num_used == 0) {-    CHECK_ERR(pthread_cond_broadcast(&subtask_queue->cond), "pthread_cond_broadcast");-    CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");-    return 1;-  }--  if (subtask_queue->dead) {-    CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");-    return -1;-  }--  // Tasks gets stolen from the "front"-  struct subtask *cur_back = subtask_queue->buffer[subtask_queue->first];-  struct subtask *new_subtask = NULL;-  int remaining_iter = cur_back->end - cur_back->start;-  // If subtask is chunkable, we steal half of the iterations-  if (cur_back->chunkable && remaining_iter > 1) {-      int64_t half = remaining_iter / 2;-      new_subtask = malloc(sizeof(struct subtask));-      *new_subtask = *cur_back;-      new_subtask->start = cur_back->end - half;-      cur_back->end = new_subtask->start;-      __atomic_fetch_add(cur_back->counter, 1, __ATOMIC_RELAXED);-  } else {-    new_subtask = cur_back;-    subtask_queue->num_used--;-    subtask_queue->first = (subtask_queue->first + 1) % subtask_queue->capacity;-  }-  *subtask = new_subtask;--  if (*subtask == NULL) {-    CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthred_mutex_unlock");-    return 1;-  }--#ifdef MCPROFILE-  uint64_t end = get_wall_time();-  subtask_queue->time_dequeue += (end - start);-  subtask_queue->n_dequeues++;-#endif--  // Broadcast a writer (if any) that there is now room for more.-  CHECK_ERR(pthread_cond_broadcast(&subtask_queue->cond), "pthread_cond_broadcast");-  CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");--  return 0;-}---// Pop an element from the back of the job queue.-// Optional argument can be provided to block or not-static inline int subtask_queue_dequeue(struct worker *worker,-                                        struct subtask **subtask, int blocking)-{-  assert(worker != NULL);-  struct subtask_queue *subtask_queue = &worker->q;--#ifdef MCPROFILE-  uint64_t start = get_wall_time();-#endif--  CHECK_ERR(pthread_mutex_lock(&subtask_queue->mutex), "pthread_mutex_lock");-  if (subtask_queue->num_used == 0 && !blocking) {-    CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");-    return 1;-  }-  // Try to steal some work while the subtask_queue is empty-  while (subtask_queue->num_used == 0 && !subtask_queue->dead) {-    pthread_cond_wait(&subtask_queue->cond, &subtask_queue->mutex);-  }--  if (subtask_queue->dead) {-    CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");-    return -1;-  }--  // dequeue pops from the back-  *subtask = subtask_queue->buffer[(subtask_queue->first + subtask_queue->num_used - 1) % subtask_queue->capacity];-  subtask_queue->num_used--;--  if (*subtask == NULL) {-    assert(!"got NULL ptr");-    CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthred_mutex_unlock");-    return -1;-  }--#ifdef MCPROFILE-  uint64_t end = get_wall_time();-  subtask_queue->time_dequeue += (end - start);-  subtask_queue->n_dequeues++;-#endif--  // Broadcast a writer (if any) that there is now room for more.-  CHECK_ERR(pthread_cond_broadcast(&subtask_queue->cond), "pthread_cond_broadcast");-  CHECK_ERR(pthread_mutex_unlock(&subtask_queue->mutex), "pthread_mutex_unlock");--  return 0;-}--static inline int subtask_queue_is_empty(struct subtask_queue *subtask_queue)-{-  return subtask_queue->num_used == 0;-}---#endif--// End of subtask_queue.h
rts/c/util.h view
@@ -30,15 +30,19 @@   static inline void check_err(int errval, int sets_errno, const char *fun, int line,-                            const char *msg, ...)-{+                            const char *msg, ...) {   if (errval) {     char str[256];     char errnum[10];-    sprintf(errnum, "%d", errval);-    sprintf(str, "ERROR: %s in %s() at line %d with error code %s\n", msg, fun, line,++    va_list vl;+    va_start(vl, msg);++    fprintf(stderr, "ERROR: ");+    vfprintf(stderr, msg, vl);+    fprintf(stderr, " in %s() at line %d with error code %s\n",+            fun, line,             sets_errno ? strerror(errno) : errnum);-    fprintf(stderr, "%s", str);     exit(errval);   } }
src/Futhark/CLI/Check.hs view
@@ -5,6 +5,8 @@ import Control.Monad.IO.Class import Futhark.Compiler import Futhark.Util.Options+import Futhark.Util.Pretty (pretty)+import Language.Futhark.Warnings import System.Console.GetOpt import System.IO @@ -28,6 +30,6 @@   case args of     [file] -> Just $ do       (warnings, _, _) <- readProgramOrDie file-      when (checkWarn cfg) $-        liftIO $ hPutStr stderr $ show warnings+      when (checkWarn cfg && anyWarnings warnings) $+        liftIO $ hPutStrLn stderr $ pretty warnings     _ -> Nothing
src/Futhark/CLI/REPL.hs view
@@ -160,12 +160,13 @@       -- Then make the prelude available in the type checker.       (tenv, d, src') <-         badOnLeft pretty $-          T.checkDec-            imports-            src-            T.initialEnv-            (T.mkInitialImport ".")-            $ mkOpen "/prelude/prelude"+          snd $+            T.checkDec+              imports+              src+              T.initialEnv+              (T.mkInitialImport ".")+              $ mkOpen "/prelude/prelude"       -- Then in the interpreter.       ienv' <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv d)       return (imports, src', tenv, ienv')@@ -177,7 +178,7 @@                 `catch` \(err :: IOException) ->                   return (externalErrorS (show err))             )-      liftIO $ print ws+      liftIO $ putStrLn $ pretty ws        let imp = T.mkInitialImport "."       ienv1 <-@@ -185,12 +186,14 @@           map (fmap fileProg) imports       (tenv1, d1, src') <-         badOnLeft pretty $-          T.checkDec imports src T.initialEnv imp $-            mkOpen "/prelude/prelude"+          snd $+            T.checkDec imports src T.initialEnv imp $+              mkOpen "/prelude/prelude"       (tenv2, d2, src'') <-         badOnLeft pretty $-          T.checkDec imports src' tenv1 imp $-            mkOpen $ toPOSIX $ dropExtension file+          snd $+            T.checkDec imports src' tenv1 imp $+              mkOpen $ toPOSIX $ dropExtension file       ienv2 <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv1 d1)       ienv3 <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv2 d2)       return (imports, src'', tenv2, ienv3)@@ -290,8 +293,8 @@     Left e -> liftIO $ print e     Right (_, imports', src') ->       case T.checkDec imports' src' tenv cur_import d of-        Left e -> liftIO $ putStrLn $ pretty e-        Right (tenv', d', src'') -> do+        (_, Left e) -> liftIO $ putStrLn $ pretty e+        (_, Right (tenv', d', src'')) -> do           let new_imports = filter ((`notElem` map fst imports) . fst) imports'           int_r <- runInterpreter $ do             let onImport ienv' (s, imp) =@@ -310,10 +313,9 @@ onExp :: UncheckedExp -> FutharkiM () onExp e = do   (imports, src, tenv, ienv) <- getIt-  case either (Left . pretty) Right $-    T.checkExp imports src tenv e of-    Left err -> liftIO $ putStrLn err-    Right (tparams, e')+  case T.checkExp imports src tenv e of+    (_, Left err) -> liftIO $ putStrLn $ pretty err+    (_, Right (tparams, e'))       | null tparams -> do         r <- runInterpreter $ I.interpretExp ienv e'         case r of@@ -416,7 +418,7 @@ genTypeCommand ::   Show err =>   (String -> T.Text -> Either err a) ->-  (Imports -> VNameSource -> T.Env -> a -> Either T.TypeError b) ->+  (Imports -> VNameSource -> T.Env -> a -> (Warnings, Either T.TypeError b)) ->   (b -> String) ->   Command genTypeCommand f g h e = do@@ -427,7 +429,7 @@       imports <- gets futharkiImports       src <- gets futharkiNameSource       (tenv, _) <- gets futharkiEnv-      case g imports src tenv e' of+      case snd $ g imports src tenv e' of         Left err -> liftIO $ putStrLn $ pretty err         Right x -> liftIO $ putStrLn $ h x 
src/Futhark/CLI/Run.hs view
@@ -123,7 +123,7 @@               return (externalErrorS (show err))         )   when (interpreterPrintWarnings cfg) $-    liftIO $ hPutStr stderr $ show ws+    liftIO $ hPutStr stderr $ pretty ws    let imp = T.mkInitialImport "."   ienv1 <-@@ -131,12 +131,14 @@       map (fmap fileProg) imports   (tenv1, d1, src') <-     badOnLeft pretty $-      T.checkDec imports src T.initialEnv imp $-        mkOpen "/prelude/prelude"+      snd $+        T.checkDec imports src T.initialEnv imp $+          mkOpen "/prelude/prelude"   (tenv2, d2, _) <-     badOnLeft pretty $-      T.checkDec imports src' tenv1 imp $-        mkOpen $ toPOSIX $ dropExtension file+      snd $+        T.checkDec imports src' tenv1 imp $+          mkOpen $ toPOSIX $ dropExtension file   ienv2 <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv1 d1)   ienv3 <- badOnLeft show =<< runInterpreter' (I.interpretDec ienv2 d2)   return (tenv2, ienv3)
src/Futhark/CodeGen/Backends/CCUDA/Boilerplate.hs view
@@ -461,7 +461,9 @@   GC.publicDef_ "context_clear_caches" GC.MiscDecl $ \s ->     ( [C.cedecl|int $id:s(struct $id:ctx* ctx);|],       [C.cedecl|int $id:s(struct $id:ctx* ctx) {+                         lock_lock(&ctx->lock);                          CUDA_SUCCEED(cuda_free_all(&ctx->cuda));+                         lock_unlock(&ctx->lock);                          return 0;                        }|]     )
src/Futhark/CodeGen/Backends/COpenCL/Boilerplate.hs view
@@ -211,7 +211,8 @@   GC.publicDef_ "context_config_list_devices" GC.InitDecl $ \s ->     ( [C.cedecl|void $id:s(struct $id:cfg* cfg);|],       [C.cedecl|void $id:s(struct $id:cfg* cfg) {-                         list_devices(&cfg->opencl);+                         (void)cfg;+                         list_devices();                        }|]     ) @@ -492,7 +493,9 @@   GC.publicDef_ "context_clear_caches" GC.MiscDecl $ \s ->     ( [C.cedecl|int $id:s(struct $id:ctx* ctx);|],       [C.cedecl|int $id:s(struct $id:ctx* ctx) {+                         lock_lock(&ctx->lock);                          ctx->error = OPENCL_SUCCEED_NONFATAL(opencl_free_all(&ctx->opencl));+                         lock_unlock(&ctx->lock);                          return ctx->error != NULL;                        }|]     )
src/Futhark/CodeGen/Backends/GenericC.hs view
@@ -1053,11 +1053,13 @@     (OpaqueDecl desc)     [C.cedecl|int $id:free_opaque($ty:ctx_ty *ctx, $ty:opaque_type *obj);|] +  ops <- asks envOperations+   return     [C.cunit|           int $id:free_opaque($ty:ctx_ty *ctx, $ty:opaque_type *obj) {             int ret = 0, tmp;-            $items:free_body+            $items:(criticalSection ops free_body)             free(obj);             return ret;           }@@ -1652,6 +1654,7 @@ $esc:("#include <stdint.h>") $esc:("#include <stddef.h>") $esc:("#include <stdbool.h>")+$esc:("#include <float.h>") $esc:(header_extra)  $esc:("\n// Initialisation\n")@@ -1912,8 +1915,8 @@         | null const_fields = [[C.csdecl|int dummy;|]]         | otherwise = const_fields   contextField "constants" [C.cty|struct { $sdecls:const_fields' }|] Nothing-  earlyDecl [C.cedecl|int init_constants($ty:ctx_ty*);|]-  earlyDecl [C.cedecl|int free_constants($ty:ctx_ty*);|]+  earlyDecl [C.cedecl|static int init_constants($ty:ctx_ty*);|]+  earlyDecl [C.cedecl|static int free_constants($ty:ctx_ty*);|]    -- We locally define macros for the constants, so that when we   -- generate assignments to local variables, we actually assign into@@ -1924,7 +1927,7 @@     mapM_ resetMemConst ps     compileCode init_consts   libDecl-    [C.cedecl|int init_constants($ty:ctx_ty *ctx) {+    [C.cedecl|static int init_constants($ty:ctx_ty *ctx) {       (void)ctx;       int err = 0;       $items:defs@@ -1936,7 +1939,7 @@    free_consts <- collect $ mapM_ freeConst ps   libDecl-    [C.cedecl|int free_constants($ty:ctx_ty *ctx) {+    [C.cedecl|static int free_constants($ty:ctx_ty *ctx) {       (void)ctx;       $items:free_consts       return 0;@@ -2129,7 +2132,7 @@       iexp' <- compileExp $ untyped iexp       return [C.cexp|$id:src[$exp:iexp']|]     compileLeaf (SizeOf t) =-      return [C.cexp|(typename int32_t)sizeof($ty:t')|]+      return [C.cexp|(typename int64_t)sizeof($ty:t')|]       where         t' = primTypeToCType t 
src/Futhark/CodeGen/Backends/GenericPython.hs view
@@ -475,7 +475,7 @@   let shape_name = Field arr_name "shape"       src = Index shape_name $ IdxExp $ Integer $ toInteger i   var' <- compileVar var-  stm $ Assign var' $ simpleCall "np.int32" [src]+  stm $ Assign var' $ simpleCall "np.int64" [src]  entryPointOutput :: Imp.ExternalValue -> CompilerM op s PyExp entryPointOutput (Imp.OpaqueValue desc vs) =
src/Futhark/CodeGen/Backends/MulticoreC.hs view
@@ -43,29 +43,15 @@     <=< ImpGen.compileProg   where     generateContext = do-      let multicore_defs_h = $(embedStringFile "rts/c/multicore_defs.h")-          multicore_util_h = $(embedStringFile "rts/c/multicore_util.h")-          subtask_queue_h = $(embedStringFile "rts/c/subtask_queue.h")-          scheduler_common_h = $(embedStringFile "rts/c/scheduler_common.h")-          scheduler_h = $(embedStringFile "rts/c/scheduler.h")-          scheduler_tune_h = $(embedStringFile "rts/c/scheduler_tune.h")--      mapM_-        GC.earlyDecl-        [C.cunit|-                              $esc:multicore_defs_h-                              $esc:multicore_util_h-                              $esc:subtask_queue_h-                              $esc:scheduler_common_h-                              $esc:scheduler_h-                             |]--      mapM_ GC.earlyDecl [C.cunit|int futhark_segred_tuning_program(struct futhark_context *ctx);|]-      mapM_ GC.libDecl [C.cunit|$esc:scheduler_tune_h|]+      let scheduler_h = $(embedStringFile "rts/c/scheduler.h")+      mapM_ GC.earlyDecl [C.cunit|$esc:scheduler_h|]        cfg <- GC.publicDef "context_config" GC.InitDecl $ \s ->         ( [C.cedecl|struct $id:s;|],-          [C.cedecl|struct $id:s { int debugging; int profiling; };|]+          [C.cedecl|struct $id:s { int debugging;+                                   int profiling;+                                   int num_threads;+                                 };|]         )        GC.publicDef_ "context_config_new" GC.InitDecl $ \s ->@@ -77,6 +63,7 @@                                  }                                  cfg->debugging = 0;                                  cfg->profiling = 0;+                                 cfg->num_threads = 0;                                  return cfg;                                }|]         )@@ -110,6 +97,13 @@                                }|]         ) +      GC.publicDef_ "context_config_set_num_threads" GC.InitDecl $ \s ->+        ( [C.cedecl|void $id:s(struct $id:cfg *cfg, int n);|],+          [C.cedecl|void $id:s(struct $id:cfg *cfg, int n) {+                                 cfg->num_threads = n;+                               }|]+        )+       (fields, init_fields) <- GC.contextContents        ctx <- GC.publicDef "context" GC.InitDecl $ \s ->@@ -148,38 +142,26 @@                  ctx->profiling_paused = 0;                  ctx->error = NULL;                  create_lock(&ctx->lock);-                 ctx->scheduler.num_threads = num_processors();-                 if (ctx->scheduler.num_threads < 1) return NULL; -                 $stms:init_fields--                 // futhark_segred_tuning_program(ctx);--                 ctx->scheduler.workers = calloc(ctx->scheduler.num_threads, sizeof(struct worker));-                 if (ctx->scheduler.workers == NULL) return NULL;-                 num_workers = ctx->scheduler.num_threads;--                 worker_local = &ctx->scheduler.workers[0];-                 worker_local->tid = 0;-                 worker_local->scheduler = &ctx->scheduler;-                 CHECK_ERR(subtask_queue_init(&worker_local->q, 1024), "failed to init queue for worker %d\n", 0);-+                 int tune_kappa = 0;+                 double kappa = 5.1f * 1000; -                 for (int i = 1; i < ctx->scheduler.num_threads; i++) {-                   struct worker *cur_worker = &ctx->scheduler.workers[i];-                   memset(cur_worker, 0, sizeof(struct worker));-                   cur_worker->tid = i;-                   cur_worker->output_usage = 0;-                   cur_worker->scheduler = &ctx->scheduler;-                   CHECK_ERR(subtask_queue_init(&cur_worker->q, 1024), "failed to init queue for worker %d\n", i);+                 if (tune_kappa) {+                   if (determine_kappa(&kappa) != 0) {+                     return NULL;+                   }+                 } -                   CHECK_ERR(pthread_create(&cur_worker->thread, NULL, &scheduler_worker,-                                            cur_worker),-                             "Failed to create worker %d\n", i);+                 if (scheduler_init(&ctx->scheduler,+                                    cfg->num_threads > 0 ?+                                    cfg->num_threads : num_processors(),+                                    kappa) != 0) {+                   return NULL;                  } -                 init_constants(ctx);+                 $stms:init_fields +                 init_constants(ctx);                   return ctx;               }|]@@ -189,18 +171,7 @@         ( [C.cedecl|void $id:s(struct $id:ctx* ctx);|],           [C.cedecl|void $id:s(struct $id:ctx* ctx) {                  free_constants(ctx);--                 // output_thread_usage(worker_local);-                 for (int i = 1; i < ctx->scheduler.num_threads; i++)-                 {-                   struct worker *cur_worker = &ctx->scheduler.workers[i];-                   cur_worker->dead = 1;-                   subtask_queue_destroy(&cur_worker->q);-                   CHECK_ERR(pthread_join(ctx->scheduler.workers[i].thread, NULL), "pthread_join");-                 }---                 free(ctx->scheduler.workers);+                 (void)scheduler_destroy(&ctx->scheduler);                  free_lock(&ctx->lock);                  free(ctx);                }|]@@ -222,6 +193,13 @@         optionArgument = NoArgument,         optionAction = [C.cstm|futhark_context_config_set_profiling(cfg, 1);|],         optionDescription = "Gather profiling information."+      },+    Option+      { optionLongName = "num-threads",+        optionShortName = Nothing,+        optionArgument = RequiredArgument "INT",+        optionAction = [C.cstm|futhark_context_config_set_num_threads(cfg, atoi(optarg));|],+        optionDescription = "Set number of threads used for execution."       }   ] @@ -428,7 +406,7 @@   addBenchmarkFields name tid   return     [C.citems|-     typename uint64_t $id:start;+     typename uint64_t $id:start = 0;      if (ctx->profiling && !ctx->profiling_paused) {        $id:start = get_wall_time();      }@@ -473,7 +451,7 @@   GC.libDecl =<< f s'   return s' -generateFunction ::+generateParLoopFn ::   C.ToIdent a =>   M.Map VName Space ->   String ->@@ -484,7 +462,7 @@   VName ->   VName ->   GC.CompilerM Multicore s Name-generateFunction lexical basename code fstruct free retval tid ntasks = do+generateParLoopFn lexical basename code fstruct free retval tid ntasks = do   let (fargs, fctypes) = unzip free   let (retval_args, retval_ctypes) = unzip retval   multicoreDef basename $ \s -> do@@ -549,7 +527,7 @@   fstruct <-     prepareTaskStruct "task" free_args free_ctypes retval_args retval_ctypes -  fpar_task <- generateFunction lexical (name ++ "_task") seq_code fstruct free retval tid nsubtask+  fpar_task <- generateParLoopFn lexical (name ++ "_task") seq_code fstruct free retval tid nsubtask   addTimingFields fpar_task    let ftask_name = fstruct <> "_task"@@ -570,7 +548,7 @@   fnpar_task <- case par_task of     Just (ParallelTask nested_code nested_tid) -> do       let lexical_nested = lexicalMemoryUsage $ Function False [] params nested_code [] []-      fnpar_task <- generateFunction lexical_nested (name ++ "_nested_task") nested_code fstruct free retval nested_tid nsubtask+      fnpar_task <- generateParLoopFn lexical_nested (name ++ "_nested_task") nested_code fstruct free retval nested_tid nsubtask       GC.stm [C.cstm|$id:ftask_name.nested_fn = $id:fnpar_task;|]       return $ zip [fnpar_task] [True]     Nothing -> do@@ -623,7 +601,7 @@           mapM_ GC.stm free_cached      return-      [C.cedecl|int $id:s(void *args, typename int64_t start, typename int64_t end, int $id:tid, int tid) {+      [C.cedecl|static int $id:s(void *args, typename int64_t start, typename int64_t end, int $id:tid, int tid) {                        int err = 0;                        struct $id:fstruct *$id:fstruct = (struct $id:fstruct*) args;                        struct futhark_context *ctx = $id:fstruct->ctx;@@ -645,7 +623,8 @@     benchmarkCode       ftask_total       Nothing-      [C.citems|int $id:ftask_err = scheduler_execute_task(&ctx->scheduler, &$id:ftask_name);+      [C.citems|int $id:ftask_err = scheduler_execute_task(&ctx->scheduler,+                                                           &$id:ftask_name);                if ($id:ftask_err != 0) {                  err = 1;                  goto cleanup;
src/Futhark/CodeGen/ImpGen.hs view
@@ -126,6 +126,7 @@ import qualified Data.Map.Strict as M import Data.Maybe import qualified Data.Set as S+import Data.String import Futhark.CodeGen.ImpCode   ( Bytes,     Count,@@ -394,7 +395,7 @@ -- | Emit a warning about something the user should be aware of. warn :: Located loc => loc -> [loc] -> String -> ImpM lore r op () warn loc locs problem =-  warnings $ singleWarning' (srclocOf loc) (map srclocOf locs) problem+  warnings $ singleWarning' (srclocOf loc) (map srclocOf locs) (fromString problem)  -- | Emit a function in the generated code. emitFunction :: Name -> Imp.Function op -> ImpM lore r op ()@@ -1087,9 +1088,6 @@    -- | Compile where we know the type in advance.   toExp' :: PrimType -> a -> Imp.Exp--  toInt32Exp :: a -> Imp.TExp Int32-  toInt32Exp = TPrimExp . toExp' int32    toInt64Exp :: a -> Imp.TExp Int64   toInt64Exp = TPrimExp . toExp' int64
src/Futhark/CodeGen/ImpGen/Kernels.hs view
@@ -110,11 +110,8 @@   -- The calculations are done with 64-bit integers to avoid overflow   -- issues.   let num_groups_maybe_zero =-        sMin64-          ( toInt64Exp w64-              `divUp` sExt64 (toInt32Exp group_size)-          )-          $ sExt64 (tvExp max_num_groups)+        sMin64 (toInt64Exp w64 `divUp` toInt64Exp group_size) $+          sExt64 (tvExp max_num_groups)   -- We also don't want zero groups.   let num_groups = sMax64 1 num_groups_maybe_zero   mkTV (patElemName pe) int32 <-- sExt32 num_groups
src/Futhark/CodeGen/ImpGen/Kernels/SegHist.hs view
@@ -1072,8 +1072,9 @@     -- Compute RF as the average RF over all the histograms.     hist_RF <-       dPrimVE "hist_RF" $-        sum (map (toInt32Exp . histRaceFactor . slugOp) slugs)-          `quot` genericLength slugs+        sExt32 $+          sum (map (toInt64Exp . histRaceFactor . slugOp) slugs)+            `quot` genericLength slugs      let hist_T = sExt32 $ unCount num_groups' * unCount group_size'     emit $ Imp.DebugPrint "\n# SegHist" Nothing
src/Futhark/CodeGen/ImpGen/Multicore.hs view
@@ -53,7 +53,7 @@ compileMCOp _ (OtherOp ()) = pure () compileMCOp pat (ParOp par_op op) = do   let space = getSpace op-  dPrimV_ (segFlat space) (0 :: Imp.TExp Int32)+  dPrimV_ (segFlat space) (0 :: Imp.TExp Int64)   iterations <- getIterationDomain op space   nsubtasks <- dPrim "num_tasks" $ IntType Int32   seq_code <- compileSegOp pat op nsubtasks@@ -64,7 +64,7 @@   par_code <- case par_op of     Just nested_op -> do       let space' = getSpace nested_op-      dPrimV_ (segFlat space') (0 :: Imp.TExp Int32)+      dPrimV_ (segFlat space') (0 :: Imp.TExp Int64)       compileSegOp pat nested_op nsubtasks     Nothing -> return mempty 
src/Futhark/CodeGen/ImpGen/Multicore/Base.hs view
@@ -70,11 +70,11 @@ getIterationDomain :: SegOp () MCMem -> SegSpace -> MulticoreGen (Imp.TExp Int64) getIterationDomain SegMap {} space = do   let ns = map snd $ unSegSpace space-      ns_64 = map (sExt64 . toInt32Exp) ns+      ns_64 = map toInt64Exp ns   return $ product ns_64 getIterationDomain _ space = do   let ns = map snd $ unSegSpace space-      ns_64 = map (sExt64 . toInt32Exp) ns+      ns_64 = map toInt64Exp ns   case unSegSpace space of     [_] -> return $ product ns_64     -- A segmented SegOp is over the segments
src/Futhark/CodeGen/ImpGen/Multicore/SegHist.hs view
@@ -49,12 +49,14 @@    histops' <- renameHistOpLambda histops -  collect $ do-    flat_idx <- dPrim "iter" int64-    sIf-      use_subhistogram-      (subHistogram pat flat_idx space histops num_histos kbody)-      (atomicHistogram pat flat_idx space histops' kbody)+  -- Only do something if there is actually input.+  collect $+    sUnless (product ns_64 .==. 0) $ do+      flat_idx <- dPrim "iter" int64+      sIf+        use_subhistogram+        (subHistogram pat flat_idx space histops num_histos kbody)+        (atomicHistogram pat flat_idx space histops' kbody)  -- | -- Atomic Histogram approach
src/Futhark/CodeGen/ImpGen/Multicore/SegRed.hs view
@@ -95,7 +95,7 @@   MulticoreGen () reductionStage1 space slugs kbody = do   let (is, ns) = unzip $ unSegSpace space-      ns' = map (sExt64 . toInt32Exp) ns+      ns' = map toInt64Exp ns   flat_idx <- dPrim "iter" int64    -- Create local accumulator variables in which we carry out the
src/Futhark/CodeGen/ImpGen/Multicore/SegScan.hs view
@@ -66,7 +66,7 @@       per_scan_pes = segBinOpChunks scan_ops $ patternValueElements pat   let (is, ns) = unzip $ unSegSpace space       ns' = map toInt64Exp ns-  iter <- dPrim "iter" $ IntType Int32+  iter <- dPrim "iter" $ IntType Int64    -- Stage 1 : each thread partially scans a chunk of the input   -- Writes directly to the resulting array
src/Futhark/Compiler.hs view
@@ -31,6 +31,7 @@ import qualified Futhark.TypeCheck as I import Futhark.Util.Log import Futhark.Util.Pretty (ppr, prettyText)+import Language.Futhark.Warnings import System.Exit (ExitCode (..), exitWith) import System.IO @@ -180,9 +181,9 @@ handleWarnings config m = do   (ws, a) <- m -  when (futharkWarn config) $ do-    liftIO $ hPutStr stderr $ show ws-    when (futharkWerror config && ws /= mempty) $+  when (futharkWarn config && anyWarnings ws) $ do+    liftIO $ hPutStrLn stderr $ pretty ws+    when (futharkWerror config) $       externalErrorS "Treating above warnings as errors due to --Werror."    return a
src/Futhark/Compiler/Program.hs view
@@ -26,12 +26,13 @@ import qualified Data.Text.IO as T import Futhark.Error import Futhark.FreshNames-import Futhark.Util.Pretty (ppr)+import Futhark.Util.Pretty (line, ppr, (</>)) import qualified Language.Futhark as E import Language.Futhark.Parser import Language.Futhark.Prelude import Language.Futhark.Semantic import qualified Language.Futhark.TypeChecker as E+import Language.Futhark.Warnings import qualified System.FilePath.Posix as Posix import System.IO.Error @@ -104,9 +105,14 @@   roots <- ask    case E.checkProg imports src include $ prependRoots roots prog of-    Left err ->-      externalError $ ppr err-    Right (m, ws, src') ->+    (prog_ws, Left err) -> do+      prev_ws <- gets warnings+      let ws = prev_ws <> prog_ws+      externalError $+        if anyWarnings ws+          then ppr (prev_ws <> ws) </> line <> ppr err+          else ppr err+    (ws, Right (m, src')) ->       modify $ \s ->         s           { alreadyImported = (includeToString include, m) : imports,
src/Futhark/Construct.hs view
@@ -108,6 +108,7 @@ import Control.Monad.Identity import Control.Monad.State import Control.Monad.Writer+import Data.Bifunctor (second) import Data.List (sortOn) import qualified Data.Map.Strict as M import Futhark.Binder@@ -576,11 +577,14 @@   [TypeBase ExtShape u] ->   m ([TypeBase Shape u], [Ident]) instantiateShapes' ts =-  runWriterT $ instantiateShapes instantiate ts+  -- Carefully ensure that the order of idents we produce corresponds+  -- to their existential index.+  second (map snd . sortOn fst)+    <$> runWriterT (instantiateShapes instantiate ts)   where-    instantiate _ = do+    instantiate x = do       v <- lift $ newIdent "size" $ Prim int64-      tell [v]+      tell [(x, v)]       return $ Var $ identName v  removeExistentials :: ExtType -> Type -> Type
src/Futhark/Doc/Generator.hs view
@@ -86,7 +86,7 @@ -- can generate an index. type Documented = M.Map VName IndexWhat -warn :: SrcLoc -> String -> DocM ()+warn :: SrcLoc -> Doc -> DocM () warn loc s = lift $ lift $ tell $ singleWarning loc s  document :: VName -> IndexWhat -> DocM ()@@ -724,17 +724,17 @@         case maybe_v of           Nothing -> do             warn loc $-              "Identifier '" <> name <> "' not found in namespace '"-                <> namespace+              "Identifier '" <> fromString name <> "' not found in namespace '"+                <> fromString namespace                 <> "'"-                <> maybe "" (" in file " <>) file+                <> fromString (maybe "" (" in file " <>) file)                 <> "."             unknown           Just v' -> do             link <- vnameLink v'             proceed $ "[`" <> name <> "`](" <> link <> ")"       _ -> do-        warn loc $ "Unknown namespace '" <> namespace <> "'."+        warn loc $ "Unknown namespace '" <> fromString namespace <> "'."         unknown   where     knownNamespace "term" = Just Term
src/Futhark/Internalise.hs view
@@ -87,7 +87,7 @@         Nothing -> return $ errorMsg ["Function return value does not match shape of declared return type."]        ((rettype', body_res), body_stms) <- collectStms $ do-        body_res <- internaliseExp "res" body+        body_res <- internaliseExp (baseString fname <> "_res") body         rettype_bad <- internaliseReturnType rettype         let rettype' = zeroExts rettype_bad         return (rettype', body_res)@@ -319,8 +319,9 @@           ++ locStr loc           ++ "." -internaliseBody :: E.Exp -> InternaliseM Body-internaliseBody e = insertStmsM $ resultBody <$> internaliseExp "res" e+internaliseBody :: String -> E.Exp -> InternaliseM Body+internaliseBody desc e =+  insertStmsM $ resultBody <$> internaliseExp (desc <> "_res") e  bodyFromStms ::   InternaliseM (Result, a) ->@@ -888,7 +889,7 @@         let CasePat pLast eLast _ = NE.last cs'         bFalse <- do           (_, pertinent) <- generateCond pLast ses-          eLast' <- internalisePat' pLast pertinent eLast internaliseBody+          eLast' <- internalisePat' pLast pertinent eLast (internaliseBody desc)           foldM (\bf c' -> eBody $ return $ generateCaseIf ses c' bf) eLast' $             reverse $ NE.init cs'         letTupExp' desc =<< generateCaseIf ses c bFalse@@ -918,8 +919,8 @@     letTupExp' desc       =<< eIf         (BasicOp . SubExp <$> internaliseExp1 "cond" ce)-        (internaliseBody te)-        (internaliseBody fe)+        (internaliseBody (desc <> "_t") te)+        (internaliseBody (desc <> "_f") fe)   bindExtSizes (E.toStruct ret) retext ses   return ses @@ -990,8 +991,11 @@   return (cmp, pertinent)   where     -- Literals are always primitive values.-    compares (E.PatternLit e _ _) (se : ses) = do-      e' <- internaliseExp1 "constant" e+    compares (E.PatternLit l t _) (se : ses) = do+      e' <- case l of+        PatLitPrim v -> pure $ constant $ internalisePrimValue v+        PatLitInt x -> internaliseExp1 "constant" $ E.IntLit x t mempty+        PatLitFloat x -> internaliseExp1 "constant" $ E.FloatLit x t mempty       t' <- elemType <$> subExpType se       cmp <- letSubExp "match_lit" $ I.BasicOp $ I.CmpOp (I.CmpEq t') e' se       return ([cmp], [se], ses)@@ -1038,7 +1042,7 @@ generateCaseIf :: [I.SubExp] -> Case -> I.Body -> InternaliseM I.Exp generateCaseIf ses (CasePat p eCase _) bFail = do   (cond, pertinent) <- generateCond p ses-  eCase' <- internalisePat' p pertinent eCase internaliseBody+  eCase' <- internalisePat' p pertinent eCase (internaliseBody "case")   eIf (eSubExp cond) (return eCase') (return bFail)  internalisePat ::@@ -1626,7 +1630,7 @@   internaliseLambda e rowtypes internaliseLambda (E.Lambda params body _ (Info (_, rettype)) _) rowtypes =   bindingLambdaParams params rowtypes $ \params' -> do-    body' <- internaliseBody body+    body' <- internaliseBody "lam" body     rettype' <- internaliseLambdaReturnType rettype     return (params', body', rettype') internaliseLambda e _ = error $ "internaliseLambda: unexpected expression:\n" ++ pretty e
src/Futhark/Internalise/Defunctionalise.hs view
@@ -66,6 +66,70 @@ isGlobal :: VName -> DefM a -> DefM a isGlobal v = local $ Arrow.first (S.insert v) +replaceStaticValSizes :: M.Map VName VName -> StaticVal -> StaticVal+replaceStaticValSizes substs sv =+  case sv of+    LambdaSV sizes param t e closure_env ->+      LambdaSV+        sizes+        (onAST param)+        (onType t)+        (onExtExp e)+        (onEnv closure_env)+    Dynamic t ->+      Dynamic $ onType t+    RecordSV fs ->+      RecordSV $ map (fmap (replaceStaticValSizes substs)) fs+    SumSV c svs ts ->+      SumSV c (map (replaceStaticValSizes substs) svs) $+        map (fmap (map onType)) ts+    DynamicFun (e, sv1) sv2 ->+      DynamicFun (onAST e, replaceStaticValSizes substs sv1) $+        replaceStaticValSizes substs sv2+    IntrinsicSV ->+      IntrinsicSV+  where+    onName v = fromMaybe v $ M.lookup v substs+    onQualName v = maybe v qualName $ M.lookup (qualLeaf v) substs++    tv =+      identityMapper+        { mapOnPatternType = pure . onType,+          mapOnStructType = pure . onType,+          mapOnQualName = pure . onQualName,+          mapOnExp = pure . onAST+        }++    onExtExp (ExtExp e) =+      ExtExp $ onAST e+    onExtExp (ExtLambda dims params e (als, t) loc) =+      ExtLambda dims (map onAST params) (onAST e) (als, onType t) loc++    onEnv =+      M.fromList+        . map (bimap onName $ replaceStaticValSizes substs)+        . M.toList++    onAST :: ASTMappable x => x -> x+    onAST = runIdentity . astMap tv++    onType = first onDim+      where+        onDim (NamedDim v) =+          NamedDim $ maybe v qualName $ M.lookup (qualLeaf v) substs+        onDim d = d++-- | Construct new sizes for a LambdaSV (if that is what it is).  This+-- is needed because sizes must be unique when we substitute the+-- closure for the LambdaSV into another function, because sizes float+-- to the top (see issue #1147).+newSizesForLambda :: StaticVal -> DefM StaticVal+newSizesForLambda (LambdaSV sizes param t e closure_env) = do+  sizes' <- mapM newName sizes+  let substs = M.fromList $ zip sizes sizes'+  pure $ replaceStaticValSizes substs $ LambdaSV sizes' param t e closure_env+newSizesForLambda sv = pure sv+ -- | Returns the defunctionalization environment restricted -- to the given set of variable names and types. restrictEnvTo :: NameSet -> DefM Env@@ -517,7 +581,8 @@ defuncExtExp :: ExtExp -> DefM (Exp, StaticVal) defuncExtExp (ExtExp e) = defuncExp e defuncExtExp (ExtLambda tparams pats e0 (closure, ret) loc) =-  defuncFun tparams pats e0 (closure, ret) loc+  traverse newSizesForLambda+    =<< defuncFun tparams pats e0 (closure, ret) loc  defuncCase :: StaticVal -> Case -> DefM (Case, StaticVal) defuncCase sv (CasePat p e loc) = do@@ -731,7 +796,7 @@     -- If e1 is a dynamic function, we just leave the application in place,     -- but we update the types since it may be partially applied or return     -- a higher-order term.-    DynamicFun _ sv ->+    DynamicFun _ sv -> do       let (argtypes', rettype) = dynamicFunType sv argtypes           restype = foldFunType argtypes' rettype `setAliases` aliases ret           -- FIXME: what if this application returns both a function@@ -740,7 +805,8 @@             | orderZero ret = (Info ret, Info ext)             | otherwise = (Info restype, Info ext)           apply_e = Apply e1' e2' d callret loc-       in return (apply_e, sv)+      sv' <- newSizesForLambda sv+      return (apply_e, sv')     -- Propagate the 'IntrinsicsSV' until we reach the outermost application,     -- where we construct a dynamic static value with the appropriate type.     IntrinsicSV@@ -1123,9 +1189,11 @@       <> ( (names (patternDimNames pat) <> freeVars e2)              `without` patternVars pat          )-  LetFun vn (_, pats, _, _, e1) e2 _ _ ->+  LetFun vn (tparams, pats, _, _, e1) e2 _ _ ->     ( (freeVars e1 <> names (foldMap patternDimNames pats))-        `without` foldMap patternVars pats+        `without` ( foldMap patternVars pats+                      <> foldMap (oneName . typeParamName) tparams+                  )     )       <> (freeVars e2 `without` oneName vn)   If e1 e2 e3 _ _ -> freeVars e1 <> freeVars e2 <> freeVars e3
src/Futhark/Internalise/Monomorphise.hs view
@@ -329,7 +329,7 @@     <*> pure (Info t', retext)     <*> pure loc transformExp (LetFun fname (tparams, params, retdecl, Info ret, body) e e_t loc)-  | any isTypeParam tparams = do+  | not $ null tparams = do     -- Retrieve the lifted monomorphic function bindings that are produced,     -- filter those that are monomorphic versions of the current let-bound     -- function and insert them at this point, and propagate the rest.
src/Futhark/Optimise/InPlaceLowering/LowerIntoStm.hs view
@@ -131,8 +131,8 @@           (slice', bodystms) <-             flip runBinderT scope $               traverse (toSubExp "index") $-                fixSlice (map (fmap pe32) slice) $-                  map (pe32 . Var) gtids+                fixSlice (map (fmap pe64) slice) $+                  map (pe64 . Var) gtids            let res_dims = arrayDims $ snd bindee_dec               ret' = WriteReturns res_dims src [(map DimFix slice', se)]
src/Futhark/Optimise/InPlaceLowering/SubstituteIndices.hs view
@@ -17,6 +17,7 @@ import Futhark.Construct import Futhark.IR import Futhark.IR.Prop.Aliases+import Futhark.Transform.Substitute import Futhark.Util  type IndexSubstitution dec = (Certificates, VName, dec, Slice SubExp)@@ -87,6 +88,15 @@   IndexSubstitutions (LetDec (Lore m)) ->   Exp (Lore m) ->   m (Exp (Lore m))+substituteIndicesInExp substs (Op op) = do+  let used_in_op = filter ((`nameIn` freeIn op) . fst) substs+  var_substs <- fmap mconcat $+    forM used_in_op $ \(v, (cs, src, src_dec, is)) -> do+      v' <-+        certifying cs $+          letExp "idx" $ BasicOp $ Index src $ fullSlice (typeOf src_dec) is+      pure $ M.singleton v v'+  pure $ Op $ substituteNames var_substs op substituteIndicesInExp substs e = do   substs' <- copyAnyConsumed e   let substitute =
src/Futhark/Optimise/TileLoops.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE LambdaCase #-} {-# LANGUAGE TypeFamilies #-}  -- | Perform a restricted form of loop tiling within SegMaps.  We only@@ -9,6 +10,7 @@ import Control.Monad.State import Data.List (foldl') import qualified Data.Map.Strict as M+import Data.Maybe (mapMaybe) import qualified Data.Sequence as Seq import Futhark.IR.Kernels import Futhark.MonadFreshNames@@ -101,52 +103,50 @@     descend _ [] =       return Nothing     descend prestms (stm_to_tile : poststms)-      -- 1D tiling of redomap.-      | (gtid, kdim) : top_space_rev <- reverse $ unSegSpace initial_space,+      -- 2D tiling of redomap.+      | (gtids, kdims) <- unzip $ unSegSpace initial_space,         Just (w, arrs, form) <- tileable stm_to_tile,-        not $-          any-            ( nameIn gtid-                . flip (M.findWithDefault mempty) variance-            )-            arrs,-        not $ gtid `nameIn` branch_variant,+        Just inputs <-+          mapM (invariantToOneOfTwoInnerDims branch_variant variance gtids) arrs,+        not $ null $ tiledInputs inputs,+        gtid_y : gtid_x : top_gtids_rev <- reverse gtids,+        kdim_y : kdim_x : top_kdims_rev <- reverse kdims,         (prestms', poststms') <-           preludeToPostlude variance prestms stm_to_tile (stmsFromList poststms),         used <- freeIn stm_to_tile <> freeIn poststms' <> freeIn stms_res =         Just . injectPrelude initial_space private variance prestms' used           <$> tileGeneric-            (tiling1d $ reverse top_space_rev)+            (tiling2d $ reverse $ zip top_gtids_rev top_kdims_rev)             initial_lvl             res_ts             (stmPattern stm_to_tile)-            gtid-            kdim+            (gtid_x, gtid_y)+            (kdim_x, kdim_y)             w             form-            (zip arrs $ repeat [0])+            inputs             poststms'             stms_res-      -- 2D tiling of redomap.-      | (gtids, kdims) <- unzip $ unSegSpace initial_space,+      -- 1D tiling of redomap.+      | (gtid, kdim) : top_space_rev <- reverse $ unSegSpace initial_space,         Just (w, arrs, form) <- tileable stm_to_tile,-        Just inner_perm <- mapM (invariantToOneOfTwoInnerDims branch_variant variance gtids) arrs,-        gtid_y : gtid_x : top_gtids_rev <- reverse gtids,-        kdim_y : kdim_x : top_kdims_rev <- reverse kdims,+        inputs <- map (is1DTileable gtid variance) arrs,+        not $ null $ tiledInputs inputs,+        not $ gtid `nameIn` branch_variant,         (prestms', poststms') <-           preludeToPostlude variance prestms stm_to_tile (stmsFromList poststms),         used <- freeIn stm_to_tile <> freeIn poststms' <> freeIn stms_res =         Just . injectPrelude initial_space private variance prestms' used           <$> tileGeneric-            (tiling2d $ reverse $ zip top_gtids_rev top_kdims_rev)+            (tiling1d $ reverse top_space_rev)             initial_lvl             res_ts             (stmPattern stm_to_tile)-            (gtid_x, gtid_y)-            (kdim_x, kdim_y)+            gtid+            kdim             w             form-            (zip arrs inner_perm)+            inputs             poststms'             stms_res       -- Tiling inside for-loop.@@ -453,6 +453,50 @@   | otherwise =     Nothing +-- | We classify the inputs to the tiled loop as whether they are+-- tileable (and with what permutation of the kernel indexes) or not.+-- In practice, we should have at least one tileable array per loop,+-- but this is not enforced in our representation.+data InputArray+  = InputTile [Int] VName+  | InputDontTile VName++tiledInputs :: [InputArray] -> [(VName, [Int])]+tiledInputs = mapMaybe f+  where+    f (InputTile perm arr) = Just (arr, perm)+    f InputDontTile {} = Nothing++-- | A tile (or an original untiled array).+data InputTile+  = InputTiled [Int] VName+  | InputUntiled VName++-- First VNames are the tiles, second are the untiled.+inputsToTiles :: [InputArray] -> [VName] -> [InputTile]+inputsToTiles (InputTile perm _ : inputs) (tile : tiles) =+  InputTiled perm tile : inputsToTiles inputs tiles+inputsToTiles (InputDontTile arr : inputs) tiles =+  InputUntiled arr : inputsToTiles inputs tiles+inputsToTiles _ _ = []++-- The atual tile size may be smaller for the last tile, so we have to+-- be careful now.+sliceUntiled ::+  MonadBinder m =>+  VName ->+  SubExp ->+  SubExp ->+  SubExp ->+  m VName+sliceUntiled arr tile_id full_tile_size this_tile_size = do+  arr_t <- lookupType arr+  slice_offset <-+    letSubExp "slice_offset" =<< toExp (pe64 tile_id * pe64 full_tile_size)+  let slice = DimSlice slice_offset this_tile_size (intConst Int64 1)+  letExp "untiled_slice" $+    BasicOp $ Index arr $ fullSlice arr_t [slice]+ -- | Statements that we insert directly into every thread-private -- SegMaps.  This is for things that cannot efficiently be computed -- once in advance in the prelude SegMap, primarily (exclusively?)@@ -479,6 +523,27 @@  type ReadPrelude = Slice SubExp -> Binder Kernels () +data ProcessTileArgs = ProcessTileArgs+  { processPrivStms :: PrivStms,+    processComm :: Commutativity,+    processRedLam :: Lambda Kernels,+    processMapLam :: Lambda Kernels,+    processTiles :: [InputTile],+    processAcc :: [VName],+    processTileId :: SubExp+  }++data ResidualTileArgs = ResidualTileArgs+  { residualPrivStms :: PrivStms,+    residualComm :: Commutativity,+    residualRedLam :: Lambda Kernels,+    residualMapLam :: Lambda Kernels,+    residualInput :: [InputArray],+    residualAcc :: [VName],+    residualInputSize :: SubExp,+    residualNumWholeTiles :: SubExp+  }+ -- | Information about a loop that has been tiled inside a kernel, as -- well as the kinds of changes that we would then like to perform on -- the kernel.@@ -495,25 +560,13 @@       TileKind ->       PrivStms ->       SubExp ->-      [(VName, [Int])] ->-      Binder Kernels [VName],+      [InputArray] ->+      Binder Kernels [InputTile],     tilingProcessTile ::-      PrivStms ->-      Commutativity ->-      Lambda Kernels ->-      Lambda Kernels ->-      [(VName, [Int])] ->-      [VName] ->+      ProcessTileArgs ->       Binder Kernels [VName],     tilingProcessResidualTile ::-      PrivStms ->-      Commutativity ->-      Lambda Kernels ->-      Lambda Kernels ->-      SubExp ->-      [VName] ->-      SubExp ->-      [(VName, [Int])] ->+      ResidualTileArgs ->       Binder Kernels [VName],     tilingTileReturns :: VName -> Binder Kernels KernelResult,     tilingSpace :: SegSpace,@@ -578,11 +631,11 @@   kdims ->   SubExp ->   (Commutativity, Lambda Kernels, [SubExp], Lambda Kernels) ->-  [(VName, [Int])] ->+  [InputArray] ->   Stms Kernels ->   Result ->   TileM (Stms Kernels, Tiling, TiledBody)-tileGeneric doTiling initial_lvl res_ts pat gtids kdims w form arrs_and_perms poststms poststms_res = do+tileGeneric doTiling initial_lvl res_ts pat gtids kdims w form inputs poststms poststms_res = do   (tiling, tiling_stms) <- runBinder $ doTiling initial_lvl gtids kdims w    return (tiling_stms, tiling, tiledBody tiling)@@ -619,36 +672,24 @@         inScopeOf loopform $           localScope (scopeOfFParams $ map fst merge) $ do             -- Collectively read a tile.-            tile <- tilingReadTile tiling TilePartial privstms (Var tile_id) arrs_and_perms+            tile <- tilingReadTile tiling TilePartial privstms (Var tile_id) inputs              -- Now each thread performs a traversal of the tile and             -- updates its accumulator.-            resultBody . map Var-              <$> tilingProcessTile-                tiling-                privstms-                red_comm-                red_lam-                map_lam-                (zip tile (map snd arrs_and_perms))-                (map (paramName . fst) merge)+            let accs =+                  map (paramName . fst) merge+                tile_args =+                  ProcessTileArgs privstms red_comm red_lam map_lam tile accs (Var tile_id)+            resultBody . map Var <$> tilingProcessTile tiling tile_args        accs <- letTupExp "accs" $ DoLoop [] merge loopform loopbody        -- We possibly have to traverse a residual tile.       red_lam' <- renameLambda red_lam       map_lam' <- renameLambda map_lam-      accs' <--        tilingProcessResidualTile-          tiling-          privstms-          red_comm-          red_lam'-          map_lam'-          num_whole_tiles-          accs-          w-          arrs_and_perms+      let residual_args =+            ResidualTileArgs privstms red_comm red_lam' map_lam' inputs accs w num_whole_tiles+      accs' <- tilingProcessResidualTile tiling residual_args        -- Create a SegMap that takes care of the postlude for every thread.       postludeGeneric tiling privstms pat accs' poststms poststms_res res_ts@@ -675,6 +716,13 @@   let tile_dims = zip (map snd dims_on_top) unit_dims ++ dims   return $ TileReturns tile_dims arr' +is1DTileable :: VName -> M.Map VName Names -> VName -> InputArray+is1DTileable gtid variance arr+  | not $ nameIn gtid $ M.findWithDefault mempty arr variance =+    InputTile [0] arr+  | otherwise =+    InputDontTile arr+ segMap1D ::   String ->   SegLevel ->@@ -716,19 +764,12 @@   TileKind ->   PrivStms ->   SubExp ->-  [(VName, [Int])] ->-  Binder Kernels [VName]-readTile1D-  tile_size-  gid-  gtid-  num_groups-  group_size-  kind-  privstms-  tile_id-  arrs_and_perms =-    segMap1D "full_tile" (SegThread num_groups group_size SegNoVirt) ResultNoSimplify $ \ltid -> do+  [InputArray] ->+  Binder Kernels [InputTile]+readTile1D tile_size gid gtid num_groups group_size kind privstms tile_id inputs =+  fmap (inputsToTiles inputs)+    . segMap1D "full_tile" lvl ResultNoSimplify+    $ \ltid -> do       j <-         letSubExp "j"           =<< toExp (pe64 tile_id * pe64 tile_size + le64 ltid)@@ -736,14 +777,14 @@       reconstructGtids1D group_size gtid gid ltid       addPrivStms [DimFix $ Var ltid] privstms -      let arrs = map fst arrs_and_perms+      let arrs = map fst $ tiledInputs inputs       arr_ts <- mapM lookupType arrs       let tile_ts = map rowType arr_ts           w = arraysSize 0 arr_ts        let readTileElem arr =             -- No need for fullSlice because we are tiling only prims.-            letExp "tile_elem" $ BasicOp $ Index arr [DimFix j]+            letExp "tile_elem" (BasicOp $ Index arr [DimFix j])       fmap (map Var) $         case kind of           TilePartial ->@@ -754,6 +795,8 @@                 (eBody $ map eBlank tile_ts)           TileFull ->             mapM readTileElem arrs+  where+    lvl = SegThread num_groups group_size SegNoVirt  processTile1D ::   VName ->@@ -762,46 +805,43 @@   SubExp ->   Count NumGroups SubExp ->   Count GroupSize SubExp ->-  PrivStms ->-  Commutativity ->-  Lambda Kernels ->-  Lambda Kernels ->-  [(VName, [Int])] ->-  [VName] ->+  ProcessTileArgs ->   Binder Kernels [VName]-processTile1D-  gid-  gtid-  kdim-  tile_size-  num_groups-  group_size-  privstms-  red_comm-  red_lam-  map_lam-  tiles_and_perm-  accs = do-    let tile = map fst tiles_and_perm+processTile1D gid gtid kdim tile_size num_groups group_size tile_args = do+  let red_comm = processComm tile_args+      privstms = processPrivStms tile_args+      map_lam = processMapLam tile_args+      red_lam = processRedLam tile_args+      tiles = processTiles tile_args+      tile_id = processTileId tile_args+      accs = processAcc tile_args -    segMap1D "acc" (SegThread num_groups group_size SegNoVirt) ResultPrivate $ \ltid -> do-      reconstructGtids1D group_size gtid gid ltid-      addPrivStms [DimFix $ Var ltid] privstms+  segMap1D "acc" lvl ResultPrivate $ \ltid -> do+    reconstructGtids1D group_size gtid gid ltid+    addPrivStms [DimFix $ Var ltid] privstms -      -- We replace the neutral elements with the accumulators (this is-      -- OK because the parallel semantics are not used after this-      -- point).-      thread_accs <- forM accs $ \acc ->-        letSubExp "acc" $ BasicOp $ Index acc [DimFix $ Var ltid]-      let form' = redomapSOAC [Reduce red_comm red_lam thread_accs] map_lam+    -- We replace the neutral elements with the accumulators (this is+    -- OK because the parallel semantics are not used after this+    -- point).+    thread_accs <- forM accs $ \acc ->+      letSubExp "acc" $ BasicOp $ Index acc [DimFix $ Var ltid]+    let sliceTile (InputTiled _ arr) =+          pure arr+        sliceTile (InputUntiled arr) =+          sliceUntiled arr tile_id tile_size tile_size -      fmap (map Var) $-        letTupExp "acc"-          =<< eIf-            (toExp $ le64 gtid .<. pe64 kdim)-            (eBody [pure $ Op $ OtherOp $ Screma tile_size form' tile])-            (resultBodyM thread_accs)+    tiles' <- mapM sliceTile tiles +    let form' = redomapSOAC [Reduce red_comm red_lam thread_accs] map_lam+    fmap (map Var) $+      letTupExp "acc"+        =<< eIf+          (toExp $ le64 gtid .<. pe64 kdim)+          (eBody [pure $ Op $ OtherOp $ Screma tile_size form' tiles'])+          (resultBodyM thread_accs)+  where+    lvl = SegThread num_groups group_size SegNoVirt+ processResidualTile1D ::   VName ->   VName ->@@ -809,80 +849,62 @@   SubExp ->   Count NumGroups SubExp ->   Count GroupSize SubExp ->-  PrivStms ->-  Commutativity ->-  Lambda Kernels ->-  Lambda Kernels ->-  SubExp ->-  [VName] ->-  SubExp ->-  [(VName, [Int])] ->+  ResidualTileArgs ->   Binder Kernels [VName]-processResidualTile1D-  gid-  gtid-  kdim-  tile_size-  num_groups-  group_size-  privstms-  red_comm-  red_lam-  map_lam-  num_whole_tiles-  accs-  w-  arrs_and_perms = do-    -- The number of residual elements that are not covered by-    -- the whole tiles.-    residual_input <--      letSubExp "residual_input" $-        BasicOp $ BinOp (SRem Int64 Unsafe) w tile_size+processResidualTile1D gid gtid kdim tile_size num_groups group_size args = do+  -- The number of residual elements that are not covered by+  -- the whole tiles.+  residual_input <-+    letSubExp "residual_input" $+      BasicOp $ BinOp (SRem Int64 Unsafe) w tile_size -    letTupExp "acc_after_residual"-      =<< eIf-        (toExp $ pe64 residual_input .==. 0)-        (resultBodyM $ map Var accs)-        (nonemptyTile residual_input)-    where-      nonemptyTile residual_input = runBodyBinder $ do-        -- Collectively construct a tile.  Threads that are out-of-bounds-        -- provide a blank dummy value.-        full_tile <--          readTile1D-            tile_size-            gid-            gtid-            num_groups-            group_size-            TilePartial-            privstms-            num_whole_tiles-            arrs_and_perms-        tile <- forM full_tile $ \tile ->-          letExp "partial_tile" $-            BasicOp $-              Index-                tile-                [DimSlice (intConst Int64 0) residual_input (intConst Int64 1)]+  letTupExp "acc_after_residual"+    =<< eIf+      (toExp $ pe64 residual_input .==. 0)+      (resultBodyM $ map Var accs)+      (nonemptyTile residual_input)+  where+    red_comm = residualComm args+    map_lam = residualMapLam args+    red_lam = residualRedLam args+    privstms = residualPrivStms args+    inputs = residualInput args+    accs = residualAcc args+    num_whole_tiles = residualNumWholeTiles args+    w = residualInputSize args -        -- Now each thread performs a traversal of the tile and-        -- updates its accumulator.-        resultBody . map Var-          <$> processTile1D-            gid-            gtid-            kdim-            residual_input-            num_groups-            group_size-            privstms-            red_comm-            red_lam-            map_lam-            (zip tile $ repeat [0])-            accs+    nonemptyTile residual_input = runBodyBinder $ do+      -- Collectively construct a tile.  Threads that are out-of-bounds+      -- provide a blank dummy value.+      full_tiles <-+        readTile1D+          tile_size+          gid+          gtid+          num_groups+          group_size+          TilePartial+          privstms+          num_whole_tiles+          inputs +      let sliceTile (InputUntiled arr) =+            pure $ InputUntiled arr+          sliceTile (InputTiled perm tile) = do+            let slice =+                  DimSlice (intConst Int64 0) residual_input (intConst Int64 1)+            InputTiled perm+              <$> letExp "partial_tile" (BasicOp $ Index tile [slice])++      tiles <- mapM sliceTile full_tiles++      -- Now each thread performs a traversal of the tile and+      -- updates its accumulator.+      let tile_args =+            ProcessTileArgs privstms red_comm red_lam map_lam tiles accs num_whole_tiles+      resultBody . map Var+        <$> processTile1D gid gtid kdim residual_input num_groups group_size tile_args+ tiling1d :: [(VName, SubExp)] -> DoTiling VName SubExp tiling1d dims_on_top initial_lvl gtid kdim w = do   gid <- newVName "gid"@@ -941,17 +963,17 @@   M.Map VName Names ->   [VName] ->   VName ->-  Maybe [Int]+  Maybe InputArray invariantToOneOfTwoInnerDims branch_variant variance dims arr = do   j : i : _ <- Just $ reverse dims   let variant_to = M.findWithDefault mempty arr variance       branch_invariant = not $ nameIn j branch_variant || nameIn i branch_variant   if branch_invariant && i `nameIn` variant_to && not (j `nameIn` variant_to)-    then Just [0, 1]+    then Just $ InputTile [0, 1] arr     else       if branch_invariant && j `nameIn` variant_to && not (i `nameIn` variant_to)-        then Just [1, 0]-        else Nothing+        then Just $ InputTile [1, 0] arr+        else Just $ InputDontTile arr  segMap2D ::   String ->@@ -1001,14 +1023,15 @@   TileKind ->   PrivStms ->   SubExp ->-  [(VName, [Int])] ->-  Binder Kernels [VName]-readTile2D (kdim_x, kdim_y) (gtid_x, gtid_y) (gid_x, gid_y) tile_size num_groups group_size kind privstms tile_id arrs_and_perms =-  segMap2D-    "full_tile"-    (SegThread num_groups group_size SegNoVirtFull)-    ResultNoSimplify-    (tile_size, tile_size)+  [InputArray] ->+  Binder Kernels [InputTile]+readTile2D (kdim_x, kdim_y) (gtid_x, gtid_y) (gid_x, gid_y) tile_size num_groups group_size kind privstms tile_id inputs =+  fmap (inputsToTiles inputs)+    . segMap2D+      "full_tile"+      (SegThread num_groups group_size SegNoVirtFull)+      ResultNoSimplify+      (tile_size, tile_size)     $ \(ltid_x, ltid_y) -> do       i <-         letSubExp "i"@@ -1020,20 +1043,23 @@       reconstructGtids2D tile_size (gtid_x, gtid_y) (gid_x, gid_y) (ltid_x, ltid_y)       addPrivStms [DimFix $ Var ltid_x, DimFix $ Var ltid_y] privstms -      let (arrs, perms) = unzip arrs_and_perms-      arr_ts <- mapM lookupType arrs-      let tile_ts = map rowType arr_ts-          w = arraysSize 0 arr_ts+      let arrs_and_perms = tiledInputs inputs -      let readTileElem arr perm =+          readTileElem (arr, perm) =             -- No need for fullSlice because we are tiling only prims.-            letExp "tile_elem" $-              BasicOp $-                Index-                  arr-                  [DimFix $ last $ rearrangeShape perm [i, j]]-          readTileElemIfInBounds (tile_t, arr, perm) = do-            let idx = last $ rearrangeShape perm [i, j]+            letExp+              "tile_elem"+              ( BasicOp $+                  Index+                    arr+                    [DimFix $ last $ rearrangeShape perm [i, j]]+              )++          readTileElemIfInBounds (arr, perm) = do+            arr_t <- lookupType arr+            let tile_t = rowType arr_t+                w = arraySize 0 arr_t+                idx = last $ rearrangeShape perm [i, j]                 othercheck =                   last $                     rearrangeShape@@ -1049,10 +1075,19 @@       fmap (map Var) $         case kind of           TilePartial ->-            mapM (letExp "pre" <=< readTileElemIfInBounds) (zip3 tile_ts arrs perms)+            mapM (letExp "pre" <=< readTileElemIfInBounds) arrs_and_perms           TileFull ->-            zipWithM readTileElem arrs perms+            mapM readTileElem arrs_and_perms +findTileSize :: HasScope lore m => [InputTile] -> m SubExp+findTileSize tiles =+  case mapMaybe isTiled tiles of+    v : _ -> arraySize 0 <$> lookupType v+    [] -> pure $ intConst Int64 0+  where+    isTiled InputUntiled {} = Nothing+    isTiled (InputTiled _ tile) = Just tile+ processTile2D ::   (VName, VName) ->   (VName, VName) ->@@ -1060,64 +1095,55 @@   SubExp ->   Count NumGroups SubExp ->   Count GroupSize SubExp ->-  PrivStms ->-  Commutativity ->-  Lambda Kernels ->-  Lambda Kernels ->-  [(VName, [Int])] ->-  [VName] ->+  ProcessTileArgs ->   Binder Kernels [VName]-processTile2D-  (gid_x, gid_y)-  (gtid_x, gtid_y)-  (kdim_x, kdim_y)-  tile_size-  num_groups-  group_size-  privstms-  red_comm-  red_lam-  map_lam-  tiles_and_perms-  accs = do-    -- Might be truncated in case of a partial tile.-    actual_tile_size <- arraysSize 0 <$> mapM (lookupType . fst) tiles_and_perms+processTile2D (gid_x, gid_y) (gtid_x, gtid_y) (kdim_x, kdim_y) tile_size num_groups group_size tile_args = do+  let privstms = processPrivStms tile_args+      red_comm = processComm tile_args+      red_lam = processRedLam tile_args+      map_lam = processMapLam tile_args+      tiles = processTiles tile_args+      accs = processAcc tile_args+      tile_id = processTileId tile_args -    segMap2D-      "acc"-      (SegThread num_groups group_size SegNoVirtFull)-      ResultPrivate-      (tile_size, tile_size)-      $ \(ltid_x, ltid_y) -> do-        reconstructGtids2D tile_size (gtid_x, gtid_y) (gid_x, gid_y) (ltid_x, ltid_y)+  -- Might be truncated in case of a partial tile.+  actual_tile_size <- findTileSize tiles -        addPrivStms [DimFix $ Var ltid_x, DimFix $ Var ltid_y] privstms+  segMap2D+    "acc"+    (SegThread num_groups group_size SegNoVirtFull)+    ResultPrivate+    (tile_size, tile_size)+    $ \(ltid_x, ltid_y) -> do+      reconstructGtids2D tile_size (gtid_x, gtid_y) (gid_x, gid_y) (ltid_x, ltid_y) -        -- We replace the neutral elements with the accumulators (this is-        -- OK because the parallel semantics are not used after this-        -- point).-        thread_accs <- forM accs $ \acc ->-          letSubExp "acc" $ BasicOp $ Index acc [DimFix $ Var ltid_x, DimFix $ Var ltid_y]-        let form' = redomapSOAC [Reduce red_comm red_lam thread_accs] map_lam+      addPrivStms [DimFix $ Var ltid_x, DimFix $ Var ltid_y] privstms -        tiles' <- forM tiles_and_perms $ \(tile, perm) -> do-          tile_t <- lookupType tile-          letExp "tile" $-            BasicOp $-              Index tile $-                sliceAt-                  tile_t-                  (head perm)-                  [DimFix $ Var $ head $ rearrangeShape perm [ltid_x, ltid_y]]+      -- We replace the neutral elements with the accumulators (this is+      -- OK because the parallel semantics are not used after this+      -- point).+      thread_accs <- forM accs $ \acc ->+        letSubExp "acc" $ BasicOp $ Index acc [DimFix $ Var ltid_x, DimFix $ Var ltid_y]+      let form' = redomapSOAC [Reduce red_comm red_lam thread_accs] map_lam -        fmap (map Var) $-          letTupExp "acc"-            =<< eIf-              ( toExp $ le64 gtid_x .<. pe64 kdim_x .&&. le64 gtid_y .<. pe64 kdim_y-              )-              (eBody [pure $ Op $ OtherOp $ Screma actual_tile_size form' tiles'])-              (resultBodyM thread_accs)+          sliceTile (InputUntiled arr) =+            sliceUntiled arr tile_id tile_size actual_tile_size+          sliceTile (InputTiled perm tile) = do+            tile_t <- lookupType tile+            let idx = DimFix $ Var $ head $ rearrangeShape perm [ltid_x, ltid_y]+            letExp "tile" $+              BasicOp $ Index tile $ sliceAt tile_t (head perm) [idx] +      tiles' <- mapM sliceTile tiles++      fmap (map Var) $+        letTupExp "acc"+          =<< eIf+            ( toExp $ le64 gtid_x .<. pe64 kdim_x .&&. le64 gtid_y .<. pe64 kdim_y+            )+            (eBody [pure $ Op $ OtherOp $ Screma actual_tile_size form' tiles'])+            (resultBodyM thread_accs)+ processResidualTile2D ::   (VName, VName) ->   (VName, VName) ->@@ -1125,14 +1151,7 @@   SubExp ->   Count NumGroups SubExp ->   Count GroupSize SubExp ->-  PrivStms ->-  Commutativity ->-  Lambda Kernels ->-  Lambda Kernels ->-  SubExp ->-  [VName] ->-  SubExp ->-  [(VName, [Int])] ->+  ResidualTileArgs ->   Binder Kernels [VName] processResidualTile2D   gids@@ -1141,14 +1160,7 @@   tile_size   num_groups   group_size-  privstms-  red_comm-  red_lam-  map_lam-  num_whole_tiles-  accs-  w-  arrs_and_perms = do+  args = do     -- The number of residual elements that are not covered by     -- the whole tiles.     residual_input <-@@ -1161,6 +1173,15 @@         (resultBodyM $ map Var accs)         (nonemptyTile residual_input)     where+      privstms = residualPrivStms args+      red_comm = residualComm args+      red_lam = residualRedLam args+      map_lam = residualMapLam args+      accs = residualAcc args+      inputs = residualInput args+      num_whole_tiles = residualNumWholeTiles args+      w = residualInputSize args+       nonemptyTile residual_input = renameBody <=< runBodyBinder $ do         -- Collectively construct a tile.  Threads that are out-of-bounds         -- provide a blank dummy value.@@ -1175,17 +1196,20 @@             TilePartial             privstms             num_whole_tiles-            arrs_and_perms+            inputs -        tile <- forM full_tile $ \tile ->-          letExp "partial_tile" $-            BasicOp $-              Index-                tile-                [ DimSlice (intConst Int64 0) residual_input (intConst Int64 1),-                  DimSlice (intConst Int64 0) residual_input (intConst Int64 1)-                ]+        let slice =+              DimSlice (intConst Int64 0) residual_input (intConst Int64 1)+        tiles <- forM full_tile $ \case+          InputTiled perm tile' ->+            InputTiled perm+              <$> letExp "partial_tile" (BasicOp $ Index tile' [slice, slice])+          InputUntiled arr ->+            pure $ InputUntiled arr +        let tile_args =+              ProcessTileArgs privstms red_comm red_lam map_lam tiles accs num_whole_tiles+         -- Now each thread performs a traversal of the tile and         -- updates its accumulator.         resultBody . map Var@@ -1196,12 +1220,7 @@             tile_size             num_groups             group_size-            privstms-            red_comm-            red_lam-            map_lam-            (zip tile (map snd arrs_and_perms))-            accs+            tile_args  tiling2d :: [(VName, SubExp)] -> DoTiling (VName, VName) (SubExp, SubExp) tiling2d dims_on_top _initial_lvl (gtid_x, gtid_y) (kdim_x, kdim_y) w = do
src/Futhark/Pass/ExpandAllocations.hs view
@@ -9,7 +9,7 @@ import Control.Monad.Reader import Control.Monad.State import Control.Monad.Writer-import Data.List (foldl')+import Data.List (find, foldl') import qualified Data.Map.Strict as M import Data.Maybe import Futhark.Analysis.Rephrase@@ -157,11 +157,19 @@   let (kbody', kbody_allocs) =         extractKernelBodyAllocations lvl bound_outside bound_in_kernel kbody       (ops', ops_allocs) = unzip $ map (extractLambdaAllocations lvl bound_outside mempty) ops-      variantAlloc (_, Var v, _) = v `nameIn` bound_in_kernel+      variantAlloc (_, Var v, _) = not $ v `nameIn` bound_outside       variantAlloc _ = False       allocs = kbody_allocs <> mconcat ops_allocs       (variant_allocs, invariant_allocs) = M.partition variantAlloc allocs+      badVariant (_, Var v, _) = not $ v `nameIn` bound_in_kernel+      badVariant _ = False +  case find badVariant $ M.elems variant_allocs of+    Just v ->+      throwError $ "Cannot handle un-sliceable allocation size: " ++ pretty v+    Nothing ->+      return ()+   case lvl of     SegGroup {}       | not $ null variant_allocs ->@@ -175,11 +183,12 @@     return (alloc_stms, (ops'', kbody''))   where     bound_in_kernel =-      namesFromList $-        M.keys $-          scopeOfSegSpace space-            <> scopeOf (kernelBodyStms kbody)+      namesFromList (M.keys $ scopeOfSegSpace space)+        <> boundInKernelBody kbody +boundInKernelBody :: KernelBody KernelsMem -> Names+boundInKernelBody = namesFromList . M.keys . scopeOf . kernelBodyStms+ allocsForBody ::   Extraction ->   Extraction ->@@ -288,10 +297,11 @@     Extraction   ) extractGenericBodyAllocations lvl bound_outside bound_kernel get_stms set_stms body =-  let (stms, allocs) =+  let bound_kernel' = bound_kernel <> boundByStms (get_stms body)+      (stms, allocs) =         runWriter $           fmap catMaybes $-            mapM (extractStmAllocations lvl bound_outside bound_kernel) $+            mapM (extractStmAllocations lvl bound_outside bound_kernel') $               stmsToList $ get_stms body    in (set_stms (stmsFromList stms) body, allocs) 
src/Futhark/Pass/ExtractKernels.hs view
@@ -870,7 +870,9 @@           -- Normally the permutation is for the output pattern, but           -- we can't really change that, so we change the result           -- order instead.-          let lam_res' = rearrangeShape perm $ bodyResult $ lambdaBody lam+          let lam_res' =+                rearrangeShape (rearrangeInverse perm) $+                  bodyResult $ lambdaBody lam               lam' = lam {lambdaBody = (lambdaBody lam) {bodyResult = lam_res'}}               map_nesting = MapNesting pat aux w $ zip (lambdaParams lam) arrs               nest' = pushInnerKernelNesting (pat, lam_res') map_nesting nest
src/Futhark/Pass/ExtractKernels/DistributeNests.hs view
@@ -323,6 +323,7 @@       isMap (stmExp stm)         && not ("sequential" `inAttrs` stmAuxAttrs (stmAux stm))     isMap Op {} = True+    isMap (DoLoop _ _ ForLoop {} body) = bodyContainsParallelism body     isMap _ = False  lambdaContainsParallelism :: Lambda SOACS -> Bool
src/Futhark/Pkg/Info.hs view
@@ -284,7 +284,7 @@       | [hash, ref] <- T.words l,         ["refs", "tags", t] <- T.splitOn "/" ref,         "v" `T.isPrefixOf` t,-        Right v <- semver $ T.drop 1 t,+        Right v <- parseVersion $ T.drop 1 t,         _svMajor v `elem` versions = do         pinfo <-           ghglLookupCommit@@ -359,7 +359,7 @@         <> "/"         <> T.pack futharkPkg     mk_zip_dir r-      | Right _ <- semver r = repo <> "-v" <> r+      | Right _ <- parseVersion r = repo <> "-v" <> r       | otherwise = repo <> "-" <> r  -- | A package registry is a mapping from package paths to information
src/Futhark/Pkg/Types.hs view
@@ -41,12 +41,13 @@ import Data.Either import Data.Foldable import Data.List (sortOn)+import qualified Data.List.NonEmpty as NE import qualified Data.Map as M import Data.Maybe import qualified Data.Text as T import qualified Data.Text.IO as T import Data.Traversable-import Data.Versions (SemVer (..), VUnit (..), prettySemVer, semver)+import Data.Versions (SemVer (..), VUnit (..), prettySemVer) import Data.Void import System.FilePath import qualified System.FilePath.Posix as Posix@@ -69,13 +70,13 @@ -- @hash@ (typically the Git commit ID).  This function detects such -- versions. isCommitVersion :: SemVer -> Maybe T.Text-isCommitVersion (SemVer 0 0 0 [_] [[Str s]]) = Just s+isCommitVersion (SemVer 0 0 0 [_] [Str s NE.:| []]) = Just s isCommitVersion _ = Nothing  -- | @commitVersion timestamp commit@ constructs a commit version. commitVersion :: T.Text -> T.Text -> SemVer commitVersion time commit =-  SemVer 0 0 0 [[Str time]] [[Str commit]]+  SemVer 0 0 0 [Str time NE.:| []] [Str commit NE.:| []]  -- | Unfortunately, Data.Versions has a buggy semver parser that -- collapses consecutive zeroes in the metadata field.  So, we define
src/Futhark/Util.hs view
@@ -9,7 +9,8 @@ -- note where you got it from (and make sure that the license is -- compatible). module Futhark.Util-  ( mapAccumLM,+  ( nubOrd,+    mapAccumLM,     maxinum,     chunk,     chunks,@@ -53,7 +54,8 @@ import qualified Data.ByteString as BS import Data.Char import Data.Either-import Data.List (foldl', genericDrop, genericSplitAt)+import Data.List (foldl', genericDrop, genericSplitAt, sort)+import qualified Data.List.NonEmpty as NE import Data.Maybe import qualified Data.Text as T import qualified Data.Text.Encoding as T@@ -67,6 +69,10 @@ import System.IO (hIsTerminalDevice, stdout) import System.IO.Unsafe import System.Process.ByteString++-- | Like 'nub', but without the quadratic runtime.+nubOrd :: Ord a => [a] -> [a]+nubOrd = map NE.head . NE.group . sort  -- | Like 'Data.Traversable.mapAccumL', but monadic. mapAccumLM ::
src/Language/Futhark/Interpreter.hs view
@@ -527,9 +527,12 @@ patternMatch env (PatternParens p _) v = patternMatch env p v patternMatch env (PatternAscription p _ _) v =   patternMatch env p v-patternMatch env (PatternLit e _ _) v = do-  v' <- lift $ eval env e-  if v == v'+patternMatch env (PatternLit l t _) v = do+  l' <- case l of+    PatLitInt x -> lift $ eval env $ IntLit x t mempty+    PatLitFloat x -> lift $ eval env $ FloatLit x t mempty+    PatLitPrim lv -> pure $ ValuePrim lv+  if v == l'     then pure env     else mzero patternMatch env (PatternConstr n _ ps _) (ValueSum _ n' vs)
src/Language/Futhark/Parser/Parser.y view
@@ -796,14 +796,12 @@                  : CFieldPattern ',' CFieldPatterns1 { $1 : $3 }                  | CFieldPattern                    { [$1] } -CaseLiteral :: { (UncheckedExp, SrcLoc) }-             : PrimLit        { (Literal (fst $1) (snd $1), snd $1) }-             | charlit        { let L loc (CHARLIT x) = $1-                                in (IntLit (toInteger (ord x)) NoInfo loc, loc) }-             | intlit         { let L loc (INTLIT x) = $1 in (IntLit x NoInfo loc, loc) }-             | floatlit       { let L loc (FLOATLIT x) = $1 in (FloatLit x NoInfo loc, loc) }-             | stringlit      { let L loc (STRINGLIT s) = $1 in-                              (StringLit (encode s) loc, loc) }+CaseLiteral :: { (PatLit, SrcLoc) }+             : PrimLit  { (PatLitPrim (fst $1), snd $1) }+             | charlit  { let L loc (CHARLIT x) = $1+                          in (PatLitInt (toInteger (ord x)), loc) }+             | intlit   { let L loc (INTLIT x) = $1 in (PatLitInt x, loc) }+             | floatlit { let L loc (FLOATLIT x) = $1 in (PatLitFloat x, loc) }  LoopForm :: { LoopFormBase NoInfo Name } LoopForm : for VarId '<' Exp
src/Language/Futhark/Pretty.hs view
@@ -373,6 +373,11 @@   ppr (While cond) =     text "while" <+> ppr cond +instance Pretty PatLit where+  ppr (PatLitInt x) = ppr x+  ppr (PatLitFloat f) = ppr f+  ppr (PatLitPrim v) = ppr v+ instance (Eq vn, IsName vn, Annot f) => Pretty (PatternBase f vn) where   ppr (PatternAscription p t _) = ppr p <> colon <+> align (ppr t)   ppr (PatternParens p _) = parens $ ppr p
src/Language/Futhark/Syntax.hs view
@@ -56,6 +56,7 @@     FieldBase (..),     CaseBase (..),     LoopFormBase (..),+    PatLit (..),     PatternBase (..),      -- * Module language@@ -894,6 +895,13 @@  deriving instance Ord (LoopFormBase NoInfo VName) +-- | A literal in a pattern.+data PatLit+  = PatLitInt Integer+  | PatLitFloat Double+  | PatLitPrim PrimValue+  deriving (Eq, Ord, Show)+ -- | A pattern as used most places where variables are bound (function -- parameters, @let@ expressions, etc). data PatternBase f vn@@ -903,7 +911,7 @@   | Id vn (f PatternType) SrcLoc   | Wildcard (f PatternType) SrcLoc -- Nothing, i.e. underscore.   | PatternAscription (PatternBase f vn) (TypeDeclBase f vn) SrcLoc-  | PatternLit (ExpBase f vn) (f PatternType) SrcLoc+  | PatternLit PatLit (f PatternType) SrcLoc   | PatternConstr Name (f PatternType) [PatternBase f vn] SrcLoc  deriving instance Showable f vn => Show (PatternBase f vn)
src/Language/Futhark/Traversals.hs view
@@ -337,8 +337,8 @@     PatternAscription <$> astMap tv pat <*> astMap tv t <*> pure loc   astMap tv (Wildcard (Info t) loc) =     Wildcard <$> (Info <$> mapOnPatternType tv t) <*> pure loc-  astMap tv (PatternLit e (Info t) loc) =-    PatternLit <$> astMap tv e <*> (Info <$> mapOnPatternType tv t) <*> pure loc+  astMap tv (PatternLit v (Info t) loc) =+    PatternLit v <$> (Info <$> mapOnPatternType tv t) <*> pure loc   astMap tv (PatternConstr n (Info t) ps loc) =     PatternConstr n <$> (Info <$> mapOnPatternType tv t) <*> mapM (astMap tv) ps <*> pure loc @@ -393,7 +393,7 @@ barePat (Wildcard _ loc) = Wildcard NoInfo loc barePat (PatternAscription pat (TypeDecl t _) loc) =   PatternAscription (barePat pat) (TypeDecl t NoInfo) loc-barePat (PatternLit e _ loc) = PatternLit (bareExp e) NoInfo loc+barePat (PatternLit v _ loc) = PatternLit v NoInfo loc barePat (PatternConstr c _ ps loc) = PatternConstr c NoInfo (map barePat ps) loc  bareDimIndex :: DimIndexBase Info VName -> DimIndexBase NoInfo VName
src/Language/Futhark/TypeChecker.hs view
@@ -20,6 +20,7 @@  import Control.Monad.Except import Control.Monad.Writer hiding (Sum)+import Data.Bifunctor (second) import Data.Char (isAlpha, isAlphaNum) import Data.Either import Data.List (isPrefixOf)@@ -49,7 +50,7 @@   VNameSource ->   ImportName ->   UncheckedProg ->-  Either TypeError (FileModule, Warnings, VNameSource)+  (Warnings, Either TypeError (FileModule, VNameSource)) checkProg files src name prog =   runTypeM initialEnv files' name src $ checkProgM prog   where@@ -65,10 +66,9 @@   VNameSource ->   Env ->   UncheckedExp ->-  Either TypeError ([TypeParam], Exp)-checkExp files src env e = do-  (e', _, _) <- runTypeM env files' (mkInitialImport "") src $ checkOneExp e-  return e'+  (Warnings, Either TypeError ([TypeParam], Exp))+checkExp files src env e =+  second (fmap fst) $ runTypeM env files' (mkInitialImport "") src $ checkOneExp e   where     files' = M.map fileEnv $ M.fromList files @@ -82,13 +82,15 @@   Env ->   ImportName ->   UncheckedDec ->-  Either TypeError (Env, Dec, VNameSource)-checkDec files src env name d = do-  ((env', d'), _, src') <- runTypeM env files' name src $ do-    (_, env', d') <- checkOneDec d-    return (env' <> env, d')-  return (env', d', src')+  (Warnings, Either TypeError (Env, Dec, VNameSource))+checkDec files src env name d =+  second (fmap massage) $+    runTypeM env files' name src $ do+      (_, env', d') <- checkOneDec d+      return (env' <> env, d')   where+    massage ((env', d'), src') =+      (env', d', src')     files' = M.map fileEnv $ M.fromList files  -- | Type check a single module expression containing no type information,@@ -100,10 +102,9 @@   VNameSource ->   Env ->   ModExpBase NoInfo Name ->-  Either TypeError (MTy, ModExpBase Info VName)-checkModExp files src env me = do-  (x, _, _) <- runTypeM env files' (mkInitialImport "") src $ checkOneModExp me-  return x+  (Warnings, Either TypeError (MTy, ModExpBase Info VName))+checkModExp files src env me =+  second (fmap fst) $ runTypeM env files' (mkInitialImport "") src $ checkOneModExp me   where     files' = M.map fileEnv $ M.fromList files @@ -298,7 +299,7 @@       (lookupType loc qn >> warnAbout qn)         `catchError` \_ -> return ()     warnAbout qn =-      warn loc $ "Inclusion shadows type " ++ quote (pretty qn) ++ "."+      warn loc $ "Inclusion shadows type" <+> pquote (ppr qn) <+> "."  checkSigExp :: SigExpBase NoInfo Name -> TypeM (MTy, SigExpBase Info VName) checkSigExp (SigParens e loc) = do@@ -615,16 +616,14 @@         typeError loc mempty "Entry point functions must not be size-polymorphic in their return type."       | p : _ <- filter nastyParameter params' ->         warn loc $-          pretty $-            "Entry point parameter\n"-              </> indent 2 (ppr p)-              </> "\nwill have an opaque type, so the entry point will likely not be callable."+          "Entry point parameter\n"+            </> indent 2 (ppr p)+            </> "\nwill have an opaque type, so the entry point will likely not be callable."       | nastyReturnType maybe_tdecl' rettype ->         warn loc $-          pretty $-            "Entry point return type\n"-              </> indent 2 (ppr rettype)-              </> "\nwill have an opaque type, so the result will likely not be usable."+          "Entry point return type\n"+            </> indent 2 (ppr rettype)+            </> "\nwill have an opaque type, so the result will likely not be usable."     _ -> return ()    let arrow (xp, xt) yt = Scalar $ Arrow () xp xt yt
+ src/Language/Futhark/TypeChecker/Match.hs view
@@ -0,0 +1,171 @@+{-# LANGUAGE OverloadedStrings #-}++-- | Checking for missing cases in a match expression.  Based on+-- "Warnings for pattern matching" by Luc Maranget.  We only detect+-- inexhaustiveness here - ideally, we would also like to check for+-- redundant cases.+module Language.Futhark.TypeChecker.Match+  ( unmatched,+    Match,+  )+where++import qualified Data.Map.Strict as M+import Data.Maybe+import Futhark.Util (maybeHead, nubOrd)+import Futhark.Util.Pretty hiding (bool, group, space)+import Language.Futhark hiding (ExpBase (Constr), unscopeType)++data Constr+  = Constr Name+  | ConstrTuple+  | ConstrRecord [Name]+  | -- | Treated as 0-ary.+    ConstrLit PatLit+  deriving (Eq, Ord, Show)++-- | A representation of the essentials of a pattern.+data Match+  = MatchWild StructType+  | MatchConstr Constr [Match] StructType+  deriving (Eq, Ord, Show)++matchType :: Match -> StructType+matchType (MatchWild t) = t+matchType (MatchConstr _ _ t) = t++pprMatch :: Int -> Match -> Doc+pprMatch _ MatchWild {} = "_"+pprMatch _ (MatchConstr (ConstrLit l) _ _) = ppr l+pprMatch p (MatchConstr (Constr c) ps _) =+  parensIf (not (null ps) && p >= 10) $+    "#" <> ppr c <> mconcat (map ((" " <>) . pprMatch 10) ps)+pprMatch _ (MatchConstr ConstrTuple ps _) =+  parens $ commasep $ map (pprMatch (-1)) ps+pprMatch _ (MatchConstr (ConstrRecord fs) ps _) =+  braces $ commasep $ zipWith ppField fs ps+  where+    ppField name t = text (nameToString name) <> equals <> pprMatch (-1) t++instance Pretty Match where+  ppr = pprMatch (-1)++patternToMatch :: Pattern -> Match+patternToMatch (Id _ (Info t) _) = MatchWild $ toStruct t+patternToMatch (Wildcard (Info t) _) = MatchWild $ toStruct t+patternToMatch (PatternParens p _) = patternToMatch p+patternToMatch (PatternAscription p _ _) = patternToMatch p+patternToMatch (PatternLit l (Info t) _) =+  MatchConstr (ConstrLit l) [] $ toStruct t+patternToMatch p@(TuplePattern ps _) =+  MatchConstr ConstrTuple (map patternToMatch ps) $+    patternStructType p+patternToMatch p@(RecordPattern fs _) =+  MatchConstr (ConstrRecord fnames) (map patternToMatch ps) $+    patternStructType p+  where+    (fnames, ps) = unzip $ sortFields $ M.fromList fs+patternToMatch (PatternConstr c (Info t) args _) =+  MatchConstr (Constr c) (map patternToMatch args) $ toStruct t++isConstr :: Match -> Maybe Name+isConstr (MatchConstr (Constr c) _ _) = Just c+isConstr _ = Nothing++complete :: [Match] -> Bool+complete xs+  | Just x <- maybeHead xs,+    Scalar (Sum all_cs) <- matchType x,+    Just xs_cs <- mapM isConstr xs =+    all (`elem` xs_cs) (M.keys all_cs)+  | otherwise =+    (any (isBool True) xs && any (isBool False) xs)+      || all isRecord xs+      || all isTuple xs+  where+    isBool b1 (MatchConstr (ConstrLit (PatLitPrim (BoolValue b2))) _ _) = b1 == b2+    isBool _ _ = False+    isRecord (MatchConstr ConstrRecord {} _ _) = True+    isRecord _ = False+    isTuple (MatchConstr ConstrTuple _ _) = True+    isTuple _ = False++specialise :: [StructType] -> Match -> [[Match]] -> [[Match]]+specialise ats c1 = go+  where+    go ((c2 : row) : ps)+      | Just args <- match c1 c2 =+        (args ++ row) : go ps+      | otherwise =+        go ps+    go _ = []++    match (MatchConstr c1' _ _) (MatchConstr c2' args _)+      | c1' == c2' =+        Just args+      | otherwise =+        Nothing+    match _ MatchWild {} =+      Just $ map MatchWild ats+    match _ _ =+      Nothing++defaultMat :: [[Match]] -> [[Match]]+defaultMat = mapMaybe onRow+  where+    onRow (MatchConstr {} : _) = Nothing+    onRow (MatchWild {} : ps) = Just ps+    onRow [] = Nothing -- Should not happen.++findUnmatched :: [[Match]] -> Int -> [[Match]]+findUnmatched pmat n+  | ((p : _) : _) <- pmat,+    Just heads <- mapM maybeHead pmat =+    if complete heads+      then completeCase heads+      else incompleteCase (matchType p) heads+  where+    completeCase cs = do+      c <- cs+      let ats = case c of+            MatchConstr _ args _ -> map matchType args+            MatchWild _ -> []+          a_k = length ats+          pmat' = specialise ats c pmat+      u <- findUnmatched pmat' (a_k + n - 1)+      pure $ case c of+        MatchConstr c' _ t ->+          let (r, p) = splitAt a_k u+           in MatchConstr c' r t : p+        MatchWild t ->+          MatchWild t : u++    incompleteCase pt cs = do+      u <- findUnmatched (defaultMat pmat) (n - 1)+      if null cs+        then return $ MatchWild pt : u+        else case pt of+          Scalar (Sum all_cs) -> do+            -- Figure out which constructors are missing.+            let sigma = mapMaybe isConstr cs+                notCovered (k, _) = k `notElem` sigma+            (cname, ts) <- filter notCovered $ M.toList all_cs+            pure $ MatchConstr (Constr cname) (map MatchWild ts) pt : u+          _ ->+            -- This is where we could have enumerated missing match+            -- values (e.g. for booleans), rather than just emitting a+            -- wildcard.+            pure $ MatchWild pt : u++-- If we get here, then the number of columns must be zero.+findUnmatched [] _ = [[]]+findUnmatched _ _ = []++{-# NOINLINE unmatched #-}+unmatched :: [Pattern] -> [Match]+unmatched orig_ps =+  -- The algorithm may find duplicate example, which we filter away+  -- here.+  nubOrd $+    mapMaybe maybeHead $+      findUnmatched (map ((: []) . patternToMatch) orig_ps) 1
src/Language/Futhark/TypeChecker/Monad.hs view
@@ -1,5 +1,7 @@ {-# LANGUAGE FlexibleContexts #-}+{-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE GeneralizedNewtypeDeriving #-}+{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE Trustworthy #-} {-# LANGUAGE TupleSections #-}@@ -48,7 +50,8 @@ where  import Control.Monad.Except-import Control.Monad.RWS.Strict hiding (Sum)+import Control.Monad.Reader+import Control.Monad.State.Strict import Data.Either import Data.List (find, isPrefixOf) import qualified Data.Map.Strict as M@@ -130,14 +133,20 @@     contextImportName :: ImportName   } +data TypeState = TypeState+  { stateNameSource :: VNameSource,+    stateWarnings :: Warnings+  }+ -- | The type checker runs in this monad. newtype TypeM a   = TypeM-      ( RWST-          Context -- Reader-          Warnings -- Writer-          VNameSource -- State-          (Except TypeError) -- Inner monad+      ( ReaderT+          Context+          ( StateT+              TypeState+              (Except (Warnings, TypeError))+          )           a       )   deriving@@ -145,11 +154,21 @@       Functor,       Applicative,       MonadReader Context,-      MonadWriter Warnings,-      MonadState VNameSource,-      MonadError TypeError+      MonadState TypeState     ) +instance MonadError TypeError TypeM where+  throwError e = TypeM $ do+    ws <- gets stateWarnings+    throwError (ws, e)++  catchError (TypeM m) f =+    TypeM $ m `catchError` f'+    where+      f' (_, e) =+        let TypeM m' = f e+         in m'+ -- | Run a 'TypeM' computation. runTypeM ::   Env ->@@ -157,10 +176,13 @@   ImportName ->   VNameSource ->   TypeM a ->-  Either TypeError (a, Warnings, VNameSource)+  (Warnings, Either TypeError (a, VNameSource)) runTypeM env imports fpath src (TypeM m) = do-  (x, src', ws) <- runExcept $ runRWST m (Context env imports fpath) src-  return (x, ws, src')+  let ctx = Context env imports fpath+      s = TypeState src mempty+  case runExcept $ runStateT (runReaderT m ctx) s of+    Left (ws, e) -> (ws, Left e)+    Right (x, TypeState src' ws) -> (ws, Right (x, src'))  -- | Retrieve the current 'Env'. askEnv :: TypeM Env@@ -202,7 +224,7 @@ -- internal interface is because we use distinct monads for checking -- expressions and declarations. class Monad m => MonadTypeChecker m where-  warn :: Located loc => loc -> String -> m ()+  warn :: Located loc => loc -> Doc -> m ()    newName :: VName -> m VName   newID :: Name -> m VName@@ -241,13 +263,17 @@   bindNameMap mapping body  instance MonadTypeChecker TypeM where-  warn loc problem = tell $ singleWarning (srclocOf loc) problem+  warn loc problem =+    modify $ \s ->+      s+        { stateWarnings = stateWarnings s <> singleWarning (srclocOf loc) problem+        } -  newName s = do-    src <- get-    let (s', src') = Futhark.FreshNames.newName src s-    put src'-    return s'+  newName v = do+    s <- get+    let (v', src') = Futhark.FreshNames.newName (stateNameSource s) v+    put $ s {stateNameSource = src'}+    return v'    newID s = newName $ VName s 0 
src/Language/Futhark/TypeChecker/Terms.hs view
@@ -27,7 +27,7 @@ import Data.Bifunctor import Data.Char (isAscii) import Data.Either-import Data.List (find, foldl', group, isPrefixOf, nub, sort, transpose, (\\))+import Data.List (find, foldl', isPrefixOf, nub, sort) import qualified Data.List.NonEmpty as NE import qualified Data.Map.Strict as M import Data.Maybe@@ -37,6 +37,7 @@ import Language.Futhark hiding (unscopeType) import Language.Futhark.Semantic (includeToString) import Language.Futhark.Traversals+import Language.Futhark.TypeChecker.Match import Language.Futhark.TypeChecker.Monad hiding (BoundV) import qualified Language.Futhark.TypeChecker.Monad as TypeM import Language.Futhark.TypeChecker.Types hiding (checkTypeDecl)@@ -692,12 +693,14 @@     "Variable" <+> pprName name <+> "previously consumed at"       <+> text (locStrRel loc2 loc1) <> "." -badLetWithValue :: SrcLoc -> TermTypeM a-badLetWithValue loc =-  typeError-    loc-    mempty-    "New value for elements in let-with shares data with source array.  This is illegal, as it prevents in-place modification."+badLetWithValue :: (Pretty arr, Pretty src) => arr -> src -> SrcLoc -> TermTypeM a+badLetWithValue arre vale loc =+  typeError loc mempty $+    "Source array for in-place update"+      </> indent 2 (ppr arre)+      </> "might alias update value"+      </> indent 2 (ppr vale)+      </> "Hint: use" <+> pquote "copy" <+> "to remove aliases from the value."  returnAliased :: Name -> Name -> SrcLoc -> TermTypeM () returnAliased fname name loc =@@ -750,6 +753,20 @@   = NoneInferred   | Ascribed PatternType +-- All this complexity is just so we can handle un-suffixed numeric+-- literals in patterns.+patLitMkType :: PatLit -> SrcLoc -> TermTypeM StructType+patLitMkType (PatLitInt _) loc = do+  t <- newTypeVar loc "t"+  mustBeOneOf anyNumberType (mkUsage loc "integer literal") t+  return t+patLitMkType (PatLitFloat _) loc = do+  t <- newTypeVar loc "t"+  mustBeOneOf anyFloatType (mkUsage loc "float literal") t+  return t+patLitMkType (PatLitPrim v) _ =+  pure $ Scalar $ Prim $ primValueType v+ checkPattern' ::   UncheckedPattern ->   InferredType ->@@ -838,15 +855,13 @@         <*> pure loc   where     unifyUniqueness u1 u2 = if u2 `subuniqueOf` u1 then Just u1 else Nothing-checkPattern' (PatternLit e NoInfo loc) (Ascribed t) = do-  e' <- checkExp e-  t' <- expTypeFully e'-  unify (mkUsage loc "matching against literal") (toStruct t') (toStruct t)-  return $ PatternLit e' (Info t') loc-checkPattern' (PatternLit e NoInfo loc) NoneInferred = do-  e' <- checkExp e-  t' <- expTypeFully e'-  return $ PatternLit e' (Info t') loc+checkPattern' (PatternLit l NoInfo loc) (Ascribed t) = do+  t' <- patLitMkType l loc+  unify (mkUsage loc "matching against literal") t' (toStruct t)+  return $ PatternLit l (Info (fromStruct t')) loc+checkPattern' (PatternLit l NoInfo loc) NoneInferred = do+  t' <- patLitMkType l loc+  return $ PatternLit l (Info (fromStruct t')) loc checkPattern' (PatternConstr n NoInfo ps loc) (Ascribed (Scalar (Sum cs)))   | Just ts <- M.lookup n cs = do     ps' <- zipWithM checkPattern' ps $ map Ascribed ts@@ -1518,7 +1533,7 @@     sequentially (unifies "type of target array" (toStruct elemt) =<< checkExp ve) $ \ve' _ -> do       ve_t <- expTypeFully ve'       when (AliasBound (identName src') `S.member` aliases ve_t) $-        badLetWithValue loc+        badLetWithValue src ve loc        bindingIdent dest (src_t `setAliases` S.empty) $ \dest' -> do         body' <- consuming src' $ checkExp body@@ -1542,7 +1557,7 @@        let src_als = aliases src_t       ve_t <- expTypeFully ve'-      unless (S.null $ src_als `S.intersection` aliases ve_t) $ badLetWithValue loc+      unless (S.null $ src_als `S.intersection` aliases ve_t) $ badLetWithValue src ve loc        consume loc src_als       return $ Update src' idxes' ve' loc@@ -2069,7 +2084,7 @@ -- | An unmatched pattern. Used in in the generation of -- unmatched pattern warnings by the type checker. data Unmatched p-  = UnmatchedNum p [ExpBase Info VName]+  = UnmatchedNum p [PatLit]   | UnmatchedBool p   | UnmatchedConstr p   | Unmatched p@@ -2093,43 +2108,12 @@       ppr' (PatternLit e _ _) = ppr e       ppr' (PatternConstr n _ ps _) = "#" <> ppr n <+> sep (map ppr' ps) -unpackPat :: Pattern -> [Maybe Pattern]-unpackPat Wildcard {} = [Nothing]-unpackPat (PatternParens p _) = unpackPat p-unpackPat Id {} = [Nothing]-unpackPat (TuplePattern ps _) = Just <$> ps-unpackPat (RecordPattern fs _) = Just . snd <$> sortFields (M.fromList fs)-unpackPat (PatternAscription p _ _) = unpackPat p-unpackPat p@PatternLit {} = [Just p]-unpackPat p@PatternConstr {} = [Just p]--wildPattern :: Pattern -> Int -> Unmatched Pattern -> Unmatched Pattern-wildPattern (TuplePattern ps loc) pos um = wildTuple <$> um-  where-    wildTuple p = TuplePattern (take (pos - 1) ps' ++ [p] ++ drop pos ps') loc-    ps' = map wildOut ps-    wildOut p = Wildcard (Info (patternType p)) (srclocOf p)-wildPattern (RecordPattern fs loc) pos um = wildRecord <$> um-  where-    wildRecord p =-      RecordPattern (take (pos - 1) fs' ++ [(fst (fs !! (pos - 1)), p)] ++ drop pos fs') loc-    fs' = map wildOut fs-    wildOut (f, p) = (f, Wildcard (Info (patternType p)) (srclocOf p))-wildPattern (PatternAscription p _ _) pos um = wildPattern p pos um-wildPattern (PatternParens p _) pos um = wildPattern p pos um-wildPattern (PatternConstr n t ps loc) pos um = wildConstr <$> um-  where-    wildConstr p = PatternConstr n t (take (pos - 1) ps' ++ [p] ++ drop pos ps') loc-    ps' = map wildOut ps-    wildOut p = Wildcard (Info (patternType p)) (srclocOf p)-wildPattern _ _ um = um- checkUnmatched :: Exp -> TermTypeM () checkUnmatched e = void $ checkUnmatched' e >> astMap tv e   where     checkUnmatched' (Match _ cs _ loc) =       let ps = fmap (\(CasePat p _ _) -> p) cs-       in case unmatched id $ NE.toList ps of+       in case unmatched $ NE.toList ps of             [] -> return ()             ps' ->               typeError loc mempty $@@ -2146,134 +2130,6 @@           mapOnPatternType = pure         } --- | A data type for constructor patterns.  This is used to make the--- code for detecting unmatched constructors cleaner, by separating--- the constructor-pattern cases from other cases.-data ConstrPat = ConstrPat-  { constrName :: Name,-    constrType :: PatternType,-    constrPayload :: [Pattern],-    constrSrcLoc :: SrcLoc-  }---- Be aware of these fishy equality instances!--instance Eq ConstrPat where-  ConstrPat c1 _ _ _ == ConstrPat c2 _ _ _ = c1 == c2--instance Ord ConstrPat where-  ConstrPat c1 _ _ _ `compare` ConstrPat c2 _ _ _ = c1 `compare` c2--unmatched :: (Unmatched Pattern -> Unmatched Pattern) -> [Pattern] -> [Unmatched Pattern]-unmatched hole orig_ps-  | p : _ <- orig_ps,-    sameStructure labeledCols = do-    (i, cols) <- labeledCols-    let hole' = if isConstr p then hole else hole . wildPattern p i-    case sequence cols of-      Nothing -> []-      Just cs-        | all isPatternLit cs -> map hole' $ localUnmatched cs-        | otherwise -> unmatched hole' cs-  | otherwise = []-  where-    labeledCols = zip [1 ..] $ transpose $ map unpackPat orig_ps--    localUnmatched :: [Pattern] -> [Unmatched Pattern]-    localUnmatched [] = []-    localUnmatched ps'@(p' : _) =-      case patternType p' of-        Scalar (Sum cs'') ->-          -- We now know that we are matching a sum type, and thus-          -- that all patterns ps' are constructors (checked by-          -- 'all isPatternLit' before this function is called).-          let constrs = M.keys cs''-              matched = mapMaybe constr ps'-              unmatched' =-                map (UnmatchedConstr . buildConstr cs'') $-                  constrs \\ map constrName matched-           in case unmatched' of-                [] ->-                  let constrGroups = group (sort matched)-                      removedConstrs = mapMaybe stripConstrs constrGroups-                      transposed = (fmap . fmap) transpose removedConstrs-                      findUnmatched (pc, trans) = do-                        col <- trans-                        case col of-                          [] -> []-                          ((i, _) : _) -> unmatched (wilder i pc) (map snd col)-                      wilder i pc s = (`PatternParens` mempty) <$> wildPattern pc i s-                   in concatMap findUnmatched transposed-                _ -> unmatched'-        Scalar (Prim t) | not (any idOrWild ps') ->-          -- We now know that we are matching a sum type, and thus-          -- that all patterns ps' are literals (checked by 'all-          -- isPatternLit' before this function is called).-          case t of-            Bool ->-              let matched = nub $ mapMaybe (pExp >=> bool) $ filter isPatternLit ps'-               in map (UnmatchedBool . buildBool (Scalar (Prim t))) $ [True, False] \\ matched-            _ ->-              let matched = mapMaybe pExp $ filter isPatternLit ps'-               in [UnmatchedNum (buildId (Info $ Scalar $ Prim t) "p") matched]-        _ -> []--    isConstr PatternConstr {} = True-    isConstr (PatternParens p _) = isConstr p-    isConstr _ = False--    stripConstrs :: [ConstrPat] -> Maybe (Pattern, [[(Int, Pattern)]])-    stripConstrs (pc@ConstrPat {} : cs') = Just (unConstr pc, stripConstr pc : map stripConstr cs')-    stripConstrs [] = Nothing--    stripConstr :: ConstrPat -> [(Int, Pattern)]-    stripConstr (ConstrPat _ _ ps' _) = zip [1 ..] ps'--    sameStructure [] = True-    sameStructure (x : xs) = all (\y -> length y == length x') xs'-      where-        (x' : xs') = map snd (x : xs)--    pExp (PatternLit e' _ _) = Just e'-    pExp _ = Nothing--    constr (PatternConstr c (Info t) ps loc) = Just $ ConstrPat c t ps loc-    constr (PatternParens p _) = constr p-    constr (PatternAscription p' _ _) = constr p'-    constr _ = Nothing--    unConstr p =-      PatternConstr (constrName p) (Info $ constrType p) (constrPayload p) (constrSrcLoc p)--    isPatternLit PatternLit {} = True-    isPatternLit (PatternAscription p' _ _) = isPatternLit p'-    isPatternLit (PatternParens p' _) = isPatternLit p'-    isPatternLit PatternConstr {} = True-    isPatternLit _ = False--    idOrWild Id {} = True-    idOrWild Wildcard {} = True-    idOrWild (PatternAscription p' _ _) = idOrWild p'-    idOrWild (PatternParens p' _) = idOrWild p'-    idOrWild _ = False--    bool (Literal (BoolValue b) _) = Just b-    bool _ = Nothing--    buildConstr m c =-      let t = Scalar $ Sum m-          cs = m M.! c-          wildCS = map (\ct -> Wildcard (Info ct) mempty) cs-       in if null wildCS-            then PatternConstr c (Info t) [] mempty-            else PatternParens (PatternConstr c (Info t) wildCS mempty) mempty-    buildBool t b =-      PatternLit (Literal (BoolValue b) mempty) (Info (addSizes t)) mempty-    buildId t n =-      -- The VName tag here will never be used since the value-      -- exists exclusively for printing warnings.-      Id (VName (nameFromString n) (-1)) t mempty- checkIdent :: IdentBase NoInfo Name -> TermTypeM Ident checkIdent (Ident name _ loc) = do   (QualName _ name', vt) <- lookupVar loc (qualName name)@@ -3255,7 +3111,7 @@ checkIfUsed occs v   | not $ identName v `S.member` allOccuring occs,     not $ "_" `isPrefixOf` prettyName (identName v) =-    warn (srclocOf v) $ "Unused variable " ++ quote (pretty $ baseName $ identName v) ++ "."+    warn (srclocOf v) $ "Unused variable" <+> pquote (pprName $ identName v) <+> "."   | otherwise =     return () 
src/Language/Futhark/TypeChecker/Types.hs view
@@ -180,7 +180,7 @@ checkTypeExp (TEUnique t loc) = do   (t', st, l) <- checkTypeExp t   unless (mayContainArray st) $-    warn loc $ "Declaring " <> quote (pretty st) <> " as unique has no effect."+    warn loc $ "Declaring" <+> pquote (ppr st) <+> "as unique has no effect."   return (TEUnique t' loc, st `setUniqueness` Unique, l)   where     mayContainArray (Scalar Prim {}) = False
src/Language/Futhark/Warnings.hs view
@@ -5,21 +5,23 @@ -- 'Show'-instance produces a human-readable string. module Language.Futhark.Warnings   ( Warnings,+    anyWarnings,     singleWarning,     singleWarning',   ) where -import Data.List (intercalate, sortOn)+import Data.List (sortOn) import Data.Monoid import Futhark.Util.Console (inRed) import Futhark.Util.Loc+import Futhark.Util.Pretty import Language.Futhark.Core (locStr, prettyStacktrace) import Prelude  -- | The warnings produced by the compiler.  The 'Show' instance -- produces a human-readable description.-newtype Warnings = Warnings [(SrcLoc, [SrcLoc], String)] deriving (Eq)+newtype Warnings = Warnings [(SrcLoc, [SrcLoc], Doc)]  instance Semigroup Warnings where   Warnings ws1 <> Warnings ws2 = Warnings $ ws1 <> ws2@@ -27,30 +29,35 @@ instance Monoid Warnings where   mempty = Warnings mempty -instance Show Warnings where-  show (Warnings []) = ""-  show (Warnings ws) =-    intercalate "\n\n" ws' ++ "\n"+instance Pretty Warnings where+  ppr (Warnings []) = mempty+  ppr (Warnings ws) =+    stack $ punctuate line $ map onWarning $ sortOn (rep . wloc) ws     where-      ws' = map showWarning $ sortOn (rep . wloc) ws       wloc (x, _, _) = locOf x       rep NoLoc = ("", 0)       rep (Loc p _) = (posFile p, posCoff p)-      showWarning (loc, [], w) =-        inRed ("Warning at " ++ locStr loc ++ ":") ++ "\n"-          ++ intercalate "\n" (map ("  " <>) $ lines w)-      showWarning (loc, locs, w) =-        inRed-          ( "Warning at\n"-              ++ prettyStacktrace 0 (map locStr (loc : locs))+      onWarning (loc, [], w) =+        text (inRed ("Warning at " ++ locStr loc ++ ":"))+          </> indent 2 w+      onWarning (loc, locs, w) =+        text+          ( inRed+              ( "Warning at\n"+                  ++ prettyStacktrace 0 (map locStr (loc : locs))+              )           )-          ++ intercalate "\n" (map ("  " <>) $ lines w)+          </> indent 2 w +-- | True if there are any warnings in the set.+anyWarnings :: Warnings -> Bool+anyWarnings (Warnings ws) = not $ null ws+ -- | A single warning at the given location.-singleWarning :: SrcLoc -> String -> Warnings+singleWarning :: SrcLoc -> Doc -> Warnings singleWarning loc = singleWarning' loc []  -- | A single warning at the given location, but also with a stack -- trace (sort of) to the location.-singleWarning' :: SrcLoc -> [SrcLoc] -> String -> Warnings+singleWarning' :: SrcLoc -> [SrcLoc] -> Doc -> Warnings singleWarning' loc locs problem = Warnings [(loc, locs, problem)]
src/futhark.hs view
@@ -32,6 +32,7 @@ import Futhark.Util (maxinum) import Futhark.Util.Options import GHC.IO.Encoding (setLocaleEncoding)+import GHC.IO.Exception (IOErrorType (..), IOException (..)) import System.Environment import System.Exit import System.IO@@ -78,7 +79,14 @@ -- | Catch all IO exceptions and print a better error message if they -- happen. reportingIOErrors :: IO () -> IO ()-reportingIOErrors = flip catches [Handler onExit, Handler onICE, Handler onError]+reportingIOErrors =+  flip+    catches+    [ Handler onExit,+      Handler onICE,+      Handler onIOException,+      Handler onError+    ]   where     onExit :: ExitCode -> IO ()     onExit = throwIO@@ -88,10 +96,12 @@       T.hPutStrLn stderr "Known compiler limitation encountered.  Sorry."       T.hPutStrLn stderr "Revise your program or try a different Futhark compiler."       T.hPutStrLn stderr s+      exitWith $ ExitFailure 1     onICE (Error CompilerBug s) = do       T.hPutStrLn stderr "Internal compiler error."       T.hPutStrLn stderr "Please report this at https://github.com/diku-dk/futhark/issues."       T.hPutStrLn stderr s+      exitWith $ ExitFailure 1      onError :: SomeException -> IO ()     onError e@@ -102,6 +112,12 @@         T.hPutStrLn stderr "Please report this at https://github.com/diku-dk/futhark/issues"         T.hPutStrLn stderr $ T.pack $ show e         exitWith $ ExitFailure 1++    onIOException :: IOException -> IO ()+    onIOException e+      | ioe_type e == ResourceVanished =+        exitWith $ ExitFailure 1+      | otherwise = throw e  main :: IO () main = reportingIOErrors $ do