blob: 32513f1227777a2d0eb43976c73707963c1328e2 [file] [log] [blame]
/* 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;
}