[llvm] [BPF] Report Undefined Behavior from IR (PR #126858)
via llvm-commits
llvm-commits at lists.llvm.org
Tue Feb 11 21:10:55 PST 2025
https://github.com/yonghong-song created https://github.com/llvm/llvm-project/pull/126858
Marc Suñé (Isovalent, part of Cisco) reported an issue where an uninitialized variable caused generated bpf prog binary code not working as expected. The reproducer is in [1] where the flags “-Wall -Werror” are enabled, but there is no warning and compiler may take advantage of uninit variable to do aggressive optimization.
In discussion [2], various approaches are discussed, e.g., improving compiler to detect undefined behavior due to uninitialized variables, trying to use ubsan (-fsanitize=undefined), and making -ftrivial-auto-var-init=zero as the bpf default flags.
I tried [3] with -ftrivial-auto-var-init=zero and eventually we decided no-go since first it may introduce performance regression and second the prog may still be wrong if the prog expects a non-zero value. The ubsan apprach seems not working as well since it involves runtime callback func ([4]).
The approach here is not to do complicate compiler analysis to detect whether where is undef behavior which may impact final codegen. Rather, we relies on compiler to do its normal transformation and at later IR passes stage, a BPF backend pass is inserted to check whether undef behavior is in IR or not. Note that if undef behavior indeed impacts codes, the compiler will discard those related codes with simple 'undef' or 'unreachable'.
For example, for the case [1], before SCCPPass, the IR looks like
```
define dso_local i32 @repro(ptr noundef %0) #0 section "classifier" {
%2 = alloca %struct.ipv6_opt_hdr, align 8
%3 = tail call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @repro.____fmt, i32 noundef 6) #2
%4 = tail call ptr asm sideeffect "$0 = *(u32 *)($1 + $2)", "=r,r,i"(ptr %0, i64 76) #2, !srcloc !3
%5 = ptrtoint ptr %4 to i64
%6 = trunc i64 %5 to i32
%7 = tail call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @icmp6_ndisc_validate.____fmt, i32 noundef 23) #2
call void @llvm.lifetime.start.p0(i64 2, ptr nonnull %2) #2
%8 = getelementptr inbounds nuw i8, ptr %2, i64 1
switch i8 undef, label %51 [
i8 59, label %56
i8 44, label %57
i8 0, label %9
i8 43, label %9
i8 51, label %9
i8 60, label %9
]
9: ; preds = %1, %1, %1, %1
%10 = sub i32 40, %6
...
```
Note that 'undef' is used for switch key due to one of early pass LoopFullUnrollPass. After SCCPPass:
```
efine dso_local i32 @repro(ptr noundef %0) #0 section "classifier" {
%2 = alloca %struct.ipv6_opt_hdr, align 8
%3 = tail call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @repro.____fmt, i32 noundef 6) #2
%4 = tail call ptr asm sideeffect "$0 = *(u32 *)($1 + $2)", "=r,r,i"(ptr %0, i64 76) #2, !srcloc !3
%5 = ptrtoint ptr %4 to i64
%6 = trunc i64 %5 to i32
%7 = tail call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @icmp6_ndisc_validate.____fmt, i32 noundef 23) #2
call void @llvm.lifetime.start.p0(i64 2, ptr nonnull %2) #2
%8 = getelementptr inbounds nuw i8, ptr %2, i64 1
unreachable
}
```
For another example,
```
$ cat t.c
int foo() {
int i[2];
return i[1];
}
```
Before SROAPass pass,
```
define dso_local i32 @foo() #0 {
%1 = alloca [2 x i32], align 4
call void @llvm.lifetime.start.p0(i64 8, ptr %1) #2
%2 = getelementptr inbounds [2 x i32], ptr %1, i64 0, i64 1
%3 = load i32, ptr %2, align 4, !tbaa !3
call void @llvm.lifetime.end.p0(i64 8, ptr %1) #2
ret i32 %3
}
```
After SROAPass pass,
```
define dso_local i32 @foo() #0 {
ret i32 undef
}
```
Besides the above two test cases, the following three patterns are also covered:
- It is possible llvm may generate codes where a default branch to 'unreachable' location. Ignore such 'unreachable' instances. See [5] or some comments in [2].
- Handle pattern like __bpf_unreachable (defined in bpf_helpers.h).
- Functions with naked attribute will have 'unreachable' at the end of function. Ignore such functions.
Tested with bpf selftests and there are no warnings issued.
[1] https://github.com/msune/clang_bpf/blob/main/Makefile#L3
[2] https://discourse.llvm.org/t/detect-undefined-behavior-due-to-uninitialized-variables-in-bpf-programs/84116?u=yonghong-song
[3] https://github.com/llvm/llvm-project/pull/125601
[4] https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/ubsan/ubsan_interface.inc
[5] https://lore.kernel.org/lkml/0bf90fc0-2287-4ce0-b810-6e383e695981@linux.dev/
>From ae4596ea5063cbd5eb91eac17fbfa99e5603f50b Mon Sep 17 00:00:00 2001
From: Yonghong Song <yonghong.song at linux.dev>
Date: Mon, 10 Feb 2025 09:45:37 -0800
Subject: [PATCH] [BPF] Report Undefined Behavior from IR
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Marc Suñé (Isovalent, part of Cisco) reported an issue where an
uninitialized variable caused generated bpf prog binary code not
working as expected. The reproducer is in [1] where the flags
“-Wall -Werror” are enabled, but there is no warning and compiler
may take advantage of uninit variable to do aggressive optimization.
In discussion [2], various approaches are discussed, e.g., improving
compiler to detect undefined behavior due to uninitialized variables,
trying to use ubsan (-fsanitize=undefined), and making
-ftrivial-auto-var-init=zero as the bpf default flags.
I tried [3] with -ftrivial-auto-var-init=zero and eventually we
decided no-go since first it may introduce performance regression
and second the prog may still be wrong if the prog expects a
non-zero value. The ubsan apprach seems not working as well
since it involves runtime callback func ([4]).
The approach here is not to do complicate compiler analysis to detect
whether where is undef behavior which may impact final codegen.
Rather, we relies on compiler to do its normal transformation
and at later IR passes stage, a BPF backend pass is inserted to
check whether undef behavior is in IR or not. Note that
if undef behavior indeed impacts codes, the compiler will discard
those related codes with simple 'undef' or 'unreachable'.
For example, for the case [1], before SCCPPass, the IR looks like
```
define dso_local i32 @repro(ptr noundef %0) #0 section "classifier" {
%2 = alloca %struct.ipv6_opt_hdr, align 8
%3 = tail call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @repro.____fmt, i32 noundef 6) #2
%4 = tail call ptr asm sideeffect "$0 = *(u32 *)($1 + $2)", "=r,r,i"(ptr %0, i64 76) #2, !srcloc !3
%5 = ptrtoint ptr %4 to i64
%6 = trunc i64 %5 to i32
%7 = tail call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @icmp6_ndisc_validate.____fmt, i32 noundef 23) #2
call void @llvm.lifetime.start.p0(i64 2, ptr nonnull %2) #2
%8 = getelementptr inbounds nuw i8, ptr %2, i64 1
switch i8 undef, label %51 [
i8 59, label %56
i8 44, label %57
i8 0, label %9
i8 43, label %9
i8 51, label %9
i8 60, label %9
]
9: ; preds = %1, %1, %1, %1
%10 = sub i32 40, %6
...
```
Note that 'undef' is used for switch key due to one of early pass LoopFullUnrollPass.
After SCCPPass:
```
efine dso_local i32 @repro(ptr noundef %0) #0 section "classifier" {
%2 = alloca %struct.ipv6_opt_hdr, align 8
%3 = tail call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @repro.____fmt, i32 noundef 6) #2
%4 = tail call ptr asm sideeffect "$0 = *(u32 *)($1 + $2)", "=r,r,i"(ptr %0, i64 76) #2, !srcloc !3
%5 = ptrtoint ptr %4 to i64
%6 = trunc i64 %5 to i32
%7 = tail call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @icmp6_ndisc_validate.____fmt, i32 noundef 23) #2
call void @llvm.lifetime.start.p0(i64 2, ptr nonnull %2) #2
%8 = getelementptr inbounds nuw i8, ptr %2, i64 1
unreachable
}
```
For another example,
```
$ cat t.c
int foo() {
int i[2];
return i[1];
}
```
Before SROAPass pass,
```
define dso_local i32 @foo() #0 {
%1 = alloca [2 x i32], align 4
call void @llvm.lifetime.start.p0(i64 8, ptr %1) #2
%2 = getelementptr inbounds [2 x i32], ptr %1, i64 0, i64 1
%3 = load i32, ptr %2, align 4, !tbaa !3
call void @llvm.lifetime.end.p0(i64 8, ptr %1) #2
ret i32 %3
}
```
After SROAPass pass,
```
define dso_local i32 @foo() #0 {
ret i32 undef
}
```
Besides the above two test cases, the following three patterns are also covered:
- It is possible llvm may generate codes where a default branch to 'unreachable' location.
Ignore such 'unreachable' instances. See [5] or some comments in [2].
- Handle pattern like __bpf_unreachable (defined in bpf_helpers.h).
- Functions with naked attribute will have 'unreachable' at the end of function.
Ignore such functions.
Tested with bpf selftests and there are no warnings issued.
[1] https://github.com/msune/clang_bpf/blob/main/Makefile#L3
[2] https://discourse.llvm.org/t/detect-undefined-behavior-due-to-uninitialized-variables-in-bpf-programs/84116?u=yonghong-song
[3] https://github.com/llvm/llvm-project/pull/125601
[4] https://github.com/llvm/llvm-project/blob/main/compiler-rt/lib/ubsan/ubsan_interface.inc
[5] https://lore.kernel.org/lkml/0bf90fc0-2287-4ce0-b810-6e383e695981@linux.dev/
---
llvm/lib/Target/BPF/BPF.h | 2 +
llvm/lib/Target/BPF/BPFCheckUndefIR.cpp | 118 ++++++++++++++++
llvm/lib/Target/BPF/BPFTargetMachine.cpp | 2 +
llvm/lib/Target/BPF/CMakeLists.txt | 1 +
llvm/test/CodeGen/BPF/undef-sccp.ll | 172 +++++++++++++++++++++++
llvm/test/CodeGen/BPF/undef-sroa.ll | 14 ++
6 files changed, 309 insertions(+)
create mode 100644 llvm/lib/Target/BPF/BPFCheckUndefIR.cpp
create mode 100644 llvm/test/CodeGen/BPF/undef-sccp.ll
create mode 100644 llvm/test/CodeGen/BPF/undef-sroa.ll
diff --git a/llvm/lib/Target/BPF/BPF.h b/llvm/lib/Target/BPF/BPF.h
index f07ae4c9baf1c..52a733e0d3bcc 100644
--- a/llvm/lib/Target/BPF/BPF.h
+++ b/llvm/lib/Target/BPF/BPF.h
@@ -22,6 +22,7 @@ class BPFTargetMachine;
class InstructionSelector;
class PassRegistry;
+ModulePass *createBPFCheckUndefIR();
ModulePass *createBPFCheckAndAdjustIR();
FunctionPass *createBPFISelDag(BPFTargetMachine &TM);
@@ -34,6 +35,7 @@ InstructionSelector *createBPFInstructionSelector(const BPFTargetMachine &,
const BPFSubtarget &,
const BPFRegisterBankInfo &);
+void initializeBPFCheckUndefIRPass(PassRegistry&);
void initializeBPFCheckAndAdjustIRPass(PassRegistry&);
void initializeBPFDAGToDAGISelLegacyPass(PassRegistry &);
void initializeBPFMIPeepholePass(PassRegistry &);
diff --git a/llvm/lib/Target/BPF/BPFCheckUndefIR.cpp b/llvm/lib/Target/BPF/BPFCheckUndefIR.cpp
new file mode 100644
index 0000000000000..d7bbadba4663d
--- /dev/null
+++ b/llvm/lib/Target/BPF/BPFCheckUndefIR.cpp
@@ -0,0 +1,118 @@
+//===---------------- BPFAdjustOpt.cpp - Adjust Optimization --------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Check 'undef' and 'unreachable' IRs and issue proper warnings.
+//
+//===----------------------------------------------------------------------===//
+
+#include "BPF.h"
+#include "llvm/IR/Constants.h"
+#include "llvm/IR/Instruction.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Type.h"
+#include "llvm/IR/Value.h"
+#include "llvm/Pass.h"
+
+#define DEBUG_TYPE "bpf-check-undef-ir"
+
+using namespace llvm;
+
+namespace {
+
+class BPFCheckUndefIR final : public ModulePass {
+ bool runOnModule(Module &F) override;
+
+public:
+ static char ID;
+ BPFCheckUndefIR() : ModulePass(ID) {}
+
+private:
+ void BPFCheckUndefIRImpl(Function &F);
+ void BPFCheckInst(Function &F, BasicBlock &BB, Instruction &I);
+ void HandleReturnInsn(Function &F, ReturnInst *I);
+ void HandleUnreachableInsn(Function &F, BasicBlock &BB, Instruction &I);
+};
+} // End anonymous namespace
+
+char BPFCheckUndefIR::ID = 0;
+INITIALIZE_PASS(BPFCheckUndefIR, DEBUG_TYPE, "BPF Check Undef IRs", false,
+ false)
+
+ModulePass *llvm::createBPFCheckUndefIR() { return new BPFCheckUndefIR(); }
+
+void BPFCheckUndefIR::HandleReturnInsn(Function &F, ReturnInst *I) {
+ Value *RetValue = I->getReturnValue();
+ // PoisonValue is a special UndefValue where compiler intentionally to
+ // poisons a value since it shouldn't be used.
+ if (!RetValue || isa<PoisonValue>(RetValue) || !isa<UndefValue>(RetValue))
+ return;
+
+ dbgs() << "WARNING: return undefined value in func " << F.getName()
+ << ", due to uninitialized variable?\n";
+}
+
+void BPFCheckUndefIR::HandleUnreachableInsn(Function &F, BasicBlock &BB,
+ Instruction &I) {
+ // LLVM may create a switch statement with default to a 'unreachable' basic
+ // block. Do not warn for such cases.
+ unsigned NumNoSwitches = 0, NumSwitches = 0;
+ for (BasicBlock *Pred : predecessors(&BB)) {
+ const Instruction *Term = Pred->getTerminator();
+ if (Term && Term->getOpcode() == Instruction::Switch) {
+ NumSwitches++;
+ continue;
+ }
+ NumNoSwitches++;
+ }
+ if (NumSwitches > 0 && NumNoSwitches == 0)
+ return;
+
+ // If the previous insn is no return, do not warn for such cases.
+ // One example is __bpf_unreachable from libbpf bpf_headers.h.
+ Instruction *PrevI = I.getPrevNonDebugInstruction();
+ if (PrevI) {
+ auto *CI = dyn_cast<CallInst>(PrevI);
+ if (CI && CI->doesNotReturn())
+ return;
+ }
+
+ dbgs() << "WARNING: unreachable in func " << F.getName()
+ << ", due to uninitialized variable?\n";
+}
+
+void BPFCheckUndefIR::BPFCheckInst(Function &F, BasicBlock &BB,
+ Instruction &I) {
+ switch (I.getOpcode()) {
+ case Instruction::Ret:
+ HandleReturnInsn(F, cast<ReturnInst>(&I));
+ break;
+ case Instruction::Unreachable:
+ HandleUnreachableInsn(F, BB, I);
+ break;
+ default:
+ break;
+ }
+}
+
+void BPFCheckUndefIR::BPFCheckUndefIRImpl(Function &F) {
+ // A 'unreachable' will be added to the end of naked function.
+ // Let ignore these naked functions.
+ if (F.hasFnAttribute(Attribute::Naked))
+ return;
+
+ for (auto &BB : F) {
+ for (auto &I : BB)
+ BPFCheckInst(F, BB, I);
+ }
+}
+
+bool BPFCheckUndefIR::runOnModule(Module &M) {
+ for (Function &F : M)
+ BPFCheckUndefIRImpl(F);
+ return false;
+}
diff --git a/llvm/lib/Target/BPF/BPFTargetMachine.cpp b/llvm/lib/Target/BPF/BPFTargetMachine.cpp
index 3379af6fe8744..6523086ef10c0 100644
--- a/llvm/lib/Target/BPF/BPFTargetMachine.cpp
+++ b/llvm/lib/Target/BPF/BPFTargetMachine.cpp
@@ -45,6 +45,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeBPFTarget() {
PassRegistry &PR = *PassRegistry::getPassRegistry();
initializeGlobalISel(PR);
+ initializeBPFCheckUndefIRPass(PR);
initializeBPFCheckAndAdjustIRPass(PR);
initializeBPFMIPeepholePass(PR);
initializeBPFDAGToDAGISelLegacyPass(PR);
@@ -143,6 +144,7 @@ void BPFTargetMachine::registerPassBuilderCallbacks(PassBuilder &PB) {
}
void BPFPassConfig::addIRPasses() {
+ addPass(createBPFCheckUndefIR());
addPass(createAtomicExpandLegacyPass());
addPass(createBPFCheckAndAdjustIR());
diff --git a/llvm/lib/Target/BPF/CMakeLists.txt b/llvm/lib/Target/BPF/CMakeLists.txt
index eade4cacb7100..edc8290b7cf7c 100644
--- a/llvm/lib/Target/BPF/CMakeLists.txt
+++ b/llvm/lib/Target/BPF/CMakeLists.txt
@@ -26,6 +26,7 @@ add_llvm_target(BPFCodeGen
BPFAsmPrinter.cpp
BPFASpaceCastSimplifyPass.cpp
BPFCheckAndAdjustIR.cpp
+ BPFCheckUndefIR.cpp
BPFFrameLowering.cpp
BPFInstrInfo.cpp
BPFIRPeephole.cpp
diff --git a/llvm/test/CodeGen/BPF/undef-sccp.ll b/llvm/test/CodeGen/BPF/undef-sccp.ll
new file mode 100644
index 0000000000000..1cc6950fe304d
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/undef-sccp.ll
@@ -0,0 +1,172 @@
+; RUN: opt -S -passes='sccp' %s -o %t1
+; RUN: opt --bpf-check-undef-ir -S -mtriple=bpf-pc-linux %t1 >& %t2
+; RUN: cat %t2 | FileCheck -check-prefixes=CHECK %s
+
+%union.v6addr = type { %struct.anon.1 }
+%struct.anon.1 = type { i64, i64 }
+%union.macaddr = type { %struct.anon }
+%struct.anon = type { i32, i16 }
+%struct.icmp6hdr = type { i8, i8, i16, %union.anon }
+%union.anon = type { [1 x i32] }
+%struct.ipv6_opt_hdr = type { i8, i8 }
+
+ at repro.____fmt = internal constant [6 x i8] c"Start\00", align 1
+ at repro.____fmt.1 = internal constant [4 x i8] c"End\00", align 1
+ at __packed = dso_local global %union.v6addr zeroinitializer, align 8
+ at icmp6_ndisc_validate.____fmt = internal constant [23 x i8] c"pre ipv6_hdrlen_offset\00", align 1
+ at icmp6_ndisc_validate.____fmt.2 = internal constant [24 x i8] c"post ipv6_hdrlen_offset\00", align 1
+ at icmp6_ndisc_validate.____fmt.3 = internal constant [5 x i8] c"KO 1\00", align 1
+ at icmp6_ndisc_validate.____fmt.4 = internal constant [5 x i8] c"KO 2\00", align 1
+ at icmp6_ndisc_validate.____fmt.5 = internal constant [5 x i8] c"ACK \00", align 1
+ at ipv6_hdrlen_offset.____fmt = internal constant [17 x i8] c"OKOK %d, len: %d\00", align 1
+ at ipv6_hdrlen_offset.____fmt.6 = internal constant [18 x i8] c"KO INVALID EXTHDR\00", align 1
+ at llvm.compiler.used = appending global [1 x ptr] [ptr @repro], section "llvm.metadata"
+
+define dso_local i32 @repro(ptr noundef %0) section "classifier" {
+ %2 = alloca %struct.ipv6_opt_hdr, align 8
+ %3 = tail call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @repro.____fmt, i32 noundef 6)
+ %4 = tail call ptr asm sideeffect "$0 = *(u32 *)($1 + $2)", "=r,r,i"(ptr %0, i64 76)
+ %5 = ptrtoint ptr %4 to i64
+ %6 = trunc i64 %5 to i32
+ %7 = tail call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @icmp6_ndisc_validate.____fmt, i32 noundef 23)
+ call void @llvm.lifetime.start.p0(i64 2, ptr nonnull %2)
+ %8 = getelementptr inbounds nuw i8, ptr %2, i64 1
+ switch i8 undef, label %51 [
+ i8 59, label %56
+ i8 44, label %57
+ i8 0, label %9
+ i8 43, label %9
+ i8 51, label %9
+ i8 60, label %9
+ ]
+
+; CHECK: unreachable in func repro, due to uninitialized variable?
+; CHECK: %8 = getelementptr inbounds nuw i8, ptr %2, i64 1
+; CHECK-NEXT: unreachable
+
+9: ; preds = %1, %1, %1, %1
+ %10 = sub i32 40, %6
+ %11 = call i64 inttoptr (i64 26 to ptr)(ptr noundef %0, i32 noundef %10, ptr noundef nonnull %2, i32 noundef 2)
+ %12 = icmp slt i64 %11, 0
+ br i1 %12, label %57, label %13
+
+13: ; preds = %9
+ %14 = load i8, ptr %8, align 1
+ %15 = zext i8 %14 to i32
+ %16 = shl nuw nsw i32 %15, 3
+ %17 = add nuw nsw i32 48, %16
+ %18 = load i8, ptr %2, align 1
+ switch i8 %18, label %51 [
+ i8 59, label %56
+ i8 44, label %57
+ i8 0, label %19
+ i8 43, label %19
+ i8 51, label %19
+ i8 60, label %19
+ ]
+
+19: ; preds = %13, %13, %13, %13
+ %20 = sub i32 %17, %6
+ %21 = call i64 inttoptr (i64 26 to ptr)(ptr noundef %0, i32 noundef %20, ptr noundef nonnull %2, i32 noundef 2)
+ %22 = icmp slt i64 %21, 0
+ br i1 %22, label %57, label %23
+
+23: ; preds = %19
+ %24 = icmp eq i8 %18, 51
+ %25 = load i8, ptr %8, align 1
+ %26 = zext i8 %25 to i32
+ %27 = select i1 %24, i32 2, i32 3
+ %28 = shl nuw nsw i32 %26, %27
+ %29 = add nuw nsw i32 %17, 8
+ %30 = add nuw nsw i32 %29, %28
+ %31 = load i8, ptr %2, align 1
+ switch i8 %31, label %51 [
+ i8 59, label %56
+ i8 44, label %57
+ i8 0, label %32
+ i8 43, label %32
+ i8 51, label %32
+ i8 60, label %32
+ ]
+
+32: ; preds = %23, %23, %23, %23
+ %33 = sub i32 %30, %6
+ %34 = call i64 inttoptr (i64 26 to ptr)(ptr noundef %0, i32 noundef %33, ptr noundef nonnull %2, i32 noundef 2)
+ %35 = icmp slt i64 %34, 0
+ br i1 %35, label %57, label %36
+
+36: ; preds = %32
+ %37 = icmp eq i8 %31, 51
+ %38 = load i8, ptr %8, align 1
+ %39 = zext i8 %38 to i32
+ %40 = select i1 %37, i32 2, i32 3
+ %41 = shl nuw nsw i32 %39, %40
+ %42 = add nuw nsw i32 %30, 8
+ %43 = add nuw nsw i32 %42, %41
+ %44 = load i8, ptr %2, align 1
+ switch i8 %44, label %51 [
+ i8 59, label %56
+ i8 44, label %57
+ i8 0, label %45
+ i8 43, label %45
+ i8 51, label %45
+ i8 60, label %45
+ ]
+
+45: ; preds = %36, %36, %36, %36
+ %46 = sub i32 %43, %6
+ %47 = call i64 inttoptr (i64 26 to ptr)(ptr noundef %0, i32 noundef %46, ptr noundef nonnull %2, i32 noundef 2)
+ %48 = icmp slt i64 %47, 0
+ br i1 %48, label %57, label %49
+
+49: ; preds = %45
+ %50 = call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @ipv6_hdrlen_offset.____fmt.6, i32 noundef 18)
+ br label %59
+
+51: ; preds = %36, %23, %13, %1
+ %52 = phi i8 [ undef, %1 ], [ %18, %13 ], [ %31, %23 ], [ %44, %36 ]
+ %53 = phi i32 [ 40, %1 ], [ %17, %13 ], [ %30, %23 ], [ %43, %36 ]
+ %54 = call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @ipv6_hdrlen_offset.____fmt, i32 noundef 17, i32 noundef 0, i32 noundef %53)
+ %55 = icmp ne i8 %52, 58
+ br label %59
+
+56: ; preds = %36, %23, %13, %1
+ br label %59
+
+57: ; preds = %45, %36, %32, %23, %19, %13, %1, %9
+ %58 = phi i32 [ -134, %9 ], [ -157, %1 ], [ -157, %13 ], [ -134, %19 ], [ -157, %23 ], [ -134, %32 ], [ -157, %36 ], [ -134, %45 ]
+ br label %59
+
+59: ; preds = %57, %56, %51, %49
+ %60 = phi i1 [ %55, %51 ], [ undef, %49 ], [ undef, %56 ], [ undef, %57 ]
+ %61 = phi i32 [ %53, %51 ], [ -156, %49 ], [ -156, %56 ], [ %58, %57 ]
+ call void @llvm.lifetime.end.p0(i64 2, ptr nonnull %2)
+ %62 = call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @icmp6_ndisc_validate.____fmt.2, i32 noundef 24)
+ %63 = icmp slt i32 %61, 0
+ %64 = or i1 %63, %60
+ br i1 %64, label %65, label %67
+
+65: ; preds = %59
+ %66 = call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @icmp6_ndisc_validate.____fmt.3, i32 noundef 5)
+ br label %77
+
+67: ; preds = %59
+ %68 = call ptr asm sideeffect "$0 = *(u32 *)($1 + $2)", "=r,r,i"(ptr %0, i64 76)
+ %69 = zext nneg i32 %61 to i64
+ %70 = getelementptr inbounds nuw i8, ptr %68, i64 %69
+ %71 = load i8, ptr %70, align 4
+ %72 = icmp eq i8 %71, -121
+ br i1 %72, label %75, label %73
+
+73: ; preds = %67
+ %74 = call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @icmp6_ndisc_validate.____fmt.4, i32 noundef 5)
+ br label %77
+
+75: ; preds = %67
+ %76 = call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @icmp6_ndisc_validate.____fmt.5, i32 noundef 5)
+ br label %77
+
+77: ; preds = %65, %73, %75
+ %78 = call i64 (ptr, i32, ...) inttoptr (i64 6 to ptr)(ptr noundef nonnull @repro.____fmt.1, i32 noundef 4)
+ ret i32 0
+}
diff --git a/llvm/test/CodeGen/BPF/undef-sroa.ll b/llvm/test/CodeGen/BPF/undef-sroa.ll
new file mode 100644
index 0000000000000..73eeaf6935c8c
--- /dev/null
+++ b/llvm/test/CodeGen/BPF/undef-sroa.ll
@@ -0,0 +1,14 @@
+; RUN: opt -S -passes='sroa' %s -o %t1
+; RUN: opt --bpf-check-undef-ir -S -mtriple=bpf-pc-linux %t1 >& %t2
+; RUN: cat %t2 | FileCheck -check-prefixes=CHECK %s
+
+define dso_local i32 @foo() {
+ %1 = alloca [2 x i32], align 4
+ call void @llvm.lifetime.start.p0(i64 8, ptr %1)
+ %2 = getelementptr inbounds [2 x i32], ptr %1, i64 0, i64 1
+ %3 = load i32, ptr %2, align 4
+ call void @llvm.lifetime.end.p0(i64 8, ptr %1)
+ ret i32 %3
+}
+; CHECK: return undefined value in func foo, due to uninitialized variable?
+; CHECK: ret i32 undef
More information about the llvm-commits
mailing list