Skip to content

Commit

Permalink
Precalculate nested member offset
Browse files Browse the repository at this point in the history
Member access cab be transformed from `target->field` to `*(target + offset(field))`.
Nested member access can be combined their offsets
and needed for `offsetof`.
  • Loading branch information
tyfkda committed Oct 22, 2023
1 parent 93d0a02 commit d2387e7
Show file tree
Hide file tree
Showing 8 changed files with 128 additions and 40 deletions.
12 changes: 8 additions & 4 deletions src/cc/backend/codegen_expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -204,7 +204,7 @@ static VReg *gen_cast(Expr *expr) {
return new_ir_cast(vreg, to_vsize(dst_type), to_vflag(dst_type));
}

static VReg *gen_lval(Expr *expr) {
static VReg *gen_ref_sub(Expr *expr) {
switch (expr->kind) {
case EX_VAR:
{
Expand Down Expand Up @@ -246,13 +246,17 @@ static VReg *gen_lval(Expr *expr) {

gen_clear_local_var(varinfo);
gen_stmts(expr->complit.inits);
return gen_lval(expr->complit.var);
return gen_ref_sub(expr->complit.var);
}
default: assert(false); break;
}
return NULL;
}

static VReg *gen_lval(Expr *expr) {
return gen_ref_sub(reduce_refer(expr));
}

static VReg *gen_variable(Expr *expr) {
switch (expr->type->kind) {
case TY_FIXNUM:
Expand Down Expand Up @@ -628,7 +632,7 @@ static VReg *gen_str(Expr *expr) {
}

static VReg *gen_ref(Expr *expr) {
return gen_lval(expr->unary.sub);
return gen_ref_sub(expr->unary.sub);
}

static VReg *gen_deref(Expr *expr) {
Expand All @@ -644,7 +648,7 @@ static VReg *gen_member(Expr *expr) {
const MemberInfo *minfo = expr->member.info;
if (minfo->bitfield.width > 0) {
Type *type = get_fixnum_type(minfo->bitfield.base_kind, minfo->type->fixnum.is_unsigned, 0);
Expr *ptr = new_expr_unary(EX_REF, ptrof(type), NULL, expr); // promote-to-int
Expr *ptr = make_cast(ptrof(type), expr->token, make_refer(expr->token, expr), true);
Expr *load = new_expr_deref(NULL, ptr);
Expr *e = extract_bitfield_value(load, minfo);
return gen_expr(e);
Expand Down
114 changes: 93 additions & 21 deletions src/cc/frontend/fe_misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -329,20 +329,96 @@ static void check_referable(const Token *tok, Expr *expr, const char *error) {
check_lval(tok, expr, error);
}

static Expr *reduce_refer_deref_add(Expr *expr, Type *subtype, Expr *lhs, Fixnum rhs) {
if (lhs->kind == EX_FIXNUM) {
return new_expr_unary(EX_DEREF, expr->type, expr->token,
new_expr_fixlit(subtype, lhs->token, lhs->fixnum + rhs));
} else if (lhs->kind == EX_DEREF && lhs->unary.sub->kind == EX_FIXNUM) {
// Concat 2 deref.
Expr *sub = lhs->unary.sub;
return new_expr_unary(EX_DEREF, expr->type, expr->token,
new_expr_fixlit(sub->type, sub->token, sub->fixnum + rhs));
} else if (lhs->kind == EX_ADD && lhs->bop.rhs->kind == EX_FIXNUM) {
// *(((lhs->lhs) + (lhs->rhs)) + rhs) => *(lhs->lhs + (lhs->rhs + rhs))
return new_expr_unary(EX_DEREF, expr->type, expr->token,
new_expr_bop(EX_ADD, subtype, lhs->token, lhs->bop.lhs,
new_expr_fixlit(lhs->bop.rhs->type, lhs->bop.rhs->token,
lhs->bop.rhs->fixnum + rhs)));
}
return NULL;
}

Expr *reduce_refer(Expr *expr) {
// target->field => *(target + offset(field))
switch (expr->kind) {
case EX_MEMBER:
{
Expr *target = reduce_refer(expr->member.target);
if (target->type->kind == TY_STRUCT && target->kind == EX_DEREF) {
// (*sub).field => sub->field
target = target->unary.sub;
}

const MemberInfo *minfo = expr->member.info;
Type *ptype = minfo->type;
ptype = ptrof(ptype->kind == TY_ARRAY ? array_to_ptr(ptype) : ptype);
// target->field => *(target + offset(field))
Expr *result = reduce_refer_deref_add(expr, ptype, target, minfo->offset);
if (result != NULL)
return result;

// Transform member access to pointer dereference, only if target is referenceable.
// target->field => *(target + offset(field))
switch (target->kind) {
case EX_VAR:
case EX_DEREF:
if (target->type->kind == TY_STRUCT) {
// target.field => (&target)->field
target = new_expr_unary(EX_REF, ptrof(target->type), target->token, target);
}
return new_expr_unary(EX_DEREF, minfo->type, expr->token,
new_expr_bop(EX_ADD, ptype, expr->token, target,
new_expr_fixlit(&tySize, expr->token, minfo->offset)));
default:
// ex. funcall().x cannot be taken its reference, so keep the expression.
break;
}
}
break;
case EX_DEREF:
{
Expr *sub = expr->unary.sub;
if (sub->kind == EX_ADD) {
// *(lhs + rhs) => *lhs + rhs
Expr *lhs, *rhs;
if (sub->bop.lhs->kind != EX_FIXNUM) {
lhs = sub->bop.lhs;
rhs = sub->bop.rhs;
} else {
lhs = sub->bop.rhs;
rhs = sub->bop.lhs;
}
Expr *lhs2 = reduce_refer(lhs);
if (rhs->kind == EX_FIXNUM) {
Expr *result = reduce_refer_deref_add(expr, sub->type, lhs2, rhs->fixnum);
if (result != NULL)
return result;
}
if (lhs2 != lhs)
return new_expr_unary(EX_DEREF, expr->type, expr->token,
new_expr_bop(EX_ADD, sub->type, sub->token, lhs2, rhs));
}
}
break;
default: break;
}
return expr;
}

Expr *make_refer(const Token *tok, Expr *expr) {
check_referable(tok, expr, "Cannot take reference");

if (expr->kind == EX_MEMBER &&
expr->member.target->kind == EX_FIXNUM && expr->token->kind == TK_ARROW) {
assert(expr->member.target->type->kind == TY_PTR);
const Type *stype = expr->member.target->type->pa.ptrof;
assert(stype->kind == TY_STRUCT);
StructInfo *sinfo = stype->struct_.info;
assert(sinfo != NULL);
const MemberInfo *minfo = expr->member.info;
Fixnum value = expr->member.target->fixnum + minfo->offset;
return new_expr_fixlit(ptrof(minfo->type), tok, value);
}
expr = reduce_refer(expr);

if (expr->kind == EX_DEREF)
return expr->unary.sub;
Expand Down Expand Up @@ -639,8 +715,7 @@ Expr *assign_to_bitfield(const Token *tok, Expr *lhs, Expr *rhs, const MemberInf
Type *ptype = ptrof(type);
assert(!is_global_scope(curscope));
Expr *ptr = alloc_tmp_var(curscope, ptype);
Expr *ptr_assign = new_expr_bop(EX_ASSIGN, ptype, tok, ptr,
new_expr_unary(EX_REF, ptype, lhs->token, lhs));
Expr *ptr_assign = new_expr_bop(EX_ASSIGN, ptype, tok, ptr, make_refer(lhs->token, lhs));

Type *vtype = rhs->type;
Expr *val = alloc_tmp_var(curscope, vtype);
Expand All @@ -662,8 +737,7 @@ static Expr *transform_incdec_of_bitfield(enum ExprKind kind, Expr *target, cons
Type *ptype = ptrof(type);
assert(!is_global_scope(curscope));
Expr *ptr = alloc_tmp_var(curscope, ptype);
Expr *ptr_assign = new_expr_bop(EX_ASSIGN, ptype, tok, ptr,
new_expr_unary(EX_REF, ptype, target->token, target));
Expr *ptr_assign = new_expr_bop(EX_ASSIGN, ptype, tok, ptr, make_refer(target->token, target));
Expr *dst = new_expr_unary(EX_DEREF, type, tok, ptr);

Expr *src = alloc_tmp_var(curscope, type);
Expand Down Expand Up @@ -723,9 +797,9 @@ static enum ExprKind swap_cmp(enum ExprKind kind) {

Expr *new_expr_cmp(enum ExprKind kind, const Token *tok, Expr *lhs, Expr *rhs) {
if (lhs->type->kind == TY_FUNC)
lhs = new_expr_unary(EX_REF, ptrof(lhs->type), lhs->token, lhs);
lhs = make_refer(lhs->token, lhs);
if (rhs->type->kind == TY_FUNC)
rhs = new_expr_unary(EX_REF, ptrof(rhs->type), rhs->token, rhs);
rhs = make_refer(rhs->token, rhs);

Type *lt = lhs->type, *rt = rhs->type;
if (ptr_or_array(lt) || ptr_or_array(rt)) {
Expand Down Expand Up @@ -1043,8 +1117,7 @@ static Expr *transform_assign_with_bitfield(const Token *tok, Expr *lhs, Expr *r
Type *ptype = ptrof(type);
assert(!is_global_scope(curscope));
Expr *ptr = alloc_tmp_var(curscope, ptype);
Expr *ptr_assign = new_expr_bop(EX_ASSIGN, ptype, tok, ptr,
new_expr_unary(EX_REF, ptype, lhs->token, lhs));
Expr *ptr_assign = new_expr_bop(EX_ASSIGN, ptype, tok, ptr, make_refer(lhs->token, lhs));
Expr *dst = new_expr_unary(EX_DEREF, type, tok, ptr);

Expr *src = alloc_tmp_var(curscope, type);
Expand Down Expand Up @@ -1079,8 +1152,7 @@ Expr *transform_assign_with(const Token *tok, Expr *lhs, Expr *rhs) {
Type *ptype = ptrof(lhs->type);
assert(!is_global_scope(curscope));
Expr *ptr = alloc_tmp_var(curscope, ptype);
tmp_assign = new_expr_bop(EX_ASSIGN, ptype, tok, ptr,
new_expr_unary(EX_REF, ptype, lhs->token, lhs));
tmp_assign = new_expr_bop(EX_ASSIGN, ptype, tok, ptr, make_refer(lhs->token, lhs));
lhs = new_expr_unary(EX_DEREF, lhs->type, lhs->token, ptr);
}

Expand Down
1 change: 1 addition & 0 deletions src/cc/frontend/fe_misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ Expr *make_cast(Type *type, const Token *token, Expr *sub, bool is_explicit);
const MemberInfo *search_from_anonymous(const Type *type, const Name *name, const Token *ident,
Vector *stack);
void check_lval(const Token *tok, Expr *expr, const char *error);
Expr *reduce_refer(Expr *expr);
Expr *make_refer(const Token *tok, Expr *expr);
Expr *promote_to_int(Expr *expr);
Expr *new_expr_num_bop(enum ExprKind kind, const Token *tok, Expr *lhs, Expr *rhs);
Expand Down
6 changes: 4 additions & 2 deletions src/cc/frontend/initializer.c
Original file line number Diff line number Diff line change
Expand Up @@ -732,12 +732,13 @@ Vector *assign_initial_value(Expr *expr, Initializer *init, Vector *inits) {

const StructInfo *sinfo = expr->type->struct_.info;
if (!sinfo->is_union) {
Token *tok = alloc_token(TK_DOT, NULL, ".", NULL);
for (int i = 0, n = sinfo->member_count; i < n; ++i) {
Initializer *init_elem = init->multi->data[i];
if (init_elem == NULL)
continue;
const MemberInfo *minfo = &sinfo->members[i];
Expr *member = new_expr_member(NULL, minfo->type, expr, NULL, minfo);
Expr *member = new_expr_member(tok, minfo->type, expr, NULL, minfo);
#ifndef __NO_BITFIELD
if (minfo->bitfield.width > 0) {
if (init_elem->kind != IK_SINGLE) {
Expand All @@ -760,6 +761,7 @@ Vector *assign_initial_value(Expr *expr, Initializer *init, Vector *inits) {
parse_error(PE_FATAL, init->token, "Initializer for empty union");

int count = 0;
Token *tok = alloc_token(TK_DOT, NULL, ".", NULL);
for (int i = 0; i < n; ++i) {
Initializer *init_elem = init->multi->data[i];
if (init_elem == NULL)
Expand All @@ -770,7 +772,7 @@ Vector *assign_initial_value(Expr *expr, Initializer *init, Vector *inits) {
}

const MemberInfo *minfo = &sinfo->members[i];
Expr *mem = new_expr_member(NULL, minfo->type, expr, NULL, minfo);
Expr *mem = new_expr_member(tok, minfo->type, expr, NULL, minfo);
assign_initial_value(mem, init_elem, inits);
++count;
}
Expand Down
5 changes: 4 additions & 1 deletion src/cc/frontend/parser_expr.c
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ static Expr *parse_member_access(Expr *target, Token *acctok) {
return target;
}
Expr *p = target;
Token *tok = acctok;
for (int i = 0; i < stack->len; ++i) {
int index = (int)(long)stack->data[i];
const MemberInfo *minfo = &type->struct_.info->members[index];
Expand All @@ -149,7 +150,9 @@ static Expr *parse_member_access(Expr *target, Token *acctok) {
assert(equal_name(minfo->name, ident->ident));
member_name = ident->ident;
}
p = new_expr_member(acctok, type, p, member_name, minfo);
p = new_expr_member(tok, type, p, member_name, minfo);
if (tok->kind != TK_DOT)
tok = alloc_token(TK_DOT, acctok->line, acctok->begin, acctok->end);
}
return p;
}
Expand Down
17 changes: 8 additions & 9 deletions src/wcc/gen_wasm.c
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ static void gen_clear_local_var(const VarInfo *varinfo) {
ADD_CODE(OP_EXTENSION, OPEX_MEMORY_FILL, 0);
}

static void gen_lval(Expr *expr) {
static void gen_ref_sub(Expr *expr) {
switch (expr->kind) {
case EX_VAR:
{
Expand Down Expand Up @@ -534,6 +534,10 @@ static void gen_lval(Expr *expr) {
}
}

static void gen_lval(Expr *expr) {
gen_ref_sub(reduce_refer(expr));
}

static void gen_var(Expr *expr, bool needval) {
if (!needval)
return;
Expand Down Expand Up @@ -845,14 +849,8 @@ static void gen_cast(Expr *expr, bool needval) {

static void gen_ref(Expr *expr, bool needval) {
Expr *sub = expr->unary.sub;
/*if (sub->kind == EX_VAR && !is_global_scope(sub->var.scope)) {
const VarInfo *varinfo = scope_find(sub->var.scope, sub->var.name, NULL);
assert(varinfo != NULL);
assert(varinfo->local.vreg != NULL);
varinfo->local.vreg->flag |= VRF_REF;
}*/
if (needval)
gen_lval(sub);
gen_ref_sub(sub);
else
gen_expr(sub, false);
}
Expand Down Expand Up @@ -883,7 +881,8 @@ static void gen_member(Expr *expr, bool needval) {
#ifndef __NO_BITFIELD
const MemberInfo *minfo = expr->member.info;
if (minfo->bitfield.width > 0) {
Expr *ptr = new_expr_unary(EX_REF, ptrof(minfo->type), NULL, expr); // promote-to-int
Type *type = get_fixnum_type(minfo->bitfield.base_kind, minfo->type->fixnum.is_unsigned, 0);
Expr *ptr = make_cast(ptrof(type), expr->token, make_refer(expr->token, expr), true);
Expr *load = new_expr_deref(NULL, ptr);
Expr *e = extract_bitfield_value(load, minfo);
gen_expr(e, needval);
Expand Down
3 changes: 1 addition & 2 deletions src/wcc/traverse.c
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,7 @@ static void te_incdec(Expr **pexpr, bool needval) {
Expr *tmp = alloc_tmp_var(curscope, type);
enum ExprKind op = kOpAddSub[dec];

Expr *assign_p = new_expr_bop(EX_ASSIGN, &tyVoid, token, p,
new_expr_unary(EX_REF, ptrtype, token, target));
Expr *assign_p = new_expr_bop(EX_ASSIGN, &tyVoid, token, p, make_refer(token, target));
Expr *deref_p = new_expr_deref(token, p);
Expr *one = type->kind == TY_PTR ? new_expr_fixlit(&tySize, token, type_size(type->pa.ptrof))
: new_expr_fixlit(type, token, 1);
Expand Down
10 changes: 9 additions & 1 deletion tests/valtest.c
Original file line number Diff line number Diff line change
Expand Up @@ -1304,16 +1304,24 @@ TEST(struct) {
#define NULL ((void*)0)
#undef offsetof
#define offsetof(S, m) ((long)(&((S*)NULL)->m))
struct X {char x[5]; char z;};
struct X {char x[5]; char z; struct {char b;} s; struct {char c[3];} t[4]; };
char a[offsetof(struct X, z)];
EXPECT("offsetof is const", 5, sizeof(a));
char b[offsetof(struct X, s.b)];
EXPECT("offsetof is const", 6, sizeof(b));
char c[offsetof(struct X, t[3].c)];
EXPECT("offsetof is const", 16, sizeof(c));
char d[offsetof(struct X, t[3].c[2])];
EXPECT("offsetof is const", 18, sizeof(d));
}

{
FooStruct s = return_struct();
EXPECT("return struct", 46, s.x + s.y);

EXPECT("return struct member", 12, return_struct().x);
int y = return_struct().y;
EXPECT("return struct member", 34, y);
}
{
int dummy[1];
Expand Down

0 comments on commit d2387e7

Please sign in to comment.