Odin icon indicating copy to clipboard operation
Odin copied to clipboard

Allow #align for #packed structs

Open JesseRMeyer opened this issue 2 years ago • 6 comments

blah:: struct #packed #align 2 {
...
}

This should work, as per discussion on Discord. Currently "Syntax Error: '#align' cannot be applied with '#packed'"

JesseRMeyer avatar Apr 22 '23 17:04 JesseRMeyer

With regards to allowing #align on a #packed struct, what should be the size of this data structure?

blah :: struct #packed #align 2 {x: [3]u8}
size_of(blah) == ???

Should the answer be 4 or error?

gingerBill avatar Apr 27 '23 10:04 gingerBill

Why not 3?

JesseRMeyer avatar Apr 27 '23 13:04 JesseRMeyer

@JesseRMeyer Because there is a rule in most programming languages that size_of(T) % align_of(T) == 0 . Making it 3 would break this rule.

gingerBill avatar Apr 27 '23 13:04 gingerBill

I think we can rightfully question the applicability of that rule (read: convention) given the combination of these features. Adding a byte to the size is equivalent to adding a tail padding field, violating at least one sense of #packed.

However, I think most people would find any implicit behavior here, however logically consistent, to be surprising given the prevalence of the rule. In the spirit of 'minimal implicitness', emit an error with guidance from the compiler for the user to do it right up front.

JesseRMeyer avatar Apr 27 '23 13:04 JesseRMeyer

From what you re saying, it ought to be an error.

gingerBill avatar Apr 27 '23 21:04 gingerBill

In this particular case, yes.

JesseRMeyer avatar Apr 27 '23 21:04 JesseRMeyer

This is probably dead but, this would actually be quite a useful feature for kernel development. Most notable example I can think of is for implementing a GDT

GDT_Entry :: struct #packed
{
    limit0:     u16,
    base0:      u16,
    base1:      u8,
    accessByte: u8,
    limitFlags: u8,
    base2:      u8,
}

GDT :: struct #packed #align 0x1000
{
    null:       GDT_Entry,
    kernelCode: GDT_Entry,
    kernelData: GDT_Entry,
    userNull:   GDT_Entry,
    userCode:   GDT_Entry,
    userData:   GDT_Entry,
}

Hachem-H avatar Jul 04 '23 20:07 Hachem-H

This problem has now been solved with #field_align(N).

#field_align(N) will set the minimum alignment for each field to N or its natural alignment, whichever is larger.

Foo :: struct #field_align(4) {
    x: u8,  // offset == 0
    y: u8,  // offset == 4
    z: u16, // offset == 8
    w: u64, // offset = 16
}

gingerBill avatar Jan 28 '24 21:01 gingerBill

@Hachem-H I wonder if this solves this usecase

GDT_Entry :: struct #packed {
    limit0:     u16,
    base0:      u16,
    base1:      u8,
    accessByte: u8,
    limitFlags: u8,
    base2:      u8,
}

GDT :: struct #align 0x1000 {
    using _: struct #packed {
      null:       GDT_Entry,
      kernelCode: GDT_Entry,
      kernelData: GDT_Entry,
      userNull:   GDT_Entry,
      userCode:   GDT_Entry,
      userData:   GDT_Entry,
    },
}

Also aren't GDT entries sized according to the CPU mode, i.e. 32-bit in protected and compat mode, and 64 bits in long mode?

flysand7 avatar Jan 28 '24 22:01 flysand7

@flysand7 Yes actually though its rather finicky but it certainly works thank you! and also yes you're correct it depends on the CPU mode I just tried to write a GDT entry off the top of my head if i'm being completely honest.

Hachem-H avatar Jan 29 '24 19:01 Hachem-H