1 | //===------ SemaBPF.cpp ---------- BPF target-specific routines -----------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file implements semantic analysis functions specific to BPF. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "clang/Sema/SemaBPF.h" |
14 | #include "clang/AST/Decl.h" |
15 | #include "clang/AST/Type.h" |
16 | #include "clang/Basic/DiagnosticSema.h" |
17 | #include "clang/Basic/TargetBuiltins.h" |
18 | #include "clang/Sema/ParsedAttr.h" |
19 | #include "clang/Sema/Sema.h" |
20 | #include "llvm/ADT/APSInt.h" |
21 | #include <optional> |
22 | |
23 | namespace clang { |
24 | |
25 | SemaBPF::SemaBPF(Sema &S) : SemaBase(S) {} |
26 | |
27 | static bool isValidPreserveFieldInfoArg(Expr *Arg) { |
28 | if (Arg->getType()->getAsPlaceholderType()) |
29 | return false; |
30 | |
31 | // The first argument needs to be a record field access. |
32 | // If it is an array element access, we delay decision |
33 | // to BPF backend to check whether the access is a |
34 | // field access or not. |
35 | return (Arg->IgnoreParens()->getObjectKind() == OK_BitField || |
36 | isa<MemberExpr>(Val: Arg->IgnoreParens()) || |
37 | isa<ArraySubscriptExpr>(Val: Arg->IgnoreParens())); |
38 | } |
39 | |
40 | static bool isValidPreserveTypeInfoArg(Expr *Arg) { |
41 | QualType ArgType = Arg->getType(); |
42 | if (ArgType->getAsPlaceholderType()) |
43 | return false; |
44 | |
45 | // for TYPE_EXISTENCE/TYPE_MATCH/TYPE_SIZEOF reloc type |
46 | // format: |
47 | // 1. __builtin_preserve_type_info(*(<type> *)0, flag); |
48 | // 2. <type> var; |
49 | // __builtin_preserve_type_info(var, flag); |
50 | if (!isa<DeclRefExpr>(Val: Arg->IgnoreParens()) && |
51 | !isa<UnaryOperator>(Val: Arg->IgnoreParens())) |
52 | return false; |
53 | |
54 | // Typedef type. |
55 | if (ArgType->getAs<TypedefType>()) |
56 | return true; |
57 | |
58 | // Record type or Enum type. |
59 | const Type *Ty = ArgType->getUnqualifiedDesugaredType(); |
60 | if (const auto *RT = Ty->getAs<RecordType>()) { |
61 | if (!RT->getDecl()->getDeclName().isEmpty()) |
62 | return true; |
63 | } else if (const auto *ET = Ty->getAs<EnumType>()) { |
64 | if (!ET->getDecl()->getDeclName().isEmpty()) |
65 | return true; |
66 | } |
67 | |
68 | return false; |
69 | } |
70 | |
71 | static bool isValidPreserveEnumValueArg(Expr *Arg) { |
72 | QualType ArgType = Arg->getType(); |
73 | if (ArgType->getAsPlaceholderType()) |
74 | return false; |
75 | |
76 | // for ENUM_VALUE_EXISTENCE/ENUM_VALUE reloc type |
77 | // format: |
78 | // __builtin_preserve_enum_value(*(<enum_type> *)<enum_value>, |
79 | // flag); |
80 | const auto *UO = dyn_cast<UnaryOperator>(Val: Arg->IgnoreParens()); |
81 | if (!UO) |
82 | return false; |
83 | |
84 | const auto *CE = dyn_cast<CStyleCastExpr>(Val: UO->getSubExpr()); |
85 | if (!CE) |
86 | return false; |
87 | if (CE->getCastKind() != CK_IntegralToPointer && |
88 | CE->getCastKind() != CK_NullToPointer) |
89 | return false; |
90 | |
91 | // The integer must be from an EnumConstantDecl. |
92 | const auto *DR = dyn_cast<DeclRefExpr>(Val: CE->getSubExpr()); |
93 | if (!DR) |
94 | return false; |
95 | |
96 | const EnumConstantDecl *Enumerator = |
97 | dyn_cast<EnumConstantDecl>(Val: DR->getDecl()); |
98 | if (!Enumerator) |
99 | return false; |
100 | |
101 | // The type must be EnumType. |
102 | const Type *Ty = ArgType->getUnqualifiedDesugaredType(); |
103 | const auto *ET = Ty->getAs<EnumType>(); |
104 | if (!ET) |
105 | return false; |
106 | |
107 | // The enum value must be supported. |
108 | return llvm::is_contained(Range: ET->getDecl()->enumerators(), Element: Enumerator); |
109 | } |
110 | |
111 | bool SemaBPF::CheckBPFBuiltinFunctionCall(unsigned BuiltinID, |
112 | CallExpr *TheCall) { |
113 | assert((BuiltinID == BPF::BI__builtin_preserve_field_info || |
114 | BuiltinID == BPF::BI__builtin_btf_type_id || |
115 | BuiltinID == BPF::BI__builtin_preserve_type_info || |
116 | BuiltinID == BPF::BI__builtin_preserve_enum_value) && |
117 | "unexpected BPF builtin" ); |
118 | ASTContext &Context = getASTContext(); |
119 | if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 2)) |
120 | return true; |
121 | |
122 | // The second argument needs to be a constant int |
123 | Expr *Arg = TheCall->getArg(Arg: 1); |
124 | std::optional<llvm::APSInt> Value = Arg->getIntegerConstantExpr(Ctx: Context); |
125 | diag::kind kind; |
126 | if (!Value) { |
127 | if (BuiltinID == BPF::BI__builtin_preserve_field_info) |
128 | kind = diag::err_preserve_field_info_not_const; |
129 | else if (BuiltinID == BPF::BI__builtin_btf_type_id) |
130 | kind = diag::err_btf_type_id_not_const; |
131 | else if (BuiltinID == BPF::BI__builtin_preserve_type_info) |
132 | kind = diag::err_preserve_type_info_not_const; |
133 | else |
134 | kind = diag::err_preserve_enum_value_not_const; |
135 | Diag(Loc: Arg->getBeginLoc(), DiagID: kind) << 2 << Arg->getSourceRange(); |
136 | return true; |
137 | } |
138 | |
139 | // The first argument |
140 | Arg = TheCall->getArg(Arg: 0); |
141 | bool InvalidArg = false; |
142 | bool ReturnUnsignedInt = true; |
143 | if (BuiltinID == BPF::BI__builtin_preserve_field_info) { |
144 | if (!isValidPreserveFieldInfoArg(Arg)) { |
145 | InvalidArg = true; |
146 | kind = diag::err_preserve_field_info_not_field; |
147 | } |
148 | } else if (BuiltinID == BPF::BI__builtin_preserve_type_info) { |
149 | if (!isValidPreserveTypeInfoArg(Arg)) { |
150 | InvalidArg = true; |
151 | kind = diag::err_preserve_type_info_invalid; |
152 | } |
153 | } else if (BuiltinID == BPF::BI__builtin_preserve_enum_value) { |
154 | if (!isValidPreserveEnumValueArg(Arg)) { |
155 | InvalidArg = true; |
156 | kind = diag::err_preserve_enum_value_invalid; |
157 | } |
158 | ReturnUnsignedInt = false; |
159 | } else if (BuiltinID == BPF::BI__builtin_btf_type_id) { |
160 | ReturnUnsignedInt = false; |
161 | } |
162 | |
163 | if (InvalidArg) { |
164 | Diag(Loc: Arg->getBeginLoc(), DiagID: kind) << 1 << Arg->getSourceRange(); |
165 | return true; |
166 | } |
167 | |
168 | if (ReturnUnsignedInt) |
169 | TheCall->setType(Context.UnsignedIntTy); |
170 | else |
171 | TheCall->setType(Context.UnsignedLongTy); |
172 | return false; |
173 | } |
174 | |
175 | void SemaBPF::handlePreserveAIRecord(RecordDecl *RD) { |
176 | // Add preserve_access_index attribute to all fields and inner records. |
177 | for (auto *D : RD->decls()) { |
178 | if (D->hasAttr<BPFPreserveAccessIndexAttr>()) |
179 | continue; |
180 | |
181 | D->addAttr(A: BPFPreserveAccessIndexAttr::CreateImplicit(Ctx&: getASTContext())); |
182 | if (auto *Rec = dyn_cast<RecordDecl>(Val: D)) |
183 | handlePreserveAIRecord(RD: Rec); |
184 | } |
185 | } |
186 | |
187 | void SemaBPF::handlePreserveAccessIndexAttr(Decl *D, const ParsedAttr &AL) { |
188 | auto *Rec = cast<RecordDecl>(Val: D); |
189 | handlePreserveAIRecord(RD: Rec); |
190 | Rec->addAttr(A: ::new (getASTContext()) |
191 | BPFPreserveAccessIndexAttr(getASTContext(), AL)); |
192 | } |
193 | |
194 | } // namespace clang |
195 | |