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

allowzero pointers don't work on wasm32-freestanding in release mode. #4668

Open
puzzleddev opened this issue Mar 7, 2020 · 7 comments
Open
Labels
backend-llvm The LLVM backend outputs an LLVM IR Module. bug Observed behavior contradicts documented or intended behavior
Milestone

Comments

@puzzleddev
Copy link

When compiling for wasm32-freestanding, writes to allowzero pointers, which are known at compile time to point to 0x0000 result in unreachable instructions.

A minimal example tested on master #80ff549e2 (latest at the time of writing) can be found here: https://gist.github.com/puzzleddev/620c2ddd28cb9c7a1f1b0f01673364ff

This really isn't important, given that there is no real reason to write to 0x0000 in WASM. It does, however, support such writes and, as such allowzero should function or an exemption has to be made. The WASM people tried to do so a while ago as seen here: WebAssembly/design#204

This seems to have gone nowhere, as today the memory instruction section mentions that accessible addresses start at 0 (https://webassembly.github.io/spec/core/syntax/instructions.html#syntax-instr-memory).

@LemonBoy
Copy link
Contributor

LemonBoy commented Mar 7, 2020

result in unreachable instructions

Do you have a stack trace? Is the problem inside the compiler or in the generated binary?

@puzzleddev
Copy link
Author

It will generate a program no problem, just the write to 0x0000 is optimized as if it was invalid.

According to this IR dump, Zig generates the proper instructions, so it may be LLVM that's the problem here.

@fengb
Copy link
Contributor

fengb commented Mar 7, 2020

https://godbolt.org/z/FQk5t3 raw wasm dump shows this:

  (func $glue_startup (type 0) (result i32)
    unreachable
    unreachable)

@andrewrk andrewrk added this to the 0.7.0 milestone Mar 7, 2020
@andrewrk
Copy link
Member

andrewrk commented Mar 7, 2020

I think we have to do some annoying things with "address spaces" in LLVM to make the semantics correct. If you search http://llvm.org/docs/LangRef.html for "address space" you can find a bunch of separate things talking about this special address space 0, and how LLVM makes all kinds of assumptions about address 0 in this address space.

@andrewrk andrewrk modified the milestones: 0.7.0, 0.8.0 Oct 17, 2020
@andrewrk andrewrk modified the milestones: 0.8.0, 0.9.0 Nov 6, 2020
@SpexGuy SpexGuy added backend-llvm The LLVM backend outputs an LLVM IR Module. bug Observed behavior contradicts documented or intended behavior labels Mar 21, 2021
@andrewrk andrewrk modified the milestones: 0.9.0, 0.10.0 May 19, 2021
@joshgoebel
Copy link

@puzzleddev I'm seeing the same thing you area... There is generated code, but it's marked as null by LLVM and in the compiled WASM it seems to disappear entirely:

@FRAMEBUFFER = internal unnamed_addr constant [16320 x i8]* null, align 4
@FRAMEBUFFER2 = internal unnamed_addr constant [16320 x i8]* inttoptr (i32 1 to [16320 x i8]*), align 4

...

 WhileBody:                                        ; preds = %WhileCond
   store i8 -103, i8* null, align 1
   %2 = load i32, i32* %i, align 4
   %3 = getelementptr inbounds [16320 x i8], [16320 x i8]* null, i32 0, i32 %2
   store i8 86, i8* %3, align 1
   %4 = load i32, i32* %i, align 4
   %5 = getelementptr inbounds [16320 x i8], [16320 x i8]* inttoptr (i32 1 to [16320 x i8]*), i32 0, i32 %     4
   store i8 103, i8* %5, align 1

@joshgoebel
Copy link

joshgoebel commented Jan 3, 2022

Oh, wait... yes with a single discrete pointer I get the unreachable behavior, but with an array pointer I just get the code dropped...

pub const ZERO: *allowzero u8 = @intToPtr(*allowzero u8, 0);
pub const FRAMEBUFFER: *allowzero [16320]u8 = @intToPtr(*allowzero[16320]u8, 0);
pub const FRAMEBUFFER2: * [16320]u8 = @intToPtr(*[16320]u8, 1);
    // compiles to unreachable
    tic.ZERO.* = 0x99;

    // doesn't appear in the generated WASM at all
    tic.FRAMEBUFFER.*[i]=0x56;

    // works as expected
    tic.FRAMEBUFFER2.*[i]=0x67;

This is a problem when trying to use Zig/LLVM for TIC-80 since our VRAM starts at address 0 of memory space.

@joshgoebel
Copy link

joshgoebel commented Jan 4, 2022

I fixed my problem by adding volatile though I'm not 100% sure what voodoo this is doing...

pub const FRAMEBUFFER: *allowzero volatile [16320]u8 = @intToPtr(*allowzero volatile [16320]u8, 0);

But the behavior is now correct and I can address the VRAM from position 0 and forward. Looks like LLVM adds a volatile to the opcode:

--- before.log	2022-01-03 20:41:23.000000000 -0500
+++ after.log	2022-01-03 20:41:29.000000000 -0500
@@ -157,7 +157,7 @@
   %2 = load i32, i32* %i, align 4
   %3 = load [16320 x i8]*, [16320 x i8]** @FRAMEBUFFER, align 4
   %4 = getelementptr inbounds [16320 x i8], [16320 x i8]* %3, i32 0, i32 %2
-  store i8 86, i8* %4, align 1
+  store volatile i8 86, i8* %4, align 1
   %5 = load i32, i32* %i, align 4
   %6 = add nuw i32 %5, 1
   store i32 %6, i32* %i, align 4

That's the whole diff...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backend-llvm The LLVM backend outputs an LLVM IR Module. bug Observed behavior contradicts documented or intended behavior
Projects
None yet
Development

No branches or pull requests

6 participants