| /* Parser for GIMPLE. |
| Copyright (C) 2016-2018 Free Software Foundation, Inc. |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify it under |
| the terms of the GNU General Public License as published by the Free |
| Software Foundation; either version 3, or (at your option) any later |
| version. |
| |
| GCC is distributed in the hope that it will be useful, but WITHOUT ANY |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
| for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with GCC; see the file COPYING3. If not see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "config.h" |
| #include "system.h" |
| #include "coretypes.h" |
| #include "target.h" |
| #include "function.h" |
| #include "c-tree.h" |
| #include "timevar.h" |
| #include "stringpool.h" |
| #include "cgraph.h" |
| #include "attribs.h" |
| #include "stor-layout.h" |
| #include "varasm.h" |
| #include "trans-mem.h" |
| #include "c-family/c-pragma.h" |
| #include "c-lang.h" |
| #include "c-family/c-objc.h" |
| #include "plugin.h" |
| #include "builtins.h" |
| #include "gomp-constants.h" |
| #include "c-family/c-indentation.h" |
| #include "gimple-expr.h" |
| #include "context.h" |
| #include "gcc-rich-location.h" |
| #include "c-parser.h" |
| #include "tree-vrp.h" |
| #include "tree-pass.h" |
| #include "tree-pretty-print.h" |
| #include "tree.h" |
| #include "basic-block.h" |
| #include "gimple.h" |
| #include "gimple-pretty-print.h" |
| #include "tree-ssa.h" |
| #include "pass_manager.h" |
| #include "tree-ssanames.h" |
| #include "gimple-ssa.h" |
| #include "tree-dfa.h" |
| |
| |
| /* Gimple parsing functions. */ |
| static bool c_parser_gimple_compound_statement (c_parser *, gimple_seq *); |
| static void c_parser_gimple_label (c_parser *, gimple_seq *); |
| static void c_parser_gimple_statement (c_parser *, gimple_seq *); |
| static struct c_expr c_parser_gimple_binary_expression (c_parser *); |
| static struct c_expr c_parser_gimple_unary_expression (c_parser *); |
| static struct c_expr c_parser_gimple_postfix_expression (c_parser *); |
| static struct c_expr c_parser_gimple_postfix_expression_after_primary (c_parser *, |
| location_t, |
| struct c_expr); |
| static void c_parser_gimple_declaration (c_parser *); |
| static void c_parser_gimple_goto_stmt (location_t, tree, gimple_seq *); |
| static void c_parser_gimple_if_stmt (c_parser *, gimple_seq *); |
| static void c_parser_gimple_switch_stmt (c_parser *, gimple_seq *); |
| static void c_parser_gimple_return_stmt (c_parser *, gimple_seq *); |
| static void c_finish_gimple_return (location_t, tree); |
| static tree c_parser_gimple_paren_condition (c_parser *); |
| static void c_parser_gimple_expr_list (c_parser *, vec<tree> *); |
| |
| |
| /* Parse the body of a function declaration marked with "__GIMPLE". */ |
| |
| void |
| c_parser_parse_gimple_body (c_parser *parser) |
| { |
| gimple_seq seq = NULL; |
| gimple_seq body = NULL; |
| tree stmt = push_stmt_list (); |
| push_scope (); |
| location_t loc1 = c_parser_peek_token (parser)->location; |
| |
| init_tree_ssa (cfun); |
| |
| if (! c_parser_gimple_compound_statement (parser, &seq)) |
| { |
| gimple *ret = gimple_build_return (NULL); |
| gimple_seq_add_stmt (&seq, ret); |
| } |
| |
| tree block = pop_scope (); |
| stmt = pop_stmt_list (stmt); |
| stmt = c_build_bind_expr (loc1, block, stmt); |
| |
| block = DECL_INITIAL (current_function_decl); |
| BLOCK_SUBBLOCKS (block) = NULL_TREE; |
| BLOCK_CHAIN (block) = NULL_TREE; |
| TREE_ASM_WRITTEN (block) = 1; |
| |
| gbind *bind_stmt = gimple_build_bind (BIND_EXPR_VARS (stmt), NULL, |
| BIND_EXPR_BLOCK (stmt)); |
| gimple_bind_set_body (bind_stmt, seq); |
| gimple_seq_add_stmt (&body, bind_stmt); |
| gimple_set_body (current_function_decl, body); |
| |
| /* While we have SSA names in the IL we do not have a CFG built yet |
| and PHIs are represented using a PHI internal function. We do |
| have lowered control flow and exception handling (well, we do not |
| have parser support for EH yet). But as we still have BINDs |
| we have to go through lowering again. */ |
| cfun->curr_properties = PROP_gimple_any; |
| |
| dump_function (TDI_gimple, current_function_decl); |
| } |
| |
| /* Parse a compound statement in gimple function body. |
| |
| gimple-statement: |
| gimple-statement |
| gimple-declaration-statement |
| gimple-if-statement |
| gimple-switch-statement |
| gimple-labeled-statement |
| gimple-expression-statement |
| gimple-goto-statement |
| gimple-phi-statement |
| gimple-return-statement |
| */ |
| |
| static bool |
| c_parser_gimple_compound_statement (c_parser *parser, gimple_seq *seq) |
| { |
| bool return_p = false; |
| |
| if (! c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>")) |
| return false; |
| |
| /* A compund statement starts with optional declarations. */ |
| while (c_parser_next_tokens_start_declaration (parser)) |
| { |
| c_parser_gimple_declaration (parser); |
| if (! c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>")) |
| return false; |
| } |
| |
| while (c_parser_next_token_is_not (parser, CPP_CLOSE_BRACE)) |
| { |
| if (c_parser_error (parser)) |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, NULL); |
| return return_p; |
| } |
| else if (c_parser_next_token_is (parser, CPP_EOF)) |
| { |
| c_parser_error (parser, "expected declaration or statement"); |
| return return_p; |
| } |
| |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_KEYWORD: |
| switch (c_parser_peek_token (parser)->keyword) |
| { |
| case RID_IF: |
| c_parser_gimple_if_stmt (parser, seq); |
| break; |
| case RID_SWITCH: |
| c_parser_gimple_switch_stmt (parser, seq); |
| break; |
| case RID_GOTO: |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| c_parser_gimple_goto_stmt (loc, |
| c_parser_peek_token |
| (parser)->value, |
| seq); |
| c_parser_consume_token (parser); |
| if (! c_parser_require (parser, CPP_SEMICOLON, |
| "expected %<;%>")) |
| return return_p; |
| } |
| } |
| break; |
| case RID_RETURN: |
| return_p = true; |
| c_parser_gimple_return_stmt (parser, seq); |
| if (! c_parser_require (parser, CPP_SEMICOLON, |
| "expected %<;%>")) |
| return return_p; |
| break; |
| default: |
| goto expr_stmt; |
| } |
| break; |
| case CPP_NAME: |
| if (c_parser_peek_2nd_token (parser)->type == CPP_COLON) |
| { |
| c_parser_gimple_label (parser, seq); |
| break; |
| } |
| goto expr_stmt; |
| |
| case CPP_SEMICOLON: |
| { |
| /* Empty stmt. */ |
| location_t loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| gimple *nop = gimple_build_nop (); |
| gimple_set_location (nop, loc); |
| gimple_seq_add_stmt (seq, nop); |
| break; |
| } |
| |
| default: |
| expr_stmt: |
| c_parser_gimple_statement (parser, seq); |
| if (! c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>")) |
| c_parser_skip_until_found (parser, CPP_SEMICOLON, NULL); |
| } |
| } |
| c_parser_consume_token (parser); |
| return return_p; |
| } |
| |
| /* Parse a gimple statement. |
| |
| gimple-statement: |
| gimple-call-expression |
| gimple-assign-statement |
| gimple-phi-statement |
| |
| gimple-assign-statement: |
| gimple-unary-expression = gimple-assign-rhs |
| |
| gimple-assign-rhs: |
| gimple-cast-expression |
| gimple-unary-expression |
| gimple-binary-expression |
| gimple-call-expression |
| |
| gimple-phi-statement: |
| identifier = __PHI ( label : gimple_primary-expression, ... ) |
| |
| gimple-call-expr: |
| gimple-primary-expression ( argument-list ) |
| |
| gimple-cast-expression: |
| ( type-name ) gimple-primary-expression |
| |
| */ |
| |
| static void |
| c_parser_gimple_statement (c_parser *parser, gimple_seq *seq) |
| { |
| struct c_expr lhs, rhs; |
| gimple *assign = NULL; |
| location_t loc; |
| tree arg = NULL_TREE; |
| auto_vec<tree> vargs; |
| |
| lhs = c_parser_gimple_unary_expression (parser); |
| loc = EXPR_LOCATION (lhs.value); |
| rhs.set_error (); |
| |
| /* GIMPLE call statement without LHS. */ |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON) |
| && TREE_CODE (lhs.value) == CALL_EXPR) |
| { |
| gimple *call; |
| call = gimple_build_call_from_tree (lhs.value, NULL); |
| gimple_seq_add_stmt (seq, call); |
| gimple_set_location (call, loc); |
| return; |
| } |
| |
| /* All following cases are statements with LHS. */ |
| if (! c_parser_require (parser, CPP_EQ, "expected %<=%>")) |
| return; |
| |
| /* Cast expression. */ |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN) |
| && c_token_starts_typename (c_parser_peek_2nd_token (parser))) |
| { |
| c_parser_consume_token (parser); |
| struct c_type_name *type_name = c_parser_type_name (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, "expected %<)%>"); |
| if (type_name == NULL) |
| return; |
| /* ??? The actual type used in the cast expression is ignored as |
| in GIMPLE it is encoded by the type of the LHS. */ |
| rhs = c_parser_gimple_postfix_expression (parser); |
| if (lhs.value != error_mark_node |
| && rhs.value != error_mark_node) |
| { |
| enum tree_code code = NOP_EXPR; |
| if (VECTOR_TYPE_P (TREE_TYPE (lhs.value))) |
| { |
| code = VIEW_CONVERT_EXPR; |
| rhs.value = build1 (VIEW_CONVERT_EXPR, |
| TREE_TYPE (lhs.value), rhs.value); |
| } |
| else if (FLOAT_TYPE_P (TREE_TYPE (lhs.value)) |
| && ! FLOAT_TYPE_P (TREE_TYPE (rhs.value))) |
| code = FLOAT_EXPR; |
| else if (! FLOAT_TYPE_P (TREE_TYPE (lhs.value)) |
| && FLOAT_TYPE_P (TREE_TYPE (rhs.value))) |
| code = FIX_TRUNC_EXPR; |
| assign = gimple_build_assign (lhs.value, code, rhs.value); |
| gimple_seq_add_stmt (seq, assign); |
| gimple_set_location (assign, loc); |
| return; |
| } |
| } |
| |
| /* Unary expression. */ |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_NAME: |
| { |
| tree id = c_parser_peek_token (parser)->value; |
| if (strcmp (IDENTIFIER_POINTER (id), "__ABS") == 0) |
| goto build_unary_expr; |
| break; |
| } |
| case CPP_KEYWORD: |
| if (c_parser_peek_token (parser)->keyword != RID_REALPART |
| && c_parser_peek_token (parser)->keyword != RID_IMAGPART) |
| break; |
| /* Fallthru. */ |
| case CPP_AND: |
| case CPP_PLUS: |
| case CPP_MINUS: |
| case CPP_COMPL: |
| case CPP_NOT: |
| case CPP_MULT: /* pointer deref */ |
| build_unary_expr: |
| rhs = c_parser_gimple_unary_expression (parser); |
| if (rhs.value != error_mark_node) |
| { |
| assign = gimple_build_assign (lhs.value, rhs.value); |
| gimple_set_location (assign, loc); |
| gimple_seq_add_stmt (seq, assign); |
| } |
| return; |
| |
| default:; |
| } |
| |
| /* GIMPLE PHI statement. */ |
| if (c_parser_next_token_is_keyword (parser, RID_PHI)) |
| { |
| c_parser_consume_token (parser); |
| |
| if (! c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| return; |
| |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) |
| c_parser_consume_token (parser); |
| |
| while (c_parser_next_token_is_not (parser, CPP_CLOSE_PAREN)) |
| { |
| if (c_parser_next_token_is (parser, CPP_NAME) |
| && c_parser_peek_2nd_token (parser)->type == CPP_COLON) |
| { |
| arg = lookup_label_for_goto (loc, |
| c_parser_peek_token (parser)->value); |
| c_parser_consume_token (parser); |
| |
| if (c_parser_next_token_is (parser, CPP_COLON)) |
| c_parser_consume_token (parser); |
| vargs.safe_push (arg); |
| } |
| else if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| else |
| { |
| arg = c_parser_gimple_unary_expression (parser).value; |
| vargs.safe_push (arg); |
| } |
| } |
| |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| |
| /* Build internal function for PHI. */ |
| gcall *call_stmt = gimple_build_call_internal_vec (IFN_PHI, vargs); |
| gimple_call_set_lhs (call_stmt, lhs.value); |
| gimple_set_location (call_stmt, UNKNOWN_LOCATION); |
| gimple_seq_add_stmt (seq, call_stmt); |
| return; |
| } |
| |
| /* GIMPLE call with lhs. */ |
| if (c_parser_next_token_is (parser, CPP_NAME) |
| && c_parser_peek_2nd_token (parser)->type == CPP_OPEN_PAREN |
| && lookup_name (c_parser_peek_token (parser)->value)) |
| { |
| rhs = c_parser_gimple_unary_expression (parser); |
| if (rhs.value != error_mark_node) |
| { |
| gimple *call = gimple_build_call_from_tree (rhs.value, NULL); |
| gimple_call_set_lhs (call, lhs.value); |
| gimple_seq_add_stmt (seq, call); |
| gimple_set_location (call, loc); |
| } |
| return; |
| } |
| |
| rhs = c_parser_gimple_binary_expression (parser); |
| if (lhs.value != error_mark_node |
| && rhs.value != error_mark_node) |
| { |
| /* If we parsed a comparison and the next token is a '?' then |
| parse a conditional expression. */ |
| if (COMPARISON_CLASS_P (rhs.value) |
| && c_parser_next_token_is (parser, CPP_QUERY)) |
| { |
| struct c_expr trueval, falseval; |
| c_parser_consume_token (parser); |
| trueval = c_parser_gimple_postfix_expression (parser); |
| falseval.set_error (); |
| if (c_parser_require (parser, CPP_COLON, "expected %<:%>")) |
| falseval = c_parser_gimple_postfix_expression (parser); |
| if (trueval.value == error_mark_node |
| || falseval.value == error_mark_node) |
| return; |
| rhs.value = build3_loc (loc, COND_EXPR, TREE_TYPE (trueval.value), |
| rhs.value, trueval.value, falseval.value); |
| } |
| assign = gimple_build_assign (lhs.value, rhs.value); |
| gimple_seq_add_stmt (seq, assign); |
| gimple_set_location (assign, loc); |
| } |
| return; |
| } |
| |
| /* Parse gimple binary expr. |
| |
| gimple-binary-expression: |
| gimple-unary-expression * gimple-unary-expression |
| gimple-unary-expression / gimple-unary-expression |
| gimple-unary-expression % gimple-unary-expression |
| gimple-unary-expression + gimple-unary-expression |
| gimple-unary-expression - gimple-unary-expression |
| gimple-unary-expression << gimple-unary-expression |
| gimple-unary-expression >> gimple-unary-expression |
| gimple-unary-expression < gimple-unary-expression |
| gimple-unary-expression > gimple-unary-expression |
| gimple-unary-expression <= gimple-unary-expression |
| gimple-unary-expression >= gimple-unary-expression |
| gimple-unary-expression == gimple-unary-expression |
| gimple-unary-expression != gimple-unary-expression |
| gimple-unary-expression & gimple-unary-expression |
| gimple-unary-expression ^ gimple-unary-expression |
| gimple-unary-expression | gimple-unary-expression |
| |
| */ |
| |
| static c_expr |
| c_parser_gimple_binary_expression (c_parser *parser) |
| { |
| /* Location of the binary operator. */ |
| struct c_expr ret, lhs, rhs; |
| enum tree_code code = ERROR_MARK; |
| ret.set_error (); |
| lhs = c_parser_gimple_postfix_expression (parser); |
| if (c_parser_error (parser)) |
| return ret; |
| tree ret_type = TREE_TYPE (lhs.value); |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_MULT: |
| code = MULT_EXPR; |
| break; |
| case CPP_DIV: |
| code = TRUNC_DIV_EXPR; |
| break; |
| case CPP_MOD: |
| code = TRUNC_MOD_EXPR; |
| break; |
| case CPP_PLUS: |
| if (POINTER_TYPE_P (TREE_TYPE (lhs.value))) |
| code = POINTER_PLUS_EXPR; |
| else |
| code = PLUS_EXPR; |
| break; |
| case CPP_MINUS: |
| code = MINUS_EXPR; |
| break; |
| case CPP_LSHIFT: |
| code = LSHIFT_EXPR; |
| break; |
| case CPP_RSHIFT: |
| code = RSHIFT_EXPR; |
| break; |
| case CPP_LESS: |
| code = LT_EXPR; |
| ret_type = boolean_type_node; |
| break; |
| case CPP_GREATER: |
| code = GT_EXPR; |
| ret_type = boolean_type_node; |
| break; |
| case CPP_LESS_EQ: |
| code = LE_EXPR; |
| ret_type = boolean_type_node; |
| break; |
| case CPP_GREATER_EQ: |
| code = GE_EXPR; |
| ret_type = boolean_type_node; |
| break; |
| case CPP_EQ_EQ: |
| code = EQ_EXPR; |
| ret_type = boolean_type_node; |
| break; |
| case CPP_NOT_EQ: |
| code = NE_EXPR; |
| ret_type = boolean_type_node; |
| break; |
| case CPP_AND: |
| code = BIT_AND_EXPR; |
| break; |
| case CPP_XOR: |
| code = BIT_XOR_EXPR; |
| break; |
| case CPP_OR: |
| code = BIT_IOR_EXPR; |
| break; |
| case CPP_AND_AND: |
| c_parser_error (parser, "%<&&%> not valid in GIMPLE"); |
| return ret; |
| case CPP_OR_OR: |
| c_parser_error (parser, "%<||%> not valid in GIMPLE"); |
| return ret; |
| default: |
| /* Not a binary expression. */ |
| return lhs; |
| } |
| location_t ret_loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| rhs = c_parser_gimple_postfix_expression (parser); |
| if (lhs.value != error_mark_node && rhs.value != error_mark_node) |
| ret.value = build2_loc (ret_loc, code, ret_type, lhs.value, rhs.value); |
| return ret; |
| } |
| |
| /* Parse gimple unary expression. |
| |
| gimple-unary-expression: |
| gimple-postfix-expression |
| unary-operator gimple-postfix-expression |
| |
| unary-operator: one of |
| & * + - ~ abs_expr |
| */ |
| |
| static c_expr |
| c_parser_gimple_unary_expression (c_parser *parser) |
| { |
| struct c_expr ret, op; |
| location_t op_loc = c_parser_peek_token (parser)->location; |
| location_t finish; |
| ret.set_error (); |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_AND: |
| c_parser_consume_token (parser); |
| op = c_parser_gimple_postfix_expression (parser); |
| mark_exp_read (op.value); |
| return parser_build_unary_op (op_loc, ADDR_EXPR, op); |
| case CPP_MULT: |
| { |
| c_parser_consume_token (parser); |
| op = c_parser_gimple_postfix_expression (parser); |
| if (op.value == error_mark_node) |
| return ret; |
| if (! POINTER_TYPE_P (TREE_TYPE (op.value))) |
| { |
| error_at (op_loc, "expected pointer as argument of unary %<*%>"); |
| return ret; |
| } |
| finish = op.get_finish (); |
| location_t combined_loc = make_location (op_loc, op_loc, finish); |
| ret.value = build_simple_mem_ref_loc (combined_loc, op.value); |
| TREE_SIDE_EFFECTS (ret.value) |
| = TREE_THIS_VOLATILE (ret.value) |
| = TYPE_VOLATILE (TREE_TYPE (TREE_TYPE (op.value))); |
| ret.src_range.m_start = op_loc; |
| ret.src_range.m_finish = finish; |
| return ret; |
| } |
| case CPP_PLUS: |
| c_parser_consume_token (parser); |
| op = c_parser_gimple_postfix_expression (parser); |
| return parser_build_unary_op (op_loc, CONVERT_EXPR, op); |
| case CPP_MINUS: |
| c_parser_consume_token (parser); |
| op = c_parser_gimple_postfix_expression (parser); |
| return parser_build_unary_op (op_loc, NEGATE_EXPR, op); |
| case CPP_COMPL: |
| c_parser_consume_token (parser); |
| op = c_parser_gimple_postfix_expression (parser); |
| return parser_build_unary_op (op_loc, BIT_NOT_EXPR, op); |
| case CPP_NOT: |
| c_parser_error (parser, "%<!%> not valid in GIMPLE"); |
| return ret; |
| case CPP_KEYWORD: |
| switch (c_parser_peek_token (parser)->keyword) |
| { |
| case RID_REALPART: |
| c_parser_consume_token (parser); |
| op = c_parser_gimple_postfix_expression (parser); |
| return parser_build_unary_op (op_loc, REALPART_EXPR, op); |
| case RID_IMAGPART: |
| c_parser_consume_token (parser); |
| op = c_parser_gimple_postfix_expression (parser); |
| return parser_build_unary_op (op_loc, IMAGPART_EXPR, op); |
| default: |
| return c_parser_gimple_postfix_expression (parser); |
| } |
| case CPP_NAME: |
| { |
| tree id = c_parser_peek_token (parser)->value; |
| if (strcmp (IDENTIFIER_POINTER (id), "__ABS") == 0) |
| { |
| c_parser_consume_token (parser); |
| op = c_parser_gimple_postfix_expression (parser); |
| return parser_build_unary_op (op_loc, ABS_EXPR, op); |
| } |
| else |
| return c_parser_gimple_postfix_expression (parser); |
| } |
| default: |
| return c_parser_gimple_postfix_expression (parser); |
| } |
| } |
| |
| /* Decompose ID into base name (ID until ver_offset) and VERSION. Return |
| true if ID matches a SSA name. */ |
| |
| static bool |
| c_parser_parse_ssa_name_id (tree id, unsigned *version, unsigned *ver_offset) |
| { |
| const char *token = IDENTIFIER_POINTER (id); |
| const char *var_version = strrchr (token, '_'); |
| if (! var_version) |
| return false; |
| |
| *ver_offset = var_version - token; |
| for (const char *p = var_version + 1; *p; ++p) |
| if (! ISDIGIT (*p)) |
| return false; |
| *version = atoi (var_version + 1); |
| return *version > 0; |
| } |
| |
| /* Get at the actual SSA name ID with VERSION starting at VER_OFFSET. |
| TYPE is the type if the SSA name is being declared. */ |
| |
| static tree |
| c_parser_parse_ssa_name (c_parser *parser, |
| tree id, tree type, unsigned version, |
| unsigned ver_offset) |
| { |
| tree name = NULL_TREE; |
| const char *token = IDENTIFIER_POINTER (id); |
| |
| if (ver_offset == 0) |
| { |
| /* Anonymous unnamed SSA name. */ |
| if (version < num_ssa_names) |
| name = ssa_name (version); |
| if (! name) |
| { |
| if (! type) |
| { |
| c_parser_error (parser, "SSA name undeclared"); |
| return error_mark_node; |
| } |
| name = make_ssa_name_fn (cfun, type, NULL, version); |
| } |
| } |
| else |
| { |
| if (version < num_ssa_names) |
| name = ssa_name (version); |
| if (! name) |
| { |
| /* Separate var name from version. */ |
| char *var_name = XNEWVEC (char, ver_offset + 1); |
| memcpy (var_name, token, ver_offset); |
| var_name[ver_offset] = '\0'; |
| /* lookup for parent decl. */ |
| id = get_identifier (var_name); |
| tree parent = lookup_name (id); |
| XDELETEVEC (var_name); |
| if (! parent || parent == error_mark_node) |
| { |
| c_parser_error (parser, "base variable or SSA name undeclared"); |
| return error_mark_node; |
| } |
| if (!(VAR_P (parent) |
| || TREE_CODE (parent) == PARM_DECL |
| || TREE_CODE (parent) == RESULT_DECL)) |
| { |
| error ("invalid base %qE for SSA name", parent); |
| return error_mark_node; |
| } |
| if (VECTOR_TYPE_P (TREE_TYPE (parent)) |
| || TREE_CODE (TREE_TYPE (parent)) == COMPLEX_TYPE) |
| DECL_GIMPLE_REG_P (parent) = 1; |
| name = make_ssa_name_fn (cfun, parent, |
| gimple_build_nop (), version); |
| } |
| } |
| |
| return name; |
| } |
| |
| /* Parse gimple postfix expression. |
| |
| gimple-postfix-expression: |
| gimple-primary-expression |
| gimple-primary-xpression [ gimple-primary-expression ] |
| gimple-primary-expression ( gimple-argument-expression-list[opt] ) |
| postfix-expression . identifier |
| postfix-expression -> identifier |
| |
| gimple-argument-expression-list: |
| gimple-unary-expression |
| gimple-argument-expression-list , gimple-unary-expression |
| |
| gimple-primary-expression: |
| identifier |
| constant |
| string-literal |
| |
| */ |
| |
| static struct c_expr |
| c_parser_gimple_postfix_expression (c_parser *parser) |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| source_range tok_range = c_parser_peek_token (parser)->get_range (); |
| struct c_expr expr; |
| expr.set_error (); |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_NUMBER: |
| expr.value = c_parser_peek_token (parser)->value; |
| set_c_expr_source_range (&expr, tok_range); |
| loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| break; |
| case CPP_CHAR: |
| case CPP_CHAR16: |
| case CPP_CHAR32: |
| case CPP_WCHAR: |
| expr.value = c_parser_peek_token (parser)->value; |
| set_c_expr_source_range (&expr, tok_range); |
| c_parser_consume_token (parser); |
| break; |
| case CPP_STRING: |
| case CPP_STRING16: |
| case CPP_STRING32: |
| case CPP_WSTRING: |
| case CPP_UTF8STRING: |
| expr.value = c_parser_peek_token (parser)->value; |
| set_c_expr_source_range (&expr, tok_range); |
| expr.original_code = STRING_CST; |
| c_parser_consume_token (parser); |
| break; |
| case CPP_NAME: |
| if (c_parser_peek_token (parser)->id_kind == C_ID_ID) |
| { |
| tree id = c_parser_peek_token (parser)->value; |
| if (strcmp (IDENTIFIER_POINTER (id), "__MEM") == 0) |
| { |
| /* __MEM '<' type-name [ ',' number ] '>' |
| '(' [ '(' type-name ')' ] unary-expression |
| [ '+' number ] ')' */ |
| location_t loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| struct c_type_name *type_name = NULL; |
| tree alignment = NULL_TREE; |
| if (c_parser_require (parser, CPP_LESS, "expected %<<%>")) |
| { |
| type_name = c_parser_type_name (parser); |
| /* Optional alignment. */ |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| { |
| c_parser_consume_token (parser); |
| alignment |
| = c_parser_gimple_postfix_expression (parser).value; |
| } |
| c_parser_skip_until_found (parser, |
| CPP_GREATER, "expected %<>%>"); |
| } |
| struct c_expr ptr; |
| ptr.value = error_mark_node; |
| tree alias_off = NULL_TREE; |
| if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| tree alias_type = NULL_TREE; |
| /* Optional alias-type cast. */ |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) |
| { |
| c_parser_consume_token (parser); |
| struct c_type_name *alias_type_name |
| = c_parser_type_name (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| if (alias_type_name) |
| { |
| tree tem; |
| alias_type = groktypename (alias_type_name, |
| &tem, NULL); |
| } |
| } |
| ptr = c_parser_gimple_unary_expression (parser); |
| if (ptr.value == error_mark_node |
| || ! POINTER_TYPE_P (TREE_TYPE (ptr.value))) |
| { |
| if (ptr.value != error_mark_node) |
| error_at (ptr.get_start (), |
| "invalid type of %<__MEM%> operand"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| return expr; |
| } |
| if (! alias_type) |
| alias_type = TREE_TYPE (ptr.value); |
| /* Optional constant offset. */ |
| if (c_parser_next_token_is (parser, CPP_PLUS)) |
| { |
| c_parser_consume_token (parser); |
| alias_off |
| = c_parser_gimple_postfix_expression (parser).value; |
| alias_off = fold_convert (alias_type, alias_off); |
| } |
| if (! alias_off) |
| alias_off = build_int_cst (alias_type, 0); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| } |
| if (! type_name || c_parser_error (parser)) |
| { |
| c_parser_set_error (parser, false); |
| return expr; |
| } |
| tree tem = NULL_TREE; |
| tree type = groktypename (type_name, &tem, NULL); |
| if (alignment) |
| type = build_aligned_type (type, tree_to_uhwi (alignment)); |
| expr.value = build2_loc (loc, MEM_REF, |
| type, ptr.value, alias_off); |
| break; |
| } |
| else if (strcmp (IDENTIFIER_POINTER (id), "_Literal") == 0) |
| { |
| /* _Literal '(' type-name ')' [ '-' ] constant */ |
| c_parser_consume_token (parser); |
| tree type = NULL_TREE; |
| if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| struct c_type_name *type_name = c_parser_type_name (parser); |
| tree tem; |
| if (type_name) |
| type = groktypename (type_name, &tem, NULL); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| } |
| bool neg_p; |
| if ((neg_p = c_parser_next_token_is (parser, CPP_MINUS))) |
| c_parser_consume_token (parser); |
| tree val = c_parser_gimple_postfix_expression (parser).value; |
| if (! type |
| || ! val |
| || val == error_mark_node |
| || ! CONSTANT_CLASS_P (val)) |
| { |
| c_parser_error (parser, "invalid _Literal"); |
| return expr; |
| } |
| if (neg_p) |
| { |
| val = const_unop (NEGATE_EXPR, TREE_TYPE (val), val); |
| if (! val) |
| { |
| c_parser_error (parser, "invalid _Literal"); |
| return expr; |
| } |
| } |
| expr.value = fold_convert (type, val); |
| return expr; |
| } |
| else if (strcmp (IDENTIFIER_POINTER (id), "__FMA") == 0) |
| { |
| c_parser_consume_token (parser); |
| auto_vec<tree> args; |
| |
| if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| c_parser_gimple_expr_list (parser, &args); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| } |
| if (args.length () != 3) |
| { |
| error_at (loc, "invalid number of operands to __FMA"); |
| expr.value = error_mark_node; |
| return expr; |
| } |
| expr.value = build3_loc (loc, FMA_EXPR, TREE_TYPE (args[0]), |
| args[0], args[1], args[2]); |
| return expr; |
| } |
| |
| /* SSA name. */ |
| unsigned version, ver_offset; |
| if (! lookup_name (id) |
| && c_parser_parse_ssa_name_id (id, &version, &ver_offset)) |
| { |
| c_parser_consume_token (parser); |
| expr.value = c_parser_parse_ssa_name (parser, id, NULL_TREE, |
| version, ver_offset); |
| if (expr.value == error_mark_node) |
| return expr; |
| set_c_expr_source_range (&expr, tok_range); |
| /* For default definition SSA names. */ |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN) |
| && c_parser_peek_2nd_token (parser)->type == CPP_NAME |
| && strcmp ("D", |
| IDENTIFIER_POINTER |
| (c_parser_peek_2nd_token (parser)->value)) == 0 |
| && c_parser_peek_nth_token (parser, 3)->type == CPP_CLOSE_PAREN) |
| { |
| c_parser_consume_token (parser); |
| c_parser_consume_token (parser); |
| c_parser_consume_token (parser); |
| if (! SSA_NAME_IS_DEFAULT_DEF (expr.value)) |
| { |
| if (!SSA_NAME_VAR (expr.value)) |
| { |
| error_at (loc, "anonymous SSA name cannot have" |
| " default definition"); |
| expr.value = error_mark_node; |
| return expr; |
| } |
| set_ssa_default_def (cfun, SSA_NAME_VAR (expr.value), |
| expr.value); |
| SSA_NAME_DEF_STMT (expr.value) = gimple_build_nop (); |
| } |
| } |
| } |
| else |
| { |
| c_parser_consume_token (parser); |
| expr.value |
| = build_external_ref (loc, id, |
| (c_parser_peek_token (parser)->type |
| == CPP_OPEN_PAREN), &expr.original_type); |
| set_c_expr_source_range (&expr, tok_range); |
| } |
| break; |
| } |
| else |
| { |
| c_parser_error (parser, "expected expression"); |
| expr.set_error (); |
| break; |
| } |
| break; |
| default: |
| c_parser_error (parser, "expected expression"); |
| expr.set_error (); |
| break; |
| } |
| return c_parser_gimple_postfix_expression_after_primary |
| (parser, EXPR_LOC_OR_LOC (expr.value, loc), expr); |
| } |
| |
| /* Parse a gimple postfix expression after the initial primary or compound |
| literal. */ |
| |
| static struct c_expr |
| c_parser_gimple_postfix_expression_after_primary (c_parser *parser, |
| location_t expr_loc, |
| struct c_expr expr) |
| { |
| location_t start; |
| location_t finish; |
| tree ident; |
| location_t comp_loc; |
| |
| while (true) |
| { |
| location_t op_loc = c_parser_peek_token (parser)->location; |
| switch (c_parser_peek_token (parser)->type) |
| { |
| case CPP_OPEN_SQUARE: |
| { |
| c_parser_consume_token (parser); |
| tree idx = c_parser_gimple_unary_expression (parser).value; |
| |
| if (! c_parser_require (parser, CPP_CLOSE_SQUARE, "expected %<]%>")) |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_SQUARE, NULL); |
| break; |
| } |
| |
| start = expr.get_start (); |
| finish = c_parser_tokens_buf (parser, 0)->location; |
| expr.value = build_array_ref (op_loc, expr.value, idx); |
| set_c_expr_source_range (&expr, start, finish); |
| |
| expr.original_code = ERROR_MARK; |
| expr.original_type = NULL; |
| break; |
| } |
| case CPP_OPEN_PAREN: |
| { |
| /* Function call. */ |
| c_parser_consume_token (parser); |
| auto_vec<tree> exprlist; |
| if (! c_parser_next_token_is (parser, CPP_CLOSE_PAREN)) |
| c_parser_gimple_expr_list (parser, &exprlist); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| expr.value = build_call_array_loc |
| (expr_loc, TREE_TYPE (TREE_TYPE (expr.value)), |
| expr.value, exprlist.length (), exprlist.address ()); |
| expr.original_code = ERROR_MARK; |
| expr.original_type = NULL; |
| break; |
| } |
| case CPP_DOT: |
| { |
| /* Structure element reference. */ |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| c_token *comp_tok = c_parser_peek_token (parser); |
| ident = comp_tok->value; |
| comp_loc = comp_tok->location; |
| } |
| else |
| { |
| c_parser_error (parser, "expected identifier"); |
| expr.set_error (); |
| expr.original_code = ERROR_MARK; |
| expr.original_type = NULL; |
| return expr; |
| } |
| start = expr.get_start (); |
| finish = c_parser_peek_token (parser)->get_finish (); |
| c_parser_consume_token (parser); |
| expr.value = build_component_ref (op_loc, expr.value, ident, |
| comp_loc); |
| set_c_expr_source_range (&expr, start, finish); |
| expr.original_code = ERROR_MARK; |
| if (TREE_CODE (expr.value) != COMPONENT_REF) |
| expr.original_type = NULL; |
| else |
| { |
| /* Remember the original type of a bitfield. */ |
| tree field = TREE_OPERAND (expr.value, 1); |
| if (TREE_CODE (field) != FIELD_DECL) |
| expr.original_type = NULL; |
| else |
| expr.original_type = DECL_BIT_FIELD_TYPE (field); |
| } |
| break; |
| } |
| case CPP_DEREF: |
| { |
| /* Structure element reference. */ |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| c_token *comp_tok = c_parser_peek_token (parser); |
| ident = comp_tok->value; |
| comp_loc = comp_tok->location; |
| } |
| else |
| { |
| c_parser_error (parser, "expected identifier"); |
| expr.set_error (); |
| expr.original_code = ERROR_MARK; |
| expr.original_type = NULL; |
| return expr; |
| } |
| start = expr.get_start (); |
| finish = c_parser_peek_token (parser)->get_finish (); |
| c_parser_consume_token (parser); |
| expr.value = build_component_ref (op_loc, |
| build_simple_mem_ref_loc |
| (op_loc, expr.value), |
| ident, comp_loc); |
| set_c_expr_source_range (&expr, start, finish); |
| expr.original_code = ERROR_MARK; |
| if (TREE_CODE (expr.value) != COMPONENT_REF) |
| expr.original_type = NULL; |
| else |
| { |
| /* Remember the original type of a bitfield. */ |
| tree field = TREE_OPERAND (expr.value, 1); |
| if (TREE_CODE (field) != FIELD_DECL) |
| expr.original_type = NULL; |
| else |
| expr.original_type = DECL_BIT_FIELD_TYPE (field); |
| } |
| break; |
| } |
| default: |
| return expr; |
| } |
| } |
| } |
| |
| /* Parse expression list. |
| |
| gimple-expr-list: |
| gimple-unary-expression |
| gimple-expr-list , gimple-unary-expression |
| |
| */ |
| |
| static void |
| c_parser_gimple_expr_list (c_parser *parser, vec<tree> *ret) |
| { |
| struct c_expr expr; |
| |
| expr = c_parser_gimple_unary_expression (parser); |
| ret->safe_push (expr.value); |
| while (c_parser_next_token_is (parser, CPP_COMMA)) |
| { |
| c_parser_consume_token (parser); |
| expr = c_parser_gimple_unary_expression (parser); |
| ret->safe_push (expr.value); |
| } |
| } |
| |
| /* Parse gimple label. |
| |
| gimple-label: |
| identifier : |
| case constant-expression : |
| default : |
| |
| */ |
| |
| static void |
| c_parser_gimple_label (c_parser *parser, gimple_seq *seq) |
| { |
| tree name = c_parser_peek_token (parser)->value; |
| location_t loc1 = c_parser_peek_token (parser)->location; |
| gcc_assert (c_parser_next_token_is (parser, CPP_NAME)); |
| c_parser_consume_token (parser); |
| gcc_assert (c_parser_next_token_is (parser, CPP_COLON)); |
| c_parser_consume_token (parser); |
| tree label = define_label (loc1, name); |
| gimple_seq_add_stmt (seq, gimple_build_label (label)); |
| return; |
| } |
| |
| /* Parse gimple/RTL pass list. |
| |
| gimple-or-rtl-pass-list: |
| startwith("pass-name") |
| */ |
| |
| char * |
| c_parser_gimple_or_rtl_pass_list (c_parser *parser) |
| { |
| char *pass = NULL; |
| |
| /* Accept __GIMPLE/__RTL. */ |
| if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN)) |
| return NULL; |
| c_parser_consume_token (parser); |
| |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| const char *op = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); |
| c_parser_consume_token (parser); |
| if (! strcmp (op, "startwith")) |
| { |
| if (! c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| return NULL; |
| if (c_parser_next_token_is_not (parser, CPP_STRING)) |
| { |
| error_at (c_parser_peek_token (parser)->location, |
| "expected pass name"); |
| return NULL; |
| } |
| pass = xstrdup (TREE_STRING_POINTER |
| (c_parser_peek_token (parser)->value)); |
| c_parser_consume_token (parser); |
| if (! c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) |
| return NULL; |
| } |
| else |
| { |
| error_at (c_parser_peek_token (parser)->location, |
| "invalid operation"); |
| return NULL; |
| } |
| } |
| |
| if (! c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) |
| return NULL; |
| |
| return pass; |
| } |
| |
| /* Parse gimple local declaration. |
| |
| declaration-specifiers: |
| storage-class-specifier declaration-specifiers[opt] |
| type-specifier declaration-specifiers[opt] |
| type-qualifier declaration-specifiers[opt] |
| function-specifier declaration-specifiers[opt] |
| alignment-specifier declaration-specifiers[opt] |
| |
| storage-class-specifier: |
| typedef |
| extern |
| static |
| auto |
| register |
| |
| type-specifier: |
| void |
| char |
| short |
| int |
| long |
| float |
| double |
| signed |
| unsigned |
| _Bool |
| _Complex |
| |
| type-qualifier: |
| const |
| restrict |
| volatile |
| address-space-qualifier |
| _Atomic |
| |
| */ |
| |
| static void |
| c_parser_gimple_declaration (c_parser *parser) |
| { |
| struct c_declarator *declarator; |
| struct c_declspecs *specs = build_null_declspecs (); |
| c_parser_declspecs (parser, specs, true, true, true, |
| true, true, cla_nonabstract_decl); |
| finish_declspecs (specs); |
| |
| /* Provide better error recovery. Note that a type name here is usually |
| better diagnosed as a redeclaration. */ |
| if (c_parser_next_token_starts_declspecs (parser) |
| && ! c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected %<;%>"); |
| c_parser_set_error (parser, false); |
| return; |
| } |
| |
| bool dummy = false; |
| declarator = c_parser_declarator (parser, |
| specs->typespec_kind != ctsk_none, |
| C_DTR_NORMAL, &dummy); |
| |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| /* Handle SSA name decls specially, they do not go into the identifier |
| table but we simply build the SSA name for later lookup. */ |
| unsigned version, ver_offset; |
| if (declarator->kind == cdk_id |
| && is_gimple_reg_type (specs->type) |
| && c_parser_parse_ssa_name_id (declarator->u.id, |
| &version, &ver_offset) |
| /* The following restricts it to unnamed anonymous SSA names |
| which fails parsing of named ones in dumps (we could |
| decide to not dump their name for -gimple). */ |
| && ver_offset == 0) |
| c_parser_parse_ssa_name (parser, declarator->u.id, specs->type, |
| version, ver_offset); |
| else |
| { |
| tree postfix_attrs = NULL_TREE; |
| tree all_prefix_attrs = specs->attrs; |
| specs->attrs = NULL; |
| tree decl = start_decl (declarator, specs, false, |
| chainon (postfix_attrs, all_prefix_attrs)); |
| if (decl) |
| finish_decl (decl, UNKNOWN_LOCATION, NULL_TREE, NULL_TREE, |
| NULL_TREE); |
| } |
| } |
| else |
| { |
| c_parser_error (parser, "expected %<;%>"); |
| return; |
| } |
| } |
| |
| /* Parse gimple goto statement. */ |
| |
| static void |
| c_parser_gimple_goto_stmt (location_t loc, tree label, gimple_seq *seq) |
| { |
| tree decl = lookup_label_for_goto (loc, label); |
| gimple_seq_add_stmt (seq, gimple_build_goto (decl)); |
| return; |
| } |
| |
| /* Parse a parenthesized condition. |
| gimple-condition: |
| ( gimple-binary-expression ) */ |
| |
| static tree |
| c_parser_gimple_paren_condition (c_parser *parser) |
| { |
| if (! c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| return error_mark_node; |
| tree cond = c_parser_gimple_binary_expression (parser).value; |
| if (! c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) |
| return error_mark_node; |
| return cond; |
| } |
| |
| /* Parse gimple if-else statement. |
| |
| if-statement: |
| if ( gimple-binary-expression ) gimple-goto-statement |
| if ( gimple-binary-expression ) gimple-goto-statement \ |
| else gimple-goto-statement |
| */ |
| |
| static void |
| c_parser_gimple_if_stmt (c_parser *parser, gimple_seq *seq) |
| { |
| tree t_label, f_label, label; |
| location_t loc; |
| c_parser_consume_token (parser); |
| tree cond = c_parser_gimple_paren_condition (parser); |
| |
| if (c_parser_next_token_is_keyword (parser, RID_GOTO)) |
| { |
| loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| if (! c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected label"); |
| return; |
| } |
| label = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| t_label = lookup_label_for_goto (loc, label); |
| if (! c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>")) |
| return; |
| } |
| else |
| { |
| c_parser_error (parser, "expected goto expression"); |
| return; |
| } |
| |
| if (c_parser_next_token_is_keyword (parser, RID_ELSE)) |
| c_parser_consume_token (parser); |
| else |
| { |
| c_parser_error (parser, "expected else statement"); |
| return; |
| } |
| |
| if (c_parser_next_token_is_keyword (parser, RID_GOTO)) |
| { |
| loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| if (! c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected label"); |
| return; |
| } |
| label = c_parser_peek_token (parser)->value; |
| f_label = lookup_label_for_goto (loc, label); |
| c_parser_consume_token (parser); |
| if (! c_parser_require (parser, CPP_SEMICOLON, "expected %<;%>")) |
| return; |
| } |
| else |
| { |
| c_parser_error (parser, "expected goto expression"); |
| return; |
| } |
| |
| if (cond != error_mark_node) |
| gimple_seq_add_stmt (seq, gimple_build_cond_from_tree (cond, t_label, |
| f_label)); |
| } |
| |
| /* Parse gimple switch-statement. |
| |
| gimple-switch-statement: |
| switch (gimple-postfix-expression) gimple-case-statement |
| |
| gimple-case-statement: |
| gimple-case-statement |
| gimple-label-statement : gimple-goto-statment |
| */ |
| |
| static void |
| c_parser_gimple_switch_stmt (c_parser *parser, gimple_seq *seq) |
| { |
| c_expr cond_expr; |
| tree case_label, label; |
| auto_vec<tree> labels; |
| tree default_label = NULL_TREE; |
| gimple_seq switch_body = NULL; |
| c_parser_consume_token (parser); |
| |
| if (! c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| return; |
| cond_expr = c_parser_gimple_postfix_expression (parser); |
| if (! c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) |
| return; |
| |
| if (! c_parser_require (parser, CPP_OPEN_BRACE, "expected %<{%>")) |
| return; |
| |
| while (c_parser_next_token_is_not (parser, CPP_CLOSE_BRACE)) |
| { |
| if (c_parser_next_token_is (parser, CPP_EOF)) |
| { |
| c_parser_error (parser, "expected statement"); |
| return; |
| } |
| |
| switch (c_parser_peek_token (parser)->keyword) |
| { |
| case RID_CASE: |
| { |
| c_expr exp1; |
| location_t loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| |
| if (c_parser_next_token_is (parser, CPP_NAME) |
| || c_parser_peek_token (parser)->type == CPP_NUMBER) |
| exp1 = c_parser_gimple_postfix_expression (parser); |
| else |
| { |
| c_parser_error (parser, "expected expression"); |
| return; |
| } |
| |
| if (c_parser_next_token_is (parser, CPP_COLON)) |
| { |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| label = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| tree decl = lookup_label_for_goto (loc, label); |
| case_label = build_case_label (exp1.value, NULL_TREE, |
| decl); |
| labels.safe_push (case_label); |
| if (! c_parser_require (parser, CPP_SEMICOLON, |
| "expected %<;%>")) |
| return; |
| } |
| else if (! c_parser_require (parser, CPP_NAME, |
| "expected label")) |
| return; |
| } |
| else if (! c_parser_require (parser, CPP_SEMICOLON, |
| "expected %<:%>")) |
| return; |
| break; |
| } |
| case RID_DEFAULT: |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_COLON)) |
| { |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| label = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| tree decl = lookup_label_for_goto (loc, label); |
| default_label = build_case_label (NULL_TREE, NULL_TREE, |
| decl); |
| if (! c_parser_require (parser, CPP_SEMICOLON, |
| "expected %<;%>")) |
| return; |
| } |
| else if (! c_parser_require (parser, CPP_NAME, |
| "expected label")) |
| return; |
| } |
| else if (! c_parser_require (parser, CPP_SEMICOLON, |
| "expected %<:%>")) |
| return; |
| break; |
| } |
| case RID_GOTO: |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| c_parser_gimple_goto_stmt (loc, |
| c_parser_peek_token |
| (parser)->value, |
| &switch_body); |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| c_parser_consume_token (parser); |
| else |
| { |
| c_parser_error (parser, "expected semicolon"); |
| return; |
| } |
| } |
| else if (! c_parser_require (parser, CPP_NAME, |
| "expected label")) |
| return; |
| break; |
| } |
| default: |
| c_parser_error (parser, "expected case label or goto statement"); |
| return; |
| } |
| |
| } |
| if (! c_parser_require (parser, CPP_CLOSE_BRACE, "expected %<}%>")) |
| return; |
| |
| if (cond_expr.value != error_mark_node) |
| { |
| gimple_seq_add_stmt (seq, gimple_build_switch (cond_expr.value, |
| default_label, labels)); |
| gimple_seq_add_seq (seq, switch_body); |
| } |
| } |
| |
| /* Parse gimple return statement. */ |
| |
| static void |
| c_parser_gimple_return_stmt (c_parser *parser, gimple_seq *seq) |
| { |
| location_t loc = c_parser_peek_token (parser)->location; |
| gimple *ret = NULL; |
| c_parser_consume_token (parser); |
| if (c_parser_next_token_is (parser, CPP_SEMICOLON)) |
| { |
| c_finish_gimple_return (loc, NULL_TREE); |
| ret = gimple_build_return (NULL); |
| gimple_seq_add_stmt (seq, ret); |
| } |
| else |
| { |
| location_t xloc = c_parser_peek_token (parser)->location; |
| c_expr expr = c_parser_gimple_unary_expression (parser); |
| if (expr.value != error_mark_node) |
| { |
| c_finish_gimple_return (xloc, expr.value); |
| ret = gimple_build_return (expr.value); |
| gimple_seq_add_stmt (seq, ret); |
| } |
| } |
| } |
| |
| /* Support function for c_parser_gimple_return_stmt. */ |
| |
| static void |
| c_finish_gimple_return (location_t loc, tree retval) |
| { |
| tree valtype = TREE_TYPE (TREE_TYPE (current_function_decl)); |
| |
| /* Use the expansion point to handle cases such as returning NULL |
| in a function returning void. */ |
| source_location xloc = expansion_point_location_if_in_system_header (loc); |
| |
| if (TREE_THIS_VOLATILE (current_function_decl)) |
| warning_at (xloc, 0, |
| "function declared %<noreturn%> has a %<return%> statement"); |
| |
| if (! retval) |
| current_function_returns_null = 1; |
| else if (valtype == 0 || TREE_CODE (valtype) == VOID_TYPE) |
| { |
| current_function_returns_null = 1; |
| if (TREE_CODE (TREE_TYPE (retval)) != VOID_TYPE) |
| { |
| error_at |
| (xloc, "%<return%> with a value, in function returning void"); |
| inform (DECL_SOURCE_LOCATION (current_function_decl), |
| "declared here"); |
| } |
| } |
| else if (TREE_CODE (valtype) != TREE_CODE (TREE_TYPE (retval))) |
| { |
| error_at |
| (xloc, "invalid conversion in return statement"); |
| inform (DECL_SOURCE_LOCATION (current_function_decl), |
| "declared here"); |
| } |
| return; |
| } |