Skip to content

Commit 23907a2

Browse files
authored
[BPF] Allow libcalls behind a feature gate (#168442)
**Problem** In Rust, checked math functions (like `checked_mul`, `overflowing_mul`, `saturating_mul`) are part of the primitive implementation of integers ([see u64](https://doc.rust-lang.org/std/primitive.u64.html) for instance). The Rust compiler builds the Rust [compiler-builtins](https://github.com/rust-lang/compiler-builtins) crate as a step in the compilation processes, since it contains the math builtins to be lowered in the target. For BPF, however, when using those functions in Rust we hit the following errors: ``` ERROR llvm: <unknown>:0:0: in function func i64 (i64, i64): A call to built-in function '__multi3' is not supported. ERROR llvm: <unknown>:0:0: in function func i64 (i64, i64): only small returns supported ``` Those errors come from the following code: ``` pub fn func(a: u64, b: u64) -> u64 { a.saturating_mul(b) } ``` Those functions invoke underneath the llvm instrinc `{ i64, i1 } @llvm.umul.with.overflow.i64(i64, i64)` or its variants. It is very useful to use safe math operations when writing BPF code in Rust, and I would like to add support for those in the target. **Changes** 1. Create a target feature `allow-builtin-calls` to enable code generation for builtin functions. 2. Implement `CanLowerReturn` to fix the error `only small returns supported`. 3. Add code to correctly invoke lib functions. 4. Add a test case together with the corresponding C code.
1 parent bab1c29 commit 23907a2

File tree

9 files changed

+79
-7
lines changed

9 files changed

+79
-7
lines changed

llvm/lib/Target/BPF/BPF.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@ def MisalignedMemAccess : SubtargetFeature<"allows-misaligned-mem-access",
3131
"AllowsMisalignedMemAccess", "true",
3232
"Allows misaligned memory access">;
3333

34+
def AllowBuiltinCall : SubtargetFeature<"allow-builtin-calls",
35+
"AllowBuiltinCalls", "true",
36+
"Allow calls to builtin functions">;
37+
3438
def : Proc<"generic", []>;
3539
def : Proc<"v1", []>;
3640
def : Proc<"v2", []>;

llvm/lib/Target/BPF/BPFISelLowering.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,7 @@ BPFTargetLowering::BPFTargetLowering(const TargetMachine &TM,
208208
HasMovsx = STI.hasMovsx();
209209

210210
AllowsMisalignedMemAccess = STI.getAllowsMisalignedMemAccess();
211+
AllowBuiltinCalls = STI.getAllowBuiltinCalls();
211212
}
212213

213214
bool BPFTargetLowering::allowsMisalignedMemoryAccesses(EVT VT, unsigned, Align,
@@ -567,9 +568,10 @@ SDValue BPFTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
567568
} else if (ExternalSymbolSDNode *E = dyn_cast<ExternalSymbolSDNode>(Callee)) {
568569
if (StringRef(E->getSymbol()) != BPF_TRAP) {
569570
Callee = DAG.getTargetExternalSymbol(E->getSymbol(), PtrVT, 0);
570-
fail(CLI.DL, DAG,
571-
Twine("A call to built-in function '" + StringRef(E->getSymbol()) +
572-
"' is not supported."));
571+
if (!AllowBuiltinCalls)
572+
fail(CLI.DL, DAG,
573+
Twine("A call to built-in function '" + StringRef(E->getSymbol()) +
574+
"' is not supported."));
573575
}
574576
}
575577

@@ -1196,3 +1198,18 @@ bool BPFTargetLowering::isLegalAddressingMode(const DataLayout &DL,
11961198

11971199
return true;
11981200
}
1201+
1202+
bool BPFTargetLowering::shouldSignExtendTypeInLibCall(Type *Ty,
1203+
bool IsSigned) const {
1204+
return IsSigned || Ty->isIntegerTy(32);
1205+
}
1206+
1207+
bool BPFTargetLowering::CanLowerReturn(
1208+
CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg,
1209+
const SmallVectorImpl<ISD::OutputArg> &Outs, LLVMContext &Context,
1210+
const Type *RetTy) const {
1211+
// At minimal return Outs.size() <= 1, or check valid types in CC.
1212+
SmallVector<CCValAssign, 16> RVLocs;
1213+
CCState CCInfo(CallConv, IsVarArg, MF, RVLocs, Context);
1214+
return CCInfo.CheckReturn(Outs, getHasAlu32() ? RetCC_BPF32 : RetCC_BPF64);
1215+
}

llvm/lib/Target/BPF/BPFISelLowering.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ class BPFTargetLowering : public TargetLowering {
6868
// Allows Misalignment
6969
bool AllowsMisalignedMemAccess;
7070

71+
bool AllowBuiltinCalls;
72+
7173
SDValue LowerSDIVSREM(SDValue Op, SelectionDAG &DAG) const;
7274
SDValue LowerDYNAMIC_STACKALLOC(SDValue Op, SelectionDAG &DAG) const;
7375
SDValue LowerBR_CC(SDValue Op, SelectionDAG &DAG) const;
@@ -163,6 +165,14 @@ class BPFTargetLowering : public TargetLowering {
163165
MachineBasicBlock *
164166
EmitInstrWithCustomInserterLDimm64(MachineInstr &MI,
165167
MachineBasicBlock *BB) const;
168+
169+
// Returns true if arguments should be sign-extended in lib calls.
170+
bool shouldSignExtendTypeInLibCall(Type *Ty, bool IsSigned) const override;
171+
172+
bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF,
173+
bool IsVarArg,
174+
const SmallVectorImpl<ISD::OutputArg> &Outs,
175+
LLVMContext &Context, const Type *RetTy) const override;
166176
};
167177
}
168178

llvm/lib/Target/BPF/BPFSubtarget.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ void BPFSubtarget::initializeEnvironment() {
7070
HasLoadAcqStoreRel = false;
7171
HasGotox = false;
7272
AllowsMisalignedMemAccess = false;
73+
AllowBuiltinCalls = false;
7374
}
7475

7576
void BPFSubtarget::initSubtargetFeatures(StringRef CPU, StringRef FS) {

llvm/lib/Target/BPF/BPFSubtarget.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,8 @@ class BPFSubtarget : public BPFGenSubtargetInfo {
7070
bool HasLdsx, HasMovsx, HasBswap, HasSdivSmod, HasGotol, HasStoreImm,
7171
HasLoadAcqStoreRel, HasGotox;
7272

73+
bool AllowBuiltinCalls;
74+
7375
std::unique_ptr<CallLowering> CallLoweringInfo;
7476
std::unique_ptr<InstructionSelector> InstSelector;
7577
std::unique_ptr<LegalizerInfo> Legalizer;
@@ -101,6 +103,7 @@ class BPFSubtarget : public BPFGenSubtargetInfo {
101103
bool hasStoreImm() const { return HasStoreImm; }
102104
bool hasLoadAcqStoreRel() const { return HasLoadAcqStoreRel; }
103105
bool hasGotox() const { return HasGotox; }
106+
bool getAllowBuiltinCalls() const { return AllowBuiltinCalls; }
104107

105108
bool isLittleEndian() const { return IsLittleEndian; }
106109

llvm/test/CodeGen/BPF/atomic-oversize.ll

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
; RUN: llc -mtriple=bpf < %s | FileCheck %s
2-
; XFAIL: *
3-
; Doesn't currently build, with error 'only small returns supported'.
42

53
define void @test(ptr %a) nounwind {
64
; CHECK-LABEL: test:
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
; RUN: llc -march=bpfel -mattr=+allow-builtin-calls < %s | FileCheck %s
2+
;
3+
; C code for this test case:
4+
;
5+
; long func(long a, long b) {
6+
; long x;
7+
; return __builtin_mul_overflow(a, b, &x);
8+
; }
9+
10+
11+
declare { i64, i1 } @llvm.smul.with.overflow.i64(i64, i64)
12+
13+
define noundef range(i64 0, 2) i64 @func(i64 noundef %a, i64 noundef %b) local_unnamed_addr {
14+
entry:
15+
%0 = tail call { i64, i1 } @llvm.smul.with.overflow.i64(i64 %a, i64 %b)
16+
%1 = extractvalue { i64, i1 } %0, 1
17+
%conv = zext i1 %1 to i64
18+
ret i64 %conv
19+
}
20+
21+
; CHECK-LABEL: func
22+
; CHECK: r4 = r2
23+
; CHECK: r2 = r1
24+
; CHECK: r3 = r2
25+
; CHECK: r3 s>>= 63
26+
; CHECK: r5 = r4
27+
; CHECK: r5 s>>= 63
28+
; CHECK: r1 = r10
29+
; CHECK: r1 += -16
30+
; CHECK: call __multi3
31+
; CHECK: r1 = *(u64 *)(r10 - 16)
32+
; CHECK: r1 s>>= 63
33+
; CHECK: w0 = 1
34+
; CHECK: r2 = *(u64 *)(r10 - 8)
35+
; CHECK: if r2 != r1 goto LBB0_2
36+
; CHECK: # %bb.1: # %entry
37+
; CHECK: w0 = 0
38+
; CHECK: LBB0_2: # %entry
39+
; CHECK: exit

llvm/test/CodeGen/BPF/struct_ret1.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
; RUN: not llc -mtriple=bpf < %s 2> %t1
22
; RUN: FileCheck %s < %t1
3-
; CHECK: error: <unknown>:0:0: in function bar { i64, i32 } (i32, i32, i32, i32, i32): aggregate returns are not supported
3+
; CHECK: error: <unknown>:0:0: in function bar { i64, i32 } (i32, i32, i32, i32, i32): stack arguments are not supported
44

55
%struct.S = type { i32, i32, i32 }
66

llvm/test/CodeGen/BPF/struct_ret2.ll

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
; RUN: not llc -mtriple=bpf < %s 2> %t1
22
; RUN: FileCheck %s < %t1
3-
; CHECK: only small returns
3+
; CHECK: too many arguments
44

55
; Function Attrs: nounwind uwtable
66
define { i64, i32 } @foo(i32 %a, i32 %b, i32 %c) #0 {

0 commit comments

Comments
 (0)