Skip to content

Commit

Permalink
Allow to call a fucntion returning a struct
Browse files Browse the repository at this point in the history
  • Loading branch information
rui314 committed Sep 30, 2020
1 parent 055386c commit e71a987
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 5 deletions.
1 change: 1 addition & 0 deletions chibicc.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,7 @@ struct Node {
Type *func_ty;
Node *args;
bool pass_by_stack;
Obj *ret_buffer;

// Goto or labeled statement
char *label;
Expand Down
81 changes: 76 additions & 5 deletions codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,12 @@ static void gen_addr(Node *node) {
gen_addr(node->lhs);
println(" add $%d, %%rax", node->member->offset);
return;
case ND_FUNCALL:
if (node->ret_buffer) {
gen_expr(node);
return;
}
break;
}

error_tok(node->tok, "not an lvalue");
Expand Down Expand Up @@ -390,10 +396,16 @@ static void push_args2(Node *args, bool first_pass) {
//
// - If a function is variadic, set the number of floating-point type
// arguments to RAX.
static int push_args(Node *args) {
static int push_args(Node *node) {
int stack = 0, gp = 0, fp = 0;

for (Node *arg = args; arg; arg = arg->next) {
// If the return type is a large struct/union, the caller passes
// a pointer to a buffer as if it were the first argument.
if (node->ret_buffer && node->ty->size > 16)
gp++;

// Load as many arguments to the registers as possible.
for (Node *arg = node->args; arg; arg = arg->next) {
Type *ty = arg->ty;

switch (ty->kind) {
Expand Down Expand Up @@ -436,11 +448,56 @@ static int push_args(Node *args) {
stack++;
}

push_args2(args, true);
push_args2(args, false);
push_args2(node->args, true);
push_args2(node->args, false);

// If the return type is a large struct/union, the caller passes
// a pointer to a buffer as if it were the first argument.
if (node->ret_buffer && node->ty->size > 16) {
println(" lea %d(%%rbp), %%rax", node->ret_buffer->offset);
push();
}

return stack;
}

static void copy_ret_buffer(Obj *var) {
Type *ty = var->ty;
int gp = 0, fp = 0;

if (has_flonum1(ty)) {
assert(ty->size == 4 || 8 <= ty->size);
if (ty->size == 4)
println(" movss %%xmm0, %d(%%rbp)", var->offset);
else
println(" movsd %%xmm0, %d(%%rbp)", var->offset);
fp++;
} else {
for (int i = 0; i < MIN(8, ty->size); i++) {
println(" mov %%al, %d(%%rbp)", var->offset + i);
println(" shr $8, %%rax");
}
gp++;
}

if (ty->size > 8) {
if (has_flonum2(ty)) {
assert(ty->size == 12 || ty->size == 16);
if (ty->size == 12)
println(" movss %%xmm%d, %d(%%rbp)", fp, var->offset + 8);
else
println(" movsd %%xmm%d, %d(%%rbp)", fp, var->offset + 8);
} else {
char *reg1 = (gp == 0) ? "%al" : "%dl";
char *reg2 = (gp == 0) ? "%rax" : "%rdx";
for (int i = 8; i < MIN(16, ty->size); i++) {
println(" mov %s, %d(%%rbp)", reg1, var->offset + i);
println(" shr $8, %s", reg2);
}
}
}
}

// Generate code for a given node.
static void gen_expr(Node *node) {
println(" .loc %d %d", node->tok->file->file_no, node->tok->line_no);
Expand Down Expand Up @@ -577,10 +634,16 @@ static void gen_expr(Node *node) {
return;
}
case ND_FUNCALL: {
int stack_args = push_args(node->args);
int stack_args = push_args(node);
gen_expr(node->lhs);

int gp = 0, fp = 0;

// If the return type is a large struct/union, the caller passes
// a pointer to a buffer as if it were the first argument.
if (node->ret_buffer && node->ty->size > 16)
pop(argreg64[gp++]);

for (Node *arg = node->args; arg; arg = arg->next) {
Type *ty = arg->ty;

Expand Down Expand Up @@ -645,6 +708,14 @@ static void gen_expr(Node *node) {
println(" movswl %%ax, %%eax");
return;
}

// If the return type is a small struct, a value is returned
// using up to two registers.
if (node->ret_buffer && node->ty->size <= 16) {
copy_ret_buffer(node->ret_buffer);
println(" lea %d(%%rbp), %%rax", node->ret_buffer->offset);
}

return;
}
}
Expand Down
5 changes: 5 additions & 0 deletions parse.c
Original file line number Diff line number Diff line change
Expand Up @@ -2238,6 +2238,11 @@ static Node *funcall(Token **rest, Token *tok, Node *fn) {
node->func_ty = ty;
node->ty = ty->return_ty;
node->args = head.next;

// If a function returns a struct, it is caller's responsibility
// to allocate a space for the return value.
if (node->ty->kind == TY_STRUCT || node->ty->kind == TY_UNION)
node->ret_buffer = new_lvar("", node->ty);
return node;
}

Expand Down
23 changes: 23 additions & 0 deletions test/common
Original file line number Diff line number Diff line change
Expand Up @@ -92,3 +92,26 @@ int struct_test7(Ty7 x, int n) {
default: return x.c;
}
}

Ty4 struct_test24(void) {
return (Ty4){10, 20, 30, 40};
}

Ty5 struct_test25(void) {
return (Ty5){10, 20, 30};
}

Ty6 struct_test26(void) {
return (Ty6){10, 20, 30};
}

typedef struct { unsigned char a[10]; } Ty20;
typedef struct { unsigned char a[20]; } Ty21;

Ty20 struct_test27(void) {
return (Ty20){10, 20, 30, 40, 50, 60, 70, 80, 90, 100};
}

Ty21 struct_test28(void) {
return (Ty21){1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20};
}
32 changes: 32 additions & 0 deletions test/function.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,15 @@ int struct_test15(Ty5 x, int n) {
}
}

typedef struct { unsigned char a[10]; } Ty20;
typedef struct { unsigned char a[20]; } Ty21;

Ty4 struct_test24(void);
Ty5 struct_test25(void);
Ty6 struct_test26(void);
Ty20 struct_test27(void);
Ty21 struct_test28(void);

int main() {
ASSERT(3, ret3());
ASSERT(8, add2(3, 5));
Expand Down Expand Up @@ -288,6 +297,29 @@ int main() {
ASSERT(20, ({ Ty5 x={10,20,30}; struct_test15(x, 1); }));
ASSERT(30, ({ Ty5 x={10,20,30}; struct_test15(x, 2); }));

ASSERT(10, struct_test24().a);
ASSERT(20, struct_test24().b);
ASSERT(30, struct_test24().c);
ASSERT(40, struct_test24().d);

ASSERT(10, struct_test25().a);
ASSERT(20, struct_test25().b);
ASSERT(30, struct_test25().c);

ASSERT(10, struct_test26().a[0]);
ASSERT(20, struct_test26().a[1]);
ASSERT(30, struct_test26().a[2]);

ASSERT(10, struct_test27().a[0]);
ASSERT(60, struct_test27().a[5]);
ASSERT(100, struct_test27().a[9]);

ASSERT(1, struct_test28().a[0]);
ASSERT(5, struct_test28().a[4]);
ASSERT(10, struct_test28().a[9]);
ASSERT(15, struct_test28().a[14]);
ASSERT(20, struct_test28().a[19]);

printf("OK\n");
return 0;
}

0 comments on commit e71a987

Please sign in to comment.