forked from OSchip/llvm-project
461 lines
12 KiB
C
461 lines
12 KiB
C
/*
|
||
* Copyright 2012-2013 Ecole Normale Superieure
|
||
*
|
||
* Use of this software is governed by the MIT license
|
||
*
|
||
* Written by Sven Verdoolaege,
|
||
* Ecole Normale Superieure, 45 rue d’Ulm, 75230 Paris, France
|
||
*/
|
||
|
||
#include <isl/aff.h>
|
||
#include <isl/ast_build.h>
|
||
|
||
#include "print.h"
|
||
#include "util.h"
|
||
|
||
__isl_give isl_printer *ppcg_start_block(__isl_take isl_printer *p)
|
||
{
|
||
p = isl_printer_start_line(p);
|
||
p = isl_printer_print_str(p, "{");
|
||
p = isl_printer_end_line(p);
|
||
p = isl_printer_indent(p, 2);
|
||
return p;
|
||
}
|
||
|
||
__isl_give isl_printer *ppcg_end_block(__isl_take isl_printer *p)
|
||
{
|
||
p = isl_printer_indent(p, -2);
|
||
p = isl_printer_start_line(p);
|
||
p = isl_printer_print_str(p, "}");
|
||
p = isl_printer_end_line(p);
|
||
return p;
|
||
}
|
||
|
||
/* Names of notes that keep track of whether min/max
|
||
* macro definitions have already been printed.
|
||
*/
|
||
static const char *ppcg_max_printed = "ppcg_max_printed";
|
||
static const char *ppcg_min_printed = "ppcg_min_printed";
|
||
|
||
/* Has the macro definition corresponding to "note_name" been printed
|
||
* to "p" before?
|
||
* That is, does "p" have an associated "note_name" note?
|
||
*/
|
||
static isl_bool printed_before(__isl_keep isl_printer *p, const char *note_name)
|
||
{
|
||
isl_ctx *ctx;
|
||
isl_id *id;
|
||
isl_bool printed;
|
||
|
||
if (!p)
|
||
return isl_bool_error;
|
||
|
||
ctx = isl_printer_get_ctx(p);
|
||
id = isl_id_alloc(ctx, note_name, NULL);
|
||
printed = isl_printer_has_note(p, id);
|
||
isl_id_free(id);
|
||
|
||
return printed;
|
||
}
|
||
|
||
/* Keep track of the fact that the macro definition corresponding
|
||
* to "note_name" has been printed to "p" by attaching a note with
|
||
* that name. The value of the note is of no importance, but it
|
||
* has to be a valid isl_id, so the note identifier is reused
|
||
* as the note.
|
||
*/
|
||
static __isl_give isl_printer *mark_printed(__isl_take isl_printer *p,
|
||
const char *note_name)
|
||
{
|
||
isl_ctx *ctx;
|
||
isl_id *id;
|
||
|
||
if (!p)
|
||
return NULL;
|
||
|
||
ctx = isl_printer_get_ctx(p);
|
||
id = isl_id_alloc(ctx, note_name, NULL);
|
||
return isl_printer_set_note(p, id, isl_id_copy(id));
|
||
}
|
||
|
||
/* Print a macro definition "def" for the macro "name" to "p",
|
||
* unless such a macro definition has been printed to "p" before.
|
||
* "note_name" is used as the name of the note that keeps track
|
||
* of whether this printing has happened.
|
||
*/
|
||
static __isl_give isl_printer *print_ppcg_macro(__isl_take isl_printer *p,
|
||
const char *name, const char *def, const char *note_name)
|
||
{
|
||
isl_bool printed;
|
||
|
||
printed = printed_before(p, note_name);
|
||
if (printed < 0)
|
||
return isl_printer_free(p);
|
||
if (printed)
|
||
return p;
|
||
|
||
p = isl_printer_start_line(p);
|
||
p = isl_printer_print_str(p, "#define ");
|
||
p = isl_printer_print_str(p, name);
|
||
p = isl_printer_print_str(p, def);
|
||
p = isl_printer_end_line(p);
|
||
|
||
p = mark_printed(p, note_name);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Structure for keeping track of definitions of some macros.
|
||
*/
|
||
struct ppcg_macros {
|
||
const char *min;
|
||
const char *max;
|
||
};
|
||
|
||
/* Free the memory allocated by a struct ppcg_macros.
|
||
*/
|
||
static void ppcg_macros_free(void *user)
|
||
{
|
||
free(user);
|
||
}
|
||
|
||
/* Default macro definitions (when GNU extensions are allowed).
|
||
*/
|
||
struct ppcg_macros ppcg_macros_default = {
|
||
.min = "(x,y) "
|
||
"({ __typeof__(x) _x = (x); __typeof__(y) _y = (y); "
|
||
"_x < _y ? _x : _y; })",
|
||
.max = "(x,y) "
|
||
"({ __typeof__(x) _x = (x); __typeof__(y) _y = (y); "
|
||
"_x > _y ? _x : _y; })",
|
||
};
|
||
|
||
/* Name used for the note that keeps track of macro definitions.
|
||
*/
|
||
static const char *ppcg_macros = "ppcg_macros";
|
||
|
||
/* Set the macro definitions for isl_ast_op_min and isl_ast_op_max
|
||
* to "min" and "max" and store them in "p".
|
||
*
|
||
* In particular, create a ppcg_macros object and attach it
|
||
* as a note to the printer.
|
||
*/
|
||
__isl_give isl_printer *ppcg_set_macros(__isl_take isl_printer *p,
|
||
const char *min, const char *max)
|
||
{
|
||
isl_ctx *ctx;
|
||
isl_id *id, *macros_id;
|
||
struct ppcg_macros *macros;
|
||
|
||
if (!p)
|
||
return NULL;
|
||
|
||
ctx = isl_printer_get_ctx(p);
|
||
macros = isl_alloc_type(ctx, struct ppcg_macros);
|
||
if (!macros)
|
||
return isl_printer_free(p);
|
||
macros->min = min;
|
||
macros->max = max;
|
||
id = isl_id_alloc(ctx, ppcg_macros, NULL);
|
||
macros_id = isl_id_alloc(ctx, NULL, macros);
|
||
if (!macros_id)
|
||
ppcg_macros_free(macros);
|
||
else
|
||
macros_id = isl_id_set_free_user(macros_id, &ppcg_macros_free);
|
||
|
||
p = isl_printer_set_note(p, id, macros_id);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Return the ppcg_macros object that holds the currently active
|
||
* macro definitions in "p".
|
||
* If "p" has a note with macro definitions, then return those.
|
||
* Otherwise, return the default macro definitions.
|
||
*/
|
||
static struct ppcg_macros *get_macros(__isl_keep isl_printer *p)
|
||
{
|
||
isl_id *id;
|
||
isl_bool has_macros;
|
||
struct ppcg_macros *macros;
|
||
|
||
id = isl_id_alloc(isl_printer_get_ctx(p), ppcg_macros, NULL);
|
||
has_macros = isl_printer_has_note(p, id);
|
||
if (has_macros < 0 || !has_macros) {
|
||
isl_id_free(id);
|
||
if (has_macros < 0)
|
||
return NULL;
|
||
return &ppcg_macros_default;
|
||
}
|
||
id = isl_printer_get_note(p, id);
|
||
macros = isl_id_get_user(id);
|
||
isl_id_free(id);
|
||
|
||
return macros;
|
||
}
|
||
|
||
/* Print the currently active macro definition for ppcg_max.
|
||
*/
|
||
static __isl_give isl_printer *print_max(__isl_take isl_printer *p)
|
||
{
|
||
struct ppcg_macros *macros;
|
||
|
||
macros = get_macros(p);
|
||
if (!macros)
|
||
return isl_printer_free(p);
|
||
return print_ppcg_macro(p, ppcg_max, macros->max, ppcg_max_printed);
|
||
}
|
||
|
||
/* Print the currently active macro definition for ppcg_min.
|
||
*/
|
||
static __isl_give isl_printer *print_min(__isl_take isl_printer *p)
|
||
{
|
||
struct ppcg_macros *macros;
|
||
|
||
macros = get_macros(p);
|
||
if (!macros)
|
||
return isl_printer_free(p);
|
||
return print_ppcg_macro(p, ppcg_min, macros->min, ppcg_min_printed);
|
||
}
|
||
|
||
/* Print a macro definition for "type" to "p".
|
||
* If GNU extensions are allowed, then print a specialized definition
|
||
* for isl_ast_op_min and isl_ast_op_max.
|
||
* Otherwise, use the default isl definition.
|
||
*/
|
||
__isl_give isl_printer *ppcg_print_macro(enum isl_ast_op_type type,
|
||
__isl_take isl_printer *p)
|
||
{
|
||
isl_ctx *ctx;
|
||
struct ppcg_options *options;
|
||
|
||
if (!p)
|
||
return NULL;
|
||
|
||
ctx = isl_printer_get_ctx(p);
|
||
options = isl_ctx_peek_options(ctx, &ppcg_options_args);
|
||
if (!options || !options->allow_gnu_extensions)
|
||
return isl_ast_op_type_print_macro(type, p);
|
||
|
||
switch (type) {
|
||
case isl_ast_op_max:
|
||
return print_max(p);
|
||
case isl_ast_op_min:
|
||
return print_min(p);
|
||
default:
|
||
return isl_ast_op_type_print_macro(type, p);
|
||
}
|
||
}
|
||
|
||
/* isl_ast_expr_foreach_ast_op_type or isl_ast_node_foreach_ast_op_type
|
||
* callback that prints a macro definition for "type".
|
||
*/
|
||
static isl_stat print_macro(enum isl_ast_op_type type, void *user)
|
||
{
|
||
isl_printer **p = user;
|
||
|
||
*p = ppcg_print_macro(type, *p);
|
||
if (!*p)
|
||
return isl_stat_error;
|
||
|
||
return isl_stat_ok;
|
||
}
|
||
|
||
/* Print the required macros for "expr".
|
||
*/
|
||
__isl_give isl_printer *ppcg_ast_expr_print_macros(
|
||
__isl_keep isl_ast_expr *expr, __isl_take isl_printer *p)
|
||
{
|
||
if (isl_ast_expr_foreach_ast_op_type(expr, &print_macro, &p) < 0)
|
||
return isl_printer_free(p);
|
||
return p;
|
||
}
|
||
|
||
/* isl_id_to_ast_expr_foreach callback that prints the required
|
||
* macro definitions for "val".
|
||
*/
|
||
static isl_stat print_expr_macros(__isl_take isl_id *key,
|
||
__isl_take isl_ast_expr *val, void *user)
|
||
{
|
||
isl_printer **p = user;
|
||
|
||
*p = ppcg_ast_expr_print_macros(val, *p);
|
||
isl_id_free(key);
|
||
isl_ast_expr_free(val);
|
||
|
||
if (!*p)
|
||
return isl_stat_error;
|
||
return isl_stat_ok;
|
||
}
|
||
|
||
/* Print the required macro definitions for the body of a statement in which
|
||
* the access expressions are replaced by the isl_ast_expr objects
|
||
* in "ref2expr".
|
||
*/
|
||
__isl_give isl_printer *ppcg_print_body_macros(__isl_take isl_printer *p,
|
||
__isl_keep isl_id_to_ast_expr *ref2expr)
|
||
{
|
||
if (isl_id_to_ast_expr_foreach(ref2expr, &print_expr_macros, &p) < 0)
|
||
return isl_printer_free(p);
|
||
return p;
|
||
}
|
||
|
||
/* Print the required macros for "node".
|
||
*/
|
||
__isl_give isl_printer *ppcg_print_macros(__isl_take isl_printer *p,
|
||
__isl_keep isl_ast_node *node)
|
||
{
|
||
if (isl_ast_node_foreach_ast_op_type(node, &print_macro, &p) < 0)
|
||
return isl_printer_free(p);
|
||
return p;
|
||
}
|
||
|
||
/* Names used for the macros that may appear in a printed isl AST.
|
||
*/
|
||
const char *ppcg_min = "ppcg_min";
|
||
const char *ppcg_max = "ppcg_max";
|
||
const char *ppcg_fdiv_q = "ppcg_fdiv_q";
|
||
|
||
/* Set the names of the macros that may appear in a printed isl AST.
|
||
*/
|
||
__isl_give isl_printer *ppcg_set_macro_names(__isl_take isl_printer *p)
|
||
{
|
||
p = isl_ast_op_type_set_print_name(p, isl_ast_op_min, ppcg_min);
|
||
p = isl_ast_op_type_set_print_name(p, isl_ast_op_max, ppcg_max);
|
||
p = isl_ast_op_type_set_print_name(p, isl_ast_op_fdiv_q, ppcg_fdiv_q);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Given a multi affine expression "mpa" without domain, modify it to have
|
||
* the schedule space of "build" as domain.
|
||
*
|
||
* If the schedule space of "build" is a parameter space, then nothing
|
||
* needs to be done.
|
||
* Otherwise, "mpa" is first given a 0D domain and then it is combined
|
||
* with a mapping from the schedule space of "build" to the same 0D domain.
|
||
*/
|
||
__isl_give isl_multi_pw_aff *ppcg_attach_multi_pw_aff(
|
||
__isl_take isl_multi_pw_aff *mpa, __isl_keep isl_ast_build *build)
|
||
{
|
||
isl_bool params;
|
||
isl_space *space;
|
||
isl_multi_aff *ma;
|
||
|
||
space = isl_ast_build_get_schedule_space(build);
|
||
params = isl_space_is_params(space);
|
||
if (params < 0 || params) {
|
||
isl_space_free(space);
|
||
if (params < 0)
|
||
return isl_multi_pw_aff_free(mpa);
|
||
return mpa;
|
||
}
|
||
space = isl_space_from_domain(space);
|
||
ma = isl_multi_aff_zero(space);
|
||
mpa = isl_multi_pw_aff_from_range(mpa);
|
||
mpa = isl_multi_pw_aff_pullback_multi_aff(mpa, ma);
|
||
|
||
return mpa;
|
||
}
|
||
|
||
/* Build an access AST expression from "size" using "build".
|
||
* "size" does not have a domain, but "build" may have a proper schedule space.
|
||
* First modify "size" to have that schedule space as domain.
|
||
*/
|
||
__isl_give isl_ast_expr *ppcg_build_size_expr(__isl_take isl_multi_pw_aff *size,
|
||
__isl_keep isl_ast_build *build)
|
||
{
|
||
size = ppcg_attach_multi_pw_aff(size, build);
|
||
return isl_ast_build_access_from_multi_pw_aff(build, size);
|
||
}
|
||
|
||
/* Print a declaration for an array with element type "base_type" and
|
||
* size "size" to "p".
|
||
*/
|
||
__isl_give isl_printer *ppcg_print_declaration_with_size(
|
||
__isl_take isl_printer *p, const char *base_type,
|
||
__isl_keep isl_ast_expr *size)
|
||
{
|
||
if (!base_type || !size)
|
||
return isl_printer_free(p);
|
||
|
||
p = ppcg_ast_expr_print_macros(size, p);
|
||
p = isl_printer_start_line(p);
|
||
p = isl_printer_print_str(p, base_type);
|
||
p = isl_printer_print_str(p, " ");
|
||
p = isl_printer_print_ast_expr(p, size);
|
||
p = isl_printer_print_str(p, ";");
|
||
p = isl_printer_end_line(p);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Print a declaration for array "array" to "p", using "build"
|
||
* to simplify any size expressions.
|
||
*
|
||
* The size is computed from the extent of the array and is
|
||
* subsequently converted to an "access expression" by "build".
|
||
*/
|
||
__isl_give isl_printer *ppcg_print_declaration(__isl_take isl_printer *p,
|
||
struct pet_array *array, __isl_keep isl_ast_build *build)
|
||
{
|
||
isl_multi_pw_aff *size;
|
||
isl_ast_expr *expr;
|
||
|
||
if (!array)
|
||
return isl_printer_free(p);
|
||
|
||
size = ppcg_size_from_extent(isl_set_copy(array->extent));
|
||
expr = isl_ast_build_access_from_multi_pw_aff(build, size);
|
||
p = ppcg_print_declaration_with_size(p, array->element_type, expr);
|
||
isl_ast_expr_free(expr);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Print declarations for the arrays in "scop" that are declared
|
||
* and that are exposed (if exposed == 1) or not exposed (if exposed == 0).
|
||
*/
|
||
static __isl_give isl_printer *print_declarations(__isl_take isl_printer *p,
|
||
struct ppcg_scop *scop, int exposed)
|
||
{
|
||
int i;
|
||
isl_ast_build *build;
|
||
|
||
if (!scop)
|
||
return isl_printer_free(p);
|
||
|
||
build = isl_ast_build_from_context(isl_set_copy(scop->context));
|
||
for (i = 0; i < scop->pet->n_array; ++i) {
|
||
struct pet_array *array = scop->pet->arrays[i];
|
||
|
||
if (!array->declared)
|
||
continue;
|
||
if (array->exposed != exposed)
|
||
continue;
|
||
|
||
p = ppcg_print_declaration(p, array, build);
|
||
}
|
||
isl_ast_build_free(build);
|
||
|
||
return p;
|
||
}
|
||
|
||
/* Print declarations for the arrays in "scop" that are declared
|
||
* and exposed to the code after the scop.
|
||
*/
|
||
__isl_give isl_printer *ppcg_print_exposed_declarations(
|
||
__isl_take isl_printer *p, struct ppcg_scop *scop)
|
||
{
|
||
return print_declarations(p, scop, 1);
|
||
}
|
||
|
||
/* Print declarations for the arrays in "scop" that are declared,
|
||
* but not exposed to the code after the scop.
|
||
*/
|
||
__isl_give isl_printer *ppcg_print_hidden_declarations(
|
||
__isl_take isl_printer *p, struct ppcg_scop *scop)
|
||
{
|
||
return print_declarations(p, scop, 0);
|
||
}
|