Help with endless Code Generation step while creating GDT array for my zig OS kernel

Hello,

I’m having an issue I haven’t come across before, namely that the Code Generation step seems to spin endlessly (longer than I expect it should take, it has run 5 or 10 minutes by now).

My code

Main builds and runs, but not gc-2024-09-24-gdt.

It seems to be specifically related to the presence of the makeGdtEntry call(s). It will compile and run if I take those calls out.

I am not sure what’s wrong here, any insight would be much appreciated ^_^

Zig Version 0.13.0

> zigup run 0.13.0 build
[0/3] steps
└─ [2] zig build-exe kernel.elf Debug x86-frees
   ├─ [132] Semantic Analysis
   │  ├─ gdt.gdt_entries
   │  └─ gdt.init
   └─ [19] Code Generation

Zig Version 0.14.0-dev.1637+8c232922b

> zigup run master build
[0/3] steps
└─ [3] zig build-exe kernel.elf Debug x86-frees
   ├─ [137] Semantic Analysis
   │  ├─ gdt.gdt_entries
   │  └─ gdt.init
   └─ [2] Code Generation

Relevant Code Samples (from src/gdt.zig)

/// The GDT entry table containing NUMBER_OF_ENTRIES entries.
var gdt_entries: [NUMBER_OF_ENTRIES]GdtEntry = .{
    // Null descriptor
    makeGdtEntry(0, 0, NULL_SEGMENT, NULL_FLAGS),

    // Kernel code descriptor
    // makeGdtEntry(0, 0xFFFFF, KERNEL_SEGMENT_CODE, PAGING_32_BIT),

    // Kernel data descriptor
    // makeGdtEntry(0, 0xFFFFF, KERNEL_SEGMENT_DATA, PAGING_32_BIT),

    // User code descriptor
    // makeGdtEntry(0, 0xFFFFF, USER_SEGMENT_CODE, PAGING_32_BIT),

    // User data descriptor
    // makeGdtEntry(0, 0xFFFFF, USER_SEGMENT_DATA, PAGING_32_BIT),

    // TSS descriptor, one each for each processor.
    // Will initialize the TSS at runtime.
    // makeGdtEntry(0, 0, NULL_SEGMENT, NULL_FLAGS),
};
///
/// Make a GDT entry.
///
/// Arguments:
///     IN base:  u32         - The linear address where the segment begins.
///     IN limit: u20         - The maximum addressable unit whether it is 1B units or page units.
///     IN access: AccessBits - The access bits for the descriptor.
///     IN flags: FlagBits    - The flag bits for the descriptor.
///
/// Return: GdtEntry
///     A new GDT entry with the given access and flag bits set.
///
fn makeGdtEntry(base: u32, limit: u20, access: AccessBits, flags: FlagBits) GdtEntry {
    return .{
        .limit_low = @truncate(limit),
        .base_low = @truncate(base),
        .access = .{
            .accessed = access.accessed,
            .read_write = access.read_write,
            .direction_conforming = access.direction_conforming,
            .executable = access.executable,
            .descriptor = access.descriptor,
            .privilege = access.privilege,
            .present = access.present,
        },
        .limit_high = @truncate(limit >> 16),
        .flags = .{
            .reserved_zero = flags.reserved_zero,
            .is_64_bit = flags.is_64_bit,
            .is_32_bit = flags.is_32_bit,
            .granularity = flags.granularity,
        },
        .base_high = @truncate(base >> 24),
    };
}
pub fn init() void {
    console.printf("NUMBER_OF_ENTRIES: {}\n", .{NUMBER_OF_ENTRIES});
    console.printf("TABLE_SIZE: {}\n", .{TABLE_SIZE});
    console.printf("KERNEL_SEGMENT_CODE: {}\n", .{KERNEL_SEGMENT_CODE});
    console.printf("gdt_entries[0]: {}\n", .{gdt_entries[0]});
...
}

Using gdt_entries[0] is enough to get the whole thing to start stalling, assuming there’s a makeGdtEntry call in there.

Bonus Screenshot

It’s compiling fine for me. I’m using today’s Zig binary.
Are you sure that it’s not just the UI bugging out? This has tripped me up a few times. Progress report sometimes forgets to clean up after it’s done, so it looks like it’s still going.

1 Like

Oh wow, interesting! Thank you for trying on your machine! Let me investigate this…

I was also looking through issues, and thought that perhaps this looked similar, but I haven’t looked into it more than reading: compiler Infinit loop when initializing an array using a block · Issue #21335 · ziglang/zig · GitHub

I’m using the same version of zig and have confirmed that I do not get a zig-out directory. So it does not seems it’s merely a graphical bug.

If you try to compile the latest commit, c031d3d, does it complete and generate a zig-out directory for you? Perhaps you were accidentally on main instead of gc-2024-09-24-gdt?

it appears to be this open issue, as the example within mirrors my experience and setup:

1 Like

As per unnest durations to workaround ziglang/zig#19730 · pkmn/engine@fb6de10 · GitHub, it is demonstrated that this bug can be worked-around by unnesting the packed struct initializations.

Here is a working makeGdtEntry function:

fn makeGdtEntry(base: u32, limit: u20, access: AccessBits, flags: FlagBits) GdtEntry {
    const access_copy: AccessBits = .{
        .accessed = access.accessed,
        .read_write = access.read_write,
        .direction_conforming = access.direction_conforming,
        .executable = access.executable,
        .descriptor = access.descriptor,
        .privilege = access.privilege,
        .present = access.present,
    };
    const flags_copy: FlagBits = .{
        .reserved_zero = flags.reserved_zero,
        .is_64_bit = flags.is_64_bit,
        .is_32_bit = flags.is_32_bit,
        .granularity = flags.granularity,
    };
    return .{
        .limit_low = @truncate(limit),
        .base_low = @truncate(base),
        .access = access_copy,
        .limit_high = @truncate(limit >> 16),
        .flags = flags_copy,
        .base_high = @truncate(base >> 24),
    };
}
2 Likes