mirror of https://github.com/rui314/chibicc.git
Add bitfield
This commit is contained in:
parent
be8b6f6d31
commit
cc852fe99d
|
@ -313,6 +313,11 @@ struct Member {
|
||||||
int idx;
|
int idx;
|
||||||
int align;
|
int align;
|
||||||
int offset;
|
int offset;
|
||||||
|
|
||||||
|
// Bitfield
|
||||||
|
bool is_bitfield;
|
||||||
|
int bit_offset;
|
||||||
|
int bit_width;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern Type *ty_void;
|
extern Type *ty_void;
|
||||||
|
|
33
codegen.c
33
codegen.c
|
@ -599,10 +599,23 @@ static void gen_expr(Node *node) {
|
||||||
println(" neg %%rax");
|
println(" neg %%rax");
|
||||||
return;
|
return;
|
||||||
case ND_VAR:
|
case ND_VAR:
|
||||||
case ND_MEMBER:
|
|
||||||
gen_addr(node);
|
gen_addr(node);
|
||||||
load(node->ty);
|
load(node->ty);
|
||||||
return;
|
return;
|
||||||
|
case ND_MEMBER: {
|
||||||
|
gen_addr(node);
|
||||||
|
load(node->ty);
|
||||||
|
|
||||||
|
Member *mem = node->member;
|
||||||
|
if (mem->is_bitfield) {
|
||||||
|
println(" shl $%d, %%rax", 64 - mem->bit_width - mem->bit_offset);
|
||||||
|
if (mem->ty->is_unsigned)
|
||||||
|
println(" shr $%d, %%rax", 64 - mem->bit_width);
|
||||||
|
else
|
||||||
|
println(" sar $%d, %%rax", 64 - mem->bit_width);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
case ND_DEREF:
|
case ND_DEREF:
|
||||||
gen_expr(node->lhs);
|
gen_expr(node->lhs);
|
||||||
load(node->ty);
|
load(node->ty);
|
||||||
|
@ -614,6 +627,24 @@ static void gen_expr(Node *node) {
|
||||||
gen_addr(node->lhs);
|
gen_addr(node->lhs);
|
||||||
push();
|
push();
|
||||||
gen_expr(node->rhs);
|
gen_expr(node->rhs);
|
||||||
|
|
||||||
|
if (node->lhs->kind == ND_MEMBER && node->lhs->member->is_bitfield) {
|
||||||
|
// If the lhs is a bitfield, we need to read the current value
|
||||||
|
// from memory and merge it with a new value.
|
||||||
|
Member *mem = node->lhs->member;
|
||||||
|
println(" mov %%rax, %%rdi");
|
||||||
|
println(" and $%ld, %%rdi", (1L << mem->bit_width) - 1);
|
||||||
|
println(" shl $%d, %%rdi", mem->bit_offset);
|
||||||
|
|
||||||
|
println(" mov (%%rsp), %%rax");
|
||||||
|
load(mem->ty);
|
||||||
|
|
||||||
|
long mask = ((1L << mem->bit_width) - 1) << mem->bit_offset;
|
||||||
|
println(" mov $%ld, %%r9", ~mask);
|
||||||
|
println(" and %%r9, %%rax");
|
||||||
|
println(" or %%rdi, %%rax");
|
||||||
|
}
|
||||||
|
|
||||||
store(node->ty);
|
store(node->ty);
|
||||||
return;
|
return;
|
||||||
case ND_STMT_EXPR:
|
case ND_STMT_EXPR:
|
||||||
|
|
32
parse.c
32
parse.c
|
@ -154,6 +154,10 @@ static bool is_function(Token *tok);
|
||||||
static Token *function(Token *tok, Type *basety, VarAttr *attr);
|
static Token *function(Token *tok, Type *basety, VarAttr *attr);
|
||||||
static Token *global_variable(Token *tok, Type *basety, VarAttr *attr);
|
static Token *global_variable(Token *tok, Type *basety, VarAttr *attr);
|
||||||
|
|
||||||
|
static int align_down(int n, int align) {
|
||||||
|
return align_to(n - align + 1, align);
|
||||||
|
}
|
||||||
|
|
||||||
static void enter_scope(void) {
|
static void enter_scope(void) {
|
||||||
Scope *sc = calloc(1, sizeof(Scope));
|
Scope *sc = calloc(1, sizeof(Scope));
|
||||||
sc->next = scope;
|
sc->next = scope;
|
||||||
|
@ -1997,6 +2001,12 @@ static void struct_members(Token **rest, Token *tok, Type *ty) {
|
||||||
mem->name = mem->ty->name;
|
mem->name = mem->ty->name;
|
||||||
mem->idx = idx++;
|
mem->idx = idx++;
|
||||||
mem->align = attr.align ? attr.align : mem->ty->align;
|
mem->align = attr.align ? attr.align : mem->ty->align;
|
||||||
|
|
||||||
|
if (consume(&tok, tok, ":")) {
|
||||||
|
mem->is_bitfield = true;
|
||||||
|
mem->bit_width = const_expr(&tok, tok);
|
||||||
|
}
|
||||||
|
|
||||||
cur = cur->next = mem;
|
cur = cur->next = mem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2066,16 +2076,28 @@ static Type *struct_decl(Token **rest, Token *tok) {
|
||||||
return ty;
|
return ty;
|
||||||
|
|
||||||
// Assign offsets within the struct to members.
|
// Assign offsets within the struct to members.
|
||||||
int offset = 0;
|
int bits = 0;
|
||||||
|
|
||||||
for (Member *mem = ty->members; mem; mem = mem->next) {
|
for (Member *mem = ty->members; mem; mem = mem->next) {
|
||||||
offset = align_to(offset, mem->align);
|
if (mem->is_bitfield) {
|
||||||
mem->offset = offset;
|
int sz = mem->ty->size;
|
||||||
offset += mem->ty->size;
|
if (bits / (sz * 8) != (bits + mem->bit_width - 1) / (sz * 8))
|
||||||
|
bits = align_to(bits, sz * 8);
|
||||||
|
|
||||||
|
mem->offset = align_down(bits / 8, sz);
|
||||||
|
mem->bit_offset = bits % (sz * 8);
|
||||||
|
bits += mem->bit_width;
|
||||||
|
} else {
|
||||||
|
bits = align_to(bits, mem->align * 8);
|
||||||
|
mem->offset = bits / 8;
|
||||||
|
bits += mem->ty->size * 8;
|
||||||
|
}
|
||||||
|
|
||||||
if (ty->align < mem->align)
|
if (ty->align < mem->align)
|
||||||
ty->align = mem->align;
|
ty->align = mem->align;
|
||||||
}
|
}
|
||||||
ty->size = align_to(offset, ty->align);
|
|
||||||
|
ty->size = align_to(bits, ty->align * 8) / 8;
|
||||||
return ty;
|
return ty;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
#include "test.h"
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
ASSERT(4, sizeof(struct {int x:1; }));
|
||||||
|
ASSERT(8, sizeof(struct {long x:1; }));
|
||||||
|
|
||||||
|
struct bit1 {
|
||||||
|
short a;
|
||||||
|
char b;
|
||||||
|
int c : 2;
|
||||||
|
int d : 3;
|
||||||
|
int e : 3;
|
||||||
|
};
|
||||||
|
|
||||||
|
ASSERT(4, sizeof(struct bit1));
|
||||||
|
ASSERT(1, ({ struct bit1 x; x.a=1; x.b=2; x.c=3; x.d=4; x.e=5; x.a; }));
|
||||||
|
ASSERT(1, ({ struct bit1 x={1,2,3,4,5}; x.a; }));
|
||||||
|
ASSERT(2, ({ struct bit1 x={1,2,3,4,5}; x.b; }));
|
||||||
|
ASSERT(-1, ({ struct bit1 x={1,2,3,4,5}; x.c; }));
|
||||||
|
ASSERT(-4, ({ struct bit1 x={1,2,3,4,5}; x.d; }));
|
||||||
|
ASSERT(-3, ({ struct bit1 x={1,2,3,4,5}; x.e; }));
|
||||||
|
|
||||||
|
printf("OK\n");
|
||||||
|
return 0;
|
||||||
|
}
|
Loading…
Reference in New Issue