blob: a3002c6bda5b41ebe069f9be31511ed195062833 [file] [log] [blame]
/*
* Sparse - a semantic source parser.
*
* Copyright (C) 2003 Transmeta Corp.
* 2003-2004 Linus Torvalds
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include "lib.h"
#include "allocate.h"
#include "token.h"
#include "parse.h"
#include "symbol.h"
#include "expression.h"
static struct expression * dup_expression(struct expression *expr)
{
struct expression *dup = alloc_expression(expr->pos, expr->type);
*dup = *expr;
return dup;
}
static struct statement * dup_statement(struct statement *stmt)
{
struct statement *dup = alloc_statement(stmt->pos, stmt->type);
*dup = *stmt;
return dup;
}
static struct symbol *copy_symbol(struct position pos, struct symbol *sym)
{
if (!sym)
return sym;
if (sym->ctype.modifiers & (MOD_STATIC | MOD_EXTERN | MOD_TOPLEVEL | MOD_INLINE))
return sym;
if (!sym->replace) {
warning(pos, "unreplaced symbol '%s'", show_ident(sym->ident));
return sym;
}
return sym->replace;
}
static struct symbol_list *copy_symbol_list(struct symbol_list *src)
{
struct symbol_list *dst = NULL;
struct symbol *sym;
FOR_EACH_PTR(src, sym) {
struct symbol *newsym = copy_symbol(sym->pos, sym);
add_symbol(&dst, newsym);
} END_FOR_EACH_PTR(sym);
return dst;
}
static struct expression * copy_expression(struct expression *expr)
{
if (!expr)
return NULL;
switch (expr->type) {
/*
* EXPR_SYMBOL is the interesting case, we may need to replace the
* symbol to the new copy.
*/
case EXPR_SYMBOL: {
struct symbol *sym = copy_symbol(expr->pos, expr->symbol);
if (sym == expr->symbol)
break;
expr = dup_expression(expr);
expr->symbol = sym;
break;
}
/* Atomics, never change, just return the expression directly */
case EXPR_VALUE:
case EXPR_STRING:
case EXPR_FVALUE:
case EXPR_TYPE:
break;
/* Unops: check if the subexpression is unique */
case EXPR_PREOP:
case EXPR_POSTOP: {
struct expression *unop = copy_expression(expr->unop);
if (expr->unop == unop)
break;
expr = dup_expression(expr);
expr->unop = unop;
break;
}
case EXPR_SLICE: {
struct expression *base = copy_expression(expr->base);
expr = dup_expression(expr);
expr->base = base;
break;
}
/* Binops: copy left/right expressions */
case EXPR_BINOP:
case EXPR_COMMA:
case EXPR_COMPARE:
case EXPR_LOGICAL: {
struct expression *left = copy_expression(expr->left);
struct expression *right = copy_expression(expr->right);
if (left == expr->left && right == expr->right)
break;
expr = dup_expression(expr);
expr->left = left;
expr->right = right;
break;
}
case EXPR_ASSIGNMENT: {
struct expression *left = copy_expression(expr->left);
struct expression *right = copy_expression(expr->right);
if (expr->op == '=' && left == expr->left && right == expr->right)
break;
expr = dup_expression(expr);
expr->left = left;
expr->right = right;
break;
}
/* Dereference */
case EXPR_DEREF: {
struct expression *deref = copy_expression(expr->deref);
expr = dup_expression(expr);
expr->deref = deref;
break;
}
/* Cast/sizeof/__alignof__ */
case EXPR_CAST:
if (expr->cast_expression->type == EXPR_INITIALIZER) {
struct expression *cast = expr->cast_expression;
struct symbol *sym = expr->cast_type;
expr = dup_expression(expr);
expr->cast_expression = copy_expression(cast);
expr->cast_type = alloc_symbol(sym->pos, sym->type);
*expr->cast_type = *sym;
break;
}
case EXPR_FORCE_CAST:
case EXPR_IMPLIED_CAST:
case EXPR_SIZEOF:
case EXPR_PTRSIZEOF:
case EXPR_ALIGNOF: {
struct expression *cast = copy_expression(expr->cast_expression);
if (cast == expr->cast_expression)
break;
expr = dup_expression(expr);
expr->cast_expression = cast;
break;
}
/* Conditional expression */
case EXPR_SELECT:
case EXPR_CONDITIONAL: {
struct expression *cond = copy_expression(expr->conditional);
struct expression *true = copy_expression(expr->cond_true);
struct expression *false = copy_expression(expr->cond_false);
if (cond == expr->conditional && true == expr->cond_true && false == expr->cond_false)
break;
expr = dup_expression(expr);
expr->conditional = cond;
expr->cond_true = true;
expr->cond_false = false;
break;
}
/* Statement expression */
case EXPR_STATEMENT: {
struct statement *stmt = alloc_statement(expr->pos, STMT_COMPOUND);
copy_statement(expr->statement, stmt);
expr = dup_expression(expr);
expr->statement = stmt;
break;
}
/* Call expression */
case EXPR_CALL: {
struct expression *fn = copy_expression(expr->fn);
struct expression_list *list = expr->args;
struct expression *arg;
expr = dup_expression(expr);
expr->fn = fn;
expr->args = NULL;
FOR_EACH_PTR(list, arg) {
add_expression(&expr->args, copy_expression(arg));
} END_FOR_EACH_PTR(arg);
break;
}
/* Initializer list statement */
case EXPR_INITIALIZER: {
struct expression_list *list = expr->expr_list;
struct expression *entry;
expr = dup_expression(expr);
expr->expr_list = NULL;
FOR_EACH_PTR(list, entry) {
add_expression(&expr->expr_list, copy_expression(entry));
} END_FOR_EACH_PTR(entry);
break;
}
/* Label in inline function - hmm. */
case EXPR_LABEL: {
struct symbol *label_symbol = copy_symbol(expr->pos, expr->label_symbol);
expr = dup_expression(expr);
expr->label_symbol = label_symbol;
break;
}
case EXPR_INDEX: {
struct expression *sub_expr = copy_expression(expr->idx_expression);
expr = dup_expression(expr);
expr->idx_expression = sub_expr;
break;
}
case EXPR_IDENTIFIER: {
struct expression *sub_expr = copy_expression(expr->ident_expression);
expr = dup_expression(expr);
expr->ident_expression = sub_expr;
break;
}
/* Position in initializer.. */
case EXPR_POS: {
struct expression *val = copy_expression(expr->init_expr);
expr = dup_expression(expr);
expr->init_expr = val;
break;
}
case EXPR_OFFSETOF: {
struct expression *val = copy_expression(expr->down);
if (expr->op == '.') {
if (expr->down != val) {
expr = dup_expression(expr);
expr->down = val;
}
} else {
struct expression *idx = copy_expression(expr->index);
if (expr->down != val || expr->index != idx) {
expr = dup_expression(expr);
expr->down = val;
expr->index = idx;
}
}
break;
}
default:
warning(expr->pos, "trying to copy expression type %d", expr->type);
}
return expr;
}
static struct expression_list *copy_asm_constraints(struct expression_list *in)
{
struct expression_list *out = NULL;
struct expression *expr;
int state = 0;
FOR_EACH_PTR(in, expr) {
switch (state) {
case 0: /* identifier */
case 1: /* constraint */
state++;
add_expression(&out, expr);
continue;
case 2: /* expression */
state = 0;
add_expression(&out, copy_expression(expr));
continue;
}
} END_FOR_EACH_PTR(expr);
return out;
}
static void set_replace(struct symbol *old, struct symbol *new)
{
new->replace = old;
old->replace = new;
}
static void unset_replace(struct symbol *sym)
{
struct symbol *r = sym->replace;
if (!r) {
warning(sym->pos, "symbol '%s' not replaced?", show_ident(sym->ident));
return;
}
r->replace = NULL;
sym->replace = NULL;
}
static void unset_replace_list(struct symbol_list *list)
{
struct symbol *sym;
FOR_EACH_PTR(list, sym) {
unset_replace(sym);
} END_FOR_EACH_PTR(sym);
}
static struct statement *copy_one_statement(struct statement *stmt)
{
if (!stmt)
return NULL;
switch(stmt->type) {
case STMT_NONE:
break;
case STMT_DECLARATION: {
struct symbol *sym;
struct statement *newstmt = dup_statement(stmt);
newstmt->declaration = NULL;
FOR_EACH_PTR(stmt->declaration, sym) {
struct symbol *newsym = copy_symbol(stmt->pos, sym);
if (newsym != sym)
newsym->initializer = copy_expression(sym->initializer);
add_symbol(&newstmt->declaration, newsym);
} END_FOR_EACH_PTR(sym);
stmt = newstmt;
break;
}
case STMT_CONTEXT:
case STMT_EXPRESSION: {
struct expression *expr = copy_expression(stmt->expression);
if (expr == stmt->expression)
break;
stmt = dup_statement(stmt);
stmt->expression = expr;
break;
}
case STMT_RANGE: {
struct expression *expr = copy_expression(stmt->range_expression);
if (expr == stmt->expression)
break;
stmt = dup_statement(stmt);
stmt->range_expression = expr;
break;
}
case STMT_COMPOUND: {
struct statement *new = alloc_statement(stmt->pos, STMT_COMPOUND);
copy_statement(stmt, new);
stmt = new;
break;
}
case STMT_IF: {
struct expression *cond = stmt->if_conditional;
struct statement *true = stmt->if_true;
struct statement *false = stmt->if_false;
cond = copy_expression(cond);
true = copy_one_statement(true);
false = copy_one_statement(false);
if (stmt->if_conditional == cond &&
stmt->if_true == true &&
stmt->if_false == false)
break;
stmt = dup_statement(stmt);
stmt->if_conditional = cond;
stmt->if_true = true;
stmt->if_false = false;
break;
}
case STMT_RETURN: {
struct expression *retval = copy_expression(stmt->ret_value);
struct symbol *sym = copy_symbol(stmt->pos, stmt->ret_target);
stmt = dup_statement(stmt);
stmt->ret_value = retval;
stmt->ret_target = sym;
break;
}
case STMT_CASE: {
stmt = dup_statement(stmt);
stmt->case_label = copy_symbol(stmt->pos, stmt->case_label);
stmt->case_label->stmt = stmt;
stmt->case_expression = copy_expression(stmt->case_expression);
stmt->case_to = copy_expression(stmt->case_to);
stmt->case_statement = copy_one_statement(stmt->case_statement);
break;
}
case STMT_SWITCH: {
struct symbol *switch_break = copy_symbol(stmt->pos, stmt->switch_break);
struct symbol *switch_case = copy_symbol(stmt->pos, stmt->switch_case);
struct expression *expr = copy_expression(stmt->switch_expression);
struct statement *switch_stmt = copy_one_statement(stmt->switch_statement);
stmt = dup_statement(stmt);
switch_case->symbol_list = copy_symbol_list(switch_case->symbol_list);
stmt->switch_break = switch_break;
stmt->switch_case = switch_case;
stmt->switch_expression = expr;
stmt->switch_statement = switch_stmt;
break;
}
case STMT_ITERATOR: {
stmt = dup_statement(stmt);
stmt->iterator_break = copy_symbol(stmt->pos, stmt->iterator_break);
stmt->iterator_continue = copy_symbol(stmt->pos, stmt->iterator_continue);
stmt->iterator_syms = copy_symbol_list(stmt->iterator_syms);
stmt->iterator_pre_statement = copy_one_statement(stmt->iterator_pre_statement);
stmt->iterator_pre_condition = copy_expression(stmt->iterator_pre_condition);
stmt->iterator_statement = copy_one_statement(stmt->iterator_statement);
stmt->iterator_post_statement = copy_one_statement(stmt->iterator_post_statement);
stmt->iterator_post_condition = copy_expression(stmt->iterator_post_condition);
break;
}
case STMT_LABEL: {
stmt = dup_statement(stmt);
stmt->label_identifier = copy_symbol(stmt->pos, stmt->label_identifier);
stmt->label_statement = copy_one_statement(stmt->label_statement);
break;
}
case STMT_GOTO: {
stmt = dup_statement(stmt);
stmt->goto_label = copy_symbol(stmt->pos, stmt->goto_label);
stmt->goto_expression = copy_expression(stmt->goto_expression);
stmt->target_list = copy_symbol_list(stmt->target_list);
break;
}
case STMT_ASM: {
stmt = dup_statement(stmt);
stmt->asm_inputs = copy_asm_constraints(stmt->asm_inputs);
stmt->asm_outputs = copy_asm_constraints(stmt->asm_outputs);
/* no need to dup "clobbers", since they are all constant strings */
break;
}
default:
warning(stmt->pos, "trying to copy statement type %d", stmt->type);
break;
}
return stmt;
}
/*
* Copy a statement tree from 'src' to 'dst', where both
* source and destination are of type STMT_COMPOUND.
*
* We do this for the tree-level inliner.
*
* This doesn't do the symbol replacement right: it's not
* re-entrant.
*/
void copy_statement(struct statement *src, struct statement *dst)
{
struct statement *stmt;
FOR_EACH_PTR(src->stmts, stmt) {
add_statement(&dst->stmts, copy_one_statement(stmt));
} END_FOR_EACH_PTR(stmt);
dst->args = copy_one_statement(src->args);
dst->ret = copy_symbol(src->pos, src->ret);
dst->inline_fn = src->inline_fn;
}
static struct symbol *create_copy_symbol(struct symbol *orig)
{
struct symbol *sym = orig;
if (orig) {
sym = alloc_symbol(orig->pos, orig->type);
*sym = *orig;
sym->bb_target = NULL;
sym->pseudo = NULL;
set_replace(orig, sym);
orig = sym;
}
return orig;
}
static struct symbol_list *create_symbol_list(struct symbol_list *src)
{
struct symbol_list *dst = NULL;
struct symbol *sym;
FOR_EACH_PTR(src, sym) {
struct symbol *newsym = create_copy_symbol(sym);
add_symbol(&dst, newsym);
} END_FOR_EACH_PTR(sym);
return dst;
}
int inline_function(struct expression *expr, struct symbol *sym)
{
struct symbol_list * fn_symbol_list;
struct symbol *fn = sym->ctype.base_type;
struct expression_list *arg_list = expr->args;
struct statement *stmt = alloc_statement(expr->pos, STMT_COMPOUND);
struct symbol_list *name_list, *arg_decl;
struct symbol *name;
struct expression *arg;
if (!fn->inline_stmt) {
sparse_error(fn->pos, "marked inline, but without a definition");
return 0;
}
if (fn->expanding)
return 0;
fn->expanding = 1;
name_list = fn->arguments;
expr->type = EXPR_STATEMENT;
expr->statement = stmt;
expr->ctype = fn->ctype.base_type;
fn_symbol_list = create_symbol_list(sym->inline_symbol_list);
arg_decl = NULL;
PREPARE_PTR_LIST(name_list, name);
FOR_EACH_PTR(arg_list, arg) {
struct symbol *a = alloc_symbol(arg->pos, SYM_NODE);
a->ctype.base_type = arg->ctype;
if (name) {
*a = *name;
set_replace(name, a);
add_symbol(&fn_symbol_list, a);
}
a->initializer = arg;
add_symbol(&arg_decl, a);
NEXT_PTR_LIST(name);
} END_FOR_EACH_PTR(arg);
FINISH_PTR_LIST(name);
copy_statement(fn->inline_stmt, stmt);
if (arg_decl) {
struct statement *decl = alloc_statement(expr->pos, STMT_DECLARATION);
decl->declaration = arg_decl;
stmt->args = decl;
}
stmt->inline_fn = sym;
unset_replace_list(fn_symbol_list);
evaluate_statement(stmt);
fn->expanding = 0;
return 1;
}
void uninline(struct symbol *sym)
{
struct symbol *fn = sym->ctype.base_type;
struct symbol_list *arg_list = fn->arguments;
struct symbol *p;
sym->symbol_list = create_symbol_list(sym->inline_symbol_list);
FOR_EACH_PTR(arg_list, p) {
p->replace = p;
} END_FOR_EACH_PTR(p);
fn->stmt = alloc_statement(fn->pos, STMT_COMPOUND);
copy_statement(fn->inline_stmt, fn->stmt);
unset_replace_list(sym->symbol_list);
unset_replace_list(arg_list);
}