diff --git a/llvm/lib/Target/BPF/BPFISelLowering.cpp b/llvm/lib/Target/BPF/BPFISelLowering.cpp --- a/llvm/lib/Target/BPF/BPFISelLowering.cpp +++ b/llvm/lib/Target/BPF/BPFISelLowering.cpp @@ -368,7 +368,7 @@ HasMemArgs = true; else report_fatal_error("unhandled argument location"); - InVals.push_back(DAG.getConstant(0, DL, VA.getLocVT())); + InVals.push_back(DAG.getUNDEF(VA.getLocVT())); } } if (HasMemArgs) @@ -532,7 +532,7 @@ return DAG.getNode(Opc, DL, MVT::Other, Chain); } - // Analize return values. + // Analyze return values. CCInfo.AnalyzeReturn(Outs, getHasAlu32() ? RetCC_BPF32 : RetCC_BPF64); SDValue Glue; @@ -572,9 +572,9 @@ CCState CCInfo(CallConv, IsVarArg, MF, RVLocs, *DAG.getContext()); if (Ins.size() > 1) { - fail(DL, DAG, "only small returns supported"); + fail(DL, DAG, "only small returns are supported"); for (auto &In : Ins) - InVals.push_back(DAG.getConstant(0, DL, In.VT)); + InVals.push_back(DAG.getUNDEF(In.VT)); return DAG.getCopyFromReg(Chain, DL, 1, Ins[0].VT, InGlue).getValue(1); } diff --git a/llvm/test/Bindings/llvm-c/many_args2.ll b/llvm/test/Bindings/llvm-c/many_args2.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Bindings/llvm-c/many_args2.ll @@ -0,0 +1,37 @@ +; RUN: llvm-c-test --test-bpf-unsupported-emit < %s | FileCheck %s +; +; Source code: +; long foo(long a, long b, long c, long d, long e, long f) { return f; } +; Compilation flag: +; clang -target bpf -S -emit-llvm test.c -o - +; +; This test contains a function that cannot be correctly compiled to BPF assembly because it exceeds +; the number of permissible arguments. +; +; Before this test was introduced, the BPF backend would generate incorrect-but-viable code +; that would pass the verifier and violate user expectations at runtime. +; +; This test ensures that when forced to miscompile, the BPF backend emits code that would be +; rejected by the verifier. + +; CHECK: foo: +; CHECK-NOT: r0 = +; CHECK: exit + +; Function Attrs: noinline nounwind optnone +define dso_local i64 @foo(i64 noundef %0, i64 noundef %1, i64 noundef %2, i64 noundef %3, i64 noundef %4, i64 noundef %5) #0 { + %7 = alloca i64, align 8 + %8 = alloca i64, align 8 + %9 = alloca i64, align 8 + %10 = alloca i64, align 8 + %11 = alloca i64, align 8 + %12 = alloca i64, align 8 + store i64 %0, ptr %7, align 8 + store i64 %1, ptr %8, align 8 + store i64 %2, ptr %9, align 8 + store i64 %3, ptr %10, align 8 + store i64 %4, ptr %11, align 8 + store i64 %5, ptr %12, align 8 + %13 = load i64, ptr %12, align 8 + ret i64 %13 +} diff --git a/llvm/test/Bindings/llvm-c/struct_ret2.ll b/llvm/test/Bindings/llvm-c/struct_ret2.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Bindings/llvm-c/struct_ret2.ll @@ -0,0 +1,24 @@ +; RUN: llvm-c-test --test-bpf-unsupported-emit < %s | FileCheck %s +; +; This test contains a function that cannot be correctly compiled to BPF assembly because it returns +; a value wider than a register. +; +; Before this test was introduced, the BPF backend would generate incorrect-but-viable code +; that would pass the verifier and violate user expectations at runtime. +; +; This test ensures that when forced to miscompile, the BPF backend emits code that would be +; rejected by the verifier. + +; CHECK: foo: +; CHECK-NOT: r0 = +; CHECK: exit + +; Function Attrs: nounwind uwtable +define i64 @foo() #0 { +entry: + %0 = tail call { i64, i32 } @bar() #3 + %1 = extractvalue { i64, i32 } %0, 0 + ret i64 %1 +} + +declare { i64, i32 } @bar() #1 diff --git a/llvm/test/CodeGen/BPF/struct_ret1.ll b/llvm/test/CodeGen/BPF/struct_ret1.ll --- a/llvm/test/CodeGen/BPF/struct_ret1.ll +++ b/llvm/test/CodeGen/BPF/struct_ret1.ll @@ -15,13 +15,3 @@ %.fca.1.insert = insertvalue { i64, i32 } %.fca.0.insert, i32 %retval.sroa.2.0.copyload, 1 ret { i64, i32 } %.fca.1.insert } - -; CHECK: error: :0:0: in function baz void (ptr): aggregate returns are not supported - -%struct.B = type { [100 x i64] } - -; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) -define dso_local void @baz(ptr noalias nocapture sret(%struct.B) align 8 %agg.result) local_unnamed_addr #0 { -entry: - ret void -} diff --git a/llvm/test/CodeGen/BPF/struct_ret2.ll b/llvm/test/CodeGen/BPF/struct_ret2.ll --- a/llvm/test/CodeGen/BPF/struct_ret2.ll +++ b/llvm/test/CodeGen/BPF/struct_ret2.ll @@ -1,6 +1,6 @@ ; RUN: not llc -march=bpf < %s 2> %t1 ; RUN: FileCheck %s < %t1 -; CHECK: only small returns +; CHECK: only small returns are supported ; Function Attrs: nounwind uwtable define { i64, i32 } @foo(i32 %a, i32 %b, i32 %c) #0 { diff --git a/llvm/test/CodeGen/BPF/struct_ret3.ll b/llvm/test/CodeGen/BPF/struct_ret3.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/BPF/struct_ret3.ll @@ -0,0 +1,11 @@ +; RUN: not llc -march=bpf < %s 2> %t1 +; RUN: FileCheck %s < %t1 +; CHECK: error: :0:0: in function baz void (ptr): aggregate returns are not supported + +%struct.B = type { [100 x i64] } + +; Function Attrs: mustprogress nofree norecurse nosync nounwind willreturn memory(none) +define dso_local void @baz(ptr noalias nocapture sret(%struct.B) align 8 %agg.result) local_unnamed_addr #0 { +entry: + ret void +} diff --git a/llvm/tools/llvm-c-test/CMakeLists.txt b/llvm/tools/llvm-c-test/CMakeLists.txt --- a/llvm/tools/llvm-c-test/CMakeLists.txt +++ b/llvm/tools/llvm-c-test/CMakeLists.txt @@ -41,6 +41,7 @@ add_llvm_tool(llvm-c-test attributes.c + bpf-unsupported-emit.c calc.c debuginfo.c diagnostic.c @@ -49,8 +50,8 @@ helpers.c include-all.c main.c - module.c metadata.c + module.c object.c targets.c ) diff --git a/llvm/tools/llvm-c-test/bpf-unsupported-emit.c b/llvm/tools/llvm-c-test/bpf-unsupported-emit.c new file mode 100644 --- /dev/null +++ b/llvm/tools/llvm-c-test/bpf-unsupported-emit.c @@ -0,0 +1,128 @@ +/*===-- targets.c - tool for testing libLLVM and llvm-c API ---------------===*\ +|* *| +|* 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 *| +|* *| +|*===----------------------------------------------------------------------===*| +|* *| +|* This file implements the --bpf-unsupported-emit command in llvm-c-test. *| +|* *| +\*===----------------------------------------------------------------------===*/ + +#include "llvm-c-test.h" +#include "llvm-c/IRReader.h" +#include "llvm-c/Target.h" +#include "llvm-c/TargetMachine.h" + +#include +#include + +static void diagnosticHandler(LLVMDiagnosticInfoRef DI, void *C) { + const char *severity = "unhandled"; + switch (LLVMGetDiagInfoSeverity(DI)) { + case LLVMDSError: + severity = "error"; + break; + case LLVMDSWarning: + severity = "warning"; + break; + case LLVMDSRemark: + severity = "remark"; + break; + case LLVMDSNote: + severity = "note"; + break; + } + char *CErr = LLVMGetDiagInfoDescription(DI); + fprintf(stderr, "%s: %s\n", severity, CErr); + LLVMDisposeMessage(CErr); +} + +int llvm_test_bpf_unsupported_emit(void) { + LLVMInitializeBPFTargetInfo(); + LLVMInitializeBPFTarget(); + LLVMInitializeBPFTargetMC(); + LLVMInitializeBPFAsmPrinter(); + + const char *bpf = "bpf"; + + LLVMTargetRef target; + { + char *error_message = NULL; + if (LLVMGetTargetFromTriple(bpf, &target, &error_message)) { + fprintf(stderr, "LLVMGetTargetFromTriple(%s) failed: %s\n", bpf, + error_message); + LLVMDisposeMessage(error_message); + return EXIT_FAILURE; + } + } + + LLVMContextRef C = LLVMContextCreate(); + LLVMContextSetDiagnosticHandler(C, diagnosticHandler, NULL); + + LLVMMemoryBufferRef mem_buf; + { + char *error_message = NULL; + if (LLVMCreateMemoryBufferWithSTDIN(&mem_buf, &error_message)) { + fprintf(stderr, "LLVMCreateMemoryBufferWithSTDIN failed: %s\n", + error_message); + LLVMDisposeMessage(error_message); + LLVMContextDispose(C); + return EXIT_FAILURE; + } + } + + LLVMModuleRef mod; + { + char *error_message = NULL; + if (LLVMParseIRInContext(C, mem_buf, &mod, &error_message)) { + fprintf(stderr, "LLVMParseIRInContext failed: %s\n", error_message); + LLVMDisposeMessage(error_message); + LLVMDisposeMemoryBuffer(mem_buf); + LLVMContextDispose(C); + return EXIT_FAILURE; + } + } + + LLVMTargetMachineRef target_machine = + LLVMCreateTargetMachine(target, bpf, NULL, NULL, LLVMCodeGenLevelDefault, + LLVMRelocDefault, LLVMCodeModelDefault); + + LLVMMemoryBufferRef asm_buf; + { + char *error_message = NULL; + if (LLVMTargetMachineEmitToMemoryBuffer( + target_machine, mod, LLVMAssemblyFile, &error_message, &asm_buf)) { + fprintf(stderr, "LLVMTargetMachineEmitToMemoryBuffer failed: %s\n", + error_message); + LLVMDisposeMessage(error_message); + LLVMDisposeTargetMachine(target_machine); + LLVMDisposeModule(mod); + LLVMContextDispose(C); + return EXIT_FAILURE; + } + } + + int ret = EXIT_SUCCESS; + + const char *asm_start = LLVMGetBufferStart(asm_buf); + const size_t asm_size = LLVMGetBufferSize(asm_buf); + for (size_t wrote = 0; wrote != asm_size;) { + size_t n = fwrite(asm_start, 1, asm_size - wrote, stdout); + if (n == 0) { + fprintf(stderr, "fwrite failed after %zu/%zu bytes\n", wrote, asm_size); + ret = EXIT_FAILURE; + break; + } + wrote += n; + } + + LLVMDisposeMemoryBuffer(asm_buf); + LLVMDisposeModule(mod); + LLVMDisposeTargetMachine(target_machine); + LLVMContextDispose(C); + + return ret; +} diff --git a/llvm/tools/llvm-c-test/llvm-c-test.h b/llvm/tools/llvm-c-test/llvm-c-test.h --- a/llvm/tools/llvm-c-test/llvm-c-test.h +++ b/llvm/tools/llvm-c-test/llvm-c-test.h @@ -29,6 +29,9 @@ int llvm_module_list_functions(void); int llvm_module_list_globals(void); +// bpf-unsupported-emit.c +int llvm_test_bpf_unsupported_emit(void); + // calc.c int llvm_calc(void); diff --git a/llvm/tools/llvm-c-test/main.c b/llvm/tools/llvm-c-test/main.c --- a/llvm/tools/llvm-c-test/main.c +++ b/llvm/tools/llvm-c-test/main.c @@ -67,6 +67,9 @@ fprintf(stderr, " * --test-dibuilder\n"); fprintf(stderr, " Run tests for the DIBuilder C API - print generated module\n\n"); + fprintf(stderr, " * --test-bpf-unsupported-emit\n"); + fprintf(stderr, + " Generate BPF asm from IR - print generated assembly\n\n"); } int main(int argc, char **argv) { @@ -114,6 +117,8 @@ return llvm_test_diagnostic_handler(); } else if (argc == 2 && !strcmp(argv[1], "--test-dibuilder")) { return llvm_test_dibuilder(); + } else if (argc == 2 && !strcmp(argv[1], "--test-bpf-unsupported-emit")) { + return llvm_test_bpf_unsupported_emit(); } else { print_usage(); } diff --git a/llvm/utils/gn/secondary/llvm/tools/llvm-c-test/BUILD.gn b/llvm/utils/gn/secondary/llvm/tools/llvm-c-test/BUILD.gn --- a/llvm/utils/gn/secondary/llvm/tools/llvm-c-test/BUILD.gn +++ b/llvm/utils/gn/secondary/llvm/tools/llvm-c-test/BUILD.gn @@ -16,6 +16,7 @@ } sources = [ "attributes.c", + "bpf-unsupported-emit.c", "calc.c", "debuginfo.c", "diagnostic.c",