aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2019-09-29 17:09:10 +0200
committerLuc Van Oostenryck <luc.vanoostenryck@gmail.com>2019-09-30 03:45:53 +0200
commitd466a02815b8109ea007736590bdd97f5d0aeb2f (patch)
tree5dd27f9401831f45ab77c6c31591327ec52c45a5
parentfded889cd4b3e0b5eef95440d3f96723e76a78e9 (diff)
parent5d0c4d96bdf4ab5df4ef0e31ab433e605299aabd (diff)
downloadsparse-d466a02815b8109ea007736590bdd97f5d0aeb2f.tar.gz
Merge branch 'fix-expand-asm' into tip
Currently, ASM operands aren't expanded or even evaluated. This causes Sparse to emit warnings about 'unknown expression' during the linearization of these operands if they contains, for example, calls to __builtin_compatible_types_p(). Note: the correct handling of ASM operands needs to make the distinction between 'memory' operands and 'normal' operands. For this, it is needed to look at the constraints and these are architecture specific. The patches in this series only consider the constraints m, v, o & Q as being for memory operands and, happily, these seems to cover most usage for the most common architectures. Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@gmail.com>
-rw-r--r--allocate.c1
-rw-r--r--allocate.h1
-rw-r--r--dissect.c8
-rw-r--r--evaluate.c158
-rw-r--r--expand.c29
-rw-r--r--expression.c11
-rw-r--r--expression.h20
-rw-r--r--inline.c28
-rw-r--r--lib.h2
-rw-r--r--linearize.c48
-rw-r--r--linearize.h1
-rw-r--r--liveness.c5
-rw-r--r--parse.c22
-rw-r--r--parse.h4
-rw-r--r--show-parse.c3
-rw-r--r--validation/asm-bad0.c41
-rw-r--r--validation/bad-type-twice0.c4
-rw-r--r--validation/bad-type-twice1.c6
-rw-r--r--validation/compare-null-to-int.c6
-rw-r--r--validation/cond_expr.c4
-rw-r--r--validation/conditional-type.c32
-rw-r--r--validation/enum-mismatch.c6
-rw-r--r--validation/eval/asm-degen.c36
-rw-r--r--validation/eval/asm-memop.c47
-rw-r--r--validation/expand/asm0.c23
-rw-r--r--validation/expand/compound-literal.c26
-rw-r--r--validation/linear/asm-memop.c23
-rw-r--r--validation/linear/compound-literal02.c1
-rw-r--r--validation/static_assert.c6
29 files changed, 455 insertions, 147 deletions
diff --git a/allocate.c b/allocate.c
index 152fa896..bef85192 100644
--- a/allocate.c
+++ b/allocate.c
@@ -141,6 +141,7 @@ ALLOCATOR(ident, "identifiers");
ALLOCATOR(token, "tokens");
ALLOCATOR(context, "contexts");
ALLOCATOR(symbol, "symbols");
+ALLOCATOR(asm_operand, "asmops");
ALLOCATOR(expression, "expressions");
ALLOCATOR(statement, "statements");
ALLOCATOR(string, "strings");
diff --git a/allocate.h b/allocate.h
index 5137ae93..a6d30537 100644
--- a/allocate.h
+++ b/allocate.h
@@ -82,6 +82,7 @@ DECLARE_ALLOCATOR(ident);
DECLARE_ALLOCATOR(token);
DECLARE_ALLOCATOR(context);
DECLARE_ALLOCATOR(symbol);
+DECLARE_ALLOCATOR(asm_operand);
DECLARE_ALLOCATOR(expression);
DECLARE_ALLOCATOR(statement);
DECLARE_ALLOCATOR(string);
diff --git a/dissect.c b/dissect.c
index 5f067eb4..14d57bf5 100644
--- a/dissect.c
+++ b/dissect.c
@@ -450,13 +450,9 @@ again:
return ret;
}
-static void do_asm_xputs(usage_t mode, struct expression_list *xputs)
+static void do_asm_xputs(usage_t mode, struct asm_operand_list *xputs)
{
- int nr = 0;
-
- DO_LIST(xputs, expr,
- if (++nr % 3 == 0)
- do_expression(U_W_AOF | mode, expr));
+ DO_LIST(xputs, op, do_expression(U_W_AOF | mode, op->expr));
}
static struct symbol *do_statement(usage_t mode, struct statement *stmt)
diff --git a/evaluate.c b/evaluate.c
index d52fd9f9..919c944c 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -283,9 +283,9 @@ warn_for_different_enum_types (struct position pos,
return;
if (typea->type == SYM_ENUM && typeb->type == SYM_ENUM) {
- warning(pos, "mixing different enum types");
- info(pos, " %s versus", show_typename(typea));
- info(pos, " %s", show_typename(typeb));
+ warning(pos, "mixing different enum types:");
+ info(pos, " %s", show_typename(typea));
+ info(pos, " %s", show_typename(typeb));
}
}
@@ -413,16 +413,16 @@ static struct symbol *bad_expr_type(struct expression *expr)
case EXPR_COMPARE:
if (!valid_subexpr_type(expr))
break;
- sparse_error(expr->pos, "incompatible types for operation (%s)", show_special(expr->op));
- info(expr->pos, " left side has type %s", show_typename(expr->left->ctype));
- info(expr->pos, " right side has type %s", show_typename(expr->right->ctype));
+ sparse_error(expr->pos, "incompatible types for operation (%s):", show_special(expr->op));
+ info(expr->pos, " %s", show_typename(expr->left->ctype));
+ info(expr->pos, " %s", show_typename(expr->right->ctype));
break;
case EXPR_PREOP:
case EXPR_POSTOP:
if (!valid_expr_type(expr->unop))
break;
- sparse_error(expr->pos, "incompatible types for operation (%s)", show_special(expr->op));
- info(expr->pos, " argument has type %s", show_typename(expr->unop->ctype));
+ sparse_error(expr->pos, "incompatible type for operation (%s):", show_special(expr->op));
+ info(expr->pos, " %s", show_typename(expr->unop->ctype));
break;
default:
break;
@@ -910,8 +910,8 @@ static struct symbol *evaluate_conditional(struct expression *expr, int iterator
if (Waddress)
warning(expr->pos, "the address of %s will always evaluate as true", "an array");
} else if (!is_scalar_type(ctype)) {
- sparse_error(expr->pos, "incorrect type in conditional (non-scalar type)");
- info(expr->pos, " got %s", show_typename(ctype));
+ sparse_error(expr->pos, "non-scalar type in conditional:");
+ info(expr->pos, " %s", show_typename(ctype));
return NULL;
}
@@ -2937,6 +2937,7 @@ static struct symbol *evaluate_cast(struct expression *expr)
* initializer, in which case we need to pass
* the type value down to that initializer rather
* than trying to evaluate it as an expression
+ * (cfr. compound literals: C99 & C11 6.5.2.5).
*
* A more complex case is when the initializer is
* dereferenced as part of a post-fix expression.
@@ -3355,9 +3356,6 @@ struct symbol *evaluate_expression(struct expression *expr)
case EXPR_SLICE:
expression_error(expr, "internal front-end error: SLICE re-evaluated");
return NULL;
- case EXPR_ASM_OPERAND:
- expression_error(expr, "internal front-end error: ASM_OPERAND evaluated");
- return NULL;
}
return NULL;
}
@@ -3497,48 +3495,128 @@ static void evaluate_iterator(struct statement *stmt)
evaluate_statement(stmt->iterator_post_statement);
}
-static void verify_output_constraint(struct expression *expr, const char *constraint)
+
+static void parse_asm_constraint(struct asm_operand *op)
{
- switch (*constraint) {
- case '=': /* Assignment */
- case '+': /* Update */
+ struct expression *constraint = op->constraint;
+ const char *str = constraint->string->data;
+ int c;
+
+ switch (str[0]) {
+ case '+':
+ op->is_modify = true;
+ /* fall-through */
+ case '=':
+ op->is_assign = true;
+ str++;
break;
- default:
- expression_error(expr, "output constraint is not an assignment constraint (\"%s\")", constraint);
}
+
+ while ((c = *str++)) {
+ switch (c) {
+ case '=':
+ case '+':
+ sparse_error(constraint->pos, "invalid ASM constraint '%c'", c);
+ break;
+
+ case '&':
+ op->is_earlyclobber = true;
+ break;
+ case '%':
+ op->is_commutative = true;
+ break;
+ case 'r':
+ op->is_register = true;
+ break;
+
+ case 'm':
+ case 'o':
+ case 'V':
+ case 'Q':
+ op->is_memory = true;
+ break;
+
+ case '<':
+ case '>':
+ // FIXME: ignored for now
+ break;
+
+ case ',':
+ // FIXME: multiple alternative constraints
+ break;
+
+ case '0' ... '9':
+ // FIXME: numeric matching constraint?
+ break;
+ case '[':
+ // FIXME: symbolic matching constraint
+ return;
+
+ default:
+ // FIXME: arch-specific (and multi-letter) constraints
+ break;
+ }
+ }
+
+ // FIXME: how to deal with multi-constraint?
+ if (op->is_register)
+ op->is_memory = 0;
}
-static void verify_input_constraint(struct expression *expr, const char *constraint)
+static void verify_output_constraint(struct asm_operand *op)
{
- switch (*constraint) {
- case '=': /* Assignment */
- case '+': /* Update */
+ struct expression *expr = op->constraint;
+ const char *constraint = expr->string->data;
+
+ if (!op->is_assign)
+ expression_error(expr, "output constraint is not an assignment constraint (\"%s\")", constraint);
+}
+
+static void verify_input_constraint(struct asm_operand *op)
+{
+ struct expression *expr = op->constraint;
+ const char *constraint = expr->string->data;
+
+ if (op->is_assign)
expression_error(expr, "input constraint with assignment (\"%s\")", constraint);
+}
+
+static void evaluate_asm_memop(struct asm_operand *op)
+{
+ if (op->is_memory) {
+ struct expression *expr = op->expr;
+ struct expression *addr;
+
+ // implicit addressof
+ addr = alloc_expression(expr->pos, EXPR_PREOP);
+ addr->op = '&';
+ addr->unop = expr;
+
+ evaluate_addressof(addr);
+ op->expr = addr;
+ } else {
+ evaluate_expression(op->expr);
+ degenerate(op->expr);
}
}
static void evaluate_asm_statement(struct statement *stmt)
{
struct expression *expr;
- struct expression *op;
+ struct asm_operand *op;
struct symbol *sym;
- expr = stmt->asm_string;
- if (!expr || expr->type != EXPR_STRING) {
- sparse_error(stmt->pos, "need constant string for inline asm");
+ if (!stmt->asm_string)
return;
- }
FOR_EACH_PTR(stmt->asm_outputs, op) {
/* Identifier */
/* Constraint */
- expr = op->constraint;
- if (!expr || expr->type != EXPR_STRING) {
- sparse_error(expr ? expr->pos : stmt->pos, "asm output constraint is not a string");
- op->constraint = NULL;
- } else
- verify_output_constraint(expr, expr->string->data);
+ if (op->constraint) {
+ parse_asm_constraint(op);
+ verify_output_constraint(op);
+ }
/* Expression */
expr = op->expr;
@@ -3547,22 +3625,22 @@ static void evaluate_asm_statement(struct statement *stmt)
if (!lvalue_expression(expr))
warning(expr->pos, "asm output is not an lvalue");
evaluate_assign_to(expr, expr->ctype);
+ evaluate_asm_memop(op);
} END_FOR_EACH_PTR(op);
FOR_EACH_PTR(stmt->asm_inputs, op) {
/* Identifier */
/* Constraint */
- expr = op->constraint;
- if (!expr || expr->type != EXPR_STRING) {
- sparse_error(expr ? expr->pos : stmt->pos, "asm input constraint is not a string");
- op->constraint = NULL;
- } else
- verify_input_constraint(expr, expr->string->data);
+ if (op->constraint) {
+ parse_asm_constraint(op);
+ verify_input_constraint(op);
+ }
/* Expression */
if (!evaluate_expression(op->expr))
return;
+ evaluate_asm_memop(op);
} END_FOR_EACH_PTR(op);
FOR_EACH_PTR(stmt->asm_clobbers, expr) {
diff --git a/expand.c b/expand.c
index 455d5bae..d66375f4 100644
--- a/expand.c
+++ b/expand.c
@@ -66,6 +66,14 @@ static int expand_symbol_expression(struct expression *expr)
expr->taint = 0;
return 0;
}
+
+ // expand compound literals (C99 & C11 6.5.2.5)
+ // FIXME: is this the correct way to identify them?
+ // All compound literals are anonymous but is
+ // the reverse true?
+ if (sym->initializer && !expr->symbol_name)
+ return expand_expression(sym->initializer);
+
/* The cost of a symbol expression is lower for on-stack symbols */
return (sym->ctype.modifiers & (MOD_STATIC | MOD_EXTERN)) ? 2 : 1;
}
@@ -1095,9 +1103,6 @@ static int expand_expression(struct expression *expr)
case EXPR_OFFSETOF:
expression_error(expr, "internal front-end error: sizeof in expansion?");
return UNSAFE;
- case EXPR_ASM_OPERAND:
- expression_error(expr, "internal front-end error: ASM_OPERAND in expansion?");
- return UNSAFE;
}
return SIDE_EFFECTS;
}
@@ -1167,6 +1172,22 @@ static int expand_if_statement(struct statement *stmt)
return SIDE_EFFECTS;
}
+static int expand_asm_statement(struct statement *stmt)
+{
+ struct asm_operand *op;
+ int cost = 0;
+
+ FOR_EACH_PTR(stmt->asm_outputs, op) {
+ cost += expand_expression(op->expr);
+ } END_FOR_EACH_PTR(op);
+
+ FOR_EACH_PTR(stmt->asm_inputs, op) {
+ cost += expand_expression(op->expr);
+ } END_FOR_EACH_PTR(op);
+
+ return cost;
+}
+
/*
* Expanding a compound statement is really just
* about adding up the costs of each individual
@@ -1257,7 +1278,7 @@ static int expand_statement(struct statement *stmt)
case STMT_NONE:
break;
case STMT_ASM:
- /* FIXME! Do the asm parameter evaluation! */
+ expand_asm_statement(stmt);
break;
case STMT_CONTEXT:
expand_expression(stmt->expression);
diff --git a/expression.c b/expression.c
index f955fb15..ffb6cb9d 100644
--- a/expression.c
+++ b/expression.c
@@ -83,6 +83,17 @@ struct token *parens_expression(struct token *token, struct expression **expr, c
return expect(token, ')', where);
}
+struct token *string_expression(struct token *token, struct expression **expr, const char *where)
+{
+ struct token *next = primary_expression(token, expr);
+
+ if (!*expr || (*expr)->type != EXPR_STRING) {
+ sparse_error(token->pos, "string literal expected for %s", where);
+ *expr = NULL;
+ }
+ return next;
+}
+
/*
* Handle __func__, __FUNCTION__ and __PRETTY_FUNCTION__ token
* conversion
diff --git a/expression.h b/expression.h
index ed6f4d65..3b79e0f1 100644
--- a/expression.h
+++ b/expression.h
@@ -64,7 +64,6 @@ enum expression_type {
EXPR_FVALUE,
EXPR_SLICE,
EXPR_OFFSETOF,
- EXPR_ASM_OPERAND,
};
@@ -136,6 +135,18 @@ enum {
Taint_comma = 1,
}; /* for expr->taint */
+struct asm_operand {
+ struct ident *name;
+ struct expression *constraint;
+ struct expression *expr;
+ unsigned int is_assign:1;
+ unsigned int is_modify:1;
+ unsigned int is_earlyclobber:1;
+ unsigned int is_commutative:1;
+ unsigned int is_register:1;
+ unsigned int is_memory:1;
+};
+
struct expression {
enum expression_type type:8;
unsigned flags:8;
@@ -235,12 +246,6 @@ struct expression {
struct expression *index;
};
};
- // EXPR_ASM_OPERAND
- struct {
- struct ident *name;
- struct expression *constraint;
- struct expression *expr;
- };
};
};
@@ -270,6 +275,7 @@ struct token *parse_expression(struct token *token, struct expression **tree);
struct token *conditional_expression(struct token *token, struct expression **tree);
struct token *primary_expression(struct token *token, struct expression **tree);
struct token *parens_expression(struct token *token, struct expression **expr, const char *where);
+struct token *string_expression(struct token *token, struct expression **expr, const char *where);
struct token *assignment_expression(struct token *token, struct expression **tree);
extern int expand_symbol(struct symbol *);
diff --git a/inline.c b/inline.c
index fcc43db5..6f73a305 100644
--- a/inline.c
+++ b/inline.c
@@ -274,26 +274,24 @@ static struct expression * copy_expression(struct expression *expr)
}
break;
}
- case EXPR_ASM_OPERAND: {
- expr = dup_expression(expr);
- expr->constraint = copy_expression(expr->constraint);
- expr->expr = copy_expression(expr->expr);
- 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)
+static struct asm_operand_list *copy_asm_operands(struct asm_operand_list *in)
{
- struct expression_list *out = NULL;
- struct expression *expr;
-
- FOR_EACH_PTR(in, expr) {
- add_expression(&out, copy_expression(expr));
- } END_FOR_EACH_PTR(expr);
+ struct asm_operand_list *out = NULL;
+ struct asm_operand *old;
+
+ FOR_EACH_PTR(in, old) {
+ struct asm_operand *new = __alloc_asm_operand(0);
+ new->name = old->name;
+ new->constraint = copy_expression(old->constraint);
+ new->expr = copy_expression(old->expr);
+ add_ptr_list(&out, new);
+ } END_FOR_EACH_PTR(old);
return out;
}
@@ -445,8 +443,8 @@ static struct statement *copy_one_statement(struct statement *stmt)
}
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);
+ stmt->asm_inputs = copy_asm_operands(stmt->asm_inputs);
+ stmt->asm_outputs = copy_asm_operands(stmt->asm_outputs);
/* no need to dup "clobbers", since they are all constant strings */
break;
}
diff --git a/lib.h b/lib.h
index 322408be..697c977a 100644
--- a/lib.h
+++ b/lib.h
@@ -67,6 +67,7 @@ struct ident;
struct token;
struct symbol;
struct statement;
+struct asm_operand;
struct expression;
struct basic_block;
struct entrypoint;
@@ -76,6 +77,7 @@ struct pseudo;
DECLARE_PTR_LIST(symbol_list, struct symbol);
DECLARE_PTR_LIST(statement_list, struct statement);
+DECLARE_PTR_LIST(asm_operand_list, struct asm_operand);
DECLARE_PTR_LIST(expression_list, struct expression);
DECLARE_PTR_LIST(basic_block_list, struct basic_block);
DECLARE_PTR_LIST(instruction_list, struct instruction);
diff --git a/linearize.c b/linearize.c
index 9ed66737..30ed2a30 100644
--- a/linearize.c
+++ b/linearize.c
@@ -2075,41 +2075,45 @@ static pseudo_t linearize_range(struct entrypoint *ep, struct statement *stmt)
ALLOCATOR(asm_rules, "asm rules");
ALLOCATOR(asm_constraint, "asm constraints");
-static void add_asm_input(struct entrypoint *ep, struct instruction *insn, struct expression *expr,
- const char *constraint, const struct ident *ident)
+static void add_asm_input(struct entrypoint *ep, struct instruction *insn, struct asm_operand *op)
{
- pseudo_t pseudo = linearize_expression(ep, expr);
+ pseudo_t pseudo = linearize_expression(ep, op->expr);
struct asm_constraint *rule = __alloc_asm_constraint(0);
- rule->ident = ident;
- rule->constraint = constraint;
+ rule->ident = op->name;
+ rule->constraint = op->constraint ? op->constraint->string->data : "";
use_pseudo(insn, pseudo, &rule->pseudo);
add_ptr_list(&insn->asm_rules->inputs, rule);
}
-static void add_asm_output(struct entrypoint *ep, struct instruction *insn, struct expression *expr,
- const char *constraint, const struct ident *ident)
+static void add_asm_output(struct entrypoint *ep, struct instruction *insn, struct asm_operand *op)
{
struct access_data ad = { NULL, };
- pseudo_t pseudo = alloc_pseudo(insn);
+ pseudo_t pseudo;
struct asm_constraint *rule;
- if (!expr || !linearize_address_gen(ep, expr, &ad))
- return;
- linearize_store_gen(ep, pseudo, &ad);
+ if (op->is_memory) {
+ pseudo = linearize_expression(ep, op->expr);
+ } else {
+ if (!linearize_address_gen(ep, op->expr, &ad))
+ return;
+ pseudo = alloc_pseudo(insn);
+ linearize_store_gen(ep, pseudo, &ad);
+ }
rule = __alloc_asm_constraint(0);
- rule->ident = ident;
- rule->constraint = constraint;
+ rule->is_memory = op->is_memory;
+ rule->ident = op->name;
+ rule->constraint = op->constraint ? op->constraint->string->data : "";
use_pseudo(insn, pseudo, &rule->pseudo);
add_ptr_list(&insn->asm_rules->outputs, rule);
}
static pseudo_t linearize_asm_statement(struct entrypoint *ep, struct statement *stmt)
{
- struct expression *expr;
struct instruction *insn;
+ struct expression *expr;
struct asm_rules *rules;
- const char *constraint;
+ struct asm_operand *op;
insn = alloc_instruction(OP_ASM, 0);
expr = stmt->asm_string;
@@ -2123,18 +2127,16 @@ static pseudo_t linearize_asm_statement(struct entrypoint *ep, struct statement
insn->asm_rules = rules;
/* Gather the inputs.. */
- FOR_EACH_PTR(stmt->asm_inputs, expr) {
- constraint = expr->constraint ? expr->constraint->string->data : "";
- add_asm_input(ep, insn, expr->expr, constraint, expr->name);
- } END_FOR_EACH_PTR(expr);
+ FOR_EACH_PTR(stmt->asm_inputs, op) {
+ add_asm_input(ep, insn, op);
+ } END_FOR_EACH_PTR(op);
add_one_insn(ep, insn);
/* Assign the outputs */
- FOR_EACH_PTR(stmt->asm_outputs, expr) {
- constraint = expr->constraint ? expr->constraint->string->data : "";
- add_asm_output(ep, insn, expr->expr, constraint, expr->name);
- } END_FOR_EACH_PTR(expr);
+ FOR_EACH_PTR(stmt->asm_outputs, op) {
+ add_asm_output(ep, insn, op);
+ } END_FOR_EACH_PTR(op);
return VOID;
}
diff --git a/linearize.h b/linearize.h
index 89da3db6..76efd0b4 100644
--- a/linearize.h
+++ b/linearize.h
@@ -68,6 +68,7 @@ struct asm_constraint {
pseudo_t pseudo;
const char *constraint;
const struct ident *ident;
+ unsigned int is_memory:1;
};
DECLARE_ALLOCATOR(asm_constraint);
diff --git a/liveness.c b/liveness.c
index 93a7cc30..33cd0483 100644
--- a/liveness.c
+++ b/liveness.c
@@ -39,7 +39,10 @@ static void asm_liveness(struct basic_block *bb, struct instruction *insn,
} END_FOR_EACH_PTR(entry);
FOR_EACH_PTR(insn->asm_rules->outputs, entry) {
- def(bb, entry->pseudo);
+ if (entry->is_memory)
+ use(bb, entry->pseudo);
+ else
+ def(bb, entry->pseudo);
} END_FOR_EACH_PTR(entry);
}
diff --git a/parse.c b/parse.c
index b01c876e..ac795bd7 100644
--- a/parse.c
+++ b/parse.c
@@ -2049,22 +2049,23 @@ static struct token *expression_statement(struct token *token, struct expression
}
static struct token *parse_asm_operands(struct token *token, struct statement *stmt,
- struct expression_list **inout)
+ struct asm_operand_list **inout)
{
/* Allow empty operands */
if (match_op(token->next, ':') || match_op(token->next, ')'))
return token->next;
do {
- struct expression *op = alloc_expression(token->pos, EXPR_ASM_OPERAND);
+ struct asm_operand *op = __alloc_asm_operand(0);
if (match_op(token->next, '[') &&
token_type(token->next->next) == TOKEN_IDENT &&
match_op(token->next->next->next, ']')) {
op->name = token->next->next->ident;
token = token->next->next->next;
}
- token = primary_expression(token->next, &op->constraint);
+ token = token->next;
+ token = string_expression(token, &op->constraint, "asm constraint");
token = parens_expression(token, &op->expr, "in asm parameter");
- add_expression(inout, op);
+ add_ptr_list(inout, op);
} while (match_op(token, ','));
return token;
}
@@ -2113,7 +2114,7 @@ static struct token *parse_asm_statement(struct token *token, struct statement *
token = token->next;
}
token = expect(token, '(', "after asm");
- token = parse_expression(token, &stmt->asm_string);
+ token = string_expression(token, &stmt->asm_string, "inline asm");
if (match_op(token, ':'))
token = parse_asm_operands(token, stmt, &stmt->asm_outputs);
if (match_op(token, ':'))
@@ -2130,7 +2131,7 @@ static struct token *parse_asm_declarator(struct token *token, struct decl_state
{
struct expression *expr;
token = expect(token, '(', "after asm");
- token = parse_expression(token->next, &expr);
+ token = string_expression(token, &expr, "inline asm");
token = expect(token, ')', "after asm");
return token;
}
@@ -2144,14 +2145,9 @@ static struct token *parse_static_assert(struct token *token, struct symbol_list
if (!cond)
sparse_error(token->pos, "Expected constant expression");
token = expect(token, ',', "after conditional expression in _Static_assert");
- token = parse_expression(token, &message);
- if (!message || message->type != EXPR_STRING) {
- struct position pos;
-
- pos = message ? message->pos : token->pos;
- sparse_error(pos, "bad or missing string literal");
+ token = string_expression(token, &message, "_Static_assert()");
+ if (!message)
cond = NULL;
- }
token = expect(token, ')', "after diagnostic message in _Static_assert");
token = expect(token, ';', "after _Static_assert()");
diff --git a/parse.h b/parse.h
index 505c91c2..0742a2a8 100644
--- a/parse.h
+++ b/parse.h
@@ -106,8 +106,8 @@ struct statement {
};
struct /* asm */ {
struct expression *asm_string;
- struct expression_list *asm_outputs;
- struct expression_list *asm_inputs;
+ struct asm_operand_list *asm_outputs;
+ struct asm_operand_list *asm_inputs;
struct expression_list *asm_clobbers;
struct symbol_list *asm_labels;
};
diff --git a/show-parse.c b/show-parse.c
index 3aa06e47..37104167 100644
--- a/show-parse.c
+++ b/show-parse.c
@@ -1185,9 +1185,6 @@ int show_expression(struct expression *expr)
case EXPR_TYPE:
warning(expr->pos, "unable to show type expression");
return 0;
- case EXPR_ASM_OPERAND:
- warning(expr->pos, "unable to show asm operand expression");
- return 0;
}
return 0;
}
diff --git a/validation/asm-bad0.c b/validation/asm-bad0.c
new file mode 100644
index 00000000..aa9bf28d
--- /dev/null
+++ b/validation/asm-bad0.c
@@ -0,0 +1,41 @@
+extern char string[];
+extern int *var;
+
+static void templ(void)
+{
+ asm(string);
+}
+
+static void ocons(void)
+{
+ asm("template" : [out] string (var) : [in] "r" (0));
+}
+
+static void icons(void)
+{
+ asm("template" : [out] "=r" (var): [in] string (0));
+}
+
+static void oexpr(oid)
+{
+ asm("template" : [out] "=" (var[) : [in] "r" (0));
+}
+
+static void iexpr(void)
+{
+ asm("template" : [out] "=r" (var) : [in] "r" (var[));
+}
+
+/*
+ * check-name: asm-bad0
+ *
+ * check-error-start
+asm-bad0.c:6:13: error: string literal expected for inline asm
+asm-bad0.c:11:32: error: string literal expected for asm constraint
+asm-bad0.c:16:49: error: string literal expected for asm constraint
+asm-bad0.c:21:41: error: Expected ] at end of array dereference
+asm-bad0.c:21:41: error: got )
+asm-bad0.c:26:59: error: Expected ] at end of array dereference
+asm-bad0.c:26:59: error: got )
+ * check-error-end
+ */
diff --git a/validation/bad-type-twice0.c b/validation/bad-type-twice0.c
index 5d107a62..9e834d47 100644
--- a/validation/bad-type-twice0.c
+++ b/validation/bad-type-twice0.c
@@ -7,7 +7,7 @@ static int foo(a)
* check-name: bad-type-twice0
*
* check-error-start
-bad-type-twice0.c:3:16: error: incorrect type in conditional (non-scalar type)
-bad-type-twice0.c:3:16: got incomplete type a
+bad-type-twice0.c:3:16: error: non-scalar type in conditional:
+bad-type-twice0.c:3:16: incomplete type a
* check-error-end
*/
diff --git a/validation/bad-type-twice1.c b/validation/bad-type-twice1.c
index cc81662a..a9ba182c 100644
--- a/validation/bad-type-twice1.c
+++ b/validation/bad-type-twice1.c
@@ -9,8 +9,8 @@ static unsigned long foo(unsigned long val, void *ref)
* check-name: bad-type-twice1
*
* check-error-start
-bad-type-twice1.c:3:17: error: incompatible types for operation (>=)
-bad-type-twice1.c:3:17: left side has type unsigned long val
-bad-type-twice1.c:3:17: right side has type void *ref
+bad-type-twice1.c:3:17: error: incompatible types for operation (>=):
+bad-type-twice1.c:3:17: unsigned long val
+bad-type-twice1.c:3:17: void *ref
* check-error-end
*/
diff --git a/validation/compare-null-to-int.c b/validation/compare-null-to-int.c
index 08e556b3..336c724d 100644
--- a/validation/compare-null-to-int.c
+++ b/validation/compare-null-to-int.c
@@ -4,8 +4,8 @@ static unsigned int comparison = (void *)0 == 1;
* check-description: Sparse used to allow this.
*
* check-error-start
-compare-null-to-int.c:1:44: error: incompatible types for operation (==)
-compare-null-to-int.c:1:44: left side has type void *
-compare-null-to-int.c:1:44: right side has type int
+compare-null-to-int.c:1:44: error: incompatible types for operation (==):
+compare-null-to-int.c:1:44: void *
+compare-null-to-int.c:1:44: int
* check-error-end
*/
diff --git a/validation/cond_expr.c b/validation/cond_expr.c
index e55711cc..9b8105c1 100644
--- a/validation/cond_expr.c
+++ b/validation/cond_expr.c
@@ -13,7 +13,7 @@ int a(void)
* check-name: Two-argument conditional expression types
*
* check-error-start
-cond_expr.c:10:16: error: incompatible types for operation (~)
-cond_expr.c:10:16: argument has type double
+cond_expr.c:10:16: error: incompatible type for operation (~):
+cond_expr.c:10:16: double
* check-error-end
*/
diff --git a/validation/conditional-type.c b/validation/conditional-type.c
index 91267212..6e2da9b5 100644
--- a/validation/conditional-type.c
+++ b/validation/conditional-type.c
@@ -79,21 +79,21 @@ static int good_if_ptr(void *ptr)
* check-name: conditional-type
*
* check-error-start
-conditional-type.c:18:18: error: incorrect type in conditional (non-scalar type)
-conditional-type.c:18:18: got void
-conditional-type.c:19:13: error: incorrect type in conditional (non-scalar type)
-conditional-type.c:19:13: got struct state s
-conditional-type.c:24:18: error: incorrect type in conditional (non-scalar type)
-conditional-type.c:24:18: got void
-conditional-type.c:29:21: error: incorrect type in conditional (non-scalar type)
-conditional-type.c:29:21: got void
-conditional-type.c:30:16: error: incorrect type in conditional (non-scalar type)
-conditional-type.c:30:16: got struct state s
-conditional-type.c:34:21: error: incorrect type in conditional (non-scalar type)
-conditional-type.c:34:21: got void
-conditional-type.c:36:20: error: incorrect type in conditional (non-scalar type)
-conditional-type.c:36:20: got void
-conditional-type.c:40:21: error: incorrect type in conditional (non-scalar type)
-conditional-type.c:40:21: got void
+conditional-type.c:18:18: error: non-scalar type in conditional:
+conditional-type.c:18:18: void
+conditional-type.c:19:13: error: non-scalar type in conditional:
+conditional-type.c:19:13: struct state s
+conditional-type.c:24:18: error: non-scalar type in conditional:
+conditional-type.c:24:18: void
+conditional-type.c:29:21: error: non-scalar type in conditional:
+conditional-type.c:29:21: void
+conditional-type.c:30:16: error: non-scalar type in conditional:
+conditional-type.c:30:16: struct state s
+conditional-type.c:34:21: error: non-scalar type in conditional:
+conditional-type.c:34:21: void
+conditional-type.c:36:20: error: non-scalar type in conditional:
+conditional-type.c:36:20: void
+conditional-type.c:40:21: error: non-scalar type in conditional:
+conditional-type.c:40:21: void
* check-error-end
*/
diff --git a/validation/enum-mismatch.c b/validation/enum-mismatch.c
index 1bdb1d6c..a6e5d72d 100644
--- a/validation/enum-mismatch.c
+++ b/validation/enum-mismatch.c
@@ -12,8 +12,8 @@ static enum eb foo(enum ea a)
* check-command: sparse -Wenum-mismatch $file
*
* check-error-start
-enum-mismatch.c:7:16: warning: mixing different enum types
-enum-mismatch.c:7:16: unsigned int enum ea versus
-enum-mismatch.c:7:16: unsigned int enum eb
+enum-mismatch.c:7:16: warning: mixing different enum types:
+enum-mismatch.c:7:16: unsigned int enum ea
+enum-mismatch.c:7:16: unsigned int enum eb
* check-error-end
*/
diff --git a/validation/eval/asm-degen.c b/validation/eval/asm-degen.c
new file mode 100644
index 00000000..7bbed925
--- /dev/null
+++ b/validation/eval/asm-degen.c
@@ -0,0 +1,36 @@
+#ifdef __CHECKER__
+#define __percpu __attribute__((noderef))
+#else
+#define __percpu
+#endif
+
+static __percpu int var;
+static __percpu int arr[4];
+
+static void foo(void)
+{
+ asm("" :: "r" (var));
+}
+
+static void bar(void)
+{
+ asm("" :: "r" (arr));
+}
+
+static void baz(void)
+{
+ asm("" :: "m" (var));
+}
+
+static void qux(void)
+{
+ asm("" :: "m" (arr));
+}
+
+/*
+ * check-name: asm-degen
+ *
+ * check-error-start
+eval/asm-degen.c:12:24: warning: dereference of noderef expression
+ * check-error-end
+ */
diff --git a/validation/eval/asm-memop.c b/validation/eval/asm-memop.c
new file mode 100644
index 00000000..33ba0e5a
--- /dev/null
+++ b/validation/eval/asm-memop.c
@@ -0,0 +1,47 @@
+extern int g;
+
+void fo0(int *p) { asm volatile ("op %0" :: "p" (&g)); }
+void fo1(int *p) { asm volatile ("op %0" :: "m" (g)); }
+
+void fo2(int *p) { asm volatile ("op %0" :: "p" (p)); }
+void fo3(int *p) { asm volatile ("op %0" :: "m" (*p)); }
+
+/*
+ * check-name: eval-asm-memop
+ * check-command: test-linearize -Wno-decl $file
+ *
+ * check-output-start
+fo0:
+.L0:
+ <entry-point>
+ asm "op %0"
+ in: "p" (g)
+ ret
+
+
+fo1:
+.L2:
+ <entry-point>
+ asm "op %0"
+ in: "m" (g)
+ ret
+
+
+fo2:
+.L4:
+ <entry-point>
+ asm "op %0"
+ in: "p" (%arg1)
+ ret
+
+
+fo3:
+.L6:
+ <entry-point>
+ asm "op %0"
+ in: "m" (%arg1)
+ ret
+
+
+ * check-output-end
+ */
diff --git a/validation/expand/asm0.c b/validation/expand/asm0.c
new file mode 100644
index 00000000..568a4d19
--- /dev/null
+++ b/validation/expand/asm0.c
@@ -0,0 +1,23 @@
+static void foo(void)
+{
+ asm("" :: "i" (42 & 3));
+ asm("" :: "i" (__builtin_constant_p(0)));
+}
+
+/*
+ * check-name: expand-asm0
+ * check-command: test-linearize $file
+ *
+ * check-output-start
+foo:
+.L0:
+ <entry-point>
+ asm ""
+ in: "i" ($2)
+ asm ""
+ in: "i" ($1)
+ ret
+
+
+ * check-output-end
+ */
diff --git a/validation/expand/compound-literal.c b/validation/expand/compound-literal.c
new file mode 100644
index 00000000..034164bc
--- /dev/null
+++ b/validation/expand/compound-literal.c
@@ -0,0 +1,26 @@
+#define SAME_TYPE(A, B) \
+ __builtin_types_compatible_p(A, B)
+
+struct s {
+ int i;
+};
+
+static void foo(struct s *p)
+{
+ *p = (struct s) { .i = SAME_TYPE(int, int), };
+}
+
+/*
+ * check-name: compound-literal
+ * check-command: test-linearize $file
+ *
+ * check-output-start
+foo:
+.L0:
+ <entry-point>
+ store.32 $1 -> 0[%arg1]
+ ret
+
+
+ * check-output-end
+ */
diff --git a/validation/linear/asm-memop.c b/validation/linear/asm-memop.c
new file mode 100644
index 00000000..245c8d0f
--- /dev/null
+++ b/validation/linear/asm-memop.c
@@ -0,0 +1,23 @@
+static int foo(int *p)
+{
+ asm("op %0" : "=m" (p[0]));
+
+ return p[0];
+}
+
+/*
+ * check-name: linear-asm-memop
+ * check-command: test-linearize $file
+ *
+ * check-output-start
+foo:
+.L0:
+ <entry-point>
+ asm "op %0"
+ out: "=m" (%arg1)
+ load.32 %r4 <- 0[%arg1]
+ ret.32 %r4
+
+
+ * check-output-end
+ */
diff --git a/validation/linear/compound-literal02.c b/validation/linear/compound-literal02.c
index 87b98d76..6ed5809e 100644
--- a/validation/linear/compound-literal02.c
+++ b/validation/linear/compound-literal02.c
@@ -13,7 +13,6 @@ int bar(void)
* check-name: compound-literal02.c
* check-command: test-linearize -Wno-decl $file
*
- * check-known-to-fail
* check-output-ignore
* check-output-contains: ret\\..*\\$6
*/
diff --git a/validation/static_assert.c b/validation/static_assert.c
index d9e96294..dd5e0c08 100644
--- a/validation/static_assert.c
+++ b/validation/static_assert.c
@@ -61,11 +61,11 @@ static_assert.c:19:16: error: static assertion failed: "expected assertion failu
static_assert.c:22:16: error: bad constant expression
static_assert.c:25:16: error: bad constant expression
static_assert.c:27:16: error: bad constant expression
-static_assert.c:35:19: error: bad or missing string literal
+static_assert.c:35:19: error: string literal expected for _Static_assert()
static_assert.c:37:18: error: bad constant expression
-static_assert.c:52:19: error: bad or missing string literal
+static_assert.c:52:19: error: string literal expected for _Static_assert()
static_assert.c:53:16: error: Expected constant expression
static_assert.c:54:16: error: Expected constant expression
-static_assert.c:54:17: error: bad or missing string literal
+static_assert.c:54:17: error: string literal expected for _Static_assert()
* check-error-end
*/