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

Richard Diamond via llvm-dev llvm-dev at lists.llvm.org
Mon Nov 2 15:57:42 PST 2015

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):

  %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
  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:

def int_blackbox : Intrinsic<[llvm_any_ty], [LLVMMatchType<0>]>;

Thus, using the previous example, `%3` would become:
  %3 = call i32 @llvm.blackbox.i32(i32 2)



Thoughts and suggestions welcome.

Richard Diamond
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20151102/ed50c469/attachment.html>

More information about the llvm-dev mailing list