Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v: implement aligned attr for struct #19915

Merged
merged 9 commits into from
Nov 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion doc/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -6107,7 +6107,7 @@ sure that the access index will be valid.

#### `[packed]`

The `[packed]` attribute can be added to a structure to create an unaligned memory layout,
The `@[packed]` attribute can be applied to a structure to create an unaligned memory layout,
which decreases the overall memory footprint of the structure. Using the `[packed]` attribute
may negatively impact performance or even be prohibited on certain CPU architectures.

Expand All @@ -6120,6 +6120,45 @@ may negatively impact performance or even be prohibited on certain CPU architect
- On CPU architectures that do not support unaligned memory access or when high-speed memory access
is needed.

#### `[aligned]`

The `@[aligned]` attribute can be applied to a structure or union to specify a minimum alignment
(in bytes) for variables of that type. Using the `@[aligned]` attribute you can only *increase*
the default alignment. Use `@[packed]` if you want to *decrease* it. The alignment of any struct
or union, should be at least a perfect multiple of the lowest common multiple of the alignments of
all of the members of the struct or union.

Example:
```v
// Each u16 in the `data` field below, takes 2 bytes, and we have 3 of them = 6 bytes.
// The smallest power of 2, bigger than 6 is 8, i.e. with `@[aligned]`, the alignment
// for the entire struct U16s, will be 8:
@[aligned]
struct U16s {
data [3]u16
}
```
**When to Use**

- Only if the instances of your types, will be used in performance critical sections, or with
specialised machine instructions, that do require a specific alignment to work.

**When to Avoid**

- On CPU architectures, that do not support unaligned memory access. If you are not working on
performance critical algorithms, you do not really need it, since the proper minimum alignment
is CPU specific, and the compiler already usually will choose a good default for you.

> **Note**
> You can leave out the alignment factor, i.e. use just `@[aligned]`, in which case the compiler
> will align a type to the maximum useful alignment for the target machine you are compiling for,
> i.e. the alignment will be the largest alignment which is ever used for any data type on the
> target machine. Doing this can often make copy operations more efficient, because the compiler
> can choose whatever instructions copy the biggest chunks of memory, when performing copies to or
> from the variables which have types that you have aligned this way.

See also ["What Every Programmer Should Know About Memory", by Ulrich Drepper](https://people.freebsd.org/~lstewart/articles/cpumemory.pdf) .

#### `[minify]`

The `[minify]` attribute can be added to a struct, allowing the compiler to reorder the fields in
Expand Down
24 changes: 22 additions & 2 deletions vlib/v/gen/c/struct.v
Original file line number Diff line number Diff line change
Expand Up @@ -462,13 +462,30 @@ fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool) {
is_minify := s.is_minify
g.type_definitions.writeln(pre_pragma)

mut aligned_attr := ''
if attr := s.attrs.find_first('aligned') {
attr_arg := if attr.arg == '' { '' } else { ' (${attr.arg})' }
aligned_attr += if g.is_cc_msvc {
'__declspec(align${attr_arg})'
} else {
' __attribute__((aligned${attr_arg}))'
}
}
if is_anon {
g.type_definitions.write_string('\t${name} ')
return
} else if s.is_union {
g.type_definitions.writeln('union ${name} {')
if g.is_cc_msvc && aligned_attr != '' {
g.type_definitions.writeln('union ${aligned_attr} ${name} {')
} else {
g.type_definitions.writeln('union ${name} {')
}
} else {
g.type_definitions.writeln('struct ${name} {')
if g.is_cc_msvc && aligned_attr != '' {
g.type_definitions.writeln('struct ${aligned_attr} ${name} {')
} else {
g.type_definitions.writeln('struct ${name} {')
}
}

if s.fields.len > 0 || s.embeds.len > 0 {
Expand Down Expand Up @@ -555,6 +572,9 @@ fn (mut g Gen) struct_decl(s ast.Struct, name string, is_anon bool) {
''
}
g.type_definitions.write_string('}${ti_attrs}')
if !g.is_cc_msvc && aligned_attr != '' {
g.type_definitions.write_string(' ${aligned_attr}')
}
if !is_anon {
g.type_definitions.write_string(';')
}
Expand Down
3 changes: 3 additions & 0 deletions vlib/v/gen/c/testdata/aligned_attr_nix.c.must_have
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
} __attribute__((aligned))
} __attribute__((aligned (16)))
} __attribute__((aligned (8)))
Empty file.
15 changes: 15 additions & 0 deletions vlib/v/gen/c/testdata/aligned_attr_nix.vv
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
@[aligned]
struct Test {
a int
}

@[aligned:16]
struct Test2 {
a int
b int
}

@[aligned:8]
union Test3 {
a int
}
3 changes: 3 additions & 0 deletions vlib/v/gen/c/testdata/aligned_attr_windows.c.must_have
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
struct __declspec(align (8)) main__Test {
struct __declspec(align (16)) main__Test2 {
union __declspec(align (8)) main__Test3 {
Empty file.
17 changes: 17 additions & 0 deletions vlib/v/gen/c/testdata/aligned_attr_windows.vv
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// vtest vflags: -cc msvc -os windows

@[aligned:8]
struct Test {
a int
}

@[aligned:16]
struct Test2 {
a int
b int
}

@[aligned:8]
union Test3 {
a int
}
Loading