[llvm-dev] [RFC] A new intrinsic, `llvm.blackbox`, to explicitly prevent constprop, die, etc optimizations

Sanjoy Das via llvm-dev llvm-dev at lists.llvm.org
Mon Nov 2 17:19:53 PST 2015


Why does this need to be an intrinsic (as opposed to generic "unknown 
function" to llvm)?

Secondly, have you looked into a volatile store / load to an alloca? 
That should work with PNaCl and WebAssembly.

E.g.

define i32 @blackbox(i32 %arg) {
  entry:
   %p = alloca i32
   store volatile i32 10, i32* %p  ;; or store %arg
   %v = load volatile i32, i32* %p
   ret i32 %v
}

-- Sanjoy

Richard Diamond via llvm-dev wrote:
> Hey all,
>
> I'd like to propose a new intrinsic for use in preventing optimizations
> from deleting IR due to constant propagation, dead code elimination, etc.
>
>
> # Background/Motivation
>
> In Rust we have a crate called `test` which provides a function,
> `black_box`, which is designed to be a no-op function that prevents
> constprop, die, etc from interfering with tests/benchmarks but otherwise
> doesn't negatively affect resulting machine code quality. `test`
> currently implements this function by using inline asm, which marks a
> pointer to the argument as used by the assembly.
>
> At the IR level, this creates an alloca, stores it's argument to it,
> calls the no-op inline asm with the alloca pointer, and then returns a
> load of the alloca. Obviously, `mem2reg` would normally optimize this
> sort of pattern away, however the deliberate use of the no-op asm
> prevents other desirable optimizations (such as the aforementioned
> `mem2reg` pass) a little too well.
>
> Existing and upcoming virtual ISA targets also don't have this luxury
> (PNaCl/JS and WebAssembly, respectively). For these kind of targets,
> Rust's `test` currently forbids inlining of `black_box`, which crudely
> achieves the same effect. This is undesirable for any target because of
> the associated call overhead.
>
> The IR for `test::black_box::<i32>` is currently (it gets inlined, as
> desired, so I've omitted the function signature):
>
> ````llvm
>    %dummy.i = alloca i32, align 4
>    %2 = bitcast i32* %dummy.i to i8*
>    call void @llvm.lifetime.start(i64 4, i8* %2) #1
> ; Here, the value operand was the original argument to
> `test::black_box::<i32>`
>    store i32 2, i32* %dummy.i, align 4
>    call void asm "", "r,~{dirflag},~{fpsr},~{flags}"(i32* %dummy.i) #1,
> !srcloc !0
>    %3 = load i32, i32* %dummy.i, align 4
>    call void @llvm.lifetime.end(i64 4, i8* %2) #1
> ````
>
> This could be better.
>
> # Solution
>
> Add a new intrinsic, called `llvm.blackbox`, which accepts a value of
> any type and returns a value of the same type. As with many other
> intrinsics, this intrinsic shall remain unknown to all optimizations,
> before and during codegen. Specifically, this intrinsic should prevent
> all optimizations which operate by assuming properties of the value
> passed to the intrinsic. Once the last optimization pass (of any kind)
> is finished, all calls can be RAUW its argument.
>
> Table-gen def:
>
> ```tablegen
> def int_blackbox : Intrinsic<[llvm_any_ty], [LLVMMatchType<0>]>;
> ```
>
> Thus, using the previous example, `%3` would become:
> ```llvm
>    %3 = call i32 @llvm.blackbox.i32(i32 2)
>
> ```
>
> #
>
> Thoughts and suggestions welcome.
>
> Thanks,
> Richard Diamond
>
> _______________________________________________
> LLVM Developers mailing list
> llvm-dev at lists.llvm.org
> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev


More information about the llvm-dev mailing list