src/ast/kernels.c
- Computation kernels
- Implicit type casting
- Function pointers
- ‘val’
- Assumes that pointers to structures are used through “inout” parameter
- Remove some reserved GLSL keywords
- ‘unsigned’ or ‘unsigned int’ is replaced by ‘uint’
- Initialization
of
coord
(must include three components) - Implicit type casts
- Typedef struct
- Postfix initializers
- Attribute access
- Arrays as parameters
- Cast expressions
- Scalar, vector and tensor lists parameters
- “inout” function parameter
- forin_declaration_statement
- forin_statement
- Dereference of “inout” parameters
- References
- Field assignments
- Undeclared or unsupported functions
- Function pointers
- Diagonalize
- Macro call
Computation kernels
#include <stdlib.h>
#include <string.h>
#include "ast.h"
#include "symbols.h"
typedef struct {
char * error;
bool nolineno;
int return_macro_index;
Ast * macroscope;
} KernelData;
Implicit type casting
GLSL does not support implicit type casting, so we insert the necessary explicit casts using the function below.
staticAst * type_cast (Ast * n, const char * type)
{
Ast * parent = n->parent;
int child = ast_child_index (n);
Ast * call = NN(n, sym_function_call,
(n, sym_postfix_expression,
NN(n, sym_primary_expression,
NNNA(n, sym_IDENTIFIER, type))),
(n, "("),
NCA,
n(n, ")"));
NCA(parent, child, call);
ast_set_child return call;
}
staticAst * implicit_type_cast (Ast * n, Stack * stack)
{
if (!n) return NULL;
Ast * type = NULL;
switch (n->sym) {
case sym_I_CONSTANT: case sym_ENUMERATION_CONSTANT:
return (Ast *) &ast_int;
case sym_F_CONSTANT:
return (Ast *) &ast_double;
case sym_types:
if (ast_terminal (n->child[0]))
return n->child[0];
elsereturn NULL;
case sym_IDENTIFIER:
if (n->parent->sym == sym_primary_expression) {
Ast * ref = ast_identifier_declaration (stack, ast_terminal (n)->start);
if (ref) {
AstDimensions dim = {0};
= ast_identifier_type (ref, &dim, stack);
type if (type && type->sym == sym_INT &&
(ref = ast_schema (ast_ancestor (type, 5), sym_declaration,
1, sym_init_declarator_list,
0, sym_init_declarator,
0, sym_declarator,
0, sym_direct_declarator,
0, sym_generic_identifier,
0, sym_IDENTIFIER)) && !strcmp (ast_terminal (ref)->start, "bool"))
= (Ast *) &ast_bool;
type if (dim.pointer)
= NULL;
type }
return type;
}
if (ast_schema (ast_ancestor (n, 2), sym_member_identifier,
0, sym_generic_identifier)) {
= ast_expression_type (n, stack, false);
n if (ast_schema (ast_ancestor (n, 2), sym_direct_declarator))
return implicit_type_cast (ast_schema (ast_parent (n, sym_struct_declaration), sym_struct_declaration,
0, sym_specifier_qualifier_list,
0, sym_type_specifier,
0, sym_types),
);
stackreturn NULL;
}
if (ast_schema (ast_ancestor (n, 2), sym_direct_declarator))
return implicit_type_cast (ast_schema (ast_parent (n, sym_declaration), sym_declaration,
0, sym_declaration_specifiers,
0, sym_type_specifier,
0, sym_types),
);
stackbreak;
case sym_unary_expression:
switch (n->child[0]->sym) {
case sym_SIZEOF: case sym_ALIGNOF:
return (Ast *) &ast_int;
case sym_unary_operator:
if (n->child[0]->child[0]->sym == token_symbol('!')) {
Ast * a = implicit_type_cast (n->child[1], stack);
if (!a || a->sym != sym_BOOL)
type_cast (n->child[1], "bool");
return (Ast *) &ast_bool;
}
}
break;
case sym_additive_expression:
case sym_multiplicative_expression:
if (n->child[1]) {
Ast * a = implicit_type_cast (n->child[0], stack);
if (a && a->sym == sym_BOOL)
type_cast (n->child[0], "int");
Ast * b = implicit_type_cast (n->child[2], stack);
if (b && b->sym == sym_BOOL)
type_cast (n->child[2], "int");
if (b && (b->sym == sym_DOUBLE || b->sym == sym_FLOAT))
return b;
return a;
}
break;
case sym_assignment_expression:
case sym_init_declarator:
if (n->child[1]) {
Ast * a = implicit_type_cast (n->child[0], stack);
Ast * b = implicit_type_cast (n->child[2], stack);
if (a && b && a->sym != b->sym && (a->sym == sym_INT || b->sym == sym_BOOL))
type_cast (n->child[2], "int");
return a;
}
break;
case sym_cast_expression: case sym_primary_expression:
if (n->child[1])
return implicit_type_cast (n->child[1], stack);
break;
case sym_relational_expression:
case sym_equality_expression:
if (n->child[1])
return (Ast *)&ast_bool;
break;
case sym_logical_and_expression:
case sym_logical_or_expression:
if (n->child[1]) {
Ast * a = implicit_type_cast (n->child[0], stack);
if (!a || a->sym != sym_BOOL)
type_cast (n->child[0], "bool");
Ast * b = implicit_type_cast (n->child[2], stack);
if (!b || b->sym != sym_BOOL)
type_cast (n->child[2], "bool");
return (Ast *)&ast_bool;
}
break;
case sym_conditional_expression:
if (n->child[1]) {
Ast * type = implicit_type_cast (n->child[0], stack);
if (!type || type->sym != sym_BOOL) {
(ast_left_terminal (n->child[0])->before, "bool(");
str_prepend (n->child[0], ")");
ast_after }
return implicit_type_cast (n->child[2], stack);
}
break;
case sym_selection_statement: {
Ast * type = implicit_type_cast (n->child[2], stack);
if (!type || type->sym != sym_BOOL)
type_cast (n->child[2], "bool");
return type;
}
case sym_function_call: {
Ast * identifier = ast_function_call_identifier (n);
if (identifier) {
if (!strcmp (ast_terminal (identifier)->start, "val") ||
!strcmp (ast_terminal (identifier)->start, "val_out_"))
return (Ast *) &ast_double;
if (!strcmp (ast_terminal (identifier)->start, "_attr"))
return implicit_type_cast (n->child[2], stack);
= ast_identifier_declaration (stack, ast_terminal (identifier)->start);
identifier Ast * declaration = ast_parent (identifier, sym_function_declaration);
if (!declaration)
= ast_parent (identifier, sym_declaration);
declaration if (declaration)
return implicit_type_cast (ast_child (declaration, sym_declaration_specifiers), stack);
elsereturn NULL;
}
break;
}
}
if (n->child)
for (Ast ** c = n->child; *c; c++)
= implicit_type_cast (*c, stack);
type
return type;
}
staticvoid kernel (Ast * n, Stack * stack, void * data)
{
KernelData * d = data;
if (d->error)
return;
switch (n->sym) {
case sym_IDENTIFIER: {
Function pointers
if (ast_is_function_pointer (n, stack))
(ast_terminal (n)->start, "_p"); str_prepend
‘val’
Buggy GLSL preprocessors do not make the difference between ‘val’ as a variable identifier and ‘val()’ as a macro call.
else if (!ast_schema (ast_ancestor (n, 3), sym_function_call,
0, sym_postfix_expression,
0, sym_primary_expression) &&
!strcmp (ast_terminal (n)->start, "val"))
free (ast_terminal (n)->start), ast_terminal (n)->start = strdup ("_val");
break;
}
Assumes that pointers to structures are used through “inout” parameter
case sym_PTR_OP:
(n)->start[0] = '.';
ast_terminal(n)->start[1] = '\0';
ast_terminalbreak;
Remove some reserved GLSL keywords
case sym_STATIC: case sym_INLINE:
(n)->start[0] = '\0';
ast_terminal break;
‘unsigned’ or ‘unsigned int’ is replaced by ‘uint’
case sym_UNSIGNED: {
Ast * identifier;
if ((identifier = ast_schema (ast_ancestor (n, 3), sym_declaration_specifiers,
1, sym_declaration_specifiers,
0, sym_type_specifier,
0, sym_types,
0, sym_INT))) {
(n)->start[0] = '\0';
ast_terminal free (ast_terminal (identifier)->start);
(identifier)->start = strdup ("uint");
ast_terminal }
else if (ast_ancestor (n, 4)->sym == sym_declaration)
(ast_terminal (n)->start, "uint");
strcpy break;
}
Initialization
of coord
(must include three components)
case sym_TYPEDEF_NAME: {
if (n->parent->sym == sym_types && !strcmp (ast_terminal(n)->start, "coord") &&
(n = ast_schema (ast_parent (n, sym_declaration), sym_declaration,
1, sym_init_declarator_list)))
(n, 2, init_declarator) {
foreach_item Ast * list;
if ((list = ast_schema (init_declarator, sym_init_declarator,
2, sym_initializer,
1, sym_initializer_list))) {
int i = 0;
(list, 2, init)
foreach_item ++;
iAst * last = ast_schema (list->parent, sym_initializer,
2, token_symbol('}'));
if (last)
while (i++ < 3)
(ast_terminal (last)->start, ",0");
str_prepend }
}
break;
}
Implicit type casts
case sym_jump_statement:
implicit_type_cast (ast_child (n, sym_expression), stack);
break;
case sym_assignment_expression:
if (n->child[1])
implicit_type_cast (n, stack);
break;
case sym_init_declarator:
case sym_selection_statement:
implicit_type_cast (n, stack);
break;
case sym_for_declaration_statement:
case sym_iteration_statement:
if (n->child[0]->sym == sym_WHILE || n->child[0]->sym == sym_DO) {
Ast * expr = ast_child (n, sym_expression);
Ast * type = implicit_type_cast (expr, stack);
if (!type || type->sym != sym_BOOL)
type_cast (expr, "bool");
}
else if (n->child[0]->sym == sym_for_scope) {
Ast * expr = ast_schema (n->child[3], sym_expression_statement,
0, sym_expression);
if (expr) {
Ast * type = implicit_type_cast (expr, stack);
if (!type || type->sym != sym_BOOL)
type_cast (expr, "bool");
}
}
break;
Typedef struct
case sym_TYPEDEF: {
Ast * struct1, * identifier;
if ((struct1 = ast_schema (ast_ancestor (n, 2), sym_declaration_specifiers,
1, sym_declaration_specifiers,
0, sym_type_specifier,
0, sym_types,
0, sym_struct_or_union_specifier,
0, sym_struct_or_union,
0, sym_STRUCT)) &&
(identifier = ast_schema (ast_ancestor (n, 3), sym_declaration,
1, sym_init_declarator_list,
0, sym_init_declarator,
0, sym_declarator,
0, sym_direct_declarator,
0, sym_generic_identifier,
0, sym_IDENTIFIER))) {
char * s = ast_terminal (n)->start; s[0] = '\0';
(n)->start = ast_terminal (struct1)->start;
ast_terminal (struct1)->start = ast_terminal (identifier)->start;
ast_terminal (identifier)->start = s;
ast_terminal }
break;
}
case sym_postfix_expression: {
Ast * list;
if ((list = ast_schema (n, sym_postfix_expression,
3, sym_postfix_initializer,
1, sym_initializer_list))) {
Postfix initializers
Ast * a = n->child[0];
(n, 0, n->child[1]);
ast_set_child (n, 1, a);
ast_set_child = n->child[3];
a (n, 3, n->child[2]);
ast_set_child (n, 2, list);
ast_set_child (a);
ast_destroy }
else if (ast_attribute_access (n, stack) || ast_attribute_array_access (n)) {
Attribute access
if (n->parent->sym == sym_function_call) {
Ast * identifier = ast_schema (n, sym_postfix_expression,
2, sym_member_identifier,
0, sym_generic_identifier,
0, sym_IDENTIFIER);
(n->parent, "_attr_", ast_terminal (identifier)->start, "(");
ast_before (ast_schema (n, sym_postfix_expression,
ast_terminal 1, token_symbol('.')))->start[0] = ',';
(identifier)->start[0] = '\0';
ast_terminal (n->parent, ")");
ast_after }
else {
if (ast_schema (n, sym_postfix_expression,
0, sym_postfix_expression,
0, sym_array_access)) {
Ast * scalar = ast_find (n, sym_unary_expression,
0, sym_postfix_expression);
(scalar->child[1]);
ast_destroy (scalar->child[2]);
ast_destroy scalar->child[1] = NULL;
Ast * array = ast_find (n, sym_array_access);
scalar = ast_find (array, sym_expression);
(n, 0, scalar);
ast_set_child (array);
ast_destroy }
(ast_schema (n, sym_postfix_expression,
ast_terminal 1, token_symbol('.')))->start[0] = ',';
type_cast (n, "_attr");
}
}
break;
}
Arrays as parameters
This forces arrays passed as parameters to functions to behave like in C99 i.e. passing by reference (inout) rather than by value.
case sym_parameter_declaration:
if (ast_schema (n, sym_parameter_declaration,
1, sym_declarator,
0, sym_direct_declarator,
2, sym_assignment_expression))
(n, "inout ");
ast_before break;
Cast expressions
case sym_cast_expression:
if (ast_schema (n, sym_cast_expression,
1, sym_type_name)) {
Ast * a = n->child[0];
(n, 0, n->child[1]);
ast_set_child (n, 1, a);
ast_set_child = n->child[3];
a (n, 3, n->child[2]);
ast_set_child (n, 2, a);
ast_set_child }
break;
case sym_pointer: {
Ast * p, * type, * identifier;
if ((p = ast_schema (n, sym_pointer,
0, token_symbol('*'))) &&
(type = ast_schema (ast_ancestor (n, 2), sym_parameter_declaration,
0, sym_declaration_specifiers,
0, sym_type_specifier,
0, sym_types,
0, sym_TYPEDEF_NAME)) &&
(!strcmp (ast_terminal (type)->start, "scalar") ||
!strcmp (ast_terminal (type)->start, "vector") ||
!strcmp (ast_terminal (type)->start, "tensor")) &&
(identifier = ast_schema (n->parent, sym_declarator,
1, sym_direct_declarator,
0, sym_generic_identifier,
0, sym_IDENTIFIER))) {
Scalar, vector and tensor lists parameters
(p)->start[0] = '\0';
ast_terminal (identifier, "[2]"); // fixme: need to set the correct fixed size
ast_after // ast_print_tree (ast_ancestor (n, 2), stderr, 0, 0, -1);
}
else if ((identifier = ast_schema (ast_ancestor (n, 2), sym_parameter_declaration,
1, sym_declarator,
0, sym_pointer,
0, token_symbol ('*'))) &&
(type = ast_schema (ast_ancestor (n, 2), sym_parameter_declaration,
0, sym_declaration_specifiers,
0, sym_type_specifier,
0, sym_types)) &&
(type->child[0]->sym == sym_DOUBLE ||
->child[0]->sym == sym_FLOAT ||
type->child[0]->sym == sym_INT ||
type->child[0]->sym == sym_TYPEDEF_NAME)) { type
“inout” function parameter
(type->child[0], "inout ");
ast_before (identifier)->start[0] = '\0';
ast_terminal }
break;
}
forin_declaration_statement
case sym_forin_declaration_statement: {
Ast * declarator = n->child[3];
Ast * identifier = ast_schema (declarator, sym_declarator,
0, sym_direct_declarator,
0, sym_generic_identifier,
0, sym_IDENTIFIER);
if (!identifier) {
AstTerminal * t = ast_left_terminal (n);
fprintf (stderr,
"%s:%d: error: incorrect declaration\n",
->file, t->line);
texit (1);
}
(n, "{");
ast_before ((Ast *)ast_right_terminal (n->child[0]), "in");
ast_after ((Ast *)ast_right_terminal (n->child[2]), ",");
ast_after free (ast_terminal (n->child[4])->start);
(n->child[4])->start = strdup (",");
ast_terminal (n, " endforin()}");
ast_after break;
}
forin_statement
case sym_forin_statement: {
int narg = 0;
(n->child[2], 2, expr)
foreach_item ++;
narg(n, "{");
ast_before char suffix[20]; snprintf (suffix, 19, "%d", narg);
((Ast *)ast_right_terminal (ast_child (n, sym_for_scope)), "in", suffix);
ast_after free (ast_terminal (ast_child (n, sym_IDENTIFIER))->start);
(ast_child (n, sym_IDENTIFIER))->start = strdup (",");
ast_terminal (n, " endforin", suffix, "()}");
ast_after break;
}
case sym_unary_operator: {
Dereference of “inout” parameters
Ast * identifier, * ref, * type;
if (n->child[0]->sym == token_symbol ('*') &&
(identifier = ast_schema (n->parent, sym_unary_expression,
1, sym_cast_expression,
0, sym_unary_expression,
0, sym_postfix_expression,
0, sym_primary_expression,
0, sym_IDENTIFIER)) &&
(ref = ast_identifier_declaration (stack, ast_terminal (identifier)->start)) &&
(type = ast_schema (ast_ancestor (ref, 4), sym_parameter_declaration,
0, sym_declaration_specifiers,
0, sym_type_specifier,
0, sym_types)) &&
(type->child[0])->before &&
ast_terminal !strcmp (ast_terminal (type->child[0])->before + strlen (ast_terminal (type->child[0])->before) - 6, "inout ")) {
free (ast_terminal (n->child[0])->start);
(n->child[0])->start = strdup("");
ast_terminal }
References
We replace the ‘&’ with the ‘ast_pointer()’ macro.
Ast * ampersand;
if ((ampersand = ast_schema (n, sym_unary_operator,
0, token_symbol ('&')))) {
free (ast_terminal (ampersand)->start);
(ampersand)->start = strdup ("ast_pointer(");
ast_terminal Ast * cast = ast_schema (n->parent, sym_unary_expression,
1, sym_cast_expression);
(cast, ")");
ast_after }
break;
}
case sym_function_call: {
Ast * identifier = ast_function_call_identifier (n);
if (!identifier) break;
AstTerminal * t = ast_terminal (identifier);
if (!t) break;
Field assignments
Kernels often need to know the type of access to fields (i.e. read or write). Here we append “_out” to stencil access functions linked to assignments (i.e. “write” operations).
if (!strcmp (t->start, "val")) {
if (ast_child (ast_parent (n, sym_assignment_expression), sym_assignment_operator))
(t->start, "_out_");
str_append break;
}
if (!strcmp (t->start, "coarse") || !strcmp (t->start, "fine"))
break;
Undeclared or unsupported functions
if (!(identifier = ast_identifier_declaration (stack, t->start))) {
char s[1000];
(s, 999, "\\n@error %s:%d: GLSL: error: unknown function '%s'\\n",
snprintf ->file, d->nolineno ? 0 : t->line, t->start);
t->error = strdup (s);
dreturn;
}
Function pointers
if (ast_schema (ast_ancestor (identifier, 3), sym_declarator,
0, sym_pointer,
0, token_symbol('*'))) {
char * s = NULL;
(s, "_f", t->start);
str_append free (t->start);
->start = s;
tAstTerminal * o = ast_terminal (ast_child (n, token_symbol('(')));
free (o->start); o->start = strdup ("((");
AstTerminal * c = ast_terminal (ast_child (n, token_symbol(')')));
free (c->start); c->start = strdup ("))");
break;
}
break;
}
case sym_macro_statement: {
Ast * identifier = ast_schema (n, sym_macro_statement,
0, sym_MACRO);
Diagonalize
if (identifier && !strcmp (ast_terminal (identifier)->start, "diagonalize")) {
Ast * field = ast_schema (n, sym_macro_statement,
2, sym_argument_expression_list,
0, sym_argument_expression_list_item,
0, sym_assignment_expression);
if (field && (field = ast_is_identifier_expression (field))) {
(stack, &n);
stack_push (n, stack, ast_diagonalize, field);
ast_traverse (stack, n);
ast_pop_scope }
}
Macro call
These are the remaining “postmacros” which have not been expanded yet.
else {
(n, "{");
ast_before (n, "end_", ast_terminal (identifier)->start, "()}");
ast_after }
break;
}
}
}
staticchar * stringify (Ast * n, char * output, bool nolineno)
{
AstTerminal * t = ast_left_terminal (n);
char * before = t->before;
if (n->sym == sym_function_definition)
->before = NULL;
tchar * s = ast_str_print (n, NULL, 0, 1);
->before = before;
tfor (char * i = s; *i; i++) {
char a[] = "1";
switch (*i) {
case '\n': str_append (output, "\\n"); break;
case '\\': str_append (output, "\\\\"); break;
case '"': str_append (output, "\\\""); break;
case '#':
if (i[-1] == '\n') {
(output, "// #");
str_append if (nolineno && !strncmp (i, "#line ", 6)) {
(output, "line 0");
str_append += 6;
i while (*i >= '0' && *i <= '9') i++;
--;
i}
break;
}
// fall through
default: a[0] = *i; str_append (output, a); break;
}
}
free (s);
(n);
ast_destroy return output;
}
static void postmacros (Ast * n, Stack * stack, void * data)
{
if (n->sym == sym_statement || n->sym == sym_function_call) {
KernelData * d = data;
(n, n, stack, d->nolineno, 1, false, &d->return_macro_index, d->macroscope);
ast_macro_replacement }
}
char * ast_kernel (Ast * n, char * argument, bool nolineno, Ast * macroscope)
{
AstRoot * root = ast_get_root (n);
Stack * stack = root->stack;
(stack, &n);
stack_push KernelData d = {0, nolineno, 0, macroscope};
Ast * statement = n->sym == sym_function_definition ?
(n) : ast_copy (ast_child (n, sym_statement));
ast_copy (statement, stack, postmacros, &d);
ast_traverse (statement, stack, kernel, &d);
ast_traverse
if (d.error)
(argument, "\"", d.error, "\"");
str_append else {
(argument, "\"");
str_append = stringify (statement, argument, nolineno);
argument (argument, "\"");
str_append }
free (d.error);
(stack, n);
ast_pop_scope return argument;
}