This is an archive of the discontinued LLVM Phabricator instance.

BPF: Enable frontend constant folding for VLA size
AbandonedPublic

Authored by yonghong-song on Aug 10 2021, 11:33 PM.

Details

Reviewers
rsmith
Summary

Paul Chaignon reported a bpf verifier failure ([1]) due to using
non-ABI register R11. For the test case, llvm11 is okay while
llvm12 and later generates verifier unfriendly code.

The failure is related to variable length array size.
The following mimics the variable length array definition
in the test case:

#define AA 40
struct t { char a[20]; };
void foo(void *); 
int test() {
   const int a = 8;
   char tmp[AA + sizeof(struct t) + a]; 
   foo(tmp);
   ... 
}

Paul helped bisect that the following llvm commit is
responsible:

552c6c232872 ("PR44406: Follow behavior of array bound constant
              folding in more recent versions of GCC.")

Basically, before the above commit, clang frontend did constant
folding for array size "AA + sizeof(struct t) + a" to be 68,
so used alloca for stack allocation. After the above commit,
clang frontend didn't do constant folding for array size
any more, which results in a VLA and "llvm.stacksave"/"llvm.stackrestore"
is generated.

BPF architecture API does not support stack pointer (sp) register.
The LLVM internally used R11 to indicate sp register but it should
not be in the final code. Otherwise, kernel verifier will reject it.

This patch attempts to permit array size constant folding
in clang frontend for BPF target. This allows better code and
better developer experience. Otherwise users will need to manually
recalculate the array size whenever some macro or constant value
changes.

[1] https://lore.kernel.org/bpf/20210809151202.GB1012999@Mem/

Diff Detail

Event Timeline

yonghong-song created this revision.Aug 10 2021, 11:33 PM
yonghong-song requested review of this revision.Aug 10 2021, 11:33 PM
Herald added projects: Restricted Project, Restricted Project. · View Herald TranscriptAug 10 2021, 11:33 PM
  • fix clant-format warnings.

I tested this patch by running it through Cilium's CI which compiles, loads, and validates the behavior of hundreds of slightly different BPF programs. The tests cover several different kernels and all three BPF instruction set extensions (mcpu={v1,v2,v3}). It did not generate invalid bytecode with the register R11 anymore and the tests did not uncover any other regressions with this patch.

ping. @rsmith could you help take a look at the patch? Thanks!

@rsmith ping again. Could you help take a look at this patch? Thanks!

@rsmith ping again, did you get a time to take a look?

I'd prefer not to mess with the AST if we don't need to; more differences between targets make it harder to understand any issues that come up. BPF-flavored C already has enough weird differences without adding unnecessary changes.

If all you need is to avoid unnecessary llvm.stacksave calls, can you try messing with CodeGenFunction::EmitAutoVarAlloca?

@efriedma Thanks for suggestion! Let me look at CodeGenFunction::EmitAutoVarAlloca() call instead.

I checked EmitAutoVarAlloca(). It emits the llvm.stacksave() due to

// If the type is variably-modified, emit all the VLA sizes for it.
if (Ty->isVariablyModifiedType())
  EmitVariablyModifiedType(Ty);

Here, in order not to generate llvm.stacksave(), we need to evaluate the array size expression for Ty.
But it is probably not a good idea to re-evaluate Ty during code generation phase. If we really want to evaluate, we should evaluate earlier during semantic analysis phase like this patch.

Another solution is to implement pruning llvm.stacksave/stackrestore in backend. Currently InstCombine phase tries to remove some adjacent stacksave() and stackrestore() instrinsics. We could try to enhance InstCombine to handle more complicated cases, or considering this may be just a BPF use case (which doesn't support dynamic memory allocation), we can implement a bpf backend IR phase to remove it.

@efriedma @rsmith Did you get a chance to take a look? This regression is still affecting LLVM, including the recently released v13.0.0.

I don't think my opinion has changed here. I'm against the solution proposed in this patch. The other solutions discussed in the review seem fine. (The simplest is just to make the bpf backend ignore stacksave/stackrestore calls.)

@pchaigno as @efriedma suggested, we can add some transformation in BPF target IR passes to ignore @llvm.stacksave() and @llvm.stackrestore(). I should have a patch ready soon.

yonghong-song abandoned this revision.Oct 18 2021, 10:45 AM