From 1b7331de6b8b14a2aceeed48804e8a028678f16b Mon Sep 17 00:00:00 2001 From: Tobias Grosser Date: Fri, 16 Mar 2012 17:14:44 +0100 Subject: [PATCH] Add support for the generation of c code C code generation is currently trivial. We just detect the scop and code generate it with CLoog, but do not apply any further transformations. This commit is meant to add the general infrastructure, but it obviously generates trivially correct code. The c code printing is enabled by the flag --target=c. Signed-off-by: Tobias Grosser Signed-off-by: Sven Verdoolaege --- Makefile.am | 2 + cpu.c | 243 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ cpu.h | 12 +++ ppcg.c | 8 +- ppcg_options.c | 8 ++ ppcg_options.h | 6 ++ 6 files changed, 278 insertions(+), 1 deletion(-) create mode 100644 cpu.c create mode 100644 cpu.h diff --git a/Makefile.am b/Makefile.am index dad2629..42aa492 100644 --- a/Makefile.am +++ b/Makefile.am @@ -35,6 +35,8 @@ bin_PROGRAMS = ppcg ppcg_SOURCES = \ clast_printer.c \ clast_printer.h \ + cpu.c \ + cpu.h \ cuda.c \ cuda.h \ cuda_common.h \ diff --git a/cpu.c b/cpu.c new file mode 100644 index 0000000..0ecbce7 --- /dev/null +++ b/cpu.c @@ -0,0 +1,243 @@ +/* + * Copyright 2012 INRIA Paris-Rocquencourt + * + * Use of this software is governed by the GNU LGPLv2.1 license + * + * Written by Tobias Grosser, INRIA Paris-Rocquencourt, + * Domaine de Voluceau, Rocquenqourt, B.P. 105, + * 78153 Le Chesnay Cedex France + */ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "clast_printer.h" +#include "cpu.h" +#include "pet_printer.h" +#include "rewrite.h" + +/* Derive the output file name from the input file name. + * 'input' is the entire path of the input file. The output + * is the file name plus the additional extension. + * + * We will basically replace everything after the last point + * with '.ppcg.c'. This means file.c becomes file.ppcg.c + */ +FILE *get_output_file(const char *input) +{ + char name[PATH_MAX]; + const char *base; + const char *ext; + const char ppcg_marker[] = ".ppcg"; + int len; + + base = strrchr(input, '/'); + if (base) + base++; + else + base = input; + ext = strrchr(base, '.'); + len = ext ? ext - base : strlen(base); + + memcpy(name, base, len); + strcpy(name + len, ppcg_marker); + strcpy(name + len + sizeof(ppcg_marker) - 1, ext); + + return fopen(name, "w"); +} + +/* Print a memory access 'access' to the output file 'out'. + * + * Given a map [a,b,c] -> {S[i,j] -> A[i,j+a]} we will print: A[i][j+a]. + * + * In case the output dimensions is one dimensional and unnamed we assume this + * is not a memory access, but just an expression. This means the example + * [a,b,c] -> {S[i,j] -> [j+a]} will be printed as: j+a + * + * The code that is printed is C code and the variable parameter and input + * dimension names are derived from the isl_dim names. + */ +static void print_access(__isl_take isl_map *access, FILE *out) +{ + int i; + const char *name; + unsigned n_index; + isl_printer *prn; + isl_pw_multi_aff *pma; + + n_index = isl_map_dim(access, isl_dim_out); + name = isl_map_get_tuple_name(access, isl_dim_out); + pma = isl_pw_multi_aff_from_map(access); + pma = isl_pw_multi_aff_coalesce(pma); + prn = isl_printer_to_file(isl_map_get_ctx(access), out); + prn = isl_printer_set_output_format(prn, ISL_FORMAT_C); + + if (name == NULL) { + isl_pw_aff *index; + index = isl_pw_multi_aff_get_pw_aff(pma, 0); + isl_printer_print_str(prn, "("); + prn = isl_printer_print_pw_aff(prn, index); + isl_printer_print_str(prn, ")"); + isl_pw_aff_free(index); + isl_printer_free(prn); + isl_pw_multi_aff_free(pma); + return; + } + + fprintf(out, "%s", name); + + for (i = 0; i < n_index; ++i) { + isl_pw_aff *index; + + index = isl_pw_multi_aff_get_pw_aff(pma, i); + + isl_printer_print_str(prn, "["); + prn = isl_printer_print_pw_aff(prn, index); + isl_printer_print_str(prn, "]"); + isl_pw_aff_free(index); + } + + isl_printer_free(prn); + isl_pw_multi_aff_free(pma); +} + +static void print_cpu_access(struct pet_expr *expr, void *usr) +{ + FILE *out = (FILE *) usr; + isl_map *access = isl_map_copy(expr->acc.access); + print_access(access, out); +} + +static void print_stmt_body(FILE *out, struct pet_stmt *stmt) +{ + print_pet_expr(out, stmt->body, print_cpu_access, out); +} + +/* Create a CloogInput data structure that describes the 'scop'. + */ +static CloogInput *cloog_input_from_scop(CloogState *state, + struct pet_scop *scop) +{ + CloogDomain *cloog_context; + CloogUnionDomain *ud; + CloogInput *input; + isl_set *context; + isl_union_set *domain_set; + isl_union_map *schedule_map; + + scop = pet_scop_align_params(scop); + + context = isl_set_copy(scop->context); + domain_set = pet_scop_collect_domains(scop); + schedule_map = pet_scop_collect_schedule(scop); + schedule_map = isl_union_map_intersect_domain(schedule_map, domain_set); + + ud = cloog_union_domain_from_isl_union_map(schedule_map); + + cloog_context = cloog_domain_from_isl_set(context); + + input = cloog_input_alloc(cloog_context, ud); + + return input; +} + +/* Print a #define macro for every statement in the 'scop'. + */ +static void print_stmt_definitions(struct pet_scop *scop, FILE *output) +{ + int i, j; + + for (i = 0; i < scop->n_stmt; ++i) { + struct pet_stmt *stmt = scop->stmts[i]; + const char *name = isl_set_get_tuple_name(stmt->domain); + + fprintf(output, "#define %s(", name); + + for (j = 0; j < isl_set_dim(stmt->domain, isl_dim_set); ++j) { + const char *name; + + if (j) + fprintf(output, ", "); + + name = isl_set_get_dim_name(stmt->domain, isl_dim_set, j); + fprintf(output, "%s", name); + } + + fprintf(output, ") "); + + print_stmt_body(output, stmt); + + fprintf(output, "\n"); + } +} + +/* Code generate the scop 'scop' and print the corresponding C code to + * 'output'. + */ +static void print_scop(isl_ctx *ctx, struct pet_scop *scop, FILE *output) +{ + CloogState *state; + CloogOptions *options; + CloogInput *input; + struct clast_stmt *stmt; + struct clast_printer_info code; + + state = cloog_isl_state_malloc(ctx); + + options = cloog_options_malloc(state); + options->language = CLOOG_LANGUAGE_C; + options->otl = 1; + options->strides = 1; + + input = cloog_input_from_scop(state, scop); + stmt = cloog_clast_create_from_input(input, options); + + code.indent = 0; + code.dst = output; + code.print_user_stmt = NULL; + code.print_user_stmt_list = NULL; + code.print_for_head = NULL; + code.print_for_foot = NULL; + + print_cloog_macros(output); + fprintf(output, "\n"); + print_stmt_definitions(scop, output); + fprintf(output, "\n"); + print_clast(&code, stmt); + + cloog_clast_free(stmt); + cloog_options_free(options); + cloog_state_free(state); + + fprintf(output, "\n"); +} + +int generate_cpu(isl_ctx *ctx, struct pet_scop *scop, + struct ppcg_options *options, const char *input) +{ + FILE *input_file; + FILE *output_file; + + if (!scop) + return -1; + + input_file = fopen(input, "r"); + output_file = get_output_file(input); + + copy_before_scop(input_file, output_file); + fprintf(output_file, "/* ppcg generated CPU code */\n\n"); + print_scop(ctx, scop, output_file); + copy_after_scop(input_file, output_file); + + fclose(output_file); + fclose(input_file); + + return 0; +} diff --git a/cpu.h b/cpu.h new file mode 100644 index 0000000..0a45e13 --- /dev/null +++ b/cpu.h @@ -0,0 +1,12 @@ +#ifndef _CPU_H +#define _CPU_H + +#include + +struct pet_scop; +struct ppcg_options; + +int generate_cpu(isl_ctx *ctx, struct pet_scop *scop, + struct ppcg_options *options, const char *input); + +#endif diff --git a/ppcg.c b/ppcg.c index ef3b1e8..6c2946f 100644 --- a/ppcg.c +++ b/ppcg.c @@ -16,6 +16,7 @@ #include #include "ppcg_options.h" #include "cuda.h" +#include "cpu.h" struct options { struct isl_options *isl; @@ -48,7 +49,12 @@ int main(int argc, char **argv) argc = options_parse(options, argc, argv, ISL_ARG_ALL); scop = pet_scop_extract_from_C_source(ctx, options->input, NULL); - r = cuda_pet(ctx, scop, options->ppcg, options->input); + + if (options->ppcg->target == PPCG_TARGET_CUDA) + r = cuda_pet(ctx, scop, options->ppcg, options->input); + else + r = generate_cpu(ctx, scop, options->ppcg, options->input); + pet_scop_free(scop); isl_ctx_free(ctx); diff --git a/ppcg_options.c b/ppcg_options.c index f5830d2..b7d03ba 100644 --- a/ppcg_options.c +++ b/ppcg_options.c @@ -10,6 +10,12 @@ #include "ppcg_options.h" +static struct isl_arg_choice target[] = { + {"c", PPCG_TARGET_C}, + {"cuda", PPCG_TARGET_CUDA}, + {0} +}; + ISL_ARGS_START(struct ppcg_options, ppcg_options_args) ISL_ARG_BOOL(struct ppcg_options, scale_tile_loops, 0, "scale-tile-loops", 1, NULL) @@ -25,4 +31,6 @@ ISL_ARG_STR(struct ppcg_options, sizes, 0, "sizes", "sizes", NULL, "Per kernel tile, grid and block sizes") ISL_ARG_INT(struct ppcg_options, max_shared_memory, 0, "max-shared-memory", "size", 8192, "maximal amount of shared memory") +ISL_ARG_CHOICE(struct ppcg_options, target, 0, "target", target, + PPCG_TARGET_CUDA, "the target to generate code for") ISL_ARGS_END diff --git a/ppcg_options.h b/ppcg_options.h index 1f21c1d..e6da829 100644 --- a/ppcg_options.h +++ b/ppcg_options.h @@ -20,8 +20,14 @@ struct ppcg_options { /* Maximal amount of shared memory. */ int max_shared_memory; + + /* The target we generate code for. */ + int target; }; ISL_ARG_DECL(ppcg_options, struct ppcg_options, ppcg_options_args) +#define PPCG_TARGET_C 0 +#define PPCG_TARGET_CUDA 1 + #endif -- 2.11.4.GIT