ir_reader: Add a pattern matching system and use it everywhere.

Previously, the IR reader was riddled with code that:
1. Checked for the right number of list elements (via a linked list walk)
2. Retrieved references to each component (via ->next->next pointers)
3. Downcasted as necessary to make sure that each sub-component was the
   right type (i.e. symbol, int, list).
4. Checking that the tag (i.e. "declare") was correct.

This was all very ad-hoc and a bit ugly.  Error checking had to be done
at both steps 1, 3, and 4.  Most code didn't even check the tag, relying
on the caller to do so.  Not all callers did.

The new pattern matching module performs the whole process in a single
straightforward function call, resulting in shorter, more readable code.

Unfortunately, MSVC does not support C99-style anonymous arrays, so the
pattern must be declared outside of the match call.
This commit is contained in:
Kenneth Graunke
2010-11-03 12:47:06 -07:00
parent 407184fe08
commit daeb0c646e
3 changed files with 297 additions and 311 deletions

View File

@@ -21,8 +21,6 @@
* DEALINGS IN THE SOFTWARE. * DEALINGS IN THE SOFTWARE.
*/ */
#include <cstdarg>
extern "C" { extern "C" {
#include <talloc.h> #include <talloc.h>
} }
@@ -43,7 +41,7 @@ static void scan_for_prototypes(_mesa_glsl_parse_state *, exec_list *,
static ir_function *read_function(_mesa_glsl_parse_state *, s_list *, static ir_function *read_function(_mesa_glsl_parse_state *, s_list *,
bool skip_body); bool skip_body);
static void read_function_sig(_mesa_glsl_parse_state *, ir_function *, static void read_function_sig(_mesa_glsl_parse_state *, ir_function *,
s_list *, bool skip_body); s_expression *, bool skip_body);
static void read_instructions(_mesa_glsl_parse_state *, exec_list *, static void read_instructions(_mesa_glsl_parse_state *, exec_list *,
s_expression *, ir_loop *); s_expression *, ir_loop *);
@@ -124,47 +122,23 @@ ir_read_error(_mesa_glsl_parse_state *state, s_expression *expr,
static const glsl_type * static const glsl_type *
read_type(_mesa_glsl_parse_state *st, s_expression *expr) read_type(_mesa_glsl_parse_state *st, s_expression *expr)
{ {
s_list *list = SX_AS_LIST(expr); s_expression *s_base_type;
if (list != NULL) { s_int *s_size;
s_symbol *type_sym = SX_AS_SYMBOL(list->subexpressions.get_head());
if (type_sym == NULL) {
ir_read_error(st, expr, "expected type (array ...) or (struct ...)");
return NULL;
}
if (strcmp(type_sym->value(), "array") == 0) {
if (list->length() != 3) {
ir_read_error(st, expr, "expected type (array <type> <int>)");
return NULL;
}
// Read base type s_pattern pat[] = { "array", s_base_type, s_size };
s_expression *base_expr = (s_expression*) type_sym->next; if (MATCH(expr, pat)) {
const glsl_type *base_type = read_type(st, base_expr); const glsl_type *base_type = read_type(st, s_base_type);
if (base_type == NULL) { if (base_type == NULL) {
ir_read_error(st, NULL, "when reading base type of array"); ir_read_error(st, NULL, "when reading base type of array type");
return NULL; return NULL;
} }
// Read array size return glsl_type::get_array_instance(base_type, s_size->value());
s_int *size = SX_AS_INT(base_expr->next);
if (size == NULL) {
ir_read_error(st, expr, "found non-integer array size");
return NULL;
}
return glsl_type::get_array_instance(base_type, size->value());
} else if (strcmp(type_sym->value(), "struct") == 0) {
assert(false); // FINISHME
} else {
ir_read_error(st, expr, "expected (array ...) or (struct ...); "
"found (%s ...)", type_sym->value());
return NULL;
}
} }
s_symbol *type_sym = SX_AS_SYMBOL(expr); s_symbol *type_sym = SX_AS_SYMBOL(expr);
if (type_sym == NULL) { if (type_sym == NULL) {
ir_read_error(st, expr, "expected <type> (symbol or list)"); ir_read_error(st, expr, "expected <type>");
return NULL; return NULL;
} }
@@ -207,14 +181,11 @@ read_function(_mesa_glsl_parse_state *st, s_list *list, bool skip_body)
{ {
void *ctx = st; void *ctx = st;
bool added = false; bool added = false;
if (list->length() < 3) { s_symbol *name;
ir_read_error(st, list, "Expected (function <name> (signature ...) ...)");
return NULL;
}
s_symbol *name = SX_AS_SYMBOL(list->subexpressions.head->next); s_pattern pat[] = { "function", name };
if (name == NULL) { if (!PARTIAL_MATCH(list, pat)) {
ir_read_error(st, list, "Expected (function <name> ...)"); ir_read_error(st, list, "Expected (function <name> (signature ...) ...)");
return NULL; return NULL;
} }
@@ -229,46 +200,32 @@ read_function(_mesa_glsl_parse_state *st, s_list *list, bool skip_body)
it.next(); // skip "function" tag it.next(); // skip "function" tag
it.next(); // skip function name it.next(); // skip function name
for (/* nothing */; it.has_next(); it.next()) { for (/* nothing */; it.has_next(); it.next()) {
s_list *siglist = SX_AS_LIST(it.get()); s_expression *s_sig = (s_expression *) it.get();
if (siglist == NULL) { read_function_sig(st, f, s_sig, skip_body);
ir_read_error(st, list, "Expected (function (signature ...) ...)");
return NULL;
}
s_symbol *tag = SX_AS_SYMBOL(siglist->subexpressions.get_head());
if (tag == NULL || strcmp(tag->value(), "signature") != 0) {
ir_read_error(st, siglist, "Expected (signature ...)");
return NULL;
}
read_function_sig(st, f, siglist, skip_body);
} }
return added ? f : NULL; return added ? f : NULL;
} }
static void static void
read_function_sig(_mesa_glsl_parse_state *st, ir_function *f, s_list *list, read_function_sig(_mesa_glsl_parse_state *st, ir_function *f,
bool skip_body) s_expression *expr, bool skip_body)
{ {
void *ctx = st; void *ctx = st;
if (list->length() != 4) { s_expression *type_expr;
ir_read_error(st, list, "Expected (signature <type> (parameters ...) " s_list *paramlist;
s_list *body_list;
s_pattern pat[] = { "signature", type_expr, paramlist, body_list };
if (!MATCH(expr, pat)) {
ir_read_error(st, expr, "Expected (signature <type> (parameters ...) "
"(<instruction> ...))"); "(<instruction> ...))");
return; return;
} }
s_expression *type_expr = (s_expression*) list->subexpressions.head->next;
const glsl_type *return_type = read_type(st, type_expr); const glsl_type *return_type = read_type(st, type_expr);
if (return_type == NULL) if (return_type == NULL)
return; return;
s_list *paramlist = SX_AS_LIST(type_expr->next);
s_list *body_list = SX_AS_LIST(type_expr->next->next);
if (paramlist == NULL || body_list == NULL) {
ir_read_error(st, list, "Expected (signature <type> (parameters ...) "
"(<instruction> ...))");
return;
}
s_symbol *paramtag = SX_AS_SYMBOL(paramlist->subexpressions.get_head()); s_symbol *paramtag = SX_AS_SYMBOL(paramlist->subexpressions.get_head());
if (paramtag == NULL || strcmp(paramtag->value(), "parameters") != 0) { if (paramtag == NULL || strcmp(paramtag->value(), "parameters") != 0) {
ir_read_error(st, paramlist, "Expected (parameters ...)"); ir_read_error(st, paramlist, "Expected (parameters ...)");
@@ -298,13 +255,13 @@ read_function_sig(_mesa_glsl_parse_state *st, ir_function *f, s_list *list,
} else if (sig != NULL) { } else if (sig != NULL) {
const char *badvar = sig->qualifiers_match(&hir_parameters); const char *badvar = sig->qualifiers_match(&hir_parameters);
if (badvar != NULL) { if (badvar != NULL) {
ir_read_error(st, list, "function `%s' parameter `%s' qualifiers " ir_read_error(st, expr, "function `%s' parameter `%s' qualifiers "
"don't match prototype", f->name, badvar); "don't match prototype", f->name, badvar);
return; return;
} }
if (sig->return_type != return_type) { if (sig->return_type != return_type) {
ir_read_error(st, list, "function `%s' return type doesn't " ir_read_error(st, expr, "function `%s' return type doesn't "
"match prototype", f->name); "match prototype", f->name);
return; return;
} }
@@ -319,7 +276,7 @@ read_function_sig(_mesa_glsl_parse_state *st, ir_function *f, s_list *list,
if (!skip_body && !body_list->subexpressions.is_empty()) { if (!skip_body && !body_list->subexpressions.is_empty()) {
if (sig->is_defined) { if (sig->is_defined) {
ir_read_error(st, list, "function %s redefined", f->name); ir_read_error(st, expr, "function %s redefined", f->name);
return; return;
} }
st->current_function = sig; st->current_function = sig;
@@ -406,42 +363,30 @@ read_instruction(_mesa_glsl_parse_state *st, s_expression *expr,
return inst; return inst;
} }
static ir_variable * static ir_variable *
read_declaration(_mesa_glsl_parse_state *st, s_list *list) read_declaration(_mesa_glsl_parse_state *st, s_list *list)
{ {
void *ctx = st; s_list *s_quals;
if (list->length() != 4) { s_expression *s_type;
s_symbol *s_name;
s_pattern pat[] = { "declare", s_quals, s_type, s_name };
if (!MATCH(list, pat)) {
ir_read_error(st, list, "expected (declare (<qualifiers>) <type> " ir_read_error(st, list, "expected (declare (<qualifiers>) <type> "
"<name>)"); "<name>)");
return NULL; return NULL;
} }
s_list *quals = SX_AS_LIST(list->subexpressions.head->next); const glsl_type *type = read_type(st, s_type);
if (quals == NULL) {
ir_read_error(st, list, "expected a list of variable qualifiers");
return NULL;
}
s_expression *type_expr = (s_expression*) quals->next;
const glsl_type *type = read_type(st, type_expr);
if (type == NULL) if (type == NULL)
return NULL; return NULL;
s_symbol *var_name = SX_AS_SYMBOL(type_expr->next); ir_variable *var = new(st) ir_variable(type, s_name->value(), ir_var_auto);
if (var_name == NULL) {
ir_read_error(st, list, "expected variable name, found non-symbol");
return NULL;
}
ir_variable *var = new(ctx) ir_variable(type, var_name->value(), foreach_iter(exec_list_iterator, it, s_quals->subexpressions) {
ir_var_auto);
foreach_iter(exec_list_iterator, it, quals->subexpressions) {
s_symbol *qualifier = SX_AS_SYMBOL(it.get()); s_symbol *qualifier = SX_AS_SYMBOL(it.get());
if (qualifier == NULL) { if (qualifier == NULL) {
ir_read_error(st, list, "qualifier list must contain only symbols"); ir_read_error(st, list, "qualifier list must contain only symbols");
delete var;
return NULL; return NULL;
} }
@@ -468,7 +413,6 @@ read_declaration(_mesa_glsl_parse_state *st, s_list *list)
var->interpolation = ir_var_noperspective; var->interpolation = ir_var_noperspective;
} else { } else {
ir_read_error(st, list, "unknown qualifier: %s", qualifier->value()); ir_read_error(st, list, "unknown qualifier: %s", qualifier->value());
delete var;
return NULL; return NULL;
} }
} }
@@ -483,27 +427,27 @@ read_declaration(_mesa_glsl_parse_state *st, s_list *list)
static ir_if * static ir_if *
read_if(_mesa_glsl_parse_state *st, s_list *list, ir_loop *loop_ctx) read_if(_mesa_glsl_parse_state *st, s_list *list, ir_loop *loop_ctx)
{ {
void *ctx = st; s_expression *s_cond;
if (list->length() != 4) { s_expression *s_then;
s_expression *s_else;
s_pattern pat[] = { "if", s_cond, s_then, s_else };
if (!MATCH(list, pat)) {
ir_read_error(st, list, "expected (if <condition> (<then> ...) " ir_read_error(st, list, "expected (if <condition> (<then> ...) "
"(<else> ...))"); "(<else> ...))");
return NULL; return NULL;
} }
s_expression *cond_expr = (s_expression*) list->subexpressions.head->next; ir_rvalue *condition = read_rvalue(st, s_cond);
ir_rvalue *condition = read_rvalue(st, cond_expr);
if (condition == NULL) { if (condition == NULL) {
ir_read_error(st, NULL, "when reading condition of (if ...)"); ir_read_error(st, NULL, "when reading condition of (if ...)");
return NULL; return NULL;
} }
s_expression *then_expr = (s_expression*) cond_expr->next; ir_if *iff = new(st) ir_if(condition);
s_expression *else_expr = (s_expression*) then_expr->next;
ir_if *iff = new(ctx) ir_if(condition); read_instructions(st, &iff->then_instructions, s_then, loop_ctx);
read_instructions(st, &iff->else_instructions, s_else, loop_ctx);
read_instructions(st, &iff->then_instructions, then_expr, loop_ctx);
read_instructions(st, &iff->else_instructions, else_expr, loop_ctx);
if (st->error) { if (st->error) {
delete iff; delete iff;
iff = NULL; iff = NULL;
@@ -515,23 +459,19 @@ read_if(_mesa_glsl_parse_state *st, s_list *list, ir_loop *loop_ctx)
static ir_loop * static ir_loop *
read_loop(_mesa_glsl_parse_state *st, s_list *list) read_loop(_mesa_glsl_parse_state *st, s_list *list)
{ {
void *ctx = st; s_expression *s_counter, *s_from, *s_to, *s_inc, *s_body;
if (list->length() != 6) {
s_pattern pat[] = { "loop", s_counter, s_from, s_to, s_inc, s_body };
if (!MATCH(list, pat)) {
ir_read_error(st, list, "expected (loop <counter> <from> <to> " ir_read_error(st, list, "expected (loop <counter> <from> <to> "
"<increment> <body>)"); "<increment> <body>)");
return NULL; return NULL;
} }
s_expression *count_expr = (s_expression*) list->subexpressions.head->next;
s_expression *from_expr = (s_expression*) count_expr->next;
s_expression *to_expr = (s_expression*) from_expr->next;
s_expression *inc_expr = (s_expression*) to_expr->next;
s_expression *body_expr = (s_expression*) inc_expr->next;
// FINISHME: actually read the count/from/to fields. // FINISHME: actually read the count/from/to fields.
ir_loop *loop = new(ctx) ir_loop; ir_loop *loop = new(st) ir_loop;
read_instructions(st, &loop->body_instructions, body_expr, loop); read_instructions(st, &loop->body_instructions, s_body, loop);
if (st->error) { if (st->error) {
delete loop; delete loop;
loop = NULL; loop = NULL;
@@ -543,21 +483,21 @@ read_loop(_mesa_glsl_parse_state *st, s_list *list)
static ir_return * static ir_return *
read_return(_mesa_glsl_parse_state *st, s_list *list) read_return(_mesa_glsl_parse_state *st, s_list *list)
{ {
void *ctx = st; s_expression *expr;
if (list->length() != 2) {
s_pattern pat[] = { "return", expr };
if (!MATCH(list, pat)) {
ir_read_error(st, list, "expected (return <rvalue>)"); ir_read_error(st, list, "expected (return <rvalue>)");
return NULL; return NULL;
} }
s_expression *expr = (s_expression*) list->subexpressions.head->next;
ir_rvalue *retval = read_rvalue(st, expr); ir_rvalue *retval = read_rvalue(st, expr);
if (retval == NULL) { if (retval == NULL) {
ir_read_error(st, NULL, "when reading return value"); ir_read_error(st, NULL, "when reading return value");
return NULL; return NULL;
} }
return new(ctx) ir_return(retval); return new(st) ir_return(retval);
} }
@@ -597,37 +537,27 @@ read_rvalue(_mesa_glsl_parse_state *st, s_expression *expr)
static ir_assignment * static ir_assignment *
read_assignment(_mesa_glsl_parse_state *st, s_list *list) read_assignment(_mesa_glsl_parse_state *st, s_list *list)
{ {
void *ctx = st; s_expression *cond_expr, *lhs_expr, *rhs_expr;
if (list->length() != 5) { s_list *mask_list;
s_pattern pat[] = { "assign", cond_expr, mask_list, lhs_expr, rhs_expr };
if (!MATCH(list, pat)) {
ir_read_error(st, list, "expected (assign <condition> (<write mask>) " ir_read_error(st, list, "expected (assign <condition> (<write mask>) "
"<lhs> <rhs>)"); "<lhs> <rhs>)");
return NULL; return NULL;
} }
s_expression *cond_expr = (s_expression*) list->subexpressions.head->next;
s_list *mask_list = SX_AS_LIST(cond_expr->next);
s_expression *lhs_expr = (s_expression*) cond_expr->next->next;
s_expression *rhs_expr = (s_expression*) lhs_expr->next;
ir_rvalue *condition = read_rvalue(st, cond_expr); ir_rvalue *condition = read_rvalue(st, cond_expr);
if (condition == NULL) { if (condition == NULL) {
ir_read_error(st, NULL, "when reading condition of assignment"); ir_read_error(st, NULL, "when reading condition of assignment");
return NULL; return NULL;
} }
if (mask_list == NULL || mask_list->length() > 1) {
ir_read_error(st, mask_list, "expected () or (<write mask>)");
return NULL;
}
unsigned mask = 0; unsigned mask = 0;
if (mask_list->length() == 1) {
s_symbol *mask_symbol = SX_AS_SYMBOL(mask_list->subexpressions.head);
if (mask_symbol == NULL) {
ir_read_error(st, list, "expected a write mask; found non-symbol");
return NULL;
}
s_symbol *mask_symbol;
s_pattern mask_pat[] = { mask_symbol };
if (MATCH(mask_list, mask_pat)) {
const char *mask_str = mask_symbol->value(); const char *mask_str = mask_symbol->value();
unsigned mask_length = strlen(mask_str); unsigned mask_length = strlen(mask_str);
if (mask_length > 4) { if (mask_length > 4) {
@@ -645,6 +575,9 @@ read_assignment(_mesa_glsl_parse_state *st, s_list *list)
} }
mask |= 1 << idx_map[mask_str[i] - 'w']; mask |= 1 << idx_map[mask_str[i] - 'w'];
} }
} else if (!mask_list->subexpressions.is_empty()) {
ir_read_error(st, mask_list, "expected () or (<write mask>)");
return NULL;
} }
ir_dereference *lhs = read_dereference(st, lhs_expr); ir_dereference *lhs = read_dereference(st, lhs_expr);
@@ -664,21 +597,18 @@ read_assignment(_mesa_glsl_parse_state *st, s_list *list)
return NULL; return NULL;
} }
return new(ctx) ir_assignment(lhs, rhs, condition, mask); return new(st) ir_assignment(lhs, rhs, condition, mask);
} }
static ir_call * static ir_call *
read_call(_mesa_glsl_parse_state *st, s_list *list) read_call(_mesa_glsl_parse_state *st, s_list *list)
{ {
void *ctx = st; void *ctx = st;
if (list->length() != 3) { s_symbol *name;
ir_read_error(st, list, "expected (call <name> (<param> ...))"); s_list *params;
return NULL;
}
s_symbol *name = SX_AS_SYMBOL(list->subexpressions.head->next); s_pattern pat[] = { "call", name, params };
s_list *params = SX_AS_LIST(list->subexpressions.head->next->next); if (!MATCH(list, pat)) {
if (name == NULL || params == NULL) {
ir_read_error(st, list, "expected (call <name> (<param> ...))"); ir_read_error(st, list, "expected (call <name> (<param> ...))");
return NULL; return NULL;
} }
@@ -716,61 +646,54 @@ static ir_expression *
read_expression(_mesa_glsl_parse_state *st, s_list *list) read_expression(_mesa_glsl_parse_state *st, s_list *list)
{ {
void *ctx = st; void *ctx = st;
const unsigned list_length = list->length(); s_expression *s_type;
if (list_length < 4) { s_symbol *s_op;
s_expression *s_arg1;
s_pattern pat[] = { "expression", s_type, s_op, s_arg1 };
if (!PARTIAL_MATCH(list, pat)) {
ir_read_error(st, list, "expected (expression <type> <operator> " ir_read_error(st, list, "expected (expression <type> <operator> "
"<operand> [<operand>])"); "<operand> [<operand>])");
return NULL; return NULL;
} }
s_expression *s_arg2 = (s_expression *) s_arg1->next; // may be tail sentinel
s_expression *type_expr = (s_expression*) list->subexpressions.head->next; const glsl_type *type = read_type(st, s_type);
const glsl_type *type = read_type(st, type_expr);
if (type == NULL) if (type == NULL)
return NULL; return NULL;
/* Read the operator */ /* Read the operator */
s_symbol *op_sym = SX_AS_SYMBOL(type_expr->next); ir_expression_operation op = ir_expression::get_operator(s_op->value());
if (op_sym == NULL) {
ir_read_error(st, list, "expected operator, found non-symbol");
return NULL;
}
ir_expression_operation op = ir_expression::get_operator(op_sym->value());
if (op == (ir_expression_operation) -1) { if (op == (ir_expression_operation) -1) {
ir_read_error(st, list, "invalid operator: %s", op_sym->value()); ir_read_error(st, list, "invalid operator: %s", s_op->value());
return NULL; return NULL;
} }
/* Now that we know the operator, check for the right number of operands */ unsigned num_operands = ir_expression::get_num_operands(op);
if (ir_expression::get_num_operands(op) == 2) { if (num_operands == 1 && !s_arg1->next->is_tail_sentinel()) {
if (list_length != 5) {
ir_read_error(st, list, "expected (expression <type> %s <operand> "
" <operand>)", op_sym->value());
return NULL;
}
} else {
if (list_length != 4) {
ir_read_error(st, list, "expected (expression <type> %s <operand>)", ir_read_error(st, list, "expected (expression <type> %s <operand>)",
op_sym->value()); s_op->value());
return NULL; return NULL;
} }
}
s_expression *exp1 = (s_expression*) (op_sym->next); ir_rvalue *arg1 = read_rvalue(st, s_arg1);
ir_rvalue *arg1 = read_rvalue(st, exp1); ir_rvalue *arg2 = NULL;
if (arg1 == NULL) { if (arg1 == NULL) {
ir_read_error(st, NULL, "when reading first operand of %s", ir_read_error(st, NULL, "when reading first operand of %s",
op_sym->value()); s_op->value());
return NULL; return NULL;
} }
ir_rvalue *arg2 = NULL; if (num_operands == 2) {
if (ir_expression::get_num_operands(op) == 2) { if (s_arg2->is_tail_sentinel() || !s_arg2->next->is_tail_sentinel()) {
s_expression *exp2 = (s_expression*) (exp1->next); ir_read_error(st, list, "expected (expression <type> %s <operand> "
arg2 = read_rvalue(st, exp2); "<operand>)", s_op->value());
return NULL;
}
arg2 = read_rvalue(st, s_arg2);
if (arg2 == NULL) { if (arg2 == NULL) {
ir_read_error(st, NULL, "when reading second operand of %s", ir_read_error(st, NULL, "when reading second operand of %s",
op_sym->value()); s_op->value());
return NULL; return NULL;
} }
} }
@@ -781,14 +704,12 @@ read_expression(_mesa_glsl_parse_state *st, s_list *list)
static ir_swizzle * static ir_swizzle *
read_swizzle(_mesa_glsl_parse_state *st, s_list *list) read_swizzle(_mesa_glsl_parse_state *st, s_list *list)
{ {
if (list->length() != 3) { s_symbol *swiz;
ir_read_error(st, list, "expected (swiz <swizzle> <rvalue>)"); s_expression *sub;
return NULL;
}
s_symbol *swiz = SX_AS_SYMBOL(list->subexpressions.head->next); s_pattern pat[] = { "swiz", swiz, sub };
if (swiz == NULL) { if (!MATCH(list, pat)) {
ir_read_error(st, list, "expected a valid swizzle; found non-symbol"); ir_read_error(st, list, "expected (swiz <swizzle> <rvalue>)");
return NULL; return NULL;
} }
@@ -798,7 +719,6 @@ read_swizzle(_mesa_glsl_parse_state *st, s_list *list)
return NULL; return NULL;
} }
s_expression *sub = (s_expression*) swiz->next;
ir_rvalue *rvalue = read_rvalue(st, sub); ir_rvalue *rvalue = read_rvalue(st, sub);
if (rvalue == NULL) if (rvalue == NULL)
return NULL; return NULL;
@@ -815,17 +735,19 @@ static ir_constant *
read_constant(_mesa_glsl_parse_state *st, s_list *list) read_constant(_mesa_glsl_parse_state *st, s_list *list)
{ {
void *ctx = st; void *ctx = st;
if (list->length() != 3) { s_expression *type_expr;
s_list *values;
s_pattern pat[] = { "constant", type_expr, values };
if (!MATCH(list, pat)) {
ir_read_error(st, list, "expected (constant <type> (...))"); ir_read_error(st, list, "expected (constant <type> (...))");
return NULL; return NULL;
} }
s_expression *type_expr = (s_expression*) list->subexpressions.head->next;
const glsl_type *type = read_type(st, type_expr); const glsl_type *type = read_type(st, type_expr);
if (type == NULL) if (type == NULL)
return NULL; return NULL;
s_list *values = SX_AS_LIST(type_expr->next);
if (values == NULL) { if (values == NULL) {
ir_read_error(st, list, "expected (constant <type> (...))"); ir_read_error(st, list, "expected (constant <type> (...))");
return NULL; return NULL;
@@ -931,12 +853,10 @@ static ir_dereference_variable *
read_var_ref(_mesa_glsl_parse_state *st, s_list *list) read_var_ref(_mesa_glsl_parse_state *st, s_list *list)
{ {
void *ctx = st; void *ctx = st;
if (list->length() != 2) { s_symbol *var_name;
ir_read_error(st, list, "expected (var_ref <variable name>)");
return NULL; s_pattern pat[] = { "var_ref", var_name };
} if (!MATCH(list, pat)) {
s_symbol *var_name = SX_AS_SYMBOL(list->subexpressions.head->next);
if (var_name == NULL) {
ir_read_error(st, list, "expected (var_ref <variable name>)"); ir_read_error(st, list, "expected (var_ref <variable name>)");
return NULL; return NULL;
} }
@@ -954,19 +874,21 @@ static ir_dereference_array *
read_array_ref(_mesa_glsl_parse_state *st, s_list *list) read_array_ref(_mesa_glsl_parse_state *st, s_list *list)
{ {
void *ctx = st; void *ctx = st;
if (list->length() != 3) { s_expression *subj_expr;
s_expression *idx_expr;
s_pattern pat[] = { "array_ref", subj_expr, idx_expr };
if (!MATCH(list, pat)) {
ir_read_error(st, list, "expected (array_ref <rvalue> <index>)"); ir_read_error(st, list, "expected (array_ref <rvalue> <index>)");
return NULL; return NULL;
} }
s_expression *subj_expr = (s_expression*) list->subexpressions.head->next;
ir_rvalue *subject = read_rvalue(st, subj_expr); ir_rvalue *subject = read_rvalue(st, subj_expr);
if (subject == NULL) { if (subject == NULL) {
ir_read_error(st, NULL, "when reading the subject of an array_ref"); ir_read_error(st, NULL, "when reading the subject of an array_ref");
return NULL; return NULL;
} }
s_expression *idx_expr = (s_expression*) subj_expr->next;
ir_rvalue *idx = read_rvalue(st, idx_expr); ir_rvalue *idx = read_rvalue(st, idx_expr);
return new(ctx) ir_dereference_array(subject, idx); return new(ctx) ir_dereference_array(subject, idx);
} }
@@ -975,159 +897,140 @@ static ir_dereference_record *
read_record_ref(_mesa_glsl_parse_state *st, s_list *list) read_record_ref(_mesa_glsl_parse_state *st, s_list *list)
{ {
void *ctx = st; void *ctx = st;
if (list->length() != 3) { s_expression *subj_expr;
s_symbol *field;
s_pattern pat[] = { "record_ref", subj_expr, field };
if (!MATCH(list, pat)) {
ir_read_error(st, list, "expected (record_ref <rvalue> <field>)"); ir_read_error(st, list, "expected (record_ref <rvalue> <field>)");
return NULL; return NULL;
} }
s_expression *subj_expr = (s_expression*) list->subexpressions.head->next;
ir_rvalue *subject = read_rvalue(st, subj_expr); ir_rvalue *subject = read_rvalue(st, subj_expr);
if (subject == NULL) { if (subject == NULL) {
ir_read_error(st, NULL, "when reading the subject of a record_ref"); ir_read_error(st, NULL, "when reading the subject of a record_ref");
return NULL; return NULL;
} }
s_symbol *field = SX_AS_SYMBOL(subj_expr->next);
if (field == NULL) {
ir_read_error(st, list, "expected (record_ref ... <field name>)");
return NULL;
}
return new(ctx) ir_dereference_record(subject, field->value()); return new(ctx) ir_dereference_record(subject, field->value());
} }
static bool
valid_texture_list_length(ir_texture_opcode op, s_list *list)
{
unsigned required_length = 7;
if (op == ir_txf)
required_length = 5;
else if (op == ir_tex)
required_length = 6;
return list->length() == required_length;
}
static ir_texture * static ir_texture *
read_texture(_mesa_glsl_parse_state *st, s_list *list) read_texture(_mesa_glsl_parse_state *st, s_list *list)
{ {
void *ctx = st; s_symbol *tag = NULL;
s_symbol *tag = SX_AS_SYMBOL(list->subexpressions.head); s_expression *s_sampler = NULL;
assert(tag != NULL); s_expression *s_coord = NULL;
s_list *s_offset = NULL;
s_expression *s_proj = NULL;
s_list *s_shadow = NULL;
s_expression *s_lod = NULL;
ir_texture_opcode op = ir_texture::get_opcode(tag->value()); ir_texture_opcode op;
if (op == (ir_texture_opcode) -1)
return NULL;
if (!valid_texture_list_length(op, list)) { s_pattern tex_pattern[] =
ir_read_error(st, NULL, "invalid list size in (%s ...)", tag->value()); { "tex", s_sampler, s_coord, s_offset, s_proj, s_shadow };
s_pattern txf_pattern[] =
{ "txf", s_sampler, s_coord, s_offset, s_lod };
s_pattern other_pattern[] =
{ tag, s_sampler, s_coord, s_offset, s_proj, s_shadow, s_lod };
if (MATCH(list, tex_pattern)) {
op = ir_tex;
} else if (MATCH(list, txf_pattern)) {
op = ir_txf;
} else if (MATCH(list, other_pattern)) {
op = ir_texture::get_opcode(tag->value());
if (op == -1)
return NULL; return NULL;
} }
ir_texture *tex = new(ctx) ir_texture(op); ir_texture *tex = new(st) ir_texture(op);
// Read sampler (must be a deref) // Read sampler (must be a deref)
s_expression *sampler_expr = (s_expression *) tag->next; ir_dereference *sampler = read_dereference(st, s_sampler);
ir_dereference *sampler = read_dereference(st, sampler_expr);
if (sampler == NULL) { if (sampler == NULL) {
ir_read_error(st, NULL, "when reading sampler in (%s ...)", tag->value()); ir_read_error(st, NULL, "when reading sampler in (%s ...)",
tex->opcode_string());
return NULL; return NULL;
} }
tex->set_sampler(sampler); tex->set_sampler(sampler);
// Read coordinate (any rvalue) // Read coordinate (any rvalue)
s_expression *coordinate_expr = (s_expression *) sampler_expr->next; tex->coordinate = read_rvalue(st, s_coord);
tex->coordinate = read_rvalue(st, coordinate_expr);
if (tex->coordinate == NULL) { if (tex->coordinate == NULL) {
ir_read_error(st, NULL, "when reading coordinate in (%s ...)", ir_read_error(st, NULL, "when reading coordinate in (%s ...)",
tag->value()); tex->opcode_string());
return NULL; return NULL;
} }
// Read texel offset, i.e. (0 0 0) // Read texel offset, i.e. (0 0 0)
s_list *offset_list = SX_AS_LIST(coordinate_expr->next); s_int *offset_x;
if (offset_list == NULL || offset_list->length() != 3) { s_int *offset_y;
ir_read_error(st, offset_list, "expected (<int> <int> <int>)"); s_int *offset_z;
return NULL; s_pattern offset_pat[] = { offset_x, offset_y, offset_z };
} if (!MATCH(s_offset, offset_pat)) {
s_int *offset_x = SX_AS_INT(offset_list->subexpressions.head); ir_read_error(st, s_offset, "expected (<int> <int> <int>)");
s_int *offset_y = SX_AS_INT(offset_list->subexpressions.head->next);
s_int *offset_z = SX_AS_INT(offset_list->subexpressions.head->next->next);
if (offset_x == NULL || offset_y == NULL || offset_z == NULL) {
ir_read_error(st, offset_list, "expected (<int> <int> <int>)");
return NULL; return NULL;
} }
tex->offsets[0] = offset_x->value(); tex->offsets[0] = offset_x->value();
tex->offsets[1] = offset_y->value(); tex->offsets[1] = offset_y->value();
tex->offsets[2] = offset_z->value(); tex->offsets[2] = offset_z->value();
if (op == ir_txf) { if (op != ir_txf) {
s_expression *lod_expr = (s_expression *) offset_list->next; s_int *proj_as_int = SX_AS_INT(s_proj);
tex->lod_info.lod = read_rvalue(st, lod_expr);
if (tex->lod_info.lod == NULL) {
ir_read_error(st, NULL, "when reading LOD in (txf ...)");
return NULL;
}
} else {
s_expression *proj_expr = (s_expression *) offset_list->next;
s_int *proj_as_int = SX_AS_INT(proj_expr);
if (proj_as_int && proj_as_int->value() == 1) { if (proj_as_int && proj_as_int->value() == 1) {
tex->projector = NULL; tex->projector = NULL;
} else { } else {
tex->projector = read_rvalue(st, proj_expr); tex->projector = read_rvalue(st, s_proj);
if (tex->projector == NULL) { if (tex->projector == NULL) {
ir_read_error(st, NULL, "when reading projective divide in (%s ..)", ir_read_error(st, NULL, "when reading projective divide in (%s ..)",
tag->value()); tex->opcode_string());
return NULL; return NULL;
} }
} }
s_list *shadow_list = SX_AS_LIST(proj_expr->next); if (s_shadow->subexpressions.is_empty()) {
if (shadow_list == NULL) {
ir_read_error(st, NULL, "shadow comparitor must be a list");
return NULL;
}
if (shadow_list->subexpressions.is_empty()) {
tex->shadow_comparitor = NULL; tex->shadow_comparitor = NULL;
} else { } else {
tex->shadow_comparitor = read_rvalue(st, shadow_list); tex->shadow_comparitor = read_rvalue(st, s_shadow);
if (tex->shadow_comparitor == NULL) { if (tex->shadow_comparitor == NULL) {
ir_read_error(st, NULL, "when reading shadow comparitor in (%s ..)", ir_read_error(st, NULL, "when reading shadow comparitor in (%s ..)",
tag->value()); tex->opcode_string());
return NULL; return NULL;
} }
} }
s_expression *lod_expr = (s_expression *) shadow_list->next; }
switch (op) { switch (op) {
case ir_txb: case ir_txb:
tex->lod_info.bias = read_rvalue(st, lod_expr); tex->lod_info.bias = read_rvalue(st, s_lod);
if (tex->lod_info.bias == NULL) { if (tex->lod_info.bias == NULL) {
ir_read_error(st, NULL, "when reading LOD bias in (txb ...)"); ir_read_error(st, NULL, "when reading LOD bias in (txb ...)");
return NULL; return NULL;
} }
break; break;
case ir_txl: case ir_txl:
tex->lod_info.lod = read_rvalue(st, lod_expr); case ir_txf:
tex->lod_info.lod = read_rvalue(st, s_lod);
if (tex->lod_info.lod == NULL) { if (tex->lod_info.lod == NULL) {
ir_read_error(st, NULL, "when reading LOD in (txl ...)"); ir_read_error(st, NULL, "when reading LOD in (%s ...)",
tex->opcode_string());
return NULL; return NULL;
} }
break; break;
case ir_txd: { case ir_txd: {
s_list *lod_list = SX_AS_LIST(lod_expr); s_expression *s_dx, *s_dy;
if (lod_list->length() != 2) { s_pattern dxdy_pat[] = { s_dx, s_dy };
ir_read_error(st, lod_expr, "expected (dPdx dPdy) in (txd ...)"); if (!MATCH(s_lod, dxdy_pat)) {
ir_read_error(st, s_lod, "expected (dPdx dPdy) in (txd ...)");
return NULL; return NULL;
} }
s_expression *dx_expr = (s_expression *) lod_list->subexpressions.head; tex->lod_info.grad.dPdx = read_rvalue(st, s_dx);
s_expression *dy_expr = (s_expression *) dx_expr->next;
tex->lod_info.grad.dPdx = read_rvalue(st, dx_expr);
if (tex->lod_info.grad.dPdx == NULL) { if (tex->lod_info.grad.dPdx == NULL) {
ir_read_error(st, NULL, "when reading dPdx in (txd ...)"); ir_read_error(st, NULL, "when reading dPdx in (txd ...)");
return NULL; return NULL;
} }
tex->lod_info.grad.dPdy = read_rvalue(st, dy_expr); tex->lod_info.grad.dPdy = read_rvalue(st, s_dy);
if (tex->lod_info.grad.dPdy == NULL) { if (tex->lod_info.grad.dPdy == NULL) {
ir_read_error(st, NULL, "when reading dPdy in (txd ...)"); ir_read_error(st, NULL, "when reading dPdy in (txd ...)");
return NULL; return NULL;
@@ -1135,9 +1038,8 @@ read_texture(_mesa_glsl_parse_state *st, s_list *list)
break; break;
} }
default: default:
// tex doesn't have any extra parameters and txf was handled earlier. // tex doesn't have any extra parameters.
break; break;
}; };
}
return tex; return tex;
} }

View File

@@ -139,3 +139,49 @@ void s_list::print()
printf(")"); printf(")");
} }
// --------------------------------------------------
bool
s_pattern::match(s_expression *expr)
{
switch (type)
{
case EXPR: *p_expr = expr; break;
case LIST: if (expr->is_list()) *p_list = (s_list *) expr; break;
case SYMBOL: if (expr->is_symbol()) *p_symbol = (s_symbol *) expr; break;
case NUMBER: if (expr->is_number()) *p_number = (s_number *) expr; break;
case INT: if (expr->is_int()) *p_int = (s_int *) expr; break;
case STRING:
s_symbol *sym = SX_AS_SYMBOL(expr);
if (sym != NULL && strcmp(sym->value(), literal) == 0)
return true;
return false;
};
return *p_expr == expr;
}
bool
s_match(s_expression *top, unsigned n, s_pattern *pattern, bool partial)
{
s_list *list = SX_AS_LIST(top);
if (list == NULL)
return false;
unsigned i = 0;
foreach_iter(exec_list_iterator, it, list->subexpressions) {
if (i >= n)
return partial; /* More actual items than the pattern expected */
s_expression *expr = (s_expression *) it.get();
if (expr == NULL || !pattern[i].match(expr))
return false;
i++;
}
if (i < n)
return false; /* Less actual items than the pattern expected */
return true;
}

View File

@@ -26,9 +26,11 @@
#ifndef S_EXPRESSION_H #ifndef S_EXPRESSION_H
#define S_EXPRESSION_H #define S_EXPRESSION_H
#include "main/core.h" /* for Elements */
#include "strtod.h" #include "strtod.h"
#include "list.h" #include "list.h"
/* Type-safe downcasting macros (also safe to pass NULL) */
#define SX_AS_(t,x) ((x) && ((s_expression*) x)->is_##t()) ? ((s_##t*) (x)) \ #define SX_AS_(t,x) ((x) && ((s_expression*) x)->is_##t()) ? ((s_##t*) (x)) \
: NULL : NULL
#define SX_AS_LIST(x) SX_AS_(list, x) #define SX_AS_LIST(x) SX_AS_(list, x)
@@ -36,6 +38,10 @@
#define SX_AS_NUMBER(x) SX_AS_(number, x) #define SX_AS_NUMBER(x) SX_AS_(number, x)
#define SX_AS_INT(x) SX_AS_(int, x) #define SX_AS_INT(x) SX_AS_(int, x)
/* Pattern matching macros */
#define MATCH(list, pat) s_match(list, Elements(pat), pat, false)
#define PARTIAL_MATCH(list, pat) s_match(list, Elements(pat), pat, true)
/* For our purposes, S-Expressions are: /* For our purposes, S-Expressions are:
* - <int> * - <int>
* - <float> * - <float>
@@ -140,4 +146,36 @@ public:
exec_list subexpressions; exec_list subexpressions;
}; };
// ------------------------------------------------------------
/**
* Part of a pattern to match - essentially a record holding a pointer to the
* storage for the component to match, along with the appropriate type.
*/
class s_pattern {
public:
s_pattern(s_expression *&s) : p_expr(&s), type(EXPR) { }
s_pattern(s_list *&s) : p_list(&s), type(LIST) { }
s_pattern(s_symbol *&s) : p_symbol(&s), type(SYMBOL) { }
s_pattern(s_number *&s) : p_number(&s), type(NUMBER) { }
s_pattern(s_int *&s) : p_int(&s), type(INT) { }
s_pattern(const char *str) : literal(str), type(STRING) { }
bool match(s_expression *expr);
private:
union {
s_expression **p_expr;
s_list **p_list;
s_symbol **p_symbol;
s_number **p_number;
s_int **p_int;
const char *literal;
};
enum { EXPR, LIST, SYMBOL, NUMBER, INT, STRING } type;
};
bool
s_match(s_expression *top, unsigned n, s_pattern *pattern, bool partial);
#endif /* S_EXPRESSION_H */ #endif /* S_EXPRESSION_H */