Skip to content

Commit e66bda1

Browse files
committed
v3: keep @[aligned] struct alignment under MSVC
The MSVC preamble defines `__attribute__` away, which also dropped the `__attribute__((aligned(N)))` suffix of `@[aligned]` structs. For MSVC, emit `__declspec(align (N))` between the tag and the name instead (16 for a bare `@[aligned]`, GCC's meaning on x86-64 and arm64), as in testdata/aligned_attr_msvc_windows.c.must_have. Map `__alignof__`, used for heap copies of such structs, to MSVC's `__alignof`.
1 parent 7494bbb commit e66bda1

3 files changed

Lines changed: 62 additions & 2 deletions

File tree

‎vlib/v/gen/c/cleanc.v‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18154,6 +18154,8 @@ fn (mut g FlatGen) preamble() {
1815418154
g.writeln('#define _Thread_local __declspec(thread)')
1815518155
g.writeln('#endif')
1815618156
g.writeln('#define _Atomic volatile')
18157+
// Heap copies of `@[aligned]` structs ask for their alignment with GCC's spelling.
18158+
g.writeln('#define __alignof__(x) __alignof(x)')
1815718159
g.writeln('#endif')
1815818160
if use_system_libc {
1815918161
g.writeln('typedef ptrdiff_t isize;')

‎vlib/v/gen/c/names_test.v‎

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,43 @@ fn test_target_libc_opaque_packed_struct_restores_packing() {
202202
assert c_code.contains('#pragma pack(push, 1)\nstruct TargetOpaque;\n#pragma pack(pop)')
203203
}
204204

205+
fn aligned_struct_decl_c(ccompiler string, attrs string, union_decl bool) string {
206+
mut a := flat.FlatAst.new()
207+
mut tc := types.TypeChecker.new(&a)
208+
name := 'Aligned'
209+
tc.structs[name] = []types.StructField{}
210+
if union_decl {
211+
tc.unions[name] = true
212+
}
213+
node_id := a.add_node(flat.Node{
214+
kind: .struct_decl
215+
value: name
216+
typ: attrs
217+
})
218+
mut g := FlatGen.new()
219+
g.a = &a
220+
g.tc = &tc
221+
g.set_ccompiler(ccompiler)
222+
g.register_struct_decl_info_at(int(node_id), name, name, 'main', '/project/main.v', a.nodes[int(node_id)])
223+
g.emit_struct(name)
224+
return g.sb.str()
225+
}
226+
227+
fn test_aligned_struct_decls_use_the_msvc_alignment_spelling() {
228+
msvc := aligned_struct_decl_c('msvc', 'aligned=8', false)
229+
assert msvc.contains('struct __declspec(align (8)) Aligned {'), msvc
230+
assert !msvc.contains('__attribute__'), msvc
231+
msvc_union := aligned_struct_decl_c('msvc', 'aligned=16', true)
232+
assert msvc_union.contains('union __declspec(align (16)) Aligned {'), msvc_union
233+
// A bare `@[aligned]` means the target's largest alignment, as with GCC.
234+
msvc_bare := aligned_struct_decl_c('msvc', 'aligned', false)
235+
assert msvc_bare.contains('struct __declspec(align (16)) Aligned {'), msvc_bare
236+
gcc := aligned_struct_decl_c('gcc', 'aligned=8', false)
237+
assert gcc.contains('struct Aligned {'), gcc
238+
assert gcc.contains('} __attribute__((aligned(8)));'), gcc
239+
assert !gcc.contains('__declspec'), gcc
240+
}
241+
205242
fn test_collect_cache_native_c_symbols_only_records_type_declarations() {
206243
mut a := flat.FlatAst.new()
207244
mut tc := types.TypeChecker.new(&a)

‎vlib/v/gen/c/struct.v‎

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4500,6 +4500,14 @@ fn struct_decl_alignment_attr(align StructDeclAlignment) string {
45004500
return '__attribute__((aligned(${align.value})))'
45014501
}
45024502

4503+
// struct_decl_alignment_declspec is MSVC's spelling of a struct alignment. A bare
4504+
// `@[aligned]` asks for the target's largest alignment, like GCC's `aligned`, which is
4505+
// 16 bytes on the x86-64 and arm64 targets MSVC compiles for.
4506+
fn struct_decl_alignment_declspec(align StructDeclAlignment) string {
4507+
value := if align.value.len > 0 { align.value } else { '16' }
4508+
return '__declspec(align (${value}))'
4509+
}
4510+
45034511
fn struct_decl_alignment_memdup_arg(align StructDeclAlignment, c_type string) string {
45044512
if align.value.len > 0 {
45054513
return align.value
@@ -6406,14 +6414,27 @@ fn (mut g FlatGen) emit_struct(name string) {
64066414
g.tc.cur_module = old_module
64076415
return
64086416
}
6409-
g.writeln('${g.struct_decl_head(name)} {')
6417+
mut align := StructDeclAlignment{}
6418+
mut has_align := false
6419+
if decl_align := g.struct_decl_alignment_for_name(name) {
6420+
align = decl_align
6421+
has_align = true
6422+
}
6423+
head := g.struct_decl_head(name)
6424+
if has_align && g.ccompiler == 'msvc' {
6425+
// MSVC takes the alignment between the tag and the name, and has no GNU
6426+
// attributes (the preamble defines `__attribute__` away).
6427+
g.writeln('${head.all_before(' ')} ${struct_decl_alignment_declspec(align)} ${head.all_after(' ')} {')
6428+
} else {
6429+
g.writeln('${head} {')
6430+
}
64106431
if fields.len == 0 {
64116432
g.writeln('\tE_STRUCT_DECL;')
64126433
}
64136434
for f in fields {
64146435
g.write_struct_field(name, f)
64156436
}
6416-
if align := g.struct_decl_alignment_for_name(name) {
6437+
if has_align && g.ccompiler != 'msvc' {
64176438
g.writeln('} ${struct_decl_alignment_attr(align)};')
64186439
} else {
64196440
g.writeln('};')

0 commit comments

Comments
 (0)