Skip to content

Commit

Permalink
Store VLA size to temporary varaible
Browse files Browse the repository at this point in the history
Handle cases that size is modified, or size contains side effect.

Assigning size to temporary variable should be executed in
allocating variable, or used in typedef.

Nested VLA have multiple assignments using comma expressions.
  • Loading branch information
tyfkda committed Dec 31, 2023
1 parent 3907db6 commit 0b1b7fe
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 33 deletions.
1 change: 1 addition & 0 deletions src/cc/frontend/ast.c
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ Expr *new_expr_str(const Token *token, const char *str, ssize_t len, enum StrKin
type->pa.length = len;
#ifndef __NO_VLA
type->pa.vla = NULL;
type->pa.size_var = NULL;
#endif

Expr *expr = new_expr(EX_STR, type, token);
Expand Down
40 changes: 26 additions & 14 deletions src/cc/frontend/fe_misc.c
Original file line number Diff line number Diff line change
Expand Up @@ -184,23 +184,35 @@ void ensure_struct(Type *type, const Token *token, Scope *scope) {

Expr *calc_type_size(const Type *type) {
#ifndef __NO_VLA
Expr *expr = NULL;
for (; type != NULL;) {
Expr *e = NULL;
if (ptr_or_array(type) && type->pa.vla != NULL) {
e = type->pa.vla;
type = type->pa.ptrof;
} else {
e = new_expr_fixlit(&tySize, NULL, type_size(type));
type = NULL;
}
expr = expr == NULL ? e : new_expr_num_bop(EX_MUL, NULL, expr, e);
if (ptr_or_array(type) && type->pa.vla != NULL) {
assert(type->pa.size_var != NULL);
return type->pa.size_var;
}
return expr;
#else
return new_expr_fixlit(&tySize, NULL, type_size(type));
#endif
return new_expr_fixlit(&tySize, NULL, type_size(type));
}

#ifndef __NO_VLA
Expr *reserve_vla_type_size(Type *type) {
assert(ptr_or_array(type) && type->pa.vla != NULL);
assert(!is_global_scope(curscope));

// If VLA is nested, calculate subtype first.
Type *subtype = type->pa.ptrof;
Expr *sub = (ptr_or_array(subtype) && subtype->pa.vla != NULL) ? reserve_vla_type_size(subtype)
: NULL;

Expr *var = alloc_tmp_var(curscope, &tySize);
Expr *value = new_expr_num_bop(EX_MUL, type->pa.vla->token, type->pa.vla,
calc_type_size(type->pa.ptrof));
Expr *assign = new_expr_bop(EX_ASSIGN, &tySize, type->pa.vla->token, var, value);
type->pa.size_var = var;

if (sub != NULL)
assign = new_expr_bop(EX_COMMA, &tySize, assign->token, sub, assign);
return assign;
}
#endif

bool check_cast(const Type *dst, const Type *src, bool zero, bool is_explicit, const Token *token) {
bool ok = can_cast(dst, src, zero, is_explicit);
Expand Down
3 changes: 3 additions & 0 deletions src/cc/frontend/fe_misc.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ void exit_scope(void);

void ensure_struct(Type *type, const Token *token, Scope *scope);
Expr *calc_type_size(const Type *type);
#ifndef __NO_VLA
Expr *reserve_vla_type_size(Type *type);
#endif
bool check_cast(const Type *dst, const Type *src, bool zero, bool is_explicit, const Token *token);
Expr *make_cast(Type *type, const Token *token, Expr *sub, bool is_explicit);
const MemberInfo *search_from_anonymous(const Type *type, const Name *name, const Token *ident,
Expand Down
22 changes: 19 additions & 3 deletions src/cc/frontend/initializer.c
Original file line number Diff line number Diff line change
Expand Up @@ -910,17 +910,33 @@ Initializer *check_vardecl(Type **ptype, const Token *ident, int storage, Initia
if (init != NULL)
parse_error(PE_NOFATAL, ident, "Variable length array with initializer");

// Transform VLA to `alloca(vla * type_size(elem))`.
Expr *assign_sizevar = NULL;
Expr *size_var;
if (type->pa.size_var != NULL) {
// `size_varname` for typedef'd type is already assigned.
size_var = type->pa.size_var;
} else {
Expr *e = assign_sizevar = reserve_vla_type_size(type);
while (e->kind == EX_COMMA)
e = e->bop.rhs;
assert(e->kind == EX_ASSIGN);
size_var = e->bop.lhs;
}
assert(size_var->kind == EX_VAR);

// Transform VLA to `alloca(size_var)`.
const Token *tok = ident;
Vector *params = new_vector();
vec_push(params, &tySize);
Type *functype = new_func_type(&tyVoidPtr, params, false);
Expr *alloca_var = new_expr_variable(alloc_name("alloca", NULL, false), functype, tok,
global_scope);
Expr *size_expr = calc_type_size(type);
Vector *args = new_vector();
vec_push(args, size_expr);
vec_push(args, size_var);
Expr *call_alloca = new_expr_funcall(tok, alloca_var, functype->func.ret, args);
if (assign_sizevar != NULL)
call_alloca = new_expr_bop(EX_COMMA, functype->func.ret, tok, assign_sizevar, call_alloca);

init = new_initializer(IK_SINGLE, tok);
init->single = make_cast(ptrof(type->pa.ptrof), tok, call_alloca, false);
}
Expand Down
24 changes: 23 additions & 1 deletion src/cc/frontend/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,23 @@ static Vector *parse_vardecl_cont(Type *rawType, Type *type, int storage, Token
assert(!is_global_scope(curscope));

if (tmp_storage & VS_TYPEDEF) {
#ifndef __NO_VLA
if (type->kind == TY_PTR && type->pa.vla != NULL) {
Expr *assign_sizevar = reserve_vla_type_size(type);
Expr *e = assign_sizevar;
while (e->kind == EX_COMMA)
e = e->bop.rhs;
assert(e->kind == EX_ASSIGN);
Expr *size_var = e->bop.lhs;
assert(size_var->kind == EX_VAR);

VarDecl *decl = new_vardecl(size_var->var.name);
decl->init_stmt = new_stmt_expr(assign_sizevar);
if (decls == NULL)
decls = new_vector();
vec_push(decls, decl);
}
#endif
def_type(type, ident);
continue;
}
Expand Down Expand Up @@ -707,8 +724,13 @@ static Declaration *parse_global_var_decl(Type *rawtype, int storage, Type *type
type = parse_type_suffix(type);

if (storage & VS_TYPEDEF) {
if (ident != NULL)
if (ident != NULL) {
#ifndef __NO_VLA
if (is_global_scope(curscope) && type->kind == TY_PTR && type->pa.vla != NULL)
parse_error(PE_NOFATAL, ident, "Variable length array cannot use in global scope");
#endif
def_type(type, ident);
}
} else {
if (type->kind == TY_VOID) {
if (ident != NULL)
Expand Down
2 changes: 2 additions & 0 deletions src/cc/frontend/type.c
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,7 @@ Type *ptrof(Type *type) {
ptr->pa.length = 0;
#ifndef __NO_VLA
ptr->pa.vla = NULL;
ptr->pa.size_var = NULL;
#endif
return ptr;
}
Expand All @@ -295,6 +296,7 @@ Type *arrayof(Type *type, ssize_t length) {
arr->pa.length = length;
#ifndef __NO_VLA
arr->pa.vla = NULL;
arr->pa.size_var = NULL;
#endif
return arr;
}
Expand Down
1 change: 1 addition & 0 deletions src/cc/frontend/type.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ typedef struct Type {
ssize_t length; // of array. Positive value, or LEN_UND, LEN_FAM or LEN_VLA.
#ifndef __NO_VLA
Expr *vla; // Variable length array.
Expr *size_var;
#endif
} pa;
struct {
Expand Down
1 change: 1 addition & 0 deletions tests/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,7 @@ test_error() {
compile_error 'conflict typedef' 'typedef int Foo; typedef long Foo; void main(){}'
compile_error 'conflict struct typedef' 'typedef struct{int x;} Foo; typedef struct{int x;} Foo; void main(){}'
compile_error 'no VLA in global' "#ifndef __NO_VLA\nint n = 3; int array[n];\n#else\n#error no VLA\n#endif\n void main(){}"
compile_error 'no VLA in global typedef' "#ifndef __NO_VLA\nint n = 3; typedef int array[n];\n#else\n#error no VLA\n#endif\n void main(){}"
compile_error 'negative array' "void main(){ int array[-1]; }"
compile_error 'size unknown' 'extern char string[]; int main(){ return sizeof(string); } char string[] = "Hello";'
compile_error 'scoped typedef' 'void sub(){typedef int T;} T g=123; int main(){return g;}'
Expand Down
39 changes: 24 additions & 15 deletions tests/valtest.c
Original file line number Diff line number Diff line change
Expand Up @@ -1910,9 +1910,8 @@ int extern_array_wo_size[] = {11, 22, 33};

#if !defined(__NO_VLA) && !defined(__STDC_NO_VLA__)
TEST(vla) {
int n = 3;

{
int n = 3;
int arr[n];
EXPECT("vla size", n * sizeof(int), sizeof(arr));
for (int i = 0; i < n; ++i)
Expand All @@ -1924,24 +1923,34 @@ TEST(vla) {
}

{
int n = 3;
typedef long ARR[n];
EXPECT("vla size", n * sizeof(long), sizeof(ARR));
}

{
typedef short ARR2[n][n];
ARR2 arr2;
EXPECT("vla[][] size", n * n * sizeof(short), sizeof(arr2));
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
arr2[i][j] = i * 10 + j + 1;
short total = 0;
for (int i = 0; i < n; ++i) {
short *p = arr2[i];
for (int j = 0; j < n; ++j)
total += p[j];
}
EXPECT("vla[][]", 108, total);
int n2 = 3;
int arr2[++n2];
EXPECT("vla side-effect", 4 * sizeof(int), sizeof(arr2));

int n3 = 2;
int arr3[n3][n3];
n3 = -1;
EXPECT("vla size var modified", 4 * sizeof(int), sizeof(arr3));

int n4 = 3;
typedef char ARR4[n4++];
ARR4 arr4;
EXPECT("typedef vla side-effect", 3 * sizeof(char), sizeof(arr4));
}

{
int n = 4;
typedef short ARR[n][n];
n = 0;
ARR arr;
EXPECT("vla[][] size", 16 * sizeof(short), sizeof(arr));
EXPECT("vla[0][] size", 4 * sizeof(short), sizeof(arr[0]));
}
} END_TEST()
#endif
Expand Down

0 comments on commit 0b1b7fe

Please sign in to comment.