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

volatile not respected when loading a packed struct #1761

Closed
andrewrk opened this issue Nov 20, 2018 · 5 comments
Closed

volatile not respected when loading a packed struct #1761

andrewrk opened this issue Nov 20, 2018 · 5 comments
Labels
bug Observed behavior contradicts documented or intended behavior miscompilation The compiler reports success but produces semantically incorrect code.
Milestone

Comments

@andrewrk
Copy link
Member

const spi_tdr = packed struct {
    unused1: u7,
    lastxfr: u1,
    unused2: u4,
    pcs: u4,
    td: u16,
};

export fn entry(ptr: *volatile spi_tdr) void {
    var tmp = ptr.*;
    tmp.lastxfr = 1;
    ptr.* = tmp;
}

generates

define void @entry(%spi_tdr* nonnull) #2 !dbg !80 {
Entry:
  %ptr = alloca %spi_tdr*, align 8
  %tmp = alloca %spi_tdr, align 1
  store %spi_tdr* %0, %spi_tdr** %ptr, align 8
  call void @llvm.dbg.declare(metadata %spi_tdr** %ptr, metadata !96, metadata !DIExpression()), !dbg !100
  %1 = load %spi_tdr*, %spi_tdr** %ptr, align 8, !dbg !101
  %2 = bitcast %spi_tdr* %1 to i8*, !dbg !102
  %3 = bitcast %spi_tdr* %tmp to i8*, !dbg !102
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %3, i8* align 1 %2, i64 4, i1 false), !dbg !102
  call void @llvm.dbg.declare(metadata %spi_tdr* %tmp, metadata !97, metadata !DIExpression()), !dbg !102
  %4 = getelementptr inbounds %spi_tdr, %spi_tdr* %tmp, i32 0, i32 0, !dbg !103
  %5 = load i8, i8* %4, align 1, !dbg !105
  %6 = and i8 %5, 127, !dbg !105
  %7 = or i8 -128, %6, !dbg !105
  store i8 %7, i8* %4, align 1, !dbg !105
  %8 = load %spi_tdr*, %spi_tdr** %ptr, align 8, !dbg !106
  %9 = bitcast %spi_tdr* %tmp to i8*, !dbg !107
  %10 = bitcast %spi_tdr* %8 to i8*, !dbg !107
  call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %10, i8* align 1 %9, i64 4, i1 true), !dbg !107
  ret void, !dbg !108
}

The problem here is call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 1 %3, i8* align 1 %2, i64 4, i1 false), !dbg !102 and specifically the last arg i1 false. That's a non-volatile load.

This load should be volatile.

Arguably Zig should generate a ptrcast from the struct pointer to a pointer to u32 since that is <= register size, but if LLVM is doing the same thing when lowering memcpys - which it appears to be doing - then it's fine just to set the volatile bit.

We don't really have a way to test this other than pattern-matching the resulting LLVM IR, so I think we'll have to add some tests that do that. I already need something like that for #1682.

@andrewrk andrewrk added the bug Observed behavior contradicts documented or intended behavior label Nov 20, 2018
@andrewrk andrewrk added this to the 0.4.0 milestone Nov 20, 2018
@andrewrk andrewrk modified the milestones: 0.4.0, 0.5.0 Apr 8, 2019
@andrewrk andrewrk added the miscompilation The compiler reports success but produces semantically incorrect code. label Sep 27, 2019
@andrewrk andrewrk modified the milestones: 0.5.0, 0.6.0 Sep 29, 2019
@shawnl
Copy link
Contributor

shawnl commented Oct 3, 2019

You can't do volatile on a packed struct unless every load/store is a single atomic operation, because otherwise you might add data races, which is prohibited by C11 (which in general we should follow where applicable, except for lack of aliasing stuff so far).

@davidgm94
Copy link
Contributor

The piece of code you posted seems to be compiling to LLVM IR fine now: https://godbolt.org/z/rcb6zE1q6

Is there anything else that is needed to solve the issue?

@Vexu
Copy link
Member

Vexu commented Aug 28, 2022

Packed structs behave differently and the original issue no longer applies to stage2.

@andrewrk
Copy link
Member Author

This is solved by the new packed struct semantics (#10113, #5049):

define dso_local void @entry(ptr nonnull align 4 %0) #0 {
Entry:
  %1 = alloca i32, align 4
  %2 = load volatile i32, ptr %0, align 4
  store i32 %2, ptr %1, align 4
  %3 = load i32, ptr %1, align 4
  %4 = and i32 %3, -129
  %5 = or i32 128, %4
  store i32 %5, ptr %1, align 4
  %6 = load i32, ptr %1, align 4
  store volatile i32 %6, ptr %0, align 4
  ret void
}

@saza-ku
Copy link

saza-ku commented Nov 6, 2023

Is this really solved? If so we should update the docs.

https://ziglang.org/documentation/master/#packed-struct

Using packed structs with volatile is problematic, and may be a compile error in the future. For details on this subscribe to this issue. TODO update these docs with a recommendation on how to use packed structs with MMIO (the use case for volatile packed structs) once this issue is resolved. Don't worry, there will be a good solution for this use case in zig.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Observed behavior contradicts documented or intended behavior miscompilation The compiler reports success but produces semantically incorrect code.
Projects
None yet
Development

No branches or pull requests

5 participants