diff --git a/clang/include/clang/Basic/BuiltinsBPF.def b/clang/include/clang/Basic/BuiltinsBPF.def --- a/clang/include/clang/Basic/BuiltinsBPF.def +++ b/clang/include/clang/Basic/BuiltinsBPF.def @@ -20,5 +20,8 @@ // Get record field information. TARGET_BUILTIN(__builtin_preserve_field_info, "Ui.", "t", "") +// Get BTF type id. +TARGET_BUILTIN(__builtin_btf_type_id, "Ui.", "t", "") + #undef BUILTIN #undef TARGET_BUILTIN diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10749,6 +10749,8 @@ "__builtin_preserve_field_info argument %0 not a field access">; def err_preserve_field_info_not_const: Error< "__builtin_preserve_field_info argument %0 not a constant">; +def err_btf_type_id_not_const: Error< + "__builtin_btf_type_id argument %0 not a constant">; def err_bit_cast_non_trivially_copyable : Error< "__builtin_bit_cast %select{source|destination}0 type must be trivially copyable">; diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -10633,33 +10633,103 @@ Value *CodeGenFunction::EmitBPFBuiltinExpr(unsigned BuiltinID, const CallExpr *E) { - assert(BuiltinID == BPF::BI__builtin_preserve_field_info && - "unexpected ARM builtin"); + assert((BuiltinID == BPF::BI__builtin_preserve_field_info || + BuiltinID == BPF::BI__builtin_btf_type_id) && + "unexpected BPF builtin"); - const Expr *Arg = E->getArg(0); - bool IsBitField = Arg->IgnoreParens()->getObjectKind() == OK_BitField; + switch (BuiltinID) { + default: + llvm_unreachable("Unexpected BPF builtin"); + case BPF::BI__builtin_preserve_field_info: { + const Expr *Arg = E->getArg(0); + bool IsBitField = Arg->IgnoreParens()->getObjectKind() == OK_BitField; - if (!getDebugInfo()) { - CGM.Error(E->getExprLoc(), "using builtin_preserve_field_info() without -g"); - return IsBitField ? EmitLValue(Arg).getBitFieldPointer() - : EmitLValue(Arg).getPointer(*this); - } + if (!getDebugInfo()) { + CGM.Error(E->getExprLoc(), + "using __builtin_preserve_field_info() without -g"); + return IsBitField ? EmitLValue(Arg).getBitFieldPointer() + : EmitLValue(Arg).getPointer(*this); + } + + // Enable underlying preserve_*_access_index() generation. + bool OldIsInPreservedAIRegion = IsInPreservedAIRegion; + IsInPreservedAIRegion = true; + Value *FieldAddr = IsBitField ? EmitLValue(Arg).getBitFieldPointer() + : EmitLValue(Arg).getPointer(*this); + IsInPreservedAIRegion = OldIsInPreservedAIRegion; + + ConstantInt *C = cast(EmitScalarExpr(E->getArg(1))); + Value *InfoKind = ConstantInt::get(Int64Ty, C->getSExtValue()); + + // Built the IR for the preserve_field_info intrinsic. + llvm::Function *FnGetFieldInfo = llvm::Intrinsic::getDeclaration( + &CGM.getModule(), llvm::Intrinsic::bpf_preserve_field_info, + {FieldAddr->getType()}); + return Builder.CreateCall(FnGetFieldInfo, {FieldAddr, InfoKind}); + } + case BPF::BI__builtin_btf_type_id: { + Value *FieldVal = nullptr; + + // The LValue cannot be converted Value in order to be used as the function + // parameter. If it is a structure, it is the "alloca" result of the LValue + // (a pointer) is used in the parameter. If it is a simple type, + // the value will be loaded from its corresponding "alloca" and used as + // the parameter. In our case, let us just get a pointer of the LValue + // since we do not really use the parameter. The purpose of parameter + // is to prevent the generated IR llvm.bpf.btf.type.id intrinsic call, + // which carries metadata, from being changed. + bool IsLValue = E->getArg(0)->isLValue(); + if (IsLValue) + FieldVal = EmitLValue(E->getArg(0)).getPointer(*this); + else + FieldVal = EmitScalarExpr(E->getArg(0)); - // Enable underlying preserve_*_access_index() generation. - bool OldIsInPreservedAIRegion = IsInPreservedAIRegion; - IsInPreservedAIRegion = true; - Value *FieldAddr = IsBitField ? EmitLValue(Arg).getBitFieldPointer() - : EmitLValue(Arg).getPointer(*this); - IsInPreservedAIRegion = OldIsInPreservedAIRegion; + if (!getDebugInfo()) { + CGM.Error(E->getExprLoc(), "using __builtin_btf_type_id() without -g"); + return nullptr; + } - ConstantInt *C = cast(EmitScalarExpr(E->getArg(1))); - Value *InfoKind = ConstantInt::get(Int64Ty, C->getSExtValue()); + // Generate debuginfo type for the first argument. + llvm::DIType *DbgInfo = + getDebugInfo()->getOrCreateStandaloneType(E->getArg(0)->getType(), + E->getArg(0)->getExprLoc()); - // Built the IR for the preserve_field_info intrinsic. - llvm::Function *FnGetFieldInfo = llvm::Intrinsic::getDeclaration( - &CGM.getModule(), llvm::Intrinsic::bpf_preserve_field_info, - {FieldAddr->getType()}); - return Builder.CreateCall(FnGetFieldInfo, {FieldAddr, InfoKind}); + ConstantInt *Flag = cast(EmitScalarExpr(E->getArg(1))); + Value *FlagValue = ConstantInt::get(Int64Ty, Flag->getSExtValue()); + + // Built the IR for the btf_type_id intrinsic. + // + // In the above, we converted LValue argument to a pointer to LValue. + // For example, the following + // int v; + // C1: __builtin_btf_type_id(v, flag); + // will be converted to + // L1: llvm.bpf.btf.type.id(&v, flag) + // This makes it hard to differentiate from + // C2: __builtin_btf_type_id(&v, flag); + // to + // L2: llvm.bpf.btf.type.id(&v, flag) + // + // If both C1 and C2 are present in the code, the llvm may later + // on do CSE on L1 and L2, which will result in incorrect tagged types. + // + // The C1->L1 transformation only happens if the argument of + // __builtin_btf_type_id() is a LValue. So Let us put whether + // the argument is an LValue or not into generated IR. This should + // prevent potential CSE from causing debuginfo type loss. + // + // The generated IR intrinsics will hence look like + // L1: llvm.bpf.btf.type.id(&v, 1, flag) !di_type_for_{v}; + // L2: llvm.bpf.btf.type.id(&v, 0, flag) !di_type_for_{&v}; + Constant *CV = ConstantInt::get(IntTy, IsLValue); + llvm::Function *FnBtfTypeId = llvm::Intrinsic::getDeclaration( + &CGM.getModule(), llvm::Intrinsic::bpf_btf_type_id, + {FieldVal->getType(), CV->getType()}); + CallInst *Fn = Builder.CreateCall(FnBtfTypeId, {FieldVal, CV, FlagValue}); + Fn->setMetadata(LLVMContext::MD_preserve_access_index, DbgInfo); + return Fn; + } + } } llvm::Value *CodeGenFunction:: diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -2487,17 +2487,33 @@ bool Sema::CheckBPFBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall) { - assert(BuiltinID == BPF::BI__builtin_preserve_field_info && + assert((BuiltinID == BPF::BI__builtin_preserve_field_info || + BuiltinID == BPF::BI__builtin_btf_type_id) && "unexpected ARM builtin"); if (checkArgCount(*this, TheCall, 2)) return true; + Expr *Arg; + if (BuiltinID == BPF::BI__builtin_btf_type_id) { + // The second argument needs to be a constant int + llvm::APSInt Value; + Arg = TheCall->getArg(1); + if (!Arg->isIntegerConstantExpr(Value, Context)) { + Diag(Arg->getBeginLoc(), diag::err_btf_type_id_not_const) + << 2 << Arg->getSourceRange(); + return true; + } + + TheCall->setType(Context.UnsignedIntTy); + return false; + } + // The first argument needs to be a record field access. // If it is an array element access, we delay decision // to BPF backend to check whether the access is a // field access or not. - Expr *Arg = TheCall->getArg(0); + Arg = TheCall->getArg(0); if (Arg->getType()->getAsPlaceholderType() || (Arg->IgnoreParens()->getObjectKind() != OK_BitField && !dyn_cast(Arg->IgnoreParens()) && @@ -2508,8 +2524,9 @@ } // The second argument needs to be a constant int + Arg = TheCall->getArg(1); llvm::APSInt Value; - if (!TheCall->getArg(1)->isIntegerConstantExpr(Value, Context)) { + if (!Arg->isIntegerConstantExpr(Value, Context)) { Diag(Arg->getBeginLoc(), diag::err_preserve_field_info_not_const) << 2 << Arg->getSourceRange(); return true; diff --git a/clang/test/CodeGen/builtin-bpf-btf-type-id.c b/clang/test/CodeGen/builtin-bpf-btf-type-id.c new file mode 100644 --- /dev/null +++ b/clang/test/CodeGen/builtin-bpf-btf-type-id.c @@ -0,0 +1,13 @@ +// REQUIRES: bpf-registered-target +// RUN: %clang -target bpf -emit-llvm -S -g %s -o - | FileCheck %s + +unsigned test1(int a) { return __builtin_btf_type_id(a, 0); } +unsigned test2(int a) { return __builtin_btf_type_id(&a, 0); } + +// CHECK: define dso_local i32 @test1 +// CHECK: call i32 @llvm.bpf.btf.type.id.p0i32.i32(i32* %{{[0-9a-z.]+}}, i32 1, i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[INT:[0-9]+]] +// CHECK: define dso_local i32 @test2 +// CHECK: call i32 @llvm.bpf.btf.type.id.p0i32.i32(i32* %{{[0-9a-z.]+}}, i32 0, i64 0), !dbg !{{[0-9]+}}, !llvm.preserve.access.index ![[INT_POINTER:[0-9]+]] +// +// CHECK: ![[INT]] = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed +// CHECK: ![[INT_POINTER]] = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: ![[INT]], size: 64 diff --git a/clang/test/Sema/builtin-bpf-btf-type-id.c b/clang/test/Sema/builtin-bpf-btf-type-id.c new file mode 100644 --- /dev/null +++ b/clang/test/Sema/builtin-bpf-btf-type-id.c @@ -0,0 +1,13 @@ +// RUN: %clang_cc1 -x c -triple bpf-pc-linux-gnu -dwarf-version=4 -fsyntax-only -verify %s + +struct { + char f1[100]; + int f2; +} tmp = {}; + +unsigned invalid1() { return __builtin_btf_type_id(1, tmp); } // expected-error {{__builtin_btf_type_id argument 2 not a constant}} +unsigned invalid2() { return __builtin_btf_type_id(1, 1, 1); } // expected-error {{too many arguments to function call, expected 2, have 3}} + +int valid1() { return __builtin_btf_type_id(tmp, 0); } +int valid2() { return __builtin_btf_type_id(&tmp, 1); } +int valid3() { return __builtin_btf_type_id(tmp.f1[4], 10); } diff --git a/llvm/include/llvm/IR/IntrinsicsBPF.td b/llvm/include/llvm/IR/IntrinsicsBPF.td --- a/llvm/include/llvm/IR/IntrinsicsBPF.td +++ b/llvm/include/llvm/IR/IntrinsicsBPF.td @@ -23,4 +23,7 @@ def int_bpf_preserve_field_info : GCCBuiltin<"__builtin_bpf_preserve_field_info">, Intrinsic<[llvm_i32_ty], [llvm_anyptr_ty, llvm_i64_ty], [IntrNoMem, ImmArg<1>]>; + def int_bpf_btf_type_id : GCCBuiltin<"__builtin_bpf_btf_type_id">, + Intrinsic<[llvm_i32_ty], [llvm_any_ty, llvm_any_ty, llvm_i64_ty], + [IntrNoMem]>; }