implement asm nodes
authorMatthias Braun <matze@braunis.de>
Tue, 29 Jul 2008 07:08:34 +0000 (07:08 +0000)
committerMatthias Braun <matze@braunis.de>
Tue, 29 Jul 2008 07:08:34 +0000 (07:08 +0000)
[r20747]

ast.c
ast.h
ast2firm.c
ast_t.h
parser.c

diff --git a/ast.c b/ast.c
index 690bec4..6342074 100644 (file)
--- a/ast.c
+++ b/ast.c
@@ -1020,23 +1020,23 @@ static void print_for_statement(const for_statement_t *statement)
 }
 
 /**
- * Print assembler constraints.
+ * Print assembler arguments.
  *
- * @param constraints   the constraints
+ * @param arguments   the arguments
  */
-static void print_asm_constraints(asm_constraint_t *constraints)
+static void print_asm_arguments(asm_argument_t *arguments)
 {
-       asm_constraint_t *constraint = constraints;
-       for( ; constraint != NULL; constraint = constraint->next) {
-               if(constraint != constraints)
+       asm_argument_t *argument = arguments;
+       for( ; argument != NULL; argument = argument->next) {
+               if(argument != arguments)
                        fputs(", ", out);
 
-               if(constraint->symbol) {
-                       fprintf(out, "[%s] ", constraint->symbol->string);
+               if(argument->symbol) {
+                       fprintf(out, "[%s] ", argument->symbol->string);
                }
-               print_quoted_string(&constraint->constraints, '"');
+               print_quoted_string(&argument->constraints, '"');
                fputs(" (", out);
-               print_expression(constraint->expression);
+               print_expression(argument->expression);
                fputs(")", out);
        }
 }
@@ -1075,12 +1075,12 @@ static void print_asm_statement(const asm_statement_t *statement)
                goto end_of_print_asm_statement;
 
        fputs(" : ", out);
-       print_asm_constraints(statement->inputs);
+       print_asm_arguments(statement->inputs);
        if(statement->outputs == NULL && statement->clobbers == NULL)
                goto end_of_print_asm_statement;
 
        fputs(" : ", out);
-       print_asm_constraints(statement->outputs);
+       print_asm_arguments(statement->outputs);
        if(statement->clobbers == NULL)
                goto end_of_print_asm_statement;
 
diff --git a/ast.h b/ast.h
index 678cceb..c6ddb82 100644 (file)
--- a/ast.h
+++ b/ast.h
@@ -81,7 +81,7 @@ typedef struct case_label_statement_t       case_label_statement_t;
 typedef struct while_statement_t            while_statement_t;
 typedef struct do_while_statement_t         do_while_statement_t;
 typedef struct for_statement_t              for_statement_t;
-typedef struct asm_constraint_t             asm_constraint_t;
+typedef struct asm_argument_t               asm_argument_t;
 typedef struct asm_clobber_t                asm_clobber_t;
 typedef struct asm_statement_t              asm_statement_t;
 typedef struct ms_try_statement_t           ms_try_statement_t;
index 52c345a..0a25f29 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <libfirm/firm.h>
 #include <libfirm/adt/obst.h>
+#include <libfirm/be.h>
 
 #include "ast2firm.h"
 
@@ -4445,29 +4446,31 @@ static void goto_to_firm(const goto_statement_t *statement)
        set_cur_block(NULL);
 }
 
-typedef enum modifier_t {
-       ASM_MODIFIER_WRITE_ONLY   = 1 << 0,
-       ASM_MODIFIER_READ_WRITE   = 1 << 1,
-       ASM_MODIFIER_COMMUTATIVE  = 1 << 2,
-       ASM_MODIFIER_EARLYCLOBBER = 1 << 3,
-} modifier_t;
-
 static void asm_statement_to_firm(const asm_statement_t *statement)
 {
-       (void) statement;
-       fprintf(stderr, "WARNING asm not implemented yet!\n");
-#if 0
        bool needs_memory = false;
 
+       if (statement->is_volatile) {
+               needs_memory = true;
+       }
+
        size_t         n_clobbers = 0;
        asm_clobber_t *clobber    = statement->clobbers;
        for( ; clobber != NULL; clobber = clobber->next) {
-               if (strcmp(clobber->clobber, "memory") == 0) {
+               const char *clobber_str = clobber->clobber.begin;
+
+               if (!be_is_valid_clobber(clobber_str)) {
+                       errorf(&statement->base.source_position,
+                                  "invalid clobber '%s' specified", clobber->clobber);
+                       continue;
+               }
+
+               if (strcmp(clobber_str, "memory") == 0) {
                        needs_memory = true;
                        continue;
                }
 
-               ident *id = new_id_from_str(clobber->clobber);
+               ident *id = new_id_from_str(clobber_str);
                obstack_ptr_grow(&asm_obst, id);
                ++n_clobbers;
        }
@@ -4477,68 +4480,210 @@ static void asm_statement_to_firm(const asm_statement_t *statement)
                clobbers = obstack_finish(&asm_obst);
        }
 
-       /* find and count input and output constraints */
-       asm_constraint_t *constraint = statement->inputs;
-       for( ; constraint != NULL; constraint = constraint->next) {
-               int  modifiers      = 0;
-               bool supports_memop = false;
-               for(const char *c = constraint->constraints; *c != 0; ++c) {
-                       /* TODO: improve error messages */
-                       switch(*c) {
-                       case '?':
-                       case '!':
-                               panic("multiple alternative assembler constraints not "
-                                     "supported");
-                       case 'm':
-                       case 'o':
-                       case 'V':
-                       case '<':
-                       case '>':
-                       case 'X':
-                               supports_memop = true;
-                               obstack_1grow(&asm_obst, *c);
-                               break;
-                       case '=':
-                               if (modifiers & ASM_MODIFIER_READ_WRITE)
-                                       panic("inconsistent register constraints");
-                               modifiers |= ASM_MODIFIER_WRITE_ONLY;
-                               break;
-                       case '+':
-                               if (modifiers & ASM_MODIFIER_WRITE_ONLY)
-                                       panic("inconsistent register constraints");
-                               modifiers |= ASM_MODIFIER_READ_WRITE;
-                               break;
-                       case '&':
-                               modifiers |= ASM_MODIFIER_EARLYCLOBBER;
-                               panic("early clobber assembler constraint not supported yet");
-                               break;
-                       case '%':
-                               modifiers |= ASM_MODIFIER_COMMUTATIVE;
-                               panic("commutative assembler constraint not supported yet");
-                               break;
-                       case '#':
-                               /* skip register preferences stuff... */
-                               while(*c != 0 && *c != ',')
-                                       ++c;
-                               break;
-                       case '*':
-                               /* skip register preferences stuff... */
-                               ++c;
-                               break;
-                       default:
-                               obstack_1grow(&asm_obst, *c);
-                               break;
+       size_t n_inputs  = 0;
+       asm_argument_t *argument = statement->inputs;
+       for ( ; argument != NULL; argument = argument->next)
+               n_inputs++;
+       size_t n_outputs = 0;
+       argument = statement->outputs;
+       for ( ; argument != NULL; argument = argument->next)
+               n_outputs++;
+
+       unsigned next_pos = 0;
+
+       ir_node *ins[n_inputs + n_outputs + 1];
+       size_t   in_size = 0;
+
+       ir_asm_constraint *tmp_in_constraints
+               = xmalloc(n_outputs * sizeof(tmp_in_constraints[0]));
+
+       const expression_t *out_exprs[n_outputs];
+       ir_node            *out_addrs[n_outputs];
+       size_t              out_size = 0;
+
+       argument = statement->outputs;
+       for ( ; argument != NULL; argument = argument->next) {
+               const char *constraints = argument->constraints.begin;
+               asm_constraint_flags_t asm_flags
+                       = be_parse_asm_constraints(constraints);
+
+               if (asm_flags & ASM_CONSTRAINT_FLAG_NO_SUPPORT) {
+                       errorf(&statement->base.source_position,
+                              "some constraints in '%s' are not supported", constraints);
+                       continue;
+               }
+               if (asm_flags & ASM_CONSTRAINT_FLAG_INVALID) {
+                       errorf(&statement->base.source_position,
+                              "some constraints in '%s' are invalid", constraints);
+                       continue;
+               }
+               if (! (asm_flags & ASM_CONSTRAINT_FLAG_MODIFIER_WRITE)) {
+                       errorf(&statement->base.source_position,
+                              "no write flag specified for output constraints '%s'",
+                              constraints);
+                       continue;
+               }
+
+               unsigned pos = next_pos++;
+               if ( (asm_flags & ASM_CONSTRAINT_FLAG_SUPPORTS_IMMEDIATE)
+                               || (asm_flags & ASM_CONSTRAINT_FLAG_SUPPORTS_REGISTER) ) {
+                       expression_t *expr = argument->expression;
+                       ir_node      *addr = expression_to_addr(expr);
+                       if (asm_flags & ASM_CONSTRAINT_FLAG_MODIFIER_READ) {
+                               ir_node *value = get_value_from_lvalue(expr, addr);
+
+                               ir_asm_constraint constraint;
+                               constraint.pos              = pos;
+                               constraint.constraint       = new_id_from_str(constraints);
+                               constraint.mode             = get_ir_mode(expr->base.type);
+                               tmp_in_constraints[in_size] = constraint;
+                               ins[in_size] = value;
+
+                               ++in_size;
                        }
+
+                       out_exprs[out_size] = expr;
+                       out_addrs[out_size] = addr;
+                       ++out_size;
+               } else if (asm_flags & ASM_CONSTRAINT_FLAG_SUPPORTS_MEMOP) {
+                       /* pure memory ops need no input (but we have to make sure we
+                        * attach to the memory) */
+                       assert(! (asm_flags &
+                                               (ASM_CONSTRAINT_FLAG_SUPPORTS_IMMEDIATE
+                                                | ASM_CONSTRAINT_FLAG_SUPPORTS_REGISTER)));
+                       needs_memory = true;
+
+                       /* we need to attach the address to the inputs */
+                       expression_t *expr = argument->expression;
+
+                       ir_asm_constraint constraint;
+                       constraint.pos              = pos;
+                       constraint.constraint       = new_id_from_str(constraints);
+                       constraint.mode             = NULL;
+                       tmp_in_constraints[in_size] = constraint;
+
+                       ins[in_size]          = expression_to_addr(expr);
+                       ++in_size;
+                       continue;
+               } else {
+                       errorf(&statement->base.source_position,
+                              "only modifiers but no place set in constraints '%s'",
+                              constraints);
+                       continue;
                }
-               obstack_1grow(&asm_obst, '\0');
-               const char *constraint_string = obstack_finish(&asm_obst);
 
-               needs_memory |= supports_memop;
-               if (supports_memop) {
+               ir_asm_constraint constraint;
+               constraint.pos        = pos;
+               constraint.constraint = new_id_from_str(constraints);
+               constraint.mode       = get_ir_mode(argument->expression->base.type);
+
+               obstack_grow(&asm_obst, &constraint, sizeof(constraint));
+       }
+       assert(obstack_object_size(&asm_obst)
+                       == out_size * sizeof(ir_asm_constraint));
+       ir_asm_constraint *output_constraints = obstack_finish(&asm_obst);
+
 
+       obstack_grow(&asm_obst, tmp_in_constraints,
+                    in_size * sizeof(tmp_in_constraints[0]));
+       free(tmp_in_constraints);
+       /* find and count input and output arguments */
+       argument = statement->inputs;
+       for( ; argument != NULL; argument = argument->next) {
+               const char *constraints = argument->constraints.begin;
+               asm_constraint_flags_t asm_flags
+                       = be_parse_asm_constraints(constraints);
+
+               if (asm_flags & ASM_CONSTRAINT_FLAG_NO_SUPPORT) {
+                       errorf(&statement->base.source_position,
+                              "some constraints in '%s' are not supported", constraints);
+                       continue;
+               }
+               if (asm_flags & ASM_CONSTRAINT_FLAG_INVALID) {
+                       errorf(&statement->base.source_position,
+                              "some constraints in '%s' are invalid", constraints);
+                       continue;
                }
+               if (asm_flags & ASM_CONSTRAINT_FLAG_MODIFIER_WRITE) {
+                       errorf(&statement->base.source_position,
+                              "write flag specified for input constraints '%s'",
+                              constraints);
+                       continue;
+               }
+
+               ir_node *input;
+               if ( (asm_flags & ASM_CONSTRAINT_FLAG_SUPPORTS_IMMEDIATE)
+                               || (asm_flags & ASM_CONSTRAINT_FLAG_SUPPORTS_REGISTER) ) {
+                       /* we can treat this as "normal" input */
+                       input = expression_to_firm(argument->expression);
+               } else if (asm_flags & ASM_CONSTRAINT_FLAG_SUPPORTS_MEMOP) {
+                       /* pure memory ops need no input (but we have to make sure we
+                        * attach to the memory) */
+                       assert(! (asm_flags &
+                                               (ASM_CONSTRAINT_FLAG_SUPPORTS_IMMEDIATE
+                                                | ASM_CONSTRAINT_FLAG_SUPPORTS_REGISTER)));
+                       needs_memory = true;
+                       input = expression_to_addr(argument->expression);
+               } else {
+                       errorf(&statement->base.source_position,
+                              "only modifiers but no place set in constraints '%s'",
+                              constraints);
+                       continue;
+               }
+
+               ir_asm_constraint constraint;
+               constraint.pos        = next_pos++;
+               constraint.constraint = new_id_from_str(constraints);
+               constraint.mode       = get_irn_mode(input);
+
+               obstack_grow(&asm_obst, &constraint, sizeof(constraint));
+               ins[in_size++] = input;
+       }
+
+       if (needs_memory) {
+               ir_asm_constraint constraint;
+               constraint.pos        = next_pos++;
+               constraint.constraint = new_id_from_str("");
+               constraint.mode       = mode_M;
+
+               obstack_grow(&asm_obst, &constraint, sizeof(constraint));
+               ins[in_size++] = get_store();
+       }
+
+       assert(obstack_object_size(&asm_obst)
+                       == in_size * sizeof(ir_asm_constraint));
+       ir_asm_constraint *input_constraints = obstack_finish(&asm_obst);
+
+       /* create asm node */
+       dbg_info *dbgi = get_dbg_info(&statement->base.source_position);
+
+       ident *asm_text = new_id_from_str(statement->asm_text.begin);
+
+       ir_node *node = new_d_ASM(dbgi, in_size, ins, input_constraints,
+                                 out_size, output_constraints,
+                                 n_clobbers, clobbers, asm_text);
+
+       if (statement->is_volatile) {
+               set_irn_pinned(node, op_pin_state_pinned);
+       } else {
+               set_irn_pinned(node, op_pin_state_floats);
+       }
+
+       /* create output projs & connect them */
+       size_t i;
+       for (i = 0; i < out_size; ++i) {
+               const expression_t *out_expr = out_exprs[i];
+               long                pn       = i;
+               ir_mode            *mode     = get_ir_mode(out_expr->base.type);
+               ir_node            *proj     = new_Proj(node, mode, pn);
+               ir_node            *addr     = out_addrs[i];
+
+               set_value_for_expression_addr(out_expr, proj, addr);
+       }
+       if (needs_memory) {
+               ir_node *projm = new_Proj(node, mode_M, i);
+               set_store(projm);
        }
-#endif
 }
 
 static void    ms_try_statement_to_firm(ms_try_statement_t *statement) {
diff --git a/ast_t.h b/ast_t.h
index 2dd76ff..3221f3c 100644 (file)
--- a/ast_t.h
+++ b/ast_t.h
@@ -686,11 +686,11 @@ struct for_statement_t {
        scope_t           scope;
 };
 
-struct asm_constraint_t {
-       string_t          constraints;
-       expression_t     *expression;
-       symbol_t         *symbol;
-       asm_constraint_t *next;
+struct asm_argument_t {
+       string_t        constraints;
+       expression_t   *expression;
+       symbol_t       *symbol;
+       asm_argument_t *next;
 };
 
 struct asm_clobber_t {
@@ -699,12 +699,12 @@ struct asm_clobber_t {
 };
 
 struct asm_statement_t {
-       statement_base_t  base;
-       string_t          asm_text;
-       asm_constraint_t *inputs;
-       asm_constraint_t *outputs;
-       asm_clobber_t    *clobbers;
-       bool              is_volatile;
+       statement_base_t base;
+       string_t         asm_text;
+       asm_argument_t  *inputs;
+       asm_argument_t  *outputs;
+       asm_clobber_t   *clobbers;
+       bool             is_volatile;
 };
 
 struct ms_try_statement_t {
index 4e37812..5d566e7 100644 (file)
--- a/parser.c
+++ b/parser.c
@@ -6461,21 +6461,29 @@ static bool has_const_fields(const compound_type_t *type)
        return false;
 }
 
+static bool is_lvalue(const expression_t *expression)
+{
+       switch (expression->kind) {
+       case EXPR_REFERENCE:
+       case EXPR_ARRAY_ACCESS:
+       case EXPR_SELECT:
+       case EXPR_UNARY_DEREFERENCE:
+               return true;
+
+       default:
+               return false;
+       }
+}
+
 static bool is_valid_assignment_lhs(expression_t const* const left)
 {
        type_t *const orig_type_left = revert_automatic_type_conversion(left);
        type_t *const type_left      = skip_typeref(orig_type_left);
 
-       switch (left->kind)  {
-               case EXPR_REFERENCE:
-               case EXPR_ARRAY_ACCESS:
-               case EXPR_SELECT:
-               case EXPR_UNARY_DEREFERENCE:
-                       break;
-
-               default:
-                       errorf(HERE, "left hand side '%E' of assignment is not an lvalue", left);
-                       return false;
+       if (!is_lvalue(left)) {
+               errorf(HERE, "left hand side '%E' of assignment is not an lvalue",
+                      left);
+               return false;
        }
 
        if (is_type_array(type_left)) {
@@ -6986,40 +6994,44 @@ static void init_expression_parsers(void)
 }
 
 /**
- * Parse a asm statement constraints specification.
+ * Parse a asm statement arguments specification.
  */
-static asm_constraint_t *parse_asm_constraints(void)
+static asm_argument_t *parse_asm_arguments(bool is_out)
 {
-       asm_constraint_t *result = NULL;
-       asm_constraint_t *last   = NULL;
+       asm_argument_t *result = NULL;
+       asm_argument_t *last   = NULL;
 
        while(token.type == T_STRING_LITERAL || token.type == '[') {
-               asm_constraint_t *constraint = allocate_ast_zero(sizeof(constraint[0]));
-               memset(constraint, 0, sizeof(constraint[0]));
+               asm_argument_t *argument = allocate_ast_zero(sizeof(argument[0]));
+               memset(argument, 0, sizeof(argument[0]));
 
                if(token.type == '[') {
                        eat('[');
                        if(token.type != T_IDENTIFIER) {
-                               parse_error_expected("while parsing asm constraint",
+                               parse_error_expected("while parsing asm argument",
                                                     T_IDENTIFIER, NULL);
                                return NULL;
                        }
-                       constraint->symbol = token.v.symbol;
+                       argument->symbol = token.v.symbol;
 
                        expect(']');
                }
 
-               constraint->constraints = parse_string_literals();
+               argument->constraints = parse_string_literals();
                expect('(');
-               constraint->expression = parse_expression();
+               argument->expression = parse_expression();
+               if (is_out && !is_lvalue(argument->expression)) {
+                       errorf(&argument->expression->base.source_position,
+                              "asm output argument is not an lvalue");
+               }
                expect(')');
 
                if(last != NULL) {
-                       last->next = constraint;
+                       last->next = argument;
                } else {
-                       result = constraint;
+                       result = argument;
                }
-               last = constraint;
+               last = argument;
 
                if(token.type != ',')
                        break;
@@ -7086,14 +7098,14 @@ static statement_t *parse_asm_statement(void)
        }
        eat(':');
 
-       asm_statement->inputs = parse_asm_constraints();
+       asm_statement->outputs = parse_asm_arguments(true);
        if(token.type != ':') {
                rem_anchor_token(':');
                goto end_of_asm;
        }
        eat(':');
 
-       asm_statement->outputs = parse_asm_constraints();
+       asm_statement->inputs = parse_asm_arguments(false);
        if(token.type != ':') {
                rem_anchor_token(':');
                goto end_of_asm;