Add sizeof() for VLA

This commit is contained in:
Rui Ueyama 2020-09-04 14:44:12 +09:00
parent 77275c546a
commit e8667afd08
4 changed files with 139 additions and 5 deletions

View File

@ -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
View File

@ -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)*

20
test/vla.c Normal file
View File

@ -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
View File

@ -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: