futhark-0.26.2: rts/c/server.h
// Start of server.h.
// Forward declarations of things that we technically don't know until
// the application header file is included, but which we need.
struct futhark_context_config;
struct futhark_context;
char *futhark_context_get_error(struct futhark_context *ctx);
int futhark_context_sync(struct futhark_context *ctx);
int futhark_context_clear_caches(struct futhark_context *ctx);
int futhark_context_config_set_tuning_param(struct futhark_context_config *cfg,
const char *param_name,
size_t new_value);
int futhark_get_tuning_param_count(void);
const char* futhark_get_tuning_param_name(int i);
const char* futhark_get_tuning_param_class(int i);
typedef int (*restore_fn)(const void*, FILE*, struct futhark_context*, void*);
typedef void (*store_fn)(const void*, FILE*, struct futhark_context*, void*);
typedef int (*free_fn)(const void*, struct futhark_context*, void*);
typedef int (*array_new_fn)(struct futhark_context *, void**, const void*, const int64_t*);
typedef int (*array_set_fn)(struct futhark_context *, const void*, const void*, const int64_t*);
typedef const int64_t* (*array_shape_fn)(struct futhark_context*, const void*);
typedef int (*array_index_fn)(struct futhark_context*, void*, const void*, const int64_t*);
typedef int (*array_values_fn)(struct futhark_context*, const void*, void*);
typedef int (*array_free_data_fn)(struct futhark_context*, void*);
typedef int (*array_zip_fn)(struct futhark_context*, void*, const void*[]);
typedef int (*project_fn)(struct futhark_context*, void*, const void*);
typedef int (*variant_fn)(struct futhark_context*, const void*);
typedef int (*new_fn)(struct futhark_context*, void**, const void*[]);
typedef int (*destruct_fn)(struct futhark_context*, const void*[], const void*);
enum kind {
PRIMITIVE,
ARRAY,
RECORD,
SUM,
OPAQUE
};
struct field {
const char *name;
const struct type *type;
project_fn project;
};
struct array {
int rank;
const struct type *element_type;
const struct primtype_info_t* info;
const char *name;
int num_fields;
const struct field *fields;
array_new_fn new;
array_set_fn set;
array_shape_fn shape;
array_index_fn index;
array_values_fn values;
array_free_data_fn free;
array_zip_fn zip;
};
struct record {
int num_fields;
const struct field* fields;
new_fn new;
};
struct variant {
const char *name;
int num_types;
const struct type **types;
new_fn new;
destruct_fn destruct;
};
struct sum {
int num_variants;
const struct variant *variants;
variant_fn variant;
};
struct type {
const char *name;
restore_fn restore;
store_fn store;
free_fn free;
const void *aux;
const enum kind kind;
const void *info;
};
int free_scalar(const void *aux, struct futhark_context *ctx, void *p) {
(void)aux;
(void)ctx;
(void)p;
// Nothing to do.
return 0;
}
#define DEF_SCALAR_TYPE(T) \
int restore_##T(const void *aux, FILE *f, \
struct futhark_context *ctx, void *p) { \
(void)aux; \
(void)ctx; \
return read_scalar(f, &T##_info, p); \
} \
\
void store_##T(const void *aux, FILE *f, \
struct futhark_context *ctx, void *p) { \
(void)aux; \
(void)ctx; \
write_scalar(f, 1, &T##_info, p); \
} \
\
struct type type_##T = \
{ .name = #T, \
.restore = restore_##T, \
.store = store_##T, \
.free = free_scalar \
} \
DEF_SCALAR_TYPE(i8);
DEF_SCALAR_TYPE(i16);
DEF_SCALAR_TYPE(i32);
DEF_SCALAR_TYPE(i64);
DEF_SCALAR_TYPE(u8);
DEF_SCALAR_TYPE(u16);
DEF_SCALAR_TYPE(u32);
DEF_SCALAR_TYPE(u64);
DEF_SCALAR_TYPE(f16);
DEF_SCALAR_TYPE(f32);
DEF_SCALAR_TYPE(f64);
DEF_SCALAR_TYPE(bool);
struct value {
const struct type *type;
union {
void *v_ptr;
int8_t v_i8;
int16_t v_i16;
int32_t v_i32;
int64_t v_i64;
uint8_t v_u8;
uint16_t v_u16;
uint32_t v_u32;
uint64_t v_u64;
uint16_t v_f16;
float v_f32;
double v_f64;
bool v_bool;
} value;
};
void* value_ptr(struct value *v) {
if (v->type == &type_i8) {
return &v->value.v_i8;
}
if (v->type == &type_i16) {
return &v->value.v_i16;
}
if (v->type == &type_i32) {
return &v->value.v_i32;
}
if (v->type == &type_i64) {
return &v->value.v_i64;
}
if (v->type == &type_u8) {
return &v->value.v_u8;
}
if (v->type == &type_u16) {
return &v->value.v_u16;
}
if (v->type == &type_u32) {
return &v->value.v_u32;
}
if (v->type == &type_u64) {
return &v->value.v_u64;
}
if (v->type == &type_f16) {
return &v->value.v_f16;
}
if (v->type == &type_f32) {
return &v->value.v_f32;
}
if (v->type == &type_f64) {
return &v->value.v_f64;
}
if (v->type == &type_bool) {
return &v->value.v_bool;
}
return &v->value.v_ptr;
}
struct variable {
// NULL name indicates free slot. Name is owned by this struct.
char *name;
struct value value;
};
typedef int (*entry_point_fn)(struct futhark_context*, void*, void**);
struct entry_point {
const char *name;
entry_point_fn f;
const char** tuning_params;
const char** attrs;
const struct type *out_type;
bool out_unique;
const struct type **in_types;
bool *in_unique;
};
int entry_num_ins(struct entry_point *e) {
int count = 0;
while (e->in_types[count]) {
count++;
}
return count;
}
struct futhark_prog {
// Last entry point identified by NULL name.
struct entry_point *entry_points;
// Last type identified by NULL name.
const struct type **types;
};
struct server_state {
struct futhark_prog prog;
struct futhark_context_config *cfg;
struct futhark_context *ctx;
int variables_capacity;
struct variable *variables;
};
struct variable* get_variable(struct server_state *s,
const char *name) {
for (int i = 0; i < s->variables_capacity; i++) {
if (s->variables[i].name != NULL &&
strcmp(s->variables[i].name, name) == 0) {
return &s->variables[i];
}
}
return NULL;
}
struct variable* create_variable(struct server_state *s,
const char *name,
const struct type *type) {
int found = -1;
for (int i = 0; i < s->variables_capacity; i++) {
if (found == -1 && s->variables[i].name == NULL) {
found = i;
} else if (s->variables[i].name != NULL &&
strcmp(s->variables[i].name, name) == 0) {
return NULL;
}
}
if (found != -1) {
// Found a free spot.
s->variables[found].name = strdup(name);
s->variables[found].value.type = type;
return &s->variables[found];
}
// Need to grow the buffer.
found = s->variables_capacity;
s->variables_capacity *= 2;
s->variables = realloc(s->variables,
s->variables_capacity * sizeof(struct variable));
s->variables[found].name = strdup(name);
s->variables[found].value.type = type;
for (int i = found+1; i < s->variables_capacity; i++) {
s->variables[i].name = NULL;
}
return &s->variables[found];
}
void drop_variable(struct variable *v) {
free(v->name);
v->name = NULL;
}
int arg_exists(const char *args[], int i) {
return args[i] != NULL;
}
const char* get_arg(const char *args[], int i) {
if (!arg_exists(args, i)) {
futhark_panic(1, "Insufficient command args.\n");
}
return args[i];
}
const struct type* get_type(struct server_state *s, const char *name) {
for (int i = 0; s->prog.types[i]; i++) {
if (strcmp(s->prog.types[i]->name, name) == 0) {
return s->prog.types[i];
}
}
futhark_panic(1, "Unknown type %s\n", name);
return NULL;
}
struct entry_point* get_entry_point(struct server_state *s, const char *name) {
for (int i = 0; s->prog.entry_points[i].name; i++) {
if (strcmp(s->prog.entry_points[i].name, name) == 0) {
return &s->prog.entry_points[i];
}
}
return NULL;
}
// Print the command-done marker, indicating that we are ready for
// more input.
void ok(void) {
printf("%%%%%% OK\n");
fflush(stdout);
}
// Print the failure marker. Output is now an error message until the
// next ok().
void failure(void) {
printf("%%%%%% FAILURE\n");
}
void error_check(struct server_state *s, int err) {
if (err != 0) {
failure();
char *error = futhark_context_get_error(s->ctx);
if (error != NULL) {
puts(error);
}
free(error);
}
}
void cmd_call(struct server_state *s, const char *args[]) {
const char *name = get_arg(args, 0);
struct entry_point *e = get_entry_point(s, name);
if (e == NULL) {
failure();
printf("Unknown entry point: %s\n", name);
return;
}
int num_ins = entry_num_ins(e);
// +1 to avoid zero-size arrays, which is UB.
void* out;
void* ins[num_ins+1];
for (int i = 0; i < num_ins; i++) {
const char *in_name = get_arg(args, 2+i);
struct variable *v = get_variable(s, in_name);
if (v == NULL) {
failure();
printf("Unknown variable: %s\n", in_name);
return;
}
if (v->value.type != e->in_types[i]) {
failure();
printf("Wrong input type. Expected %s, got %s.\n",
e->in_types[i]->name, v->value.type->name);
return;
}
ins[i] = value_ptr(&v->value);
}
const char *out_name = get_arg(args, 1);
struct variable *v = create_variable(s, out_name, e->out_type);
if (v == NULL) {
failure();
printf("Variable already exists: %s\n", out_name);
return;
}
out = value_ptr(&v->value);
int64_t t_start = get_wall_time();
int err = e->f(s->ctx, out, ins);
err |= futhark_context_sync(s->ctx);
int64_t t_end = get_wall_time();
long long int elapsed_usec = t_end - t_start;
printf("runtime: %lld\n", elapsed_usec);
error_check(s, err);
if (err != 0) {
// Need to uncreate the output variable, which would otherwise be left
// in an uninitialised state.
const char *out_name = get_arg(args, 1);
struct variable *v = get_variable(s, out_name);
if (v) {
drop_variable(v);
}
}
}
void cmd_restore(struct server_state *s, const char *args[]) {
const char *fname = get_arg(args, 0);
FILE *f = fopen(fname, "rb");
if (f == NULL) {
failure();
printf("Failed to open %s: %s\n", fname, strerror(errno));
return;
}
int bad = 0;
int values = 0;
for (int i = 1; arg_exists(args, i); i+=2, values++) {
const char *vname = get_arg(args, i);
const char *type = get_arg(args, i+1);
const struct type *t = get_type(s, type);
struct variable *v = create_variable(s, vname, t);
if (v == NULL) {
bad = 1;
failure();
printf("Variable already exists: %s\n", vname);
break;
}
errno = 0;
if (t->restore(t->aux, f, s->ctx, value_ptr(&v->value)) != 0) {
bad = 1;
failure();
printf("Failed to restore variable %s.\n"
"Possibly malformed data in %s (errno: %s)\n",
vname, fname, strerror(errno));
drop_variable(v);
break;
}
}
if (!bad && end_of_input(f) != 0) {
failure();
printf("Expected EOF after reading %d values from %s\n",
values, fname);
}
fclose(f);
if (!bad) {
int err = futhark_context_sync(s->ctx);
error_check(s, err);
}
}
void cmd_store(struct server_state *s, const char *args[]) {
const char *fname = get_arg(args, 0);
FILE *f = fopen(fname, "wb");
if (f == NULL) {
failure();
printf("Failed to open %s: %s\n", fname, strerror(errno));
} else {
for (int i = 1; arg_exists(args, i); i++) {
const char *vname = get_arg(args, i);
struct variable *v = get_variable(s, vname);
if (v == NULL) {
failure();
printf("Unknown variable: %s\n", vname);
return;
}
const struct type *t = v->value.type;
t->store(t->aux, f, s->ctx, value_ptr(&v->value));
}
fclose(f);
}
}
void cmd_free(struct server_state *s, const char *args[]) {
for (int i = 0; arg_exists(args, i); i++) {
const char *name = get_arg(args, i);
struct variable *v = get_variable(s, name);
if (v == NULL) {
failure();
printf("Unknown variable: %s\n", name);
return;
}
const struct type *t = v->value.type;
int err = t->free(t->aux, s->ctx, value_ptr(&v->value));
error_check(s, err);
drop_variable(v);
}
}
void cmd_rename(struct server_state *s, const char *args[]) {
const char *oldname = get_arg(args, 0);
const char *newname = get_arg(args, 1);
struct variable *old = get_variable(s, oldname);
struct variable *new = get_variable(s, newname);
if (old == NULL) {
failure();
printf("Unknown variable: %s\n", oldname);
return;
}
if (new != NULL) {
failure();
printf("Variable already exists: %s\n", newname);
return;
}
free(old->name);
old->name = strdup(newname);
}
void cmd_inputs(struct server_state *s, const char *args[]) {
const char *name = get_arg(args, 0);
struct entry_point *e = get_entry_point(s, name);
if (e == NULL) {
failure();
printf("Unknown entry point: %s\n", name);
return;
}
int num_ins = entry_num_ins(e);
for (int i = 0; i < num_ins; i++) {
if (e->in_unique[i]) {
putchar('*');
}
puts(e->in_types[i]->name);
}
}
void cmd_output(struct server_state *s, const char *args[]) {
const char *name = get_arg(args, 0);
struct entry_point *e = get_entry_point(s, name);
if (e == NULL) {
failure();
printf("Unknown entry point: %s\n", name);
return;
}
if (e->out_unique) {
putchar('*');
}
puts(e->out_type->name);
}
void cmd_clear(struct server_state *s, const char *args[]) {
(void)args;
int err = 0;
for (int i = 0; i < s->variables_capacity; i++) {
struct variable *v = &s->variables[i];
if (v->name != NULL) {
err |= v->value.type->free(v->value.type->aux, s->ctx, value_ptr(&v->value));
drop_variable(v);
}
}
err |= futhark_context_clear_caches(s->ctx);
error_check(s, err);
}
void cmd_pause_profiling(struct server_state *s, const char *args[]) {
(void)args;
futhark_context_pause_profiling(s->ctx);
}
void cmd_unpause_profiling(struct server_state *s, const char *args[]) {
(void)args;
futhark_context_unpause_profiling(s->ctx);
}
void cmd_report(struct server_state *s, const char *args[]) {
(void)args;
char *report = futhark_context_report(s->ctx);
if (report) {
puts(report);
} else {
failure();
report = futhark_context_get_error(s->ctx);
if (report) {
puts(report);
} else {
puts("Failed to produce profiling report.\n");
}
}
free(report);
}
void cmd_set_tuning_param(struct server_state *s, const char *args[]) {
const char *param = get_arg(args, 0);
const char *val_s = get_arg(args, 1);
size_t val = atol(val_s);
int err = futhark_context_config_set_tuning_param(s->cfg, param, val);
error_check(s, err);
if (err != 0) {
printf("Failed to set tuning parameter %s to %ld\n", param, (long)val);
}
}
void cmd_tuning_params(struct server_state *s, const char *args[]) {
const char *name = get_arg(args, 0);
struct entry_point *e = get_entry_point(s, name);
if (e == NULL) {
failure();
printf("Unknown entry point: %s\n", name);
return;
}
const char **params = e->tuning_params;
for (int i = 0; params[i] != NULL; i++) {
printf("%s\n", params[i]);
}
}
void cmd_tuning_param_class(struct server_state *s, const char *args[]) {
(void)s;
const char *param = get_arg(args, 0);
int n = futhark_get_tuning_param_count();
for (int i = 0; i < n; i++) {
if (strcmp(futhark_get_tuning_param_name(i), param) == 0) {
printf("%s\n", futhark_get_tuning_param_class(i));
return;
}
}
failure();
printf("Unknown tuning parameter: %s\n", param);
}
void cmd_attributes(struct server_state *s, const char *args[]) {
const char *name = get_arg(args, 0);
struct entry_point *e = get_entry_point(s, name);
if (e == NULL) {
failure();
printf("Unknown entry point: %s\n", name);
return;
}
const char **params = e->attrs;
for (int i = 0; params[i] != NULL; i++) {
printf("%s\n", params[i]);
}
}
void cmd_kind(struct server_state *s, const char *args[]) {
const char *type = get_arg(args, 0);
const struct type *t = get_type(s, type);
switch (t->kind) {
case PRIMITIVE: printf("primitive\n"); return;
case ARRAY: printf("array\n"); return;
case RECORD: printf("record\n"); return;
case SUM: printf("sum\n"); return;
case OPAQUE: printf("opaque\n"); return;
}
futhark_panic(1, "Invalid kind detected on type \"%s\".\n", t->name);
}
void cmd_type(struct server_state *s, const char *args[]) {
const char *from_name = get_arg(args, 0);
struct variable *v = get_variable(s, from_name);
if (v == NULL) {
failure();
printf("Unknown variable: %s\n", from_name);
return;
}
printf("%s\n", v->value.type->name);
}
void cmd_shape(struct server_state *s, const char *args[]) {
const char *name = get_arg(args, 0);
struct variable* v = get_variable(s, name);
if (v == NULL) {
failure();
printf("Unknown variable: %s\n", name);
return;
}
if (v->value.type->kind != ARRAY) {
failure();
printf("Not an array type\n");
return;
}
const struct array *a = v->value.type->info;
const int64_t *shape = a->shape(s->ctx, v->value.value.v_ptr);
for (int i = 0; i < a->rank; ++i) {
printf("%lld\n", (long long)shape[i]);
}
}
void cmd_elemtype(struct server_state *s, const char *args[]) {
const char *type = get_arg(args, 0);
const struct type *t = get_type(s, type);
if (t->kind != ARRAY) {
failure();
printf("Not an array type\n");
return;
}
const struct array *a = t->info;
printf("%s\n", a->element_type->name);
}
void cmd_rank(struct server_state *s, const char *args[]) {
const char *type = get_arg(args, 0);
const struct type *t = get_type(s, type);
if (t->kind != ARRAY) {
failure();
printf("Not an array type\n");
return;
}
const struct array *a = t->info;
printf("%d\n", a->rank);
}
void cmd_new_array(struct server_state *s, const char *args[]) {
const char *to_name = get_arg(args, 0);
const char *type_name = get_arg(args, 1);
const struct type *type = get_type(s, type_name);
struct variable *to = create_variable(s, to_name, type);
if (to == NULL) {
failure();
printf("Variable already exists: %s\n", to_name);
return;
}
if (type->kind != ARRAY) {
failure();
printf("Not an array type\n");
return;
}
const struct array *a = type->info;
int num_args = 0;
for (int i = 2; arg_exists(args, i); i++) {
num_args++;
}
if (num_args < a->rank) {
failure();
printf("Expected %d dimensions, but got %d.\n", a->rank, num_args);
return;
}
int64_t* dims = alloca((size_t)a->rank * sizeof(int64_t));
int64_t n_values = 1;
for (int i = 0; i < a->rank; ++i) {
const char *size_arg = get_arg(args, 2+i);
char* end;
errno = 0;
int64_t size = strtoll(size_arg, &end, 10);
if (errno == ERANGE || *end != '\0' || size < 0) {
failure();
printf("Invalid size `%s` of dimension %d.\n", size_arg, i+1);
return;
}
dims[i] = size;
n_values *= size;
}
if (num_args - a->rank != n_values) {
failure();
printf("Expected %d values, but got %d.\n", (int)n_values, num_args - a->rank);
return;
}
char *values = NULL;
const void **value_ptrs = NULL;
if (n_values < 0) {
failure();
printf("Invalid array size.\n");
return;
}
if (a->info != NULL) {
size_t values_size = (size_t)n_values * a->info->size;
values = malloc(values_size);
if (values == NULL) {
failure();
printf("Out of memory.\n");
return;
}
} else {
value_ptrs = malloc((size_t)n_values * sizeof(void*));
if (value_ptrs == NULL) {
failure();
printf("Out of memory.\n");
return;
}
}
for (int64_t i = 0; i < n_values; i++) {
struct variable* v = get_variable(s, args[2+a->rank+i]);
if (v == NULL) {
failure();
printf("Unknown variable: %s\n", args[2+a->rank+i]);
free(value_ptrs);
free(values);
return;
}
if (strcmp(v->value.type->name, a->element_type->name) != 0) {
failure();
printf("Value %d mismatch: expected type %s, got %s\n",
(int)i, a->element_type->name, v->value.type->name);
free(value_ptrs);
free(values);
return;
}
if (a->info != NULL) {
memcpy(values + i * a->info->size, value_ptr(&v->value), a->info->size);
} else {
value_ptrs[i] = value_ptr(&v->value);
}
}
a->new(s->ctx, value_ptr(&to->value), a->info != NULL ? (void*)values : value_ptrs, dims);
free(value_ptrs);
free(values);
}
void cmd_set(struct server_state *s, const char *args[]) {
const char *arr_name = get_arg(args, 0);
const char *val_name = get_arg(args, 1);
struct variable* arr = get_variable(s, arr_name);
struct variable* val = get_variable(s, val_name);
if (arr == NULL) {
failure();
printf("Unknown variable: %s\n", arr_name);
return;
}
if (val == NULL) {
failure();
printf("Unknown variable: %s\n", val_name);
return;
}
if (arr->value.type->kind != ARRAY) {
failure();
printf("Not an array type\n");
return;
}
const struct array *a = arr->value.type->info;
if (strcmp(val->value.type->name, a->element_type->name) != 0) {
failure();
printf("Type mismatch: expected element of type %s, got %s\n",
a->element_type->name, val->value.type->name);
return;
}
for (int i = 0; ; ++i) {
if (!arg_exists(args, 2+i)) {
if (i != a->rank) {
failure();
printf("%d indices expected but %d values provided.\n", a->rank, i);
return;
}
break;
}
}
const int64_t *shape = a->shape(s->ctx, arr->value.value.v_ptr);
int64_t* indices = alloca(a->rank * sizeof(int64_t));
for (int i = 0; i < a->rank; ++i) {
const char *idx_arg = get_arg(args, 2+i);
char* end;
errno = 0;
int64_t idx = strtoll(idx_arg, &end, 10);
if (errno == ERANGE || *end != '\0' || idx < 0 || idx >= shape[i]) {
failure();
printf("Invalid index `%s` on dimension %d.\n", idx_arg, i+1);
return;
}
indices[i] = idx;
}
a->set(s->ctx, arr->value.value.v_ptr, value_ptr(&val->value), indices);
}
void cmd_index(struct server_state *s, const char *args[]) {
const char *to_name = get_arg(args, 0);
const char *from_name = get_arg(args, 1);
struct variable* from = get_variable(s, from_name);
if (from == NULL) {
failure();
printf("Unknown variable: %s\n", from_name);
return;
}
if (from->value.type->kind != ARRAY) {
failure();
printf("Not an array type\n");
return;
}
const struct array *a = from->value.type->info;
for (int i = 0; ; ++i) {
if (!arg_exists(args, 2+i)) {
if (i != a->rank) {
failure();
printf("%d indices expected but %d values provided.\n", a->rank, i);
return;
}
break;
}
}
const int64_t *shape = a->shape(s->ctx, from->value.value.v_ptr);
int64_t* indices = alloca(a->rank * sizeof(int64_t));
for (int i = 0; i < a->rank; ++i) {
const char *idx_arg = get_arg(args, 2+i);
char* end;
errno = 0;
int64_t idx = strtoll(idx_arg, &end, 10);
if (errno == ERANGE || *end != '\0' || idx < 0 || idx >= shape[i]) {
failure();
printf("Invalid index `%s` on dimension %d.\n", idx_arg, i+1);
return;
}
indices[i] = idx;
}
struct variable* to = create_variable(s, to_name, a->element_type);
if (to == NULL) {
failure();
printf("Variable already exists: %s\n", to_name);
return;
}
a->index(s->ctx, value_ptr(&to->value), from->value.value.v_ptr, indices);
}
void cmd_zip(struct server_state *s, const char *args[]) {
const char *to_name = get_arg(args, 0);
const char *type_name = get_arg(args, 1);
const struct type *type = get_type(s, type_name);
if (type->kind != ARRAY) {
failure();
printf("Not an array type\n");
return;
}
const struct array *a = type->info;
if (a->zip == NULL || a->fields == NULL) {
failure();
printf("Cannot zip this array type\n");
return;
}
int num_args = 0;
for (int i = 2; arg_exists(args, i); i++) {
num_args++;
}
if (num_args != a->num_fields) {
failure();
printf("%d arrays expected but %d values provided.\n", a->num_fields, num_args);
return;
}
const void** value_ptrs = alloca(num_args * sizeof(void*));
for (int i = 0; i < num_args; i++) {
struct variable* v = get_variable(s, args[2+i]);
if (v == NULL) {
failure();
printf("Unknown variable: %s\n", args[2+i]);
return;
}
if (strcmp(v->value.type->name, a->fields[i].type->name) != 0) {
failure();
printf("Field %s mismatch: expected type %s, got %s\n",
a->fields[i].name, a->fields[i].type->name, v->value.type->name);
return;
}
value_ptrs[i] = v->value.value.v_ptr;
}
struct variable *to = create_variable(s, to_name, type);
if (to == NULL) {
failure();
printf("Variable already exists: %s\n", to_name);
return;
}
int err = a->zip(s->ctx, value_ptr(&to->value), value_ptrs);
err |= futhark_context_sync(s->ctx);
error_check(s, err);
if (err != 0) {
drop_variable(to);
}
}
void cmd_unzip(struct server_state *s, const char *args[]) {
const char *from_name = get_arg(args, 0);
struct variable* from = get_variable(s, from_name);
if (from == NULL) {
failure();
printf("Unknown variable: %s\n", from_name);
return;
}
if (from->value.type->kind != ARRAY) {
failure();
printf("Not an array type\n");
return;
}
const struct array *a = from->value.type->info;
if (a->fields == NULL) {
failure();
printf("Cannot unzip this array type\n");
return;
}
int num_args = 0;
for (int i = 1; arg_exists(args, i); i++) {
num_args++;
}
if (num_args != a->num_fields) {
failure();
printf("%d arrays expected but %d values provided.\n", a->num_fields, num_args);
return;
}
struct variable **outs = alloca(num_args * sizeof(struct variable*));
for (int i = 0; i < num_args; i++) {
const char *to_name = get_arg(args, i+1);
struct variable *to = create_variable(s, to_name, a->fields[i].type);
if (to == NULL) {
failure();
printf("Variable already exists: %s\n", to_name);
for (int j = 0; j < i; j++) {
drop_variable(outs[j]);
}
return;
}
outs[i] = to;
}
int err = 0;
for (int i = 0; i < num_args; i++) {
err |= a->fields[i].project(s->ctx, value_ptr(&outs[i]->value), from->value.value.v_ptr);
}
err |= futhark_context_sync(s->ctx);
error_check(s, err);
if (err != 0) {
for (int i = 0; i < num_args; i++) {
drop_variable(outs[i]);
}
}
}
void cmd_fields(struct server_state *s, const char *args[]) {
const char *type = get_arg(args, 0);
const struct type *t = get_type(s, type);
if (t->kind != RECORD) {
failure();
printf("Not a record type\n");
return;
}
const struct record *r = t->info;
for (int i = 0; i < r->num_fields; i++) {
const struct field f = r->fields[i];
printf("%s %s\n", f.name, f.type->name);
}
}
void cmd_variants(struct server_state *s, const char *args[]) {
const char *type = get_arg(args, 0);
const struct type *t = get_type(s, type);
if (t->kind != SUM) {
failure();
printf("Not a sum type\n");
return;
}
const struct sum *st = t->info;
for (int i = 0; i < st->num_variants; i++) {
const struct variant *v = &st->variants[i];
printf("%s\n", v->name);
for (int i = 0; i < v->num_types; i++) {
const struct type *f = v->types[i];
printf("- %s\n", f->name);
}
}
}
void cmd_variant(struct server_state *s, const char *args[]) {
const char *name = get_arg(args, 0);
struct variable* v = get_variable(s, name);
if (v == NULL) {
failure();
printf("Unknown variable: %s\n", name);
return;
}
const struct type *t = get_type(s, v->value.type->name);
if (t->kind != SUM) {
failure();
printf("Not a sum type\n");
return;
}
const struct sum *st = t->info;
int i = st->variant(s->ctx, v->value.value.v_ptr);
const struct variant *var = &st->variants[i];
printf("%s\n", var->name);
}
void cmd_project(struct server_state *s, const char *args[]) {
const char *to_name = get_arg(args, 0);
const char *from_name = get_arg(args, 1);
const char *field_name = get_arg(args, 2);
struct variable *from = get_variable(s, from_name);
if (from == NULL) {
failure();
printf("Unknown variable: %s\n", from_name);
return;
}
const struct type *from_type = from->value.type;
if (from_type->kind != RECORD) {
failure();
printf("Not a record type\n");
return;
}
const struct record *r = from_type->info;
const struct field *field = NULL;
for (int i = 0; i < r->num_fields; i++) {
if (strcmp(r->fields[i].name, field_name) == 0) {
field = &r->fields[i];
break;
}
}
if (field == NULL) {
failure();
printf("No such field\n");
}
struct variable *to = create_variable(s, to_name, field->type);
if (to == NULL) {
failure();
printf("Variable already exists: %s\n", to_name);
return;
}
field->project(s->ctx, value_ptr(&to->value), from->value.value.v_ptr);
}
void cmd_new(struct server_state *s, const char *args[]) {
const char *to_name = get_arg(args, 0);
const char *type_name = get_arg(args, 1);
const struct type *type = get_type(s, type_name);
struct variable *to = create_variable(s, to_name, type);
if (to == NULL) {
failure();
printf("Variable already exists: %s\n", to_name);
return;
}
if (type->kind != RECORD) {
failure();
printf("Not a record type\n");
return;
}
const struct record *r = type->info;
int num_args = 0;
for (int i = 2; arg_exists(args, i); i++) {
num_args++;
}
if (num_args != r->num_fields) {
failure();
printf("%d fields expected but %d values provided.\n", num_args, r->num_fields);
return;
}
const void** value_ptrs = alloca(num_args * sizeof(void*));
for (int i = 0; i < num_args; i++) {
struct variable* v = get_variable(s, args[2+i]);
if (v == NULL) {
failure();
printf("Unknown variable: %s\n", args[2+i]);
return;
}
if (strcmp(v->value.type->name, r->fields[i].type->name) != 0) {
failure();
printf("Field %s mismatch: expected type %s, got %s\n",
r->fields[i].name, r->fields[i].type->name, v->value.type->name);
return;
}
value_ptrs[i] = value_ptr(&v->value);
}
r->new(s->ctx, value_ptr(&to->value), value_ptrs);
}
void cmd_construct(struct server_state *s, const char *args[]) {
const char *to_name = get_arg(args, 0);
const char *type_name = get_arg(args, 1);
const char *variant_name = get_arg(args, 2);
const struct type *type = get_type(s, type_name);
struct variable *to = create_variable(s, to_name, type);
if (to == NULL) {
failure();
printf("Variable already exists: %s\n", to_name);
return;
}
if (type->kind != SUM) {
failure();
printf("Not a sum type\n");
return;
}
const struct sum *st = type->info;
for (int i = 0; i < st->num_variants; i++) {
const struct variant *var = &st->variants[i];
if (strcmp(var->name, variant_name) == 0) {
int num_args = 0;
for (int i = 3; arg_exists(args, i); i++) {
num_args++;
}
if (num_args != var->num_types) {
failure();
printf("%d values expected but %d values provided.\n", var->num_types, num_args);
return;
}
const void** value_ptrs = alloca(num_args * sizeof(void*));
for (int i = 0; i < num_args; i++) {
const char *vname = get_arg(args, 3+i);
struct variable* v = get_variable(s, vname);
if (v == NULL) {
failure();
printf("Unknown variable: %s\n", vname);
return;
}
if (strcmp(v->value.type->name, var->types[i]->name) != 0) {
failure();
printf("Value %d mismatch: expected type %s, got %s\n",
i, var->types[i]->name, v->value.type->name);
return;
}
value_ptrs[i] = value_ptr(&v->value);
}
var->new(s->ctx, value_ptr(&to->value), value_ptrs);
return;
}
}
failure();
printf("No such variant\n");
}
void cmd_destruct(struct server_state *s, const char *args[]) {
const char *from_name = get_arg(args, 0);
struct variable *v = get_variable(s, from_name);
if (v == NULL) {
failure();
printf("Unknown variable: %s\n", from_name);
return;
}
if (v->value.type->kind != SUM) {
failure();
printf("Not a sum type\n");
return;
}
const struct sum *sum = v->value.type->info;
const struct variant *var = &sum->variants[sum->variant(s->ctx, v->value.value.v_ptr)];
int num_args = 0;
for (int i = 1; arg_exists(args, i); i++) {
num_args++;
}
if (num_args != var->num_types) {
failure();
printf("%d variables expected but %d variables provided. %s\n", var->num_types, num_args, var->name);
return;
}
const void **value_ptrs = alloca(num_args * sizeof(struct variable*));
for (int i = 0; i < num_args; i++) {
const char *vname = get_arg(args, i+1);
struct variable *vn = create_variable(s, vname, var->types[i]);
if (vn == NULL) {
failure();
printf("Variable already exists: %s\n", vname);
return;
}
value_ptrs[i] = value_ptr(&vn->value);
}
var->destruct(s->ctx, value_ptrs, v->value.value.v_ptr);
return;
}
void cmd_entry_points(struct server_state *s, const char *args[]) {
(void)args;
for (int i = 0; s->prog.entry_points[i].name; i++) {
puts(s->prog.entry_points[i].name);
}
}
void cmd_types(struct server_state *s, const char *args[]) {
(void)args;
for (int i = 0; s->prog.types[i] != NULL; i++) {
puts(s->prog.types[i]->name);
}
}
char *next_word(char **line) {
char *p = *line;
while (isspace(*p)) {
p++;
}
if (*p == 0) {
return NULL;
}
if (*p == '"') {
char *save = p+1;
// Skip ahead till closing quote.
p++;
while (*p && *p != '"') {
p++;
}
if (*p == '"') {
*p = 0;
*line = p+1;
return save;
} else {
return NULL;
}
} else {
char *save = p;
// Skip ahead till next whitespace.
while (*p && !isspace(*p)) {
p++;
}
if (*p) {
*p = 0;
*line = p+1;
} else {
*line = p;
}
return save;
}
}
void process_line(struct server_state *s, char *line) {
int max_num_tokens = 1000;
const char* tokens[max_num_tokens];
int num_tokens = 0;
while ((tokens[num_tokens] = next_word(&line)) != NULL) {
num_tokens++;
if (num_tokens == max_num_tokens) {
futhark_panic(1, "Line too long.\n");
}
}
const char *command = tokens[0];
if (command == NULL) {
failure();
printf("Empty line\n");
} else if (strcmp(command, "call") == 0) {
cmd_call(s, tokens+1);
} else if (strcmp(command, "restore") == 0) {
cmd_restore(s, tokens+1);
} else if (strcmp(command, "store") == 0) {
cmd_store(s, tokens+1);
} else if (strcmp(command, "free") == 0) {
cmd_free(s, tokens+1);
} else if (strcmp(command, "rename") == 0) {
cmd_rename(s, tokens+1);
} else if (strcmp(command, "inputs") == 0) {
cmd_inputs(s, tokens+1);
} else if (strcmp(command, "output") == 0) {
cmd_output(s, tokens+1);
} else if (strcmp(command, "clear") == 0) {
cmd_clear(s, tokens+1);
} else if (strcmp(command, "pause_profiling") == 0) {
cmd_pause_profiling(s, tokens+1);
} else if (strcmp(command, "unpause_profiling") == 0) {
cmd_unpause_profiling(s, tokens+1);
} else if (strcmp(command, "report") == 0) {
cmd_report(s, tokens+1);
} else if (strcmp(command, "set_tuning_param") == 0) {
cmd_set_tuning_param(s, tokens+1);
} else if (strcmp(command, "tuning_params") == 0) {
cmd_tuning_params(s, tokens+1);
} else if (strcmp(command, "tuning_param_class") == 0) {
cmd_tuning_param_class(s, tokens+1);
} else if (strcmp(command, "kind") == 0) {
cmd_kind(s, tokens+1);
} else if (strcmp(command, "type") == 0) {
cmd_type(s, tokens+1);
} else if (strcmp(command, "shape") == 0) {
cmd_shape(s, tokens+1);
} else if (strcmp(command, "elemtype") == 0) {
cmd_elemtype(s, tokens+1);
} else if (strcmp(command, "rank") == 0) {
cmd_rank(s, tokens+1);
} else if (strcmp(command, "new_array") == 0) {
cmd_new_array(s, tokens+1);
} else if (strcmp(command, "set") == 0) {
cmd_set(s, tokens+1);
} else if (strcmp(command, "index") == 0) {
cmd_index(s, tokens+1);
} else if (strcmp(command, "zip") == 0) {
cmd_zip(s, tokens+1);
} else if (strcmp(command, "unzip") == 0) {
cmd_unzip(s, tokens+1);
} else if (strcmp(command, "fields") == 0) {
cmd_fields(s, tokens+1);
} else if (strcmp(command, "variants") == 0) {
cmd_variants(s, tokens+1);
} else if (strcmp(command, "variant") == 0) {
cmd_variant(s, tokens+1);
} else if (strcmp(command, "new") == 0) {
cmd_new(s, tokens+1);
} else if (strcmp(command, "construct") == 0) {
cmd_construct(s, tokens+1);
} else if (strcmp(command, "destruct") == 0) {
cmd_destruct(s, tokens+1);
} else if (strcmp(command, "project") == 0) {
cmd_project(s, tokens+1);
} else if (strcmp(command, "entry_points") == 0) {
cmd_entry_points(s, tokens+1);
} else if (strcmp(command, "attributes") == 0) {
cmd_attributes(s, tokens+1);
} else if (strcmp(command, "types") == 0) {
cmd_types(s, tokens+1);
} else {
futhark_panic(1, "Unknown command: %s\n", command);
}
}
void run_server(struct futhark_prog *prog,
struct futhark_context_config *cfg,
struct futhark_context *ctx) {
char *line = NULL;
size_t buflen = 0;
ssize_t linelen;
struct server_state s = {
.cfg = cfg,
.ctx = ctx,
.variables_capacity = 100,
.prog = *prog
};
s.variables = malloc(s.variables_capacity * sizeof(struct variable));
for (int i = 0; i < s.variables_capacity; i++) {
s.variables[i].name = NULL;
}
ok();
while ((linelen = getline(&line, &buflen, stdin)) > 0) {
process_line(&s, line);
ok();
}
free(s.variables);
free(line);
}
// The aux struct lets us write generic method implementations without
// code duplication.
int restore_array(const struct array *a, FILE *f,
struct futhark_context *ctx, void *p) {
void *data = NULL;
int64_t shape[a->rank];
if (read_array(f, a->info, &data, shape, a->rank) != 0) {
return 1;
}
int err = a->new(ctx, p, data, shape);
err |= futhark_context_sync(ctx);
free(data);
return err;
}
void store_array(const struct array *a, FILE *f,
struct futhark_context *ctx, void *p) {
void *arr = *(void**)p;
const int64_t *shape = a->shape(ctx, arr);
int64_t size = a->info->size;
for (int i = 0; i < a->rank; i++) {
size *= shape[i];
}
int32_t *data = malloc(size);
assert(a->values(ctx, arr, data) == 0);
assert(futhark_context_sync(ctx) == 0);
assert(write_array(f, 1, a->info, data, shape, a->rank) == 0);
free(data);
}
int free_array(const struct array *a,
struct futhark_context *ctx, void *p) {
void *arr = *(void**)p;
return a->free(ctx, arr);
}
typedef void* (*opaque_restore_fn)(struct futhark_context*, void*);
typedef int (*opaque_store_fn)(struct futhark_context*, const void*, void **, size_t *);
typedef int (*opaque_free_fn)(struct futhark_context*, void*);
struct opaque_aux {
opaque_restore_fn restore;
opaque_store_fn store;
opaque_free_fn free;
};
int restore_opaque(const struct opaque_aux *aux, FILE *f,
struct futhark_context *ctx, void *p) {
// We have a problem: we need to load data from 'f', since the
// restore function takes a pointer, but we don't know how much we
// need (and cannot possibly). So we do something hacky: we read
// *all* of the file, pass all of the data to the restore function
// (which doesn't care if there's extra at the end), then we compute
// how much space the the object actually takes in serialised form
// and rewind the file to that position. The only downside is more IO.
size_t start = ftell(f);
size_t size;
char *bytes = fslurp_file(f, &size);
void *obj = aux->restore(ctx, bytes);
free(bytes);
if (obj != NULL) {
*(void**)p = obj;
size_t obj_size;
(void)aux->store(ctx, obj, NULL, &obj_size);
fseek(f, start+obj_size, SEEK_SET);
return 0;
} else {
fseek(f, start, SEEK_SET);
return 1;
}
}
void store_opaque(const struct opaque_aux *aux, FILE *f,
struct futhark_context *ctx, void *p) {
void *obj = *(void**)p;
size_t obj_size;
void *data = NULL;
(void)aux->store(ctx, obj, &data, &obj_size);
assert(futhark_context_sync(ctx) == 0);
fwrite(data, sizeof(char), obj_size, f);
free(data);
}
int free_opaque(const struct opaque_aux *aux,
struct futhark_context *ctx, void *p) {
void *obj = *(void**)p;
return aux->free(ctx, obj);
}
// End of server.h.