Skip to content

v: add support for alignof. #24509

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

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
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
6 changes: 4 additions & 2 deletions doc/docs.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ by using any of the following commands in a terminal:
* [Trace](#trace)
* [Memory-unsafe code](#memory-unsafe-code)
* [Structs with reference fields](#structs-with-reference-fields)
* [sizeof and __offsetof](#sizeof-and-__offsetof)
* [sizeof, alignof and __offsetof](#sizeof-alignof-and-__offsetof)
* [Limited operator overloading](#limited-operator-overloading)
* [Performance tuning](#performance-tuning)
* [Atomics](#atomics)
Expand Down Expand Up @@ -6909,9 +6909,10 @@ println(baz)
println(qux)
```

## sizeof and __offsetof
## sizeof, alignof and __offsetof

* `sizeof(Type)` gives the size of a type in bytes.
* `alignof(Type)` gives the alignment of a type in bytes.
* `__offsetof(Struct, field_name)` gives the offset in bytes of a struct field.

```v
Expand All @@ -6921,6 +6922,7 @@ struct Foo {
}

assert sizeof(Foo) == 8
assert alignof[Foo]() == 4
assert __offsetof(Foo, a) == 0
assert __offsetof(Foo, b) == 4
```
Expand Down
23 changes: 17 additions & 6 deletions vlib/v/ast/ast.v
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub const int_type_name = $if new_int ? {
}

pub type Expr = NodeError
| AlignOf
| AnonFn
| ArrayDecompose
| ArrayInit
Expand Down Expand Up @@ -1912,6 +1913,16 @@ pub mut:
scope &Scope = unsafe { nil }
}

pub struct AlignOf {
pub:
guessed_type bool // a legacy `alignof( GuessedType )` => a deprecation notice, suggesting `v fmt -w .` => `alignof[ Type ]()`
is_type bool
pos token.Pos
pub mut:
expr Expr // checker uses this to set typ, when !is_type
typ Type
}

pub struct SizeOf {
pub:
guessed_type bool // a legacy `sizeof( GuessedType )` => a deprecation notice, suggesting `v fmt -w .` => `sizeof[ Type ]()`
Expand Down Expand Up @@ -2218,11 +2229,11 @@ pub fn (expr Expr) pos() token.Pos {
// println('compiler bug, unhandled EmptyExpr pos()')
token.Pos{}
}
NodeError, ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral, CallExpr,
CastExpr, ChanInit, CharLiteral, ConcatExpr, Comment, ComptimeCall, ComptimeSelector,
EnumVal, DumpExpr, FloatLiteral, GoExpr, SpawnExpr, Ident, IfExpr, IntegerLiteral,
IsRefType, Likely, LockExpr, MapInit, MatchExpr, None, OffsetOf, OrExpr, ParExpr,
PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr,
NodeError, AlignOf, ArrayDecompose, ArrayInit, AsCast, Assoc, AtExpr, BoolLiteral,
CallExpr, CastExpr, ChanInit, CharLiteral, ConcatExpr, Comment, ComptimeCall,
ComptimeSelector, EnumVal, DumpExpr, FloatLiteral, GoExpr, SpawnExpr, Ident, IfExpr,
IntegerLiteral, IsRefType, Likely, LockExpr, MapInit, MatchExpr, None, OffsetOf, OrExpr,
ParExpr, PostfixExpr, PrefixExpr, RangeExpr, SelectExpr, SelectorExpr, SizeOf, SqlExpr,
StringInterLiteral, StringLiteral, StructInit, TypeNode, TypeOf, UnsafeExpr, ComptimeType,
LambdaExpr, Nil {
expr.pos
Expand Down Expand Up @@ -2729,7 +2740,7 @@ pub fn (expr Expr) is_literal() bool {
!expr.has_arg && expr.expr.is_literal() && (expr.typ.is_any_kind_of_pointer()
|| expr.typ in [i8_type, i16_type, int_type, i64_type, u8_type, u16_type, u32_type, u64_type, f32_type, f64_type, char_type, bool_type, rune_type])
}
SizeOf, IsRefType {
AlignOf, SizeOf, IsRefType {
expr.is_type || expr.expr.is_literal()
}
else {
Expand Down
6 changes: 6 additions & 0 deletions vlib/v/ast/str.v
Original file line number Diff line number Diff line change
Expand Up @@ -413,6 +413,12 @@ pub fn (x &Expr) str() string {
stdatomic.sub_i64(&nested_expr_str_calls, 1)
}
match x {
AlignOf {
if x.is_type {
return 'alignof(${global_table.type_to_str(x.typ)})'
}
return 'alignof(${x.expr.str()})'
}
AnonFn {
return 'anon_fn'
}
Expand Down
7 changes: 7 additions & 0 deletions vlib/v/ast/types.v
Original file line number Diff line number Diff line change
Expand Up @@ -1303,6 +1303,13 @@ pub fn (t &Table) type_size(typ Type) (int, int) {
return size, align
}

// type_align returns the alignment (in bytes) of `typ`, just like C's alignof().
pub fn (t &Table) type_align(typ Type) int {
// we only care about the second (alignment) return value
_, align := t.type_size(typ)
return align
}

// round_up rounds the number `n` up to the next multiple `multiple`.
// Note: `multiple` must be a power of 2.
@[inline]
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/checker/checker.v
Original file line number Diff line number Diff line change
Expand Up @@ -3239,7 +3239,7 @@ pub fn (mut c Checker) expr(mut node ast.Expr) ast.Type {
}
return ret_type
}
ast.SizeOf {
ast.AlignOf, ast.SizeOf {
if !node.is_type {
node.typ = c.expr(mut node.expr)
}
Expand Down
4 changes: 4 additions & 0 deletions vlib/v/checker/comptime.v
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,10 @@ fn (mut c Checker) eval_comptime_const_expr(expr ast.Expr, nlevel int) ?ast.Comp
return val
}
}
ast.AlignOf {
a := c.table.type_align(expr.typ)
return i64(a)
}
ast.SizeOf {
s, _ := c.table.type_size(expr.typ)
return i64(s)
Expand Down
9 changes: 9 additions & 0 deletions vlib/v/eval/expr.c.v
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,9 @@ pub fn (mut e Eval) expr(expr ast.Expr, expecting ast.Type) Object {
// eprintln('unhandled struct init at line $expr.pos.line_nr')
return ''
}
ast.AlignOf {
return Uint{e.type_to_align(expr.typ), 64}
}
ast.SizeOf {
return Uint{e.type_to_size(expr.typ), 64}
}
Expand Down Expand Up @@ -622,6 +625,12 @@ fn (e &Eval) type_to_size(typ ast.Type) u64 {
}
}

// type_to_align returns the natural alignment (in bits) of `typ`.
fn (e &Eval) type_to_align(typ ast.Type) u64 {
// on most ABIs the alignment of a type == its size
return e.type_to_size(typ)
}

fn (e &Eval) get_escape(r rune) rune {
res := match r {
`\\` {
Expand Down
17 changes: 17 additions & 0 deletions vlib/v/fmt/fmt.v
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,9 @@ pub fn (mut f Fmt) expr(node_ ast.Expr) {
match mut node {
ast.NodeError {}
ast.EmptyExpr {}
ast.AlignOf {
f.align_of(node)
}
ast.AnonFn {
f.anon_fn(node)
}
Expand Down Expand Up @@ -3043,6 +3046,20 @@ pub fn (mut f Fmt) selector_expr(node ast.SelectorExpr) {
f.or_expr(node.or_block)
}

pub fn (mut f Fmt) align_of(node ast.AlignOf) {
f.write('alignof')
if node.is_type {
f.write('[')
f.write(f.table.type_to_str_using_aliases(node.typ, f.mod2alias))
f.write(']()')
return
} else {
f.write('(')
f.expr(node.expr)
f.write(')')
}
}

pub fn (mut f Fmt) size_of(node ast.SizeOf) {
f.write('sizeof')
if node.is_type && !node.guessed_type {
Expand Down
14 changes: 14 additions & 0 deletions vlib/v/gen/c/cgen.v
Original file line number Diff line number Diff line change
Expand Up @@ -3516,6 +3516,9 @@ fn (mut g Gen) expr(node_ ast.Expr) {
ast.EmptyExpr {
g.error('g.expr(): unhandled EmptyExpr', token.Pos{})
}
ast.AlignOf {
g.align_of(node)
}
ast.AnonFn {
g.gen_anon_fn(mut node)
}
Expand Down Expand Up @@ -7421,6 +7424,17 @@ fn (mut g Gen) get_type(typ ast.Type) ast.Type {
return if typ == g.field_data_type { g.comptime.comptime_for_field_value.typ } else { typ }
}

fn (mut g Gen) align_of(node ast.AlignOf) {
typ := g.type_resolver.typeof_type(node.expr, g.get_type(node.typ))
node_typ := g.unwrap_generic(typ)
sym := g.table.sym(node_typ)
if sym.language == .v && sym.kind in [.placeholder, .any] {
g.error('unknown type `${sym.name}`', node.pos)
}
styp := g.styp(node_typ)
g.write('alignof(${util.no_dots(styp)})')
}

fn (mut g Gen) size_of(node ast.SizeOf) {
typ := g.type_resolver.typeof_type(node.expr, g.get_type(node.typ))
node_typ := g.unwrap_generic(typ)
Expand Down
1 change: 1 addition & 0 deletions vlib/v/gen/c/cheaders.v
Original file line number Diff line number Diff line change
Expand Up @@ -595,6 +595,7 @@ typedef int (*qsort_callback_func)(const void*, const void*);
#include <string.h>

#include <stdarg.h> // for va_list
#include <stdalign.h> // for alignof

//================================== GLOBALS =================================*/
int load_so(byteptr);
Expand Down
13 changes: 13 additions & 0 deletions vlib/v/gen/golang/golang.v
Original file line number Diff line number Diff line change
Expand Up @@ -633,6 +633,9 @@ pub fn (mut f Gen) expr(node_ ast.Expr) {
ast.SelectorExpr {
f.selector_expr(node)
}
ast.AlignOf {
f.align_of(node)
}
ast.SizeOf {
f.size_of(node)
}
Expand Down Expand Up @@ -2149,6 +2152,16 @@ pub fn (mut f Gen) selector_expr(node ast.SelectorExpr) {
f.write(node.field_name)
}

pub fn (mut f Gen) align_of(node ast.AlignOf) {
f.write('alignof(')
if node.is_type {
f.write(f.table.type_to_str_using_aliases(node.typ, f.mod2alias))
} else {
f.expr(node.expr)
}
f.write(')')
}

pub fn (mut f Gen) size_of(node ast.SizeOf) {
f.write('sizeof(')
if node.is_type {
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/gen/js/js.v
Original file line number Diff line number Diff line change
Expand Up @@ -1035,7 +1035,7 @@ fn (mut g JsGen) expr(node_ ast.Expr) {
ast.SelectorExpr {
g.gen_selector_expr(node)
}
ast.SizeOf, ast.IsRefType {
ast.AlignOf, ast.SizeOf, ast.IsRefType {
// TODO
}
ast.OffsetOf {
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/markused/walker.v
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ fn (mut w Walker) expr(node_ ast.Expr) {
w.expr(node.high)
}
}
ast.SizeOf, ast.IsRefType {
ast.AlignOf, ast.SizeOf, ast.IsRefType {
w.expr(node.expr)
}
ast.StringInterLiteral {
Expand Down
22 changes: 21 additions & 1 deletion vlib/v/parser/expr.v
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,9 @@ fn (mut p Parser) check_expr(precedence int) !ast.Expr {
}
}
}
.key_sizeof, .key_isreftype {
.key_alignof, .key_sizeof, .key_isreftype {
is_reftype := p.tok.kind == .key_isreftype
is_alignof := p.tok.kind == .key_alignof
p.next() // sizeof

if p.tok.kind == .lsbr {
Expand All @@ -345,6 +346,12 @@ fn (mut p Parser) check_expr(precedence int) !ast.Expr {
typ: typ
pos: type_pos
}
} else if is_alignof {
node = ast.AlignOf{
is_type: true
typ: typ
pos: type_pos
}
} else {
node = ast.SizeOf{
is_type: true
Expand Down Expand Up @@ -375,6 +382,12 @@ fn (mut p Parser) check_expr(precedence int) !ast.Expr {
expr: expr
pos: pos
}
} else if is_alignof {
node = ast.AlignOf{
is_type: false
expr: expr
pos: pos
}
} else {
node = ast.SizeOf{
is_type: false
Expand All @@ -397,6 +410,13 @@ fn (mut p Parser) check_expr(precedence int) !ast.Expr {
typ: arg_type
pos: pos
}
} else if is_alignof {
node = ast.AlignOf{
guessed_type: true
is_type: true
typ: arg_type
pos: pos
}
} else {
node = ast.SizeOf{
guessed_type: true
Expand Down
2 changes: 1 addition & 1 deletion vlib/v/parser/parser.v
Original file line number Diff line number Diff line change
Expand Up @@ -4127,7 +4127,7 @@ fn (mut p Parser) global_decl() ast.GlobalDecl {
ast.FloatLiteral {
typ = ast.f64_type
}
ast.IntegerLiteral, ast.SizeOf {
ast.AlignOf, ast.IntegerLiteral, ast.SizeOf {
typ = ast.int_type
}
ast.StringLiteral, ast.StringInterLiteral {
Expand Down
23 changes: 23 additions & 0 deletions vlib/v/tests/alignof_test.v
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
struct S1 {
p voidptr
}

struct S2 {
i int
}

fn test_alignof() {
assert alignof[rune]() == 4
assert alignof[[44]u8]() == 1
assert alignof(`€`) == 4
// depends on -m32/64
assert alignof[S1]() in [u32(4), 8]
s := S2{}
assert alignof(s.i) == 4

assert alignof('hello') == $if x64 {
8
} $else {
4
}
}
3 changes: 3 additions & 0 deletions vlib/v/token/token.v
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ pub enum Kind {
key_select
key_like
key_ilike
key_alignof
key_sizeof
key_isreftype
key_likely
Expand Down Expand Up @@ -299,6 +300,7 @@ fn build_token_str() []string {
s[Kind.key_asm] = 'asm'
s[Kind.key_return] = 'return'
s[Kind.key_module] = 'module'
s[Kind.key_alignof] = 'alignof'
s[Kind.key_sizeof] = 'sizeof'
s[Kind.key_isreftype] = 'isreftype'
s[Kind.key_likely] = '_likely_'
Expand Down Expand Up @@ -647,6 +649,7 @@ pub fn kind_to_string(k Kind) string {
.key_select { 'key_select' }
.key_like { 'key_like' }
.key_ilike { 'key_ilike' }
.key_alignof { 'key_alignof' }
.key_sizeof { 'key_sizeof' }
.key_isreftype { 'key_isreftype' }
.key_likely { 'key_likely' }
Expand Down
Loading