mirror of https://github.com/rui314/chibicc
Add sizeof() for VLA
This commit is contained in:
parent
77275c546a
commit
e8667afd08
|
@ -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);
|
||||
|
|
109
parse.c
109
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)*
|
||||
|
|
|
@ -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;
|
||||
}
|
9
type.c
9
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:
|
||||
|
|
Loading…
Reference in New Issue