Add _Alignof and _Alignas

This commit is contained in:
Rui Ueyama 2020-09-04 11:20:55 +09:00
parent 27647455e4
commit 9df51789e7
5 changed files with 82 additions and 14 deletions

View File

@ -74,6 +74,7 @@ struct Obj {
char *name; // Variable name
Type *ty; // Type
bool is_local; // local or global/function
int align; // alignment
// Local variable
int offset;
@ -258,6 +259,7 @@ struct Member {
Token *tok; // for error message
Token *name;
int idx;
int align;
int offset;
};

View File

@ -453,7 +453,7 @@ static void assign_lvar_offsets(Obj *prog) {
int offset = 0;
for (Obj *var = fn->locals; var; var = var->next) {
offset += var->ty->size;
offset = align_to(offset, var->ty->align);
offset = align_to(offset, var->align);
var->offset = -offset;
}
fn->stack_size = align_to(offset, 16);
@ -466,7 +466,7 @@ static void emit_data(Obj *prog) {
continue;
println(" .globl %s", var->name);
println(" .align %d", var->ty->align);
println(" .align %d", var->align);
if (var->init_data) {
println(" .data");

53
parse.c
View File

@ -54,6 +54,7 @@ typedef struct {
bool is_typedef;
bool is_static;
bool is_extern;
int align;
} VarAttr;
// This struct represents a variable initializer. Since initializers
@ -110,10 +111,11 @@ static Node *current_switch;
static bool is_typename(Token *tok);
static Type *declspec(Token **rest, Token *tok, VarAttr *attr);
static Type *typename(Token **rest, Token *tok);
static Type *enum_specifier(Token **rest, Token *tok);
static Type *type_suffix(Token **rest, Token *tok, Type *ty);
static Type *declarator(Token **rest, Token *tok, Type *ty);
static Node *declaration(Token **rest, Token *tok, Type *basety);
static Node *declaration(Token **rest, Token *tok, Type *basety, VarAttr *attr);
static void initializer2(Token **rest, Token *tok, Initializer *init);
static Initializer *initializer(Token **rest, Token *tok, Type *ty, Type **new_ty);
static Node *lvar_initializer(Token **rest, Token *tok, Obj *var);
@ -280,6 +282,7 @@ static Obj *new_var(char *name, Type *ty) {
Obj *var = calloc(1, sizeof(Obj));
var->name = name;
var->ty = ty;
var->align = ty->align;
push_scope(name)->var = var;
return var;
}
@ -391,6 +394,19 @@ static Type *declspec(Token **rest, Token *tok, VarAttr *attr) {
continue;
}
if (equal(tok, "_Alignas")) {
if (!attr)
error_tok(tok, "_Alignas is not allowed in this context");
tok = skip(tok->next, "(");
if (is_typename(tok))
attr->align = typename(&tok, tok)->align;
else
attr->align = const_expr(&tok, tok);
tok = skip(tok, ")");
continue;
}
// Handle user-defined types.
Type *ty2 = find_typedef(tok);
if (equal(tok, "struct") || equal(tok, "union") || equal(tok, "enum") || ty2) {
@ -638,7 +654,7 @@ static Type *enum_specifier(Token **rest, Token *tok) {
}
// declaration = declspec (declarator ("=" expr)? ("," declarator ("=" expr)?)*)? ";"
static Node *declaration(Token **rest, Token *tok, Type *basety) {
static Node *declaration(Token **rest, Token *tok, Type *basety, VarAttr *attr) {
Node head = {};
Node *cur = &head;
int i = 0;
@ -652,6 +668,9 @@ static Node *declaration(Token **rest, Token *tok, Type *basety) {
error_tok(tok, "variable declared void");
Obj *var = new_lvar(get_ident(ty->name), ty);
if (attr && attr->align)
var->align = attr->align;
if (equal(tok, "=")) {
Node *expr = lvar_initializer(&tok, tok->next, var);
cur = cur->next = new_unary(ND_EXPR_STMT, expr, tok);
@ -1013,7 +1032,7 @@ static void gvar_initializer(Token **rest, Token *tok, Obj *var) {
static bool is_typename(Token *tok) {
static char *kw[] = {
"void", "_Bool", "char", "short", "int", "long", "struct", "union",
"typedef", "enum", "static", "extern",
"typedef", "enum", "static", "extern", "_Alignas",
};
for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)
@ -1117,7 +1136,7 @@ static Node *stmt(Token **rest, Token *tok) {
if (is_typename(tok)) {
Type *basety = declspec(&tok, tok, NULL);
node->init = declaration(&tok, tok, basety);
node->init = declaration(&tok, tok, basety, NULL);
} else {
node->init = expr_stmt(&tok, tok);
}
@ -1227,7 +1246,7 @@ static Node *compound_stmt(Token **rest, Token *tok) {
continue;
}
cur = cur->next = declaration(&tok, tok, basety);
cur = cur->next = declaration(&tok, tok, basety, &attr);
} else {
cur = cur->next = stmt(&tok, tok);
}
@ -1749,7 +1768,8 @@ static void struct_members(Token **rest, Token *tok, Type *ty) {
int idx = 0;
while (!equal(tok, "}")) {
Type *basety = declspec(&tok, tok, NULL);
VarAttr attr = {};
Type *basety = declspec(&tok, tok, &attr);
bool first = true;
while (!consume(&tok, tok, ";")) {
@ -1761,6 +1781,7 @@ static void struct_members(Token **rest, Token *tok, Type *ty) {
mem->ty = declarator(&tok, tok, basety);
mem->name = mem->ty->name;
mem->idx = idx++;
mem->align = attr.align ? attr.align : mem->ty->align;
cur = cur->next = mem;
}
}
@ -1832,12 +1853,12 @@ static Type *struct_decl(Token **rest, Token *tok) {
// Assign offsets within the struct to members.
int offset = 0;
for (Member *mem = ty->members; mem; mem = mem->next) {
offset = align_to(offset, mem->ty->align);
offset = align_to(offset, mem->align);
mem->offset = offset;
offset += mem->ty->size;
if (ty->align < mem->ty->align)
ty->align = mem->ty->align;
if (ty->align < mem->align)
ty->align = mem->align;
}
ty->size = align_to(offset, ty->align);
return ty;
@ -1855,8 +1876,8 @@ static Type *union_decl(Token **rest, Token *tok) {
// are already initialized to zero. We need to compute the
// alignment and the size though.
for (Member *mem = ty->members; mem; mem = mem->next) {
if (ty->align < mem->ty->align)
ty->align = mem->ty->align;
if (ty->align < mem->align)
ty->align = mem->align;
if (ty->size < mem->ty->size)
ty->size = mem->ty->size;
}
@ -1983,6 +2004,7 @@ static Node *funcall(Token **rest, Token *tok) {
// | "(" expr ")"
// | "sizeof" "(" type-name ")"
// | "sizeof" unary
// | "_Alignof" "(" type-name ")"
// | ident func-args?
// | str
// | num
@ -2015,6 +2037,13 @@ static Node *primary(Token **rest, Token *tok) {
return new_num(node->ty->size, tok);
}
if (equal(tok, "_Alignof")) {
tok = skip(tok->next, "(");
Type *ty = typename(&tok, tok);
*rest = skip(tok, ")");
return new_num(ty->align, tok);
}
if (tok->kind == TK_IDENT) {
// Function call
if (equal(tok->next, "("))
@ -2128,6 +2157,8 @@ static Token *global_variable(Token *tok, Type *basety, VarAttr *attr) {
Type *ty = declarator(&tok, tok, basety);
Obj *var = new_gvar(get_ident(ty->name), ty);
var->is_definition = !attr->is_extern;
if (attr->align)
var->align = attr->align;
if (equal(tok, "="))
gvar_initializer(&tok, tok->next, var);

35
test/alignof.c Normal file
View File

@ -0,0 +1,35 @@
#include "test.h"
int _Alignas(512) g1;
int _Alignas(512) g2;
char g3;
int g4;
long g5;
char g6;
int main() {
ASSERT(1, _Alignof(char));
ASSERT(2, _Alignof(short));
ASSERT(4, _Alignof(int));
ASSERT(8, _Alignof(long));
ASSERT(8, _Alignof(long long));
ASSERT(1, _Alignof(char[3]));
ASSERT(4, _Alignof(int[3]));
ASSERT(1, _Alignof(struct {char a; char b;}[2]));
ASSERT(8, _Alignof(struct {char a; long b;}[2]));
ASSERT(1, ({ _Alignas(char) char x, y; &y-&x; }));
ASSERT(8, ({ _Alignas(long) char x, y; &y-&x; }));
ASSERT(32, ({ _Alignas(32) char x, y; &y-&x; }));
ASSERT(32, ({ _Alignas(32) int *x, *y; ((char *)&y)-((char *)&x); }));
ASSERT(16, ({ struct { _Alignas(16) char x, y; } a; &a.y-&a.x; }));
ASSERT(8, ({ struct T { _Alignas(8) char a; }; _Alignof(struct T); }));
ASSERT(0, (long)(char *)&g1 % 512);
ASSERT(0, (long)(char *)&g2 % 512);
ASSERT(0, (long)(char *)&g4 % 4);
ASSERT(0, (long)(char *)&g5 % 8);
printf("OK\n");
return 0;
}

View File

@ -133,7 +133,7 @@ static bool is_keyword(Token *tok) {
"return", "if", "else", "for", "while", "int", "sizeof", "char",
"struct", "union", "short", "long", "void", "typedef", "_Bool",
"enum", "static", "goto", "break", "continue", "switch", "case",
"default", "extern",
"default", "extern", "_Alignof", "_Alignas",
};
for (int i = 0; i < sizeof(kw) / sizeof(*kw); i++)