aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
-rw-r--r--evaluate.c86
-rw-r--r--expand.c3
-rw-r--r--expression.h6
-rw-r--r--inline.c7
-rw-r--r--linearize.c22
-rw-r--r--linearize.h5
-rw-r--r--show-parse.c10
7 files changed, 129 insertions, 10 deletions
diff --git a/evaluate.c b/evaluate.c
index 8d6d4ff8..0b9113bf 100644
--- a/evaluate.c
+++ b/evaluate.c
@@ -1063,11 +1063,57 @@ static struct symbol *degenerate(struct expression *expr)
base = ctype->ctype.base_type;
/*
* Arrays degenerate into pointers to the entries, while
- * functions degenerate into pointers to themselves
+ * functions degenerate into pointers to themselves.
+ * If array was part of non-lvalue compound, we create a copy
+ * of that compound first and then act as if we were dealing with
+ * the corresponding field in there.
*/
switch (base->type) {
- case SYM_FN:
case SYM_ARRAY:
+ if (expr->type == EXPR_SLICE) {
+ struct symbol *a = alloc_symbol(expr->pos, SYM_NODE);
+ struct expression *e0, *e1, *e2, *e3, *e4;
+
+ a->ctype.base_type = expr->base->ctype;
+ a->bit_size = expr->base->ctype->bit_size;
+ a->array_size = expr->base->ctype->array_size;
+
+ e0 = alloc_expression(expr->pos, EXPR_SYMBOL);
+ e0->symbol = a;
+ e0->ctype = &lazy_ptr_ctype;
+
+ e1 = alloc_expression(expr->pos, EXPR_PREOP);
+ e1->unop = e0;
+ e1->op = '*';
+ e1->ctype = expr->base->ctype; /* XXX */
+
+ e2 = alloc_expression(expr->pos, EXPR_ASSIGNMENT);
+ e2->left = e1;
+ e2->right = expr->base;
+ e2->op = '=';
+ e2->ctype = expr->base->ctype;
+
+ if (expr->r_bitpos) {
+ e3 = alloc_expression(expr->pos, EXPR_BINOP);
+ e3->op = '+';
+ e3->left = e0;
+ e3->right = alloc_const_expression(expr->pos,
+ expr->r_bitpos >> 3);
+ e3->ctype = &lazy_ptr_ctype;
+ } else {
+ e3 = e0;
+ }
+
+ e4 = alloc_expression(expr->pos, EXPR_COMMA);
+ e4->left = e2;
+ e4->right = e3;
+ e4->ctype = &lazy_ptr_ctype;
+
+ expr->unop = e4;
+ expr->type = EXPR_PREOP;
+ expr->op = '*';
+ }
+ case SYM_FN:
if (expr->op != '*' || expr->type != EXPR_PREOP) {
warn(expr->pos, "strange non-value function or array");
return NULL;
@@ -1338,12 +1384,6 @@ static struct symbol *evaluate_member_dereference(struct expression *expr)
address_space |= ctype->ctype.as;
mod |= ctype->ctype.modifiers;
}
- if (!lvalue_expression(deref)) {
- warn(deref->pos, "expected lvalue for member dereference");
- return NULL;
- }
- deref = deref->unop;
- expr->deref = deref;
if (!ctype || (ctype->type != SYM_STRUCT && ctype->type != SYM_UNION)) {
warn(expr->pos, "expected structure or union");
return NULL;
@@ -1368,9 +1408,32 @@ static struct symbol *evaluate_member_dereference(struct expression *expr)
* the "parent" type.
*/
member = convert_to_as_mod(member, address_space, mod);
- add = evaluate_offset(deref, offset);
-
ctype = member->ctype.base_type;
+
+ if (!lvalue_expression(deref)) {
+ if (deref->type != EXPR_SLICE) {
+ expr->base = deref;
+ expr->r_bitpos = 0;
+ } else {
+ expr->base = deref->base;
+ expr->r_bitpos = deref->r_bitpos;
+ }
+ expr->r_bitpos += offset << 3;
+ expr->type = EXPR_SLICE;
+ if (ctype->type == SYM_BITFIELD) {
+ expr->r_bitpos += member->bit_offset;
+ expr->r_nrbits = member->fieldwidth;
+ } else {
+ expr->r_nrbits = member->bit_size;
+ }
+ expr->ctype = member;
+ return member;
+ }
+
+ deref = deref->unop;
+ expr->deref = deref;
+
+ add = evaluate_offset(deref, offset);
if (ctype->type == SYM_BITFIELD) {
expr->type = EXPR_BITFIELD;
expr->bitpos = member->bit_offset;
@@ -1886,6 +1949,9 @@ struct symbol *evaluate_expression(struct expression *expr)
case EXPR_POS:
warn(expr->pos, "internal front-end error: initializer in expression");
return NULL;
+ case EXPR_SLICE:
+ warn(expr->pos, "internal front-end error: SLICE re-evaluated");
+ return NULL;
}
return NULL;
}
diff --git a/expand.c b/expand.c
index 4e516f80..f38ccf6c 100644
--- a/expand.c
+++ b/expand.c
@@ -754,6 +754,9 @@ static int expand_expression(struct expression *expr)
case EXPR_INDEX:
return UNSAFE;
+ case EXPR_SLICE:
+ return expand_expression(expr->base) + 1;
+
case EXPR_POS:
return expand_expression(expr->init_expr);
diff --git a/expression.h b/expression.h
index d03cdc35..4c10db8e 100644
--- a/expression.h
+++ b/expression.h
@@ -40,6 +40,7 @@ enum expression_type {
EXPR_INDEX, // index in initializer
EXPR_POS, // position in initializer
EXPR_FVALUE,
+ EXPR_SLICE,
};
struct expression {
@@ -78,6 +79,11 @@ struct expression {
struct expression *deref;
struct ident *member;
};
+ // EXPR_SLICE
+ struct /* slice */ {
+ struct expression *base;
+ unsigned r_bitpos, r_nrbits;
+ };
// EXPR_CAST and EXPR_SIZEOF
struct /* cast_arg */ {
struct symbol *cast_type;
diff --git a/inline.c b/inline.c
index 1c40dc3e..d4323c9a 100644
--- a/inline.c
+++ b/inline.c
@@ -91,6 +91,13 @@ static struct expression * copy_expression(struct expression *expr)
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:
diff --git a/linearize.c b/linearize.c
index b8c2dbce..22e3fbe7 100644
--- a/linearize.c
+++ b/linearize.c
@@ -190,6 +190,12 @@ static void show_instruction(struct instruction *insn)
break;
}
+ case OP_SLICE:
+ printf("\t%%r%d <- slice %%r%d, %d, %d\n",
+ insn->target->nr,
+ insn->base->nr, insn->from, insn->len);
+ break;
+
case OP_BINCMP ... OP_BINCMP_END: {
static const char *opname[] = {
[OP_SET_EQ - OP_BINCMP] = "seteq",
@@ -477,6 +483,19 @@ static pseudo_t add_uniop(struct entrypoint *ep, struct expression *expr, int op
return new;
}
+static pseudo_t linearize_slice(struct entrypoint *ep, struct expression *expr)
+{
+ pseudo_t pre = linearize_expression(ep, expr->base);
+ pseudo_t new = alloc_pseudo();
+ struct instruction *insn = alloc_instruction(OP_SLICE, expr->ctype);
+ insn->target = new;
+ insn->base = pre;
+ insn->from = expr->r_bitpos;
+ insn->len = expr->r_nrbits;
+ add_one_insn(ep, expr->pos, insn);
+ return new;
+}
+
static pseudo_t linearize_regular_preop(struct entrypoint *ep, struct expression *expr)
{
pseudo_t pre = linearize_expression(ep, expr->unop);
@@ -803,6 +822,9 @@ pseudo_t linearize_expression(struct entrypoint *ep, struct expression *expr)
case EXPR_BITFIELD:
return linearize_access(ep, expr);
+ case EXPR_SLICE:
+ return linearize_slice(ep, expr);
+
default:
warn(expr->pos, "unknown expression (%d %d)", expr->type, expr->op);
return VOID;
diff --git a/linearize.h b/linearize.h
index ada834ad..003cf7f7 100644
--- a/linearize.h
+++ b/linearize.h
@@ -51,6 +51,10 @@ struct instruction {
struct /* binops */ {
pseudo_t src1, src2;
};
+ struct /* slice */ {
+ pseudo_t base;
+ unsigned from, len;
+ };
struct /* multijump */ {
int begin, end;
};
@@ -131,6 +135,7 @@ enum opcode {
OP_CALL,
OP_VANEXT,
OP_VAARG,
+ OP_SLICE,
};
struct basic_block_list;
diff --git a/show-parse.c b/show-parse.c
index ab49d517..5161c78a 100644
--- a/show-parse.c
+++ b/show-parse.c
@@ -632,6 +632,14 @@ static int show_binop(struct expression *expr)
return new;
}
+static int show_slice(struct expression *expr)
+{
+ int target = show_expression(expr->base);
+ int new = new_pseudo();
+ printf("\tslice.%d\t\tv%d,v%d,%d\n", expr->r_nrbits, target, new, expr->r_bitpos);
+ return new;
+}
+
static int show_regular_preop(struct expression *expr)
{
int target = show_expression(expr->unop);
@@ -993,6 +1001,8 @@ int show_expression(struct expression *expr)
return show_statement_expr(expr);
case EXPR_LABEL:
return show_label_expr(expr);
+ case EXPR_SLICE:
+ return show_slice(expr);
// None of these should exist as direct expressions: they are only
// valid as sub-expressions of initializers.