| /* Parser for GIMPLE. |
| Copyright (C) 2016-2022 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" |
| #include "internal-fn.h" |
| #include "cfg.h" |
| #include "cfghooks.h" |
| #include "bitmap.h" |
| #include "cfganal.h" |
| #include "tree-cfg.h" |
| #include "gimple-iterator.h" |
| #include "cfgloop.h" |
| #include "tree-phinodes.h" |
| #include "tree-into-ssa.h" |
| |
| |
| /* GIMPLE parser state. */ |
| |
| class gimple_parser |
| { |
| public: |
| gimple_parser (c_parser *p) : parser (p), edges(), current_bb(NULL) {} |
| /* c_parser is not visible here, use composition and fake inheritance |
| via a conversion operator. */ |
| operator c_parser *() { return parser; } |
| c_parser *parser; |
| |
| /* CFG build state. */ |
| class gimple_parser_edge |
| { |
| public: |
| int src; |
| int dest; |
| int flags; |
| profile_probability probability; |
| }; |
| auto_vec<gimple_parser_edge> edges; |
| basic_block current_bb; |
| |
| void push_edge (int, int, int, profile_probability); |
| }; |
| |
| void |
| gimple_parser::push_edge (int src, int dest, int flags, |
| profile_probability prob) |
| { |
| gimple_parser_edge e; |
| e.src = src; |
| e.dest = dest; |
| e.flags = flags; |
| e.probability = prob; |
| edges.safe_push (e); |
| } |
| |
| |
| /* Gimple parsing functions. */ |
| static bool c_parser_gimple_compound_statement (gimple_parser &, gimple_seq *); |
| static void c_parser_gimple_label (gimple_parser &, gimple_seq *); |
| static void c_parser_gimple_statement (gimple_parser &, gimple_seq *); |
| static struct c_expr c_parser_gimple_binary_expression (gimple_parser &); |
| static struct c_expr c_parser_gimple_unary_expression (gimple_parser &); |
| static struct c_expr c_parser_gimple_postfix_expression (gimple_parser &); |
| static struct c_expr c_parser_gimple_postfix_expression_after_primary |
| (gimple_parser &, location_t, struct c_expr); |
| static void c_parser_gimple_declaration (gimple_parser &); |
| static void c_parser_gimple_goto_stmt (gimple_parser &, location_t, |
| tree, gimple_seq *); |
| static void c_parser_gimple_try_stmt (gimple_parser &, gimple_seq *); |
| static void c_parser_gimple_if_stmt (gimple_parser &, gimple_seq *); |
| static void c_parser_gimple_switch_stmt (gimple_parser &, gimple_seq *); |
| static void c_parser_gimple_return_stmt (gimple_parser &, gimple_seq *); |
| static void c_finish_gimple_return (location_t, tree); |
| static tree c_parser_gimple_paren_condition (gimple_parser &); |
| static void c_parser_gimple_expr_list (gimple_parser &, vec<tree> *); |
| |
| |
| /* See if VAL is an identifier matching __BB<num> and return <num> |
| in *INDEX. */ |
| |
| static bool |
| c_parser_gimple_parse_bb_spec (tree val, int *index) |
| { |
| if (!startswith (IDENTIFIER_POINTER (val), "__BB")) |
| return false; |
| for (const char *p = IDENTIFIER_POINTER (val) + 4; *p; ++p) |
| if (!ISDIGIT (*p)) |
| return false; |
| *index = atoi (IDENTIFIER_POINTER (val) + 4); |
| return *index > 0; |
| } |
| |
| /* See if VAL is an identifier matching __BB<num> and return <num> |
| in *INDEX. Return true if so and parse also FREQUENCY of |
| the edge. */ |
| |
| |
| static bool |
| c_parser_gimple_parse_bb_spec_edge_probability (tree val, |
| gimple_parser &parser, |
| int *index, |
| profile_probability |
| *probability) |
| { |
| bool return_p = c_parser_gimple_parse_bb_spec (val, index); |
| if (return_p) |
| { |
| *probability = profile_probability::uninitialized (); |
| /* Parse frequency if provided. */ |
| if (c_parser_next_token_is (parser, CPP_OPEN_PAREN)) |
| { |
| tree f; |
| c_parser_consume_token (parser); |
| if (!c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected frequency quality"); |
| return false; |
| } |
| |
| profile_quality quality; |
| const char *v |
| = IDENTIFIER_POINTER (c_parser_peek_token (parser)->value); |
| if (!parse_profile_quality (v, &quality)) |
| { |
| c_parser_error (parser, "unknown profile quality"); |
| return false; |
| } |
| |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| return false; |
| |
| if (!c_parser_next_token_is (parser, CPP_NUMBER) |
| || (TREE_CODE (f = c_parser_peek_token (parser)->value) |
| != INTEGER_CST)) |
| { |
| c_parser_error (parser, "expected frequency value"); |
| return false; |
| } |
| |
| unsigned int value = TREE_INT_CST_LOW (f); |
| *probability = profile_probability (value, quality); |
| |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) |
| return false; |
| |
| if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| return false; |
| |
| } |
| |
| /* Parse the body of a function declaration marked with "__GIMPLE". */ |
| |
| void |
| c_parser_parse_gimple_body (c_parser *cparser, char *gimple_pass, |
| enum c_declspec_il cdil, |
| profile_count entry_bb_count) |
| { |
| gimple_parser parser (cparser); |
| gimple_seq seq = NULL; |
| gimple_seq body = NULL; |
| tree stmt = push_stmt_list (); |
| push_scope (); |
| location_t loc1 = c_parser_peek_token (parser)->location; |
| |
| cfun->pass_startwith = gimple_pass; |
| init_tree_ssa (cfun); |
| |
| if (cdil == cdil_gimple) |
| /* 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; |
| else |
| { |
| /* We have at least cdil_gimple_cfg. */ |
| gimple_register_cfg_hooks (); |
| init_empty_tree_cfg (); |
| parser.current_bb = ENTRY_BLOCK_PTR_FOR_FN (cfun); |
| /* Initialize the bare loop structure - we are going to only |
| mark headers and leave the rest to fixup. */ |
| set_loops_for_fn (cfun, ggc_cleared_alloc<struct loops> ()); |
| init_loops_structure (cfun, loops_for_fn (cfun), 1); |
| loops_state_set (cfun, LOOPS_NEED_FIXUP|LOOPS_MAY_HAVE_MULTIPLE_LATCHES); |
| cfun->curr_properties |
| |= PROP_gimple_lcf | PROP_gimple_leh | PROP_cfg | PROP_loops; |
| if (cdil == cdil_gimple_ssa) |
| { |
| init_ssa_operands (cfun); |
| cfun->curr_properties |= PROP_ssa; |
| } |
| } |
| |
| if (! c_parser_gimple_compound_statement (parser, &seq) |
| && cdil == cdil_gimple) |
| { |
| gimple *ret = gimple_build_return (NULL); |
| gimple_seq_add_stmt_without_update (&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; |
| |
| if (cdil == cdil_gimple) |
| { |
| 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_without_update (&body, bind_stmt); |
| gimple_set_body (current_function_decl, body); |
| } |
| else |
| { |
| /* Control-flow and binds are lowered, record local decls. */ |
| for (tree var = BIND_EXPR_VARS (stmt); var; var = DECL_CHAIN (var)) |
| if (VAR_P (var) |
| && !DECL_EXTERNAL (var)) |
| add_local_decl (cfun, var); |
| /* We have a CFG. Build the edges. */ |
| for (unsigned i = 0; i < parser.edges.length (); ++i) |
| { |
| edge e = make_edge (BASIC_BLOCK_FOR_FN (cfun, parser.edges[i].src), |
| BASIC_BLOCK_FOR_FN (cfun, parser.edges[i].dest), |
| parser.edges[i].flags); |
| e->probability = parser.edges[i].probability; |
| } |
| /* Add edges for case labels. */ |
| basic_block bb; |
| FOR_EACH_BB_FN (bb, cfun) |
| if (EDGE_COUNT (bb->succs) == 0) |
| { |
| gimple *last = last_stmt (bb); |
| if (gswitch *sw = safe_dyn_cast <gswitch *> (last)) |
| for (unsigned i = 0; i < gimple_switch_num_labels (sw); ++i) |
| { |
| basic_block label_bb = gimple_switch_label_bb (cfun, sw, i); |
| make_edge (bb, label_bb, 0); |
| } |
| } |
| /* Need those for loop fixup. */ |
| calculate_dominance_info (CDI_DOMINATORS); |
| /* With SSA lower PHIs parsed as internal function calls and |
| update stmts. */ |
| if (cdil == cdil_gimple_ssa) |
| { |
| /* Create PHI nodes, they are parsed into __PHI internal calls. */ |
| FOR_EACH_BB_FN (bb, cfun) |
| for (gimple_stmt_iterator gsi = gsi_start_bb (bb); |
| !gsi_end_p (gsi);) |
| { |
| gimple *stmt = gsi_stmt (gsi); |
| if (!gimple_call_internal_p (stmt, IFN_PHI)) |
| break; |
| |
| gphi *phi = create_phi_node (gimple_call_lhs (stmt), bb); |
| for (unsigned i = 0; i < gimple_call_num_args (stmt); i += 2) |
| { |
| int srcidx = TREE_INT_CST_LOW (gimple_call_arg (stmt, i)); |
| edge e = find_edge (BASIC_BLOCK_FOR_FN (cfun, srcidx), bb); |
| if (!e) |
| c_parser_error (parser, "edge not found"); |
| else |
| add_phi_arg (phi, gimple_call_arg (stmt, i + 1), e, |
| UNKNOWN_LOCATION); |
| } |
| gsi_remove (&gsi, true); |
| } |
| /* Fill SSA name gaps, putting them on the freelist and diagnose |
| SSA names without definition. */ |
| for (unsigned i = 1; i < num_ssa_names; ++i) |
| if (!ssa_name (i)) |
| { |
| tree name = make_ssa_name_fn (cfun, integer_type_node, NULL, i); |
| release_ssa_name_fn (cfun, name); |
| } |
| else if (!SSA_NAME_DEF_STMT (ssa_name (i))) |
| error ("SSA name %qE with version %d has no definition", |
| ssa_name (i), i); |
| /* No explicit virtual operands (yet). */ |
| bitmap_obstack_initialize (NULL); |
| update_ssa (TODO_update_ssa_only_virtuals); |
| bitmap_obstack_release (NULL); |
| /* ??? By flushing the freelist after virtual operand SSA rewrite |
| we keep the gaps available for re-use like needed for the |
| PR89595 testcase but then usually virtual operands would have |
| taken most of them. The fix is obviously to make virtual |
| operands explicit in the SSA IL. */ |
| flush_ssaname_freelist (); |
| } |
| fix_loop_structure (NULL); |
| } |
| |
| if (cfun->curr_properties & PROP_cfg) |
| { |
| ENTRY_BLOCK_PTR_FOR_FN (cfun)->count = entry_bb_count; |
| gcov_type t = param_gimple_fe_computed_hot_bb_threshold; |
| set_hot_bb_threshold (t); |
| update_max_bb_count (); |
| cgraph_node::get_create (cfun->decl); |
| cgraph_edge::rebuild_edges (); |
| } |
| 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 (gimple_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_AT_TRY: |
| c_parser_gimple_try_stmt (parser, seq); |
| break; |
| 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)) |
| { |
| tree label = c_parser_peek_token (parser)->value; |
| c_parser_consume_token (parser); |
| c_parser_gimple_goto_stmt (parser, loc, label, seq); |
| 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; |
| if (cfun->curr_properties & PROP_cfg) |
| parser.push_edge (parser.current_bb->index, EXIT_BLOCK, 0, |
| profile_probability::uninitialized ()); |
| 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; |
| } |
| if (c_parser_next_token_is (parser, CPP_NAME) |
| && c_parser_peek_token (parser)->id_kind == C_ID_ID |
| && strcmp (IDENTIFIER_POINTER (c_parser_peek_token (parser)->value), |
| "try") == 0) |
| { |
| c_parser_gimple_try_stmt (parser, seq); |
| break; |
| } |
| /* Basic block specification. |
| __BB (index, ...) */ |
| if ((cfun->curr_properties & PROP_cfg) |
| && !strcmp (IDENTIFIER_POINTER |
| (c_parser_peek_token (parser)->value), "__BB")) |
| { |
| c_parser_consume_token (parser); |
| if (! c_parser_require (parser, CPP_OPEN_PAREN, |
| "expected %<(%>")) |
| return return_p; |
| if (c_parser_next_token_is_not (parser, CPP_NUMBER)) |
| { |
| c_parser_error (parser, "expected block index"); |
| return return_p; |
| } |
| tree tnum = c_parser_peek_token (parser)->value; |
| if (TREE_CODE (tnum) != INTEGER_CST) |
| { |
| c_parser_error (parser, "expected block index"); |
| return return_p; |
| } |
| int index = TREE_INT_CST_LOW (tnum); |
| if (index < NUM_FIXED_BLOCKS |
| || (index < last_basic_block_for_fn (cfun) |
| && BASIC_BLOCK_FOR_FN (cfun, index) != NULL)) |
| { |
| c_parser_error (parser, "invalid block index"); |
| return return_p; |
| } |
| int is_loop_header_of = -1; |
| profile_count bb_count = profile_count::uninitialized (); |
| c_parser_consume_token (parser); |
| while (c_parser_next_token_is (parser, CPP_COMMA)) |
| { |
| c_parser_consume_token (parser); |
| if (! c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| c_parser_error (parser, "expected block specifier"); |
| return return_p; |
| } |
| /* loop_header (NUM) */ |
| if (!strcmp (IDENTIFIER_POINTER |
| (c_parser_peek_token (parser)->value), |
| "loop_header")) |
| { |
| c_parser_consume_token (parser); |
| if (! c_parser_require (parser, CPP_OPEN_PAREN, |
| "expected %<(%>")) |
| return return_p; |
| tree loop_num; |
| if (! c_parser_next_token_is (parser, CPP_NUMBER) |
| || TREE_CODE (loop_num |
| = c_parser_peek_token (parser)->value) |
| != INTEGER_CST) |
| { |
| c_parser_error (parser, "expected loop number"); |
| return return_p; |
| } |
| c_parser_consume_token (parser); |
| is_loop_header_of = TREE_INT_CST_LOW (loop_num); |
| if (! c_parser_require (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>")) |
| return return_p; |
| } |
| /* Parse profile: quality(value) */ |
| else |
| { |
| tree q; |
| profile_quality quality; |
| tree v = c_parser_peek_token (parser)->value; |
| if (!parse_profile_quality (IDENTIFIER_POINTER (v), |
| &quality)) |
| { |
| c_parser_error (parser, "unknown block specifier"); |
| return false; |
| } |
| |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, |
| "expected %<(%>")) |
| return false; |
| |
| if (!c_parser_next_token_is (parser, CPP_NUMBER) |
| || (TREE_CODE (q = c_parser_peek_token (parser)->value) |
| != INTEGER_CST)) |
| { |
| c_parser_error (parser, "expected count value"); |
| return false; |
| } |
| |
| bb_count |
| = profile_count::from_gcov_type (TREE_INT_CST_LOW (q), |
| quality); |
| c_parser_consume_token (parser); |
| if (! c_parser_require (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>")) |
| return return_p; |
| } |
| } |
| if (! c_parser_require (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>") |
| || ! c_parser_require (parser, CPP_COLON, |
| "expected %<:%>")) |
| return return_p; |
| |
| /* Put stmts parsed in the current block. */ |
| if (!gimple_seq_empty_p (*seq)) |
| { |
| if (!parser.current_bb) |
| c_parser_error (parser, "stmts without block"); |
| else |
| { |
| gimple_stmt_iterator gsi |
| = gsi_start_bb (parser.current_bb); |
| gsi_insert_seq_after (&gsi, *seq, GSI_CONTINUE_LINKING); |
| } |
| *seq = NULL; |
| } |
| |
| /* Build an empty block with specified index, linking them |
| in source order. */ |
| basic_block bb = alloc_block (); |
| bb->index = index; |
| link_block (bb, (parser.current_bb ? parser.current_bb |
| : ENTRY_BLOCK_PTR_FOR_FN (cfun))); |
| if (basic_block_info_for_fn (cfun)->length () <= (size_t)index) |
| vec_safe_grow_cleared (basic_block_info_for_fn (cfun), |
| index + 1, true); |
| SET_BASIC_BLOCK_FOR_FN (cfun, index, bb); |
| if (last_basic_block_for_fn (cfun) <= index) |
| last_basic_block_for_fn (cfun) = index + 1; |
| n_basic_blocks_for_fn (cfun)++; |
| if (parser.current_bb->index == ENTRY_BLOCK) |
| parser.push_edge (ENTRY_BLOCK, bb->index, EDGE_FALLTHRU, |
| profile_probability::always ()); |
| |
| /* We leave the proper setting to fixup. */ |
| class loop *loop_father = loops_for_fn (cfun)->tree_root; |
| /* If the new block is a loop header, allocate a loop |
| struct. Fixup will take care of proper placement within |
| the loop tree. */ |
| if (is_loop_header_of != -1) |
| { |
| if (number_of_loops (cfun) > (unsigned)is_loop_header_of |
| && get_loop (cfun, is_loop_header_of) != NULL) |
| { |
| c_parser_error (parser, "duplicate loop header"); |
| } |
| else |
| { |
| class loop *loop = alloc_loop (); |
| loop->num = is_loop_header_of; |
| loop->header = bb; |
| if (number_of_loops (cfun) <= (unsigned)is_loop_header_of) |
| vec_safe_grow_cleared (loops_for_fn (cfun)->larray, |
| is_loop_header_of + 1, true); |
| (*loops_for_fn (cfun)->larray)[is_loop_header_of] = loop; |
| flow_loop_tree_node_add (loops_for_fn (cfun)->tree_root, |
| loop); |
| } |
| loop_father = get_loop (cfun, is_loop_header_of); |
| } |
| bb->loop_father = loop_father; |
| bb->count = bb_count; |
| |
| /* Stmts now go to the new block. */ |
| parser.current_bb = bb; |
| 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_without_update (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); |
| |
| /* Put stmts parsed in the current block. */ |
| if ((cfun->curr_properties & PROP_cfg) |
| && !gimple_seq_empty_p (*seq)) |
| { |
| if (!parser.current_bb) |
| c_parser_error (parser, "stmts without block"); |
| else |
| { |
| gimple_stmt_iterator gsi = gsi_start_bb (parser.current_bb); |
| gsi_insert_seq_after (&gsi, *seq, GSI_CONTINUE_LINKING); |
| } |
| *seq = NULL; |
| } |
| |
| 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 (gimple_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_without_update (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 (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_without_update (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 |
| || strcmp (IDENTIFIER_POINTER (id), "__ABSU") == 0 |
| || strcmp (IDENTIFIER_POINTER (id), "__MIN") == 0 |
| || strcmp (IDENTIFIER_POINTER (id), "__MAX") == 0 |
| || strcmp (IDENTIFIER_POINTER (id), "__BIT_INSERT") == 0 |
| || strcmp (IDENTIFIER_POINTER (id), "__VEC_PERM") == 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_without_update (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 = 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); |
| int src_index = -1; |
| if (!c_parser_gimple_parse_bb_spec (arg, &src_index)) |
| c_parser_error (parser, "invalid source block specification"); |
| vargs.safe_push (size_int (src_index)); |
| } |
| 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_without_update (seq, call_stmt); |
| return; |
| } |
| |
| /* GIMPLE call with lhs. */ |
| if (c_parser_next_token_is (parser, CPP_DOT) |
| || (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_without_update (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 or an identifier and the next token |
| is a '?' then parse a conditional expression. */ |
| if ((COMPARISON_CLASS_P (rhs.value) |
| || SSA_VAR_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, |
| VECTOR_TYPE_P (TREE_TYPE (rhs.value)) |
| ? VEC_COND_EXPR : COND_EXPR, |
| TREE_TYPE (trueval.value), |
| rhs.value, trueval.value, falseval.value); |
| } |
| if (get_gimple_rhs_class (TREE_CODE (rhs.value)) == GIMPLE_INVALID_RHS) |
| { |
| c_parser_error (parser, "unexpected RHS for assignment"); |
| return; |
| } |
| assign = gimple_build_assign (lhs.value, rhs.value); |
| gimple_seq_add_stmt_without_update (seq, assign); |
| gimple_set_location (assign, loc); |
| } |
| return; |
| } |
| |
| /* Parse gimple binary expr. |
| |
| gimple-binary-expression: |
| gimple-unary-expression * gimple-unary-expression |
| gimple-unary-expression __MULT_HIGHPART 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 (gimple_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; |
| case CPP_NAME: |
| { |
| tree id = c_parser_peek_token (parser)->value; |
| if (strcmp (IDENTIFIER_POINTER (id), "__MULT_HIGHPART") == 0) |
| { |
| code = MULT_HIGHPART_EXPR; |
| break; |
| } |
| } |
| /* Fallthru. */ |
| 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 a gimple parentized binary expression. */ |
| |
| static c_expr |
| c_parser_gimple_parentized_binary_expression (gimple_parser &parser, |
| location_t op_loc, |
| tree_code code) |
| { |
| struct c_expr ret; |
| ret.set_error (); |
| |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| return ret; |
| c_expr op1 = c_parser_gimple_postfix_expression (parser); |
| if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>")) |
| return ret; |
| c_expr op2 = c_parser_gimple_postfix_expression (parser); |
| if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) |
| return ret; |
| |
| if (op1.value != error_mark_node && op2.value != error_mark_node) |
| ret.value = build2_loc (op_loc, |
| code, TREE_TYPE (op1.value), op1.value, op2.value); |
| return ret; |
| } |
| |
| /* Parse a gimple parentized binary expression. */ |
| |
| static c_expr |
| c_parser_gimple_parentized_ternary_expression (gimple_parser &parser, |
| location_t op_loc, |
| tree_code code) |
| { |
| struct c_expr ret; |
| ret.set_error (); |
| |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| return ret; |
| c_expr op1 = c_parser_gimple_postfix_expression (parser); |
| if (!c_parser_require (parser, CPP_COMMA, "expected %<,%>")) |
| return ret; |
| c_expr op2 = c_parser_gimple_postfix_expression (parser); |
| if (!c_parser_require (parser, CPP_COMMA, "expected %<)%>")) |
| return ret; |
| c_expr op3 = c_parser_gimple_postfix_expression (parser); |
| if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) |
| return ret; |
| |
| if (op1.value != error_mark_node |
| && op2.value != error_mark_node |
| && op3.value != error_mark_node) |
| ret.value = build3_loc (op_loc, |
| code, TREE_TYPE (op1.value), |
| op1.value, op2.value, op3.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 (gimple_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 if (strcmp (IDENTIFIER_POINTER (id), "__ABSU") == 0) |
| { |
| c_parser_consume_token (parser); |
| op = c_parser_gimple_postfix_expression (parser); |
| return parser_build_unary_op (op_loc, ABSU_EXPR, op); |
| } |
| else if (strcmp (IDENTIFIER_POINTER (id), "__MIN") == 0) |
| return c_parser_gimple_parentized_binary_expression (parser, |
| op_loc, |
| MIN_EXPR); |
| else if (strcmp (IDENTIFIER_POINTER (id), "__MAX") == 0) |
| return c_parser_gimple_parentized_binary_expression (parser, |
| op_loc, |
| MAX_EXPR); |
| else if (strcmp (IDENTIFIER_POINTER (id), "__VEC_PERM") == 0) |
| return c_parser_gimple_parentized_ternary_expression |
| (parser, op_loc, VEC_PERM_EXPR); |
| else if (strcmp (IDENTIFIER_POINTER (id), "__BIT_INSERT") == 0) |
| { |
| /* __BIT_INSERT '(' postfix-expression, postfix-expression, |
| integer ')' */ |
| location_t loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| c_expr op0 = c_parser_gimple_postfix_expression (parser); |
| c_parser_skip_until_found (parser, CPP_COMMA, |
| "expected %<,%>"); |
| c_expr op1 = c_parser_gimple_postfix_expression (parser); |
| c_parser_skip_until_found (parser, CPP_COMMA, |
| "expected %<,%>"); |
| c_expr op2 = c_parser_gimple_postfix_expression (parser); |
| if (TREE_CODE (op2.value) != INTEGER_CST |
| || !int_fits_type_p (op2.value, bitsizetype)) |
| c_parser_error (parser, "expected constant offset"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| if (op0.value != error_mark_node |
| && op1.value != error_mark_node |
| && TREE_CODE (op2.value) == INTEGER_CST) |
| ret.value = build3_loc (loc, BIT_INSERT_EXPR, |
| TREE_TYPE (op0.value), |
| op0.value, op1.value, |
| fold_convert (bitsizetype, |
| op2.value)); |
| } |
| return ret; |
| } |
| 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 (gimple_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; |
| } |
| name = make_ssa_name_fn (cfun, parent, |
| gimple_build_nop (), version); |
| } |
| } |
| |
| return name; |
| } |
| |
| /* Parse a gimple call to an internal function. |
| |
| gimple-call-internal: |
| . identifier ( gimple-argument-expression-list[opt] ) */ |
| |
| static struct c_expr |
| c_parser_gimple_call_internal (gimple_parser &parser) |
| { |
| struct c_expr expr; |
| expr.set_error (); |
| |
| gcc_assert (c_parser_next_token_is (parser, CPP_DOT)); |
| c_parser_consume_token (parser); |
| location_t loc = c_parser_peek_token (parser)->location; |
| if (!c_parser_next_token_is (parser, CPP_NAME) |
| || c_parser_peek_token (parser)->id_kind != C_ID_ID) |
| { |
| c_parser_error (parser, "expecting internal function name"); |
| return expr; |
| } |
| tree id = c_parser_peek_token (parser)->value; |
| internal_fn ifn = lookup_internal_fn (IDENTIFIER_POINTER (id)); |
| c_parser_consume_token (parser); |
| if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| 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 %<)%>"); |
| if (ifn == IFN_LAST) |
| error_at (loc, "unknown internal function %qE", id); |
| else |
| { |
| expr.value = build_call_expr_internal_loc_array |
| (loc, ifn, void_type_node, exprlist.length (), |
| exprlist.address ()); |
| expr.original_code = ERROR_MARK; |
| expr.original_type = NULL; |
| } |
| } |
| return expr; |
| } |
| |
| /* Parse '<' type [',' alignment] '>' and return a type on success |
| and NULL_TREE on error. */ |
| |
| static tree |
| c_parser_gimple_typespec (gimple_parser &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 %<>%>"); |
| } |
| if (!type_name) |
| return NULL_TREE; |
| tree tem; |
| tree type = groktypename (type_name, &tem, NULL); |
| if (alignment) |
| type = build_aligned_type (type, tree_to_uhwi (alignment)); |
| return type; |
| } |
| |
| /* Parse gimple postfix expression. |
| |
| gimple-postfix-expression: |
| gimple-primary-expression |
| gimple-primary-expression [ gimple-primary-expression ] |
| gimple-primary-expression ( gimple-argument-expression-list[opt] ) |
| gimple-postfix-expression . identifier |
| gimple-postfix-expression -> identifier |
| |
| gimple-argument-expression-list: |
| gimple-unary-expression |
| gimple-argument-expression-list , gimple-unary-expression |
| |
| gimple-primary-expression: |
| identifier |
| constant |
| string-literal |
| constructor |
| gimple-call-internal |
| |
| */ |
| |
| static struct c_expr |
| c_parser_gimple_postfix_expression (gimple_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_UTF8CHAR: |
| 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 = c_parser_string_literal (parser, false, true); |
| break; |
| case CPP_DOT: |
| expr = c_parser_gimple_call_internal (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); |
| tree type = c_parser_gimple_typespec (parser); |
| 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 || c_parser_error (parser)) |
| { |
| c_parser_set_error (parser, false); |
| return expr; |
| } |
| expr.value = build2_loc (loc, MEM_REF, |
| type, ptr.value, alias_off); |
| break; |
| } |
| else if (strcmp (IDENTIFIER_POINTER (id), "__VIEW_CONVERT") == 0) |
| { |
| /* __VIEW_CONVERT '<' type-name [ ',' number ] '>' |
| '(' postfix-expression ')' */ |
| location_t loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| tree type = c_parser_gimple_typespec (parser); |
| if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| c_expr op = c_parser_gimple_postfix_expression (parser); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| if (type && op.value != error_mark_node) |
| expr.value = build1_loc (loc, VIEW_CONVERT_EXPR, |
| type, op.value); |
| } |
| break; |
| } |
| else if (strcmp (IDENTIFIER_POINTER (id), "__BIT_FIELD_REF") == 0) |
| { |
| /* __BIT_FIELD_REF '<' type-name [ ',' number ] '>' |
| '(' postfix-expression, integer, integer ')' */ |
| location_t loc = c_parser_peek_token (parser)->location; |
| c_parser_consume_token (parser); |
| tree type = c_parser_gimple_typespec (parser); |
| if (c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| { |
| c_expr op0 = c_parser_gimple_postfix_expression (parser); |
| c_parser_skip_until_found (parser, CPP_COMMA, |
| "expected %<,%>"); |
| c_expr op1 = c_parser_gimple_postfix_expression (parser); |
| if (TREE_CODE (op1.value) != INTEGER_CST |
| || !int_fits_type_p (op1.value, bitsizetype)) |
| c_parser_error (parser, "expected constant size"); |
| c_parser_skip_until_found (parser, CPP_COMMA, |
| "expected %<,%>"); |
| c_expr op2 = c_parser_gimple_postfix_expression (parser); |
| if (TREE_CODE (op2.value) != INTEGER_CST |
| || !int_fits_type_p (op2.value, bitsizetype)) |
| c_parser_error (parser, "expected constant offset"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_PAREN, |
| "expected %<)%>"); |
| if (type |
| && op0.value != error_mark_node |
| && TREE_CODE (op1.value) == INTEGER_CST |
| && TREE_CODE (op2.value) == INTEGER_CST) |
| expr.value = build3_loc (loc, BIT_FIELD_REF, type, |
| op0.value, |
| fold_convert (bitsizetype, |
| op1.value), |
| fold_convert (bitsizetype, |
| op2.value)); |
| } |
| break; |
| } |
| else if (strcmp (IDENTIFIER_POINTER (id), "_Literal") == 0) |
| { |
| /* _Literal '(' type-name ')' ( [ '-' ] constant | constructor ) */ |
| 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 %<)%>"); |
| } |
| if (! type) |
| { |
| c_parser_error (parser, "invalid _Literal"); |
| return expr; |
| } |
| if (c_parser_next_token_is (parser, CPP_OPEN_BRACE)) |
| { |
| c_parser_consume_token (parser); |
| if (!AGGREGATE_TYPE_P (type) |
| && !VECTOR_TYPE_P (type)) |
| { |
| c_parser_error (parser, "invalid type for _Literal with " |
| "constructor"); |
| c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, |
| "expected %<}%>"); |
| return expr; |
| } |
| vec<constructor_elt, va_gc> *v = NULL; |
| bool constant_p = true; |
| if (VECTOR_TYPE_P (type) |
| && !c_parser_next_token_is (parser, CPP_CLOSE_BRACE)) |
| { |
| vec_alloc (v, TYPE_VECTOR_SUBPARTS (type).to_constant ()); |
| do |
| { |
| tree val |
| = c_parser_gimple_postfix_expression (parser).value; |
| if (! val |
| || val == error_mark_node |
| || (! CONSTANT_CLASS_P (val) |
| && ! SSA_VAR_P (val))) |
| { |
| c_parser_error (parser, "invalid _Literal"); |
| return expr; |
| } |
| CONSTRUCTOR_APPEND_ELT (v, NULL_TREE, val); |
| if (! CONSTANT_CLASS_P (val)) |
| constant_p = false; |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| else |
| break; |
| } |
| while (1); |
| } |
| if (c_parser_require (parser, CPP_CLOSE_BRACE, |
| "expected %<}%>")) |
| { |
| if (v && constant_p) |
| expr.value = build_vector_from_ctor (type, v); |
| else |
| expr.value = build_constructor (type, v); |
| } |
| else |
| { |
| c_parser_skip_until_found (parser, CPP_CLOSE_BRACE, |
| "expected %<}%>"); |
| return expr; |
| } |
| } |
| else |
| { |
| bool neg_p, addr_p; |
| if ((neg_p = c_parser_next_token_is (parser, CPP_MINUS))) |
| c_parser_consume_token (parser); |
| if ((addr_p = c_parser_next_token_is (parser, CPP_AND))) |
| c_parser_consume_token (parser); |
| tree val = c_parser_gimple_postfix_expression (parser).value; |
| if (! val |
| || val == error_mark_node |
| || (!CONSTANT_CLASS_P (val) && !addr_p)) |
| { |
| c_parser_error (parser, "invalid _Literal"); |
| return expr; |
| } |
| if (addr_p) |
| { |
| val = build1 (ADDR_EXPR, type, val); |
| if (!is_gimple_invariant_address (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; |
| } |
| |
| /* 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; |
| } |
| /* Fallthru. */ |
| default: |
| c_parser_error (parser, "expected expression"); |
| expr.set_error (); |
| break; |
| } |
| if (expr.value == error_mark_node) |
| return expr; |
| 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 (gimple_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 %<)%>"); |
| if (!FUNC_OR_METHOD_TYPE_P (TREE_TYPE (expr.value))) |
| { |
| c_parser_error (parser, "invalid call to non-function"); |
| expr.set_error (); |
| break; |
| } |
| 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. */ |
| if (!POINTER_TYPE_P (TREE_TYPE (expr.value))) |
| { |
| c_parser_error (parser, "dereference of non-pointer"); |
| expr.set_error (); |
| expr.original_code = ERROR_MARK; |
| expr.original_type = NULL; |
| return expr; |
| } |
| 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 (gimple_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 (gimple_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); |
| if (label) |
| gimple_seq_add_stmt_without_update (seq, gimple_build_label (label)); |
| return; |
| } |
| |
| /* Parse gimple/RTL pass list. |
| |
| gimple-or-rtl-pass-list: |
| startwith("pass-name")[,{cfg,ssa}] |
| */ |
| |
| void |
| c_parser_gimple_or_rtl_pass_list (c_parser *parser, c_declspecs *specs) |
| { |
| char *pass = NULL; |
| |
| /* Accept __GIMPLE/__RTL. */ |
| if (c_parser_next_token_is_not (parser, CPP_OPEN_PAREN)) |
| return; |
| c_parser_consume_token (parser); |
| |
| specs->entry_bb_count = profile_count::uninitialized (); |
| while (c_parser_next_token_is (parser, CPP_NAME)) |
| { |
| profile_quality quality; |
| 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; |
| if (c_parser_next_token_is_not (parser, CPP_STRING)) |
| { |
| error_at (c_parser_peek_token (parser)->location, |
| "expected pass name"); |
| return; |
| } |
| pass = xstrdup (TREE_STRING_POINTER |
| (c_parser_string_literal (parser, false, |
| false).value)); |
| if (! c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<(%>")) |
| return; |
| } |
| else if (parse_profile_quality (op, &quality)) |
| { |
| tree q; |
| if (!c_parser_require (parser, CPP_OPEN_PAREN, "expected %<(%>")) |
| return; |
| |
| if (!c_parser_next_token_is (parser, CPP_NUMBER) |
| || (TREE_CODE (q = c_parser_peek_token (parser)->value) |
| != INTEGER_CST)) |
| { |
| c_parser_error (parser, "expected count value"); |
| return; |
| } |
| |
| specs->entry_bb_count |
| = profile_count::from_gcov_type (TREE_INT_CST_LOW (q), quality); |
| c_parser_consume_token (parser); |
| if (!c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) |
| return; |
| } |
| else if (specs->declspec_il != cdil_gimple) |
| /* Allow only one IL specifier and none on RTL. */ |
| ; |
| else if (! strcmp (op, "cfg")) |
| specs->declspec_il = cdil_gimple_cfg; |
| else if (! strcmp (op, "ssa")) |
| specs->declspec_il = cdil_gimple_ssa; |
| else |
| { |
| error_at (c_parser_peek_token (parser)->location, |
| "invalid operation"); |
| return; |
| } |
| if (c_parser_next_token_is (parser, CPP_COMMA)) |
| c_parser_consume_token (parser); |
| } |
| |
| if (! c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) |
| return; |
| |
| specs->gimple_or_rtl_pass = 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 (gimple_parser &parser) |
| { |
| struct c_declarator *declarator; |
| struct c_declspecs *specs = build_null_declspecs (); |
| c_parser_declspecs (parser, specs, true, true, 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; |
| /* Handle SSA pointer declarations in a very simplistic ways, we |
| probably would like to call grokdeclarator in a special mode to |
| just build the type of the decl - start_decl already pushes |
| the identifier to the bindings for lookup, something we do not |
| want. */ |
| struct c_declarator *id_declarator = declarator; |
| while (id_declarator->kind == cdk_pointer) |
| id_declarator = id_declarator->declarator; |
| if (id_declarator->kind == cdk_id |
| && (declarator->kind == cdk_pointer |
| || is_gimple_reg_type (specs->type)) |
| && c_parser_parse_ssa_name_id (id_declarator->u.id.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) |
| { |
| struct c_declarator *p = declarator; |
| tree type = specs->type; |
| while (p->kind == cdk_pointer) |
| { |
| type = build_pointer_type (type); |
| p = p->declarator; |
| } |
| c_parser_parse_ssa_name (parser, id_declarator->u.id.id, 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 (gimple_parser &parser, |
| location_t loc, tree label, gimple_seq *seq) |
| { |
| if (cfun->curr_properties & PROP_cfg) |
| { |
| int dest_index; |
| profile_probability prob; |
| if (c_parser_gimple_parse_bb_spec_edge_probability (label, parser, |
| &dest_index, &prob)) |
| { |
| parser.push_edge (parser.current_bb->index, dest_index, |
| EDGE_FALLTHRU, prob); |
| return; |
| } |
| } |
| tree decl = lookup_label_for_goto (loc, label); |
| gimple_seq_add_stmt_without_update (seq, gimple_build_goto (decl)); |
| } |
| |
| /* Parse a parenthesized condition. |
| gimple-condition: |
| ( gimple-binary-expression ) */ |
| |
| static tree |
| c_parser_gimple_paren_condition (gimple_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 (cond != error_mark_node |
| && ! COMPARISON_CLASS_P (cond) |
| && ! CONSTANT_CLASS_P (cond) |
| && ! SSA_VAR_P (cond)) |
| { |
| c_parser_error (parser, "comparison required"); |
| cond = error_mark_node; |
| } |
| if (! c_parser_require (parser, CPP_CLOSE_PAREN, "expected %<)%>")) |
| return error_mark_node; |
| return cond; |
| } |
| |
| /* Parse gimple try statement. |
| |
| try-statement: |
| try { ... } finally { ... } |
| try { ... } finally { ... } else { ... } |
| |
| This could support try/catch as well, but it's not implemented yet. |
| */ |
| |
| static void |
| c_parser_gimple_try_stmt (gimple_parser &parser, gimple_seq *seq) |
| { |
| gimple_seq tryseq = NULL; |
| c_parser_consume_token (parser); |
| c_parser_gimple_compound_statement (parser, &tryseq); |
| |
| if ((c_parser_next_token_is (parser, CPP_KEYWORD) |
| && c_parser_peek_token (parser)->keyword == RID_AT_FINALLY) |
| || (c_parser_next_token_is (parser, CPP_NAME) |
| && c_parser_peek_token (parser)->id_kind == C_ID_ID |
| && strcmp (IDENTIFIER_POINTER (c_parser_peek_token (parser)->value), |
| "finally") == 0)) |
| { |
| gimple_seq finseq = NULL; |
| c_parser_consume_token (parser); |
| c_parser_gimple_compound_statement (parser, &finseq); |
| |
| if (c_parser_next_token_is (parser, CPP_KEYWORD) |
| && c_parser_peek_token (parser)->keyword == RID_ELSE) |
| { |
| gimple_seq elsseq = NULL; |
| c_parser_consume_token (parser); |
| c_parser_gimple_compound_statement (parser, &elsseq); |
| |
| geh_else *stmt = gimple_build_eh_else (finseq, elsseq); |
| finseq = NULL; |
| gimple_seq_add_stmt_without_update (&finseq, stmt); |
| } |
| |
| gtry *stmt = gimple_build_try (tryseq, finseq, GIMPLE_TRY_FINALLY); |
| gimple_seq_add_stmt_without_update (seq, stmt); |
| } |
| else if (c_parser_next_token_is (parser, CPP_KEYWORD) |
| && c_parser_peek_token (parser)->keyword == RID_AT_CATCH) |
| c_parser_error (parser, "%<catch%> is not supported"); |
| else |
| c_parser_error (parser, "expected %<finally%> or %<catch%>"); |
| } |
| |
| /* 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 (gimple_parser &parser, gimple_seq *seq) |
| { |
| tree t_label = NULL_TREE, f_label = NULL_TREE, 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); |
| int dest_index; |
| profile_probability prob; |
| if ((cfun->curr_properties & PROP_cfg) |
| && c_parser_gimple_parse_bb_spec_edge_probability (label, parser, |
| &dest_index, &prob)) |
| parser.push_edge (parser.current_bb->index, dest_index, |
| EDGE_TRUE_VALUE, prob); |
| else |
| 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; |
| c_parser_consume_token (parser); |
| int dest_index; |
| profile_probability prob; |
| if ((cfun->curr_properties & PROP_cfg) |
| && c_parser_gimple_parse_bb_spec_edge_probability (label, parser, |
| &dest_index, &prob)) |
| parser.push_edge (parser.current_bb->index, dest_index, |
| EDGE_FALSE_VALUE, prob); |
| else |
| f_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 (cond != error_mark_node) |
| gimple_seq_add_stmt_without_update (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 (gimple_parser &parser, gimple_seq *seq) |
| { |
| c_expr cond_expr; |
| tree case_label, label; |
| auto_vec<tree> labels; |
| tree default_label = NULL_TREE; |
| 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; |
| } |
| default: |
| c_parser_error (parser, "expected case label"); |
| return; |
| } |
| |
| } |
| if (! c_parser_require (parser, CPP_CLOSE_BRACE, "expected %<}%>")) |
| return; |
| |
| if (cond_expr.value != error_mark_node) |
| { |
| gswitch *s = gimple_build_switch (cond_expr.value, default_label, labels); |
| gimple_seq_add_stmt_without_update (seq, s); |
| } |
| } |
| |
| /* Parse gimple return statement. */ |
| |
| static void |
| c_parser_gimple_return_stmt (gimple_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_without_update (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_without_update (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. */ |
| location_t 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; |
| } |