This is an archive of the discontinued LLVM Phabricator instance.

[BPF] Reset machine register kill mark in BPFMISimplifyPatchable
ClosedPublic

Authored by eddyz87 on Aug 12 2023, 6:08 PM.

Details

Summary

When LLVM is build with LLVM_ENABLE_EXPENSIVE_CHECKS=ON option
the following C code snippet:

struct t {
  unsigned long a;
} __attribute__((preserve_access_index));

void foo(volatile struct t *t, volatile unsigned long *p) {
  *p = t->a;
  *p = t->a;
}

Causes an assertion:

$ clang -g -O2 -c --target=bpf -mcpu=v2 t2.c -o /dev/null

# After BPF PreEmit SimplifyPatchable
# Machine code for function foo: IsSSA, TracksLiveness
Function Live Ins: $r1 in %0, $r2 in %1

bb.0.entry:
  liveins: $r1, $r2
  DBG_VALUE $r1, $noreg, !"t", !DIExpression()
  DBG_VALUE $r2, $noreg, !"p", !DIExpression()
  %1:gpr = COPY $r2
  DBG_VALUE %1:gpr, $noreg, !"p", !DIExpression()
  %0:gpr = COPY $r1
  DBG_VALUE %0:gpr, $noreg, !"t", !DIExpression()
  %2:gpr = LD_imm64 @"llvm.t:0:0$0:0"
  %4:gpr = ADD_rr %0:gpr(tied-def 0), killed %2:gpr
  %5:gpr = CORE_LD 344, %0:gpr, @"llvm.t:0:0$0:0"
  STD killed %5:gpr, %1:gpr, 0
  %7:gpr = ADD_rr %0:gpr(tied-def 0), killed %2:gpr
  %8:gpr = CORE_LD 344, %0:gpr, @"llvm.t:0:0$0:0"
  STD killed %8:gpr, %1:gpr, 0
  RET

# End machine code for function foo.

*** Bad machine code: Using a killed virtual register ***
- function:    foo
- basic block: %bb.0 entry (0x6210000e6690)
- instruction: %7:gpr = ADD_rr %0:gpr(tied-def 0), killed %2:gpr
- operand 2:   killed %2:gpr

This happens because of the way
BPFMISimplifyPatchable::processDstReg() updates second operand of the
ADD_rr instruction. Code before BPFMISimplifyPatchable:

.-> %2:gpr = LD_imm64 @"llvm.t:0:0$0:0"
|
|`----------------.
|   %3:gpr = LDD %2:gpr, 0
|   %4:gpr = ADD_rr %0:gpr(tied-def 0), killed %3:gpr <--- (1)
|   %5:gpr = LDD killed %4:gpr, 0       ^^^^^^^^^^^^^
|   STD killed %5:gpr, %1:gpr, 0        this is updated
 `----------------.
    %6:gpr = LDD %2:gpr, 0
    %7:gpr = ADD_rr %0:gpr(tied-def 0), killed %6:gpr <--- (2)
    %8:gpr = LDD killed %7:gpr, 0       ^^^^^^^^^^^^^
    STD killed %8:gpr, %1:gpr, 0        this is updated

Instructions (1) and (2) would be updated to:

ADD_rr %0:gpr(tied-def 0), killed %2:gpr

The killed mark is inherited from machine operands killed %3:gpr
and killed %6:gpr which are updated inplace by processDstReg().

This commit updates processDstReg() reset kill marks for updated
machine operands to keep liveness information conservatively correct.

Diff Detail

Event Timeline

eddyz87 created this revision.Aug 12 2023, 6:08 PM
Herald added a project: Restricted Project. · View Herald TranscriptAug 12 2023, 6:08 PM
Herald added a subscriber: hiraditya. · View Herald Transcript
eddyz87 published this revision for review.Aug 13 2023, 5:52 AM
eddyz87 added a reviewer: yonghong-song.

Hi Yonghong,

Could you please take a look at this revision?
All BPF Kernel selftests are passing and there are no changes on the generated object files (I checked for cpuv4).

Herald added a project: Restricted Project. · View Herald TranscriptAug 13 2023, 5:52 AM
yonghong-song accepted this revision.Aug 13 2023, 12:10 PM

LGTM. Thanks for detailed comments to show why generated code is incorrect. Do you think we should backport this fix to llvm17?

This revision is now accepted and ready to land.Aug 13 2023, 12:10 PM

With LLVM_ENABLE_EXPENSIVE_CHECKS, even with this patch, there are still some selftest llvm crashes. I guess you are probably working on this (e.g. D157806).

LGTM. Thanks for detailed comments to show why generated code is incorrect. Do you think we should backport this fix to llvm17?

On the one hand the code dates back to Dec 2019 and I don't see it affecting code generation in practice (at-least for our selftests). On the other hand it is really easy to backport. I'd say we should backport it.

With LLVM_ENABLE_EXPENSIVE_CHECKS, even with this patch, there are still some selftest llvm crashes. I guess you are probably working on this (e.g. D157806).

Yes, I'm aware of two more issues, the one addressed by D157806 and the one in "BPF MachineSSA Peephole Optimization For TRUNC Eliminate":

*** Bad machine code: Illegal virtual register for instruction ***
- function:    _dissect
- basic block: %bb.9 if.end10 (0x621000e80d98)
- instruction: %24:gpr32 = MOV_rr %8:gpr32, debug-location !339; progs/bpf_flow.c:120:2 @[ progs/bpf_flow.c:161:9 ]
- operand 0:   %24:gpr32
Expected a GPR register, but got a GPR32 register

Which I suppose is caused by MOV_rr instruction generated in the end of BPFMIPeepholeTruncElim::eliminateTruncSeq(). I'll submit a fix for this issue a bit later.

LGTM. Thanks for detailed comments to show why generated code is incorrect. Do you think we should backport this fix to llvm17?

On the one hand the code dates back to Dec 2019 and I don't see it affecting code generation in practice (at-least for our selftests). On the other hand it is really easy to backport. I'd say we should backport it.

Sounds good. Let us do it.

With LLVM_ENABLE_EXPENSIVE_CHECKS, even with this patch, there are still some selftest llvm crashes. I guess you are probably working on this (e.g. D157806).

Yes, I'm aware of two more issues, the one addressed by D157806 and the one in "BPF MachineSSA Peephole Optimization For TRUNC Eliminate":

*** Bad machine code: Illegal virtual register for instruction ***
- function:    _dissect
- basic block: %bb.9 if.end10 (0x621000e80d98)
- instruction: %24:gpr32 = MOV_rr %8:gpr32, debug-location !339; progs/bpf_flow.c:120:2 @[ progs/bpf_flow.c:161:9 ]
- operand 0:   %24:gpr32
Expected a GPR register, but got a GPR32 register

Which I suppose is caused by MOV_rr instruction generated in the end of BPFMIPeepholeTruncElim::eliminateTruncSeq(). I'll submit a fix for this issue a bit later.

Sounds good. Thanks for discovering these issues and fixing them.