From e8667afd08ecbf7c9b05beb4ff399959d9722ff9 Mon Sep 17 00:00:00 2001 From: Rui Ueyama Date: Fri, 4 Sep 2020 14:44:12 +0900 Subject: [PATCH] Add sizeof() for VLA --- chibicc.h | 6 +++ parse.c | 109 +++++++++++++++++++++++++++++++++++++++++++++++++++-- test/vla.c | 20 ++++++++++ type.c | 9 ++++- 4 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 test/vla.c diff --git a/chibicc.h b/chibicc.h index ad4a6d7..41a2b97 100644 --- a/chibicc.h +++ b/chibicc.h @@ -294,6 +294,7 @@ typedef enum { TY_PTR, TY_FUNC, TY_ARRAY, + TY_VLA, // variable-length array TY_STRUCT, TY_UNION, } TypeKind; @@ -322,6 +323,10 @@ struct Type { // Array int array_len; + // Variable-length array + Node *vla_len; // # of elements + Obj *vla_size; // sizeof() value + // Struct Member *members; bool is_flexible; @@ -373,6 +378,7 @@ Type *copy_type(Type *ty); Type *pointer_to(Type *base); Type *func_type(Type *return_ty); Type *array_of(Type *base, int size); +Type *vla_of(Type *base, Node *expr); Type *enum_type(void); Type *struct_type(void); void add_type(Node *node); diff --git a/parse.c b/parse.c index 1b39afb..39818fb 100644 --- a/parse.c +++ b/parse.c @@ -115,6 +115,8 @@ static char *cont_label; // a switch statement. Otherwise, NULL. static Node *current_switch; +static Obj *builtin_alloca; + static bool is_typename(Token *tok); static Type *declspec(Token **rest, Token *tok, VarAttr *attr); static Type *typename(Token **rest, Token *tok); @@ -136,6 +138,7 @@ static Node *expr(Token **rest, Token *tok); static int64_t eval(Node *node); static int64_t eval2(Node *node, char **label); static int64_t eval_rval(Node *node, char **label); +static bool is_const_expr(Node *node); static Node *assign(Token **rest, Token *tok); static Node *logor(Token **rest, Token *tok); static double eval_double(Node *node); @@ -630,10 +633,13 @@ static Type *array_dimensions(Token **rest, Token *tok, Type *ty) { return array_of(ty, -1); } - int sz = const_expr(&tok, tok); + Node *expr = conditional(&tok, tok); tok = skip(tok, "]"); ty = type_suffix(rest, tok, ty); - return array_of(ty, sz); + + if (ty->kind == TY_VLA || !is_const_expr(expr)) + return vla_of(ty, expr); + return array_of(ty, eval(expr)); } // type-suffix = "(" func-params @@ -794,6 +800,37 @@ static Type *typeof_specifier(Token **rest, Token *tok) { return ty; } +// Generate code for computing a VLA size. +static Node *compute_vla_size(Type *ty, Token *tok) { + Node *node = new_node(ND_NULL_EXPR, tok); + if (ty->base) + node = new_binary(ND_COMMA, node, compute_vla_size(ty->base, tok), tok); + + if (ty->kind != TY_VLA) + return node; + + Node *base_sz; + if (ty->base->kind == TY_VLA) + base_sz = new_var_node(ty->base->vla_size, tok); + else + base_sz = new_num(ty->base->size, tok); + + ty->vla_size = new_lvar("", ty_ulong); + Node *expr = new_binary(ND_ASSIGN, new_var_node(ty->vla_size, tok), + new_binary(ND_MUL, ty->vla_len, base_sz, tok), + tok); + return new_binary(ND_COMMA, node, expr, tok); +} + +static Node *new_alloca(Node *sz) { + Node *node = new_unary(ND_FUNCALL, new_var_node(builtin_alloca, sz->tok), sz->tok); + node->func_ty = builtin_alloca->ty; + node->ty = builtin_alloca->ty->return_ty; + node->args = sz; + add_type(sz); + return node; +} + // declaration = declspec (declarator ("=" expr)? ("," declarator ("=" expr)?)*)? ";" static Node *declaration(Token **rest, Token *tok, Type *basety, VarAttr *attr) { Node head = {}; @@ -819,6 +856,28 @@ static Node *declaration(Token **rest, Token *tok, Type *basety, VarAttr *attr) continue; } + // Generate code for computing a VLA size. We need to do this + // even if ty is not VLA because ty may be a pointer to VLA + // (e.g. int (*foo)[n][m] where n and m are variables.) + cur = cur->next = new_unary(ND_EXPR_STMT, compute_vla_size(ty, tok), tok); + + if (ty->kind == TY_VLA) { + if (equal(tok, "=")) + error_tok(tok, "variable-sized object may not be initialized"); + + // Variable length arrays (VLAs) are translated to alloca() calls. + // For example, `int x[n+2]` is translated to `tmp = n + 2, + // x = alloca(tmp)`. + Obj *var = new_lvar(get_ident(ty->name), ty); + Token *tok = ty->name; + Node *expr = new_binary(ND_ASSIGN, new_var_node(var, tok), + new_alloca(new_var_node(ty->vla_size, tok)), + tok); + + cur = cur->next = new_unary(ND_EXPR_STMT, expr, tok); + continue; + } + Obj *var = new_lvar(get_ident(ty->name), ty); if (attr && attr->align) var->align = attr->align; @@ -1831,6 +1890,44 @@ static int64_t eval_rval(Node *node, char **label) { error_tok(node->tok, "invalid initializer"); } +static bool is_const_expr(Node *node) { + add_type(node); + + switch (node->kind) { + case ND_ADD: + case ND_SUB: + case ND_MUL: + case ND_DIV: + case ND_BITAND: + case ND_BITOR: + case ND_BITXOR: + case ND_SHL: + case ND_SHR: + case ND_EQ: + case ND_NE: + case ND_LT: + case ND_LE: + case ND_LOGAND: + case ND_LOGOR: + return is_const_expr(node->lhs) && is_const_expr(node->rhs); + case ND_COND: + if (!is_const_expr(node->cond)) + return false; + return is_const_expr(eval(node->cond) ? node->then : node->els); + case ND_COMMA: + return is_const_expr(node->rhs); + case ND_NEG: + case ND_NOT: + case ND_BITNOT: + case ND_CAST: + return is_const_expr(node->lhs); + case ND_NUM: + return true; + } + + return false; +} + int64_t const_expr(Token **rest, Token *tok) { Node *node = conditional(rest, tok); return eval(node); @@ -2723,12 +2820,16 @@ static Node *primary(Token **rest, Token *tok) { if (equal(tok, "sizeof") && equal(tok->next, "(") && is_typename(tok->next->next)) { Type *ty = typename(&tok, tok->next->next); *rest = skip(tok, ")"); + if (ty->kind == TY_VLA) + return new_var_node(ty->vla_size, tok); return new_ulong(ty->size, start); } if (equal(tok, "sizeof")) { Node *node = unary(rest, tok->next); add_type(node); + if (node->ty->kind == TY_VLA) + return new_var_node(node->ty->vla_size, tok); return new_ulong(node->ty->size, tok); } @@ -3004,8 +3105,8 @@ static void scan_globals(void) { static void declare_builtin_functions(void) { Type *ty = func_type(pointer_to(ty_void)); ty->params = copy_type(ty_int); - Obj *builtin = new_gvar("alloca", ty); - builtin->is_definition = false; + builtin_alloca = new_gvar("alloca", ty); + builtin_alloca->is_definition = false; } // program = (typedef | function-definition | global-variable)* diff --git a/test/vla.c b/test/vla.c new file mode 100644 index 0000000..edc3fb4 --- /dev/null +++ b/test/vla.c @@ -0,0 +1,20 @@ +#include "test.h" + +int main() { + ASSERT(20, ({ int n=5; int x[n]; sizeof(x); })); + ASSERT((5+1)*(8*2)*4, ({ int m=5, n=8; int x[m+1][n*2]; sizeof(x); })); + + ASSERT(8, ({ char n=10; int (*x)[n][n+2]; sizeof(x); })); + ASSERT(480, ({ char n=10; int (*x)[n][n+2]; sizeof(*x); })); + ASSERT(48, ({ char n=10; int (*x)[n][n+2]; sizeof(**x); })); + ASSERT(4, ({ char n=10; int (*x)[n][n+2]; sizeof(***x); })); + + ASSERT(60, ({ char n=3; int x[5][n]; sizeof(x); })); + ASSERT(12, ({ char n=3; int x[5][n]; sizeof(*x); })); + + ASSERT(60, ({ char n=3; int x[n][5]; sizeof(x); })); + ASSERT(20, ({ char n=3; int x[n][5]; sizeof(*x); })); + + printf("OK\n"); + return 0; +} diff --git a/type.c b/type.c index 31351d7..6f4166b 100644 --- a/type.c +++ b/type.c @@ -113,6 +113,13 @@ Type *array_of(Type *base, int len) { return ty; } +Type *vla_of(Type *base, Node *len) { + Type *ty = new_type(TY_VLA, 8, 8); + ty->base = base; + ty->vla_len = len; + return ty; +} + Type *enum_type(void) { return new_type(TY_ENUM, 4, 4); } @@ -214,7 +221,7 @@ void add_type(Node *node) { node->ty = ty_int; return; case ND_FUNCALL: - node->ty = ty_long; + node->ty = node->func_ty->return_ty; return; case ND_NOT: case ND_LOGOR: