diff --git a/src/check_decl.cpp b/src/check_decl.cpp index b2a3e28e3..95587a3c3 100644 --- a/src/check_decl.cpp +++ b/src/check_decl.cpp @@ -1753,6 +1753,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *e, Ast e->Variable.thread_local_model = ac.thread_local_model; e->Variable.is_export = ac.is_export; + e->Variable.custom_align = ac.align; e->flags &= ~EntityFlag_Static; if (ac.is_static) { error(e->token, "@(static) is not supported for global variables, nor required"); diff --git a/src/check_stmt.cpp b/src/check_stmt.cpp index 2c96089b9..e6a8e177f 100644 --- a/src/check_stmt.cpp +++ b/src/check_stmt.cpp @@ -2287,6 +2287,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f if (ac.link_name.len > 0) { e->Variable.link_name = ac.link_name; } + e->Variable.custom_align = ac.align; e->flags &= ~EntityFlag_Static; if (ac.is_static) { diff --git a/src/checker.cpp b/src/checker.cpp index eb46ac08e..869481008 100644 --- a/src/checker.cpp +++ b/src/checker.cpp @@ -4653,6 +4653,24 @@ gb_internal DECL_ATTRIBUTE_PROC(var_decl_attribute) { error(elem, "Expected either no value or a string for '%.*s'", LIT(name)); } return true; + } else if (name == "align") { + ExactValue ev = check_decl_attribute_value(c, value); + if (ev.kind != ExactValue_Integer) { + error(elem, "Expected a constant integer for '%.*s'", LIT(name)); + return true; + } + i64 align = exact_value_to_i64(ev); + if (ev.value_integer.used > 1 || + align < 1 || + align > (1ll<<31) || + !gb_is_power_of_two(cast(isize)align)) { + gbString str = exact_value_to_string(ev); + error(elem, "'%.*s' must be a power of 2 no greater than 2^31, got %s", LIT(name), str); + gb_string_free(str); + } else { + ac->align = align; + } + return true; } if (c->curr_proc_decl != nullptr) { diff --git a/src/checker.hpp b/src/checker.hpp index 55eaeb27b..99029f14f 100644 --- a/src/checker.hpp +++ b/src/checker.hpp @@ -123,6 +123,7 @@ struct AttributeContext { String link_section; String linkage; isize init_expr_list_count; + i64 align; String thread_local_model; String deprecated_message; String warning_message; diff --git a/src/entity.cpp b/src/entity.cpp index 226b03c5c..2ba419b41 100644 --- a/src/entity.cpp +++ b/src/entity.cpp @@ -273,6 +273,7 @@ struct Entity { String link_prefix; String link_suffix; String link_section; + i64 custom_align; CommentGroup *docs; CommentGroup *comment; bool is_foreign; diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index 378b74032..e2bb2f812 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2421,7 +2421,7 @@ gb_internal void lb_create_global_variable(lbModule *m, lbGlobalVariable *var) { LLVM_SET_INTERNAL_WEAK_LINKAGE(g.value); } lb_set_linkage_from_entity_flags(m, g.value, e->flags); - LLVMSetAlignment(g.value, cast(u32)type_align_of(e->type)); + LLVMSetAlignment(g.value, cast(u32)gb_max(type_align_of(e->type), e->Variable.custom_align)); if (e->Variable.link_section.len > 0) { LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section)); diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp index 0108c2185..9a007ecac 100644 --- a/src/llvm_backend_general.cpp +++ b/src/llvm_backend_general.cpp @@ -4676,6 +4676,9 @@ gb_internal lbAddr lb_add_local(lbProcedure *p, Type *type, Entity *e, bool zero if (is_type_matrix(type)) { alignment *= 2; // NOTE(bill): Just in case } + if (e != nullptr && e->kind == Entity_Variable) { + alignment = gb_max(alignment, cast(unsigned)e->Variable.custom_align); + } LLVMValueRef ptr = llvm_alloca(p, llvm_type, alignment, name); diff --git a/src/llvm_backend_stmt.cpp b/src/llvm_backend_stmt.cpp index c5b3b81d5..c01f2d2a9 100644 --- a/src/llvm_backend_stmt.cpp +++ b/src/llvm_backend_stmt.cpp @@ -109,7 +109,10 @@ gb_internal void lb_scan_for_sret_rvo(lbProcedure *p) { Entity *e = entity_of_node(vd->names[0]); if (e == ret_entity) { Ast *rhs = unparen_expr(vd->values[0]); - if (rhs->kind == Ast_CallExpr && lb_call_sret_eligible(p, rhs, e->type)) { + // NOTE(bill): the caller's return slot only has the type's alignment and not @(align=N)'s + if (rhs->kind == Ast_CallExpr && + e->Variable.custom_align == 0 && + lb_call_sret_eligible(p, rhs, e->type)) { decl_index = i; } goto done_scanning; @@ -2481,7 +2484,7 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) { char *c_name = alloc_cstring(permanent_allocator(), mangled_name); LLVMValueRef global = LLVMAddGlobal(p->module->mod, lb_type(p->module, e->type), c_name); - LLVMSetAlignment(global, cast(u32)type_align_of(e->type)); + LLVMSetAlignment(global, cast(u32)gb_max(type_align_of(e->type), e->Variable.custom_align)); LLVMSetInitializer(global, LLVMConstNull(lb_type(p->module, e->type))); if (e->Variable.is_rodata) { @@ -2528,7 +2531,7 @@ gb_internal void lb_build_static_variables(lbProcedure *p, AstValueDecl *vd) { if (actual_type != expected_type) { LLVMDeleteGlobal(global); global = LLVMAddGlobal(p->module->mod, actual_type, c_name); - LLVMSetAlignment(global, cast(u32)type_align_of(e->type)); + LLVMSetAlignment(global, cast(u32)gb_max(type_align_of(e->type), e->Variable.custom_align)); if (e->Variable.is_rodata) { LLVMSetGlobalConstant(global, true); } @@ -3464,7 +3467,9 @@ gb_internal void lb_build_stmt(lbProcedure *p, Ast *node) { // the literal is one of its variants, so reusing that storage would bind the // variable to a bare `[]int` and never build the union at all if (comp_lit_addr && are_types_identical(lb_addr_type(*comp_lit_addr), type_of_expr(vd->names[lval_index]))) { - if (Entity *e = entity_of_node(vd->names[lval_index])) { + Entity *e = entity_of_node(vd->names[lval_index]); + // NOTE(bill): the literal's storage only has the type's alignment and not @(align=N)'s + if (e != nullptr && e->Variable.custom_align == 0) { lbValue val = comp_lit_addr->addr; lb_add_entity(p->module, e, val); lb_add_debug_local_variable(p, val.value, e->type, e->token);