1 | //===------ SemaARM.cpp ---------- ARM 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 ARM. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "clang/Sema/SemaARM.h" |
14 | #include "clang/Basic/DiagnosticSema.h" |
15 | #include "clang/Basic/TargetBuiltins.h" |
16 | #include "clang/Sema/Initialization.h" |
17 | #include "clang/Sema/ParsedAttr.h" |
18 | #include "clang/Sema/Sema.h" |
19 | |
20 | namespace clang { |
21 | |
22 | SemaARM::SemaARM(Sema &S) : SemaBase(S) {} |
23 | |
24 | /// BuiltinARMMemoryTaggingCall - Handle calls of memory tagging extensions |
25 | bool SemaARM::BuiltinARMMemoryTaggingCall(unsigned BuiltinID, |
26 | CallExpr *TheCall) { |
27 | ASTContext &Context = getASTContext(); |
28 | |
29 | if (BuiltinID == AArch64::BI__builtin_arm_irg) { |
30 | if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 2)) |
31 | return true; |
32 | Expr *Arg0 = TheCall->getArg(Arg: 0); |
33 | Expr *Arg1 = TheCall->getArg(Arg: 1); |
34 | |
35 | ExprResult FirstArg = SemaRef.DefaultFunctionArrayLvalueConversion(E: Arg0); |
36 | if (FirstArg.isInvalid()) |
37 | return true; |
38 | QualType FirstArgType = FirstArg.get()->getType(); |
39 | if (!FirstArgType->isAnyPointerType()) |
40 | return Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_memtag_arg_must_be_pointer) |
41 | << "first" << FirstArgType << Arg0->getSourceRange(); |
42 | TheCall->setArg(Arg: 0, ArgExpr: FirstArg.get()); |
43 | |
44 | ExprResult SecArg = SemaRef.DefaultLvalueConversion(E: Arg1); |
45 | if (SecArg.isInvalid()) |
46 | return true; |
47 | QualType SecArgType = SecArg.get()->getType(); |
48 | if (!SecArgType->isIntegerType()) |
49 | return Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_memtag_arg_must_be_integer) |
50 | << "second" << SecArgType << Arg1->getSourceRange(); |
51 | |
52 | // Derive the return type from the pointer argument. |
53 | TheCall->setType(FirstArgType); |
54 | return false; |
55 | } |
56 | |
57 | if (BuiltinID == AArch64::BI__builtin_arm_addg) { |
58 | if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 2)) |
59 | return true; |
60 | |
61 | Expr *Arg0 = TheCall->getArg(Arg: 0); |
62 | ExprResult FirstArg = SemaRef.DefaultFunctionArrayLvalueConversion(E: Arg0); |
63 | if (FirstArg.isInvalid()) |
64 | return true; |
65 | QualType FirstArgType = FirstArg.get()->getType(); |
66 | if (!FirstArgType->isAnyPointerType()) |
67 | return Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_memtag_arg_must_be_pointer) |
68 | << "first" << FirstArgType << Arg0->getSourceRange(); |
69 | TheCall->setArg(Arg: 0, ArgExpr: FirstArg.get()); |
70 | |
71 | // Derive the return type from the pointer argument. |
72 | TheCall->setType(FirstArgType); |
73 | |
74 | // Second arg must be an constant in range [0,15] |
75 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 1, Low: 0, High: 15); |
76 | } |
77 | |
78 | if (BuiltinID == AArch64::BI__builtin_arm_gmi) { |
79 | if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 2)) |
80 | return true; |
81 | Expr *Arg0 = TheCall->getArg(Arg: 0); |
82 | Expr *Arg1 = TheCall->getArg(Arg: 1); |
83 | |
84 | ExprResult FirstArg = SemaRef.DefaultFunctionArrayLvalueConversion(E: Arg0); |
85 | if (FirstArg.isInvalid()) |
86 | return true; |
87 | QualType FirstArgType = FirstArg.get()->getType(); |
88 | if (!FirstArgType->isAnyPointerType()) |
89 | return Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_memtag_arg_must_be_pointer) |
90 | << "first" << FirstArgType << Arg0->getSourceRange(); |
91 | |
92 | QualType SecArgType = Arg1->getType(); |
93 | if (!SecArgType->isIntegerType()) |
94 | return Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_memtag_arg_must_be_integer) |
95 | << "second" << SecArgType << Arg1->getSourceRange(); |
96 | TheCall->setType(Context.IntTy); |
97 | return false; |
98 | } |
99 | |
100 | if (BuiltinID == AArch64::BI__builtin_arm_ldg || |
101 | BuiltinID == AArch64::BI__builtin_arm_stg) { |
102 | if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: 1)) |
103 | return true; |
104 | Expr *Arg0 = TheCall->getArg(Arg: 0); |
105 | ExprResult FirstArg = SemaRef.DefaultFunctionArrayLvalueConversion(E: Arg0); |
106 | if (FirstArg.isInvalid()) |
107 | return true; |
108 | |
109 | QualType FirstArgType = FirstArg.get()->getType(); |
110 | if (!FirstArgType->isAnyPointerType()) |
111 | return Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_memtag_arg_must_be_pointer) |
112 | << "first" << FirstArgType << Arg0->getSourceRange(); |
113 | TheCall->setArg(Arg: 0, ArgExpr: FirstArg.get()); |
114 | |
115 | // Derive the return type from the pointer argument. |
116 | if (BuiltinID == AArch64::BI__builtin_arm_ldg) |
117 | TheCall->setType(FirstArgType); |
118 | return false; |
119 | } |
120 | |
121 | if (BuiltinID == AArch64::BI__builtin_arm_subp) { |
122 | Expr *ArgA = TheCall->getArg(Arg: 0); |
123 | Expr *ArgB = TheCall->getArg(Arg: 1); |
124 | |
125 | ExprResult ArgExprA = SemaRef.DefaultFunctionArrayLvalueConversion(E: ArgA); |
126 | ExprResult ArgExprB = SemaRef.DefaultFunctionArrayLvalueConversion(E: ArgB); |
127 | |
128 | if (ArgExprA.isInvalid() || ArgExprB.isInvalid()) |
129 | return true; |
130 | |
131 | QualType ArgTypeA = ArgExprA.get()->getType(); |
132 | QualType ArgTypeB = ArgExprB.get()->getType(); |
133 | |
134 | auto isNull = [&](Expr *E) -> bool { |
135 | return E->isNullPointerConstant(Ctx&: Context, |
136 | NPC: Expr::NPC_ValueDependentIsNotNull); |
137 | }; |
138 | |
139 | // argument should be either a pointer or null |
140 | if (!ArgTypeA->isAnyPointerType() && !isNull(ArgA)) |
141 | return Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_memtag_arg_null_or_pointer) |
142 | << "first" << ArgTypeA << ArgA->getSourceRange(); |
143 | |
144 | if (!ArgTypeB->isAnyPointerType() && !isNull(ArgB)) |
145 | return Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_memtag_arg_null_or_pointer) |
146 | << "second" << ArgTypeB << ArgB->getSourceRange(); |
147 | |
148 | // Ensure Pointee types are compatible |
149 | if (ArgTypeA->isAnyPointerType() && !isNull(ArgA) && |
150 | ArgTypeB->isAnyPointerType() && !isNull(ArgB)) { |
151 | QualType pointeeA = ArgTypeA->getPointeeType(); |
152 | QualType pointeeB = ArgTypeB->getPointeeType(); |
153 | if (!Context.typesAreCompatible( |
154 | T1: Context.getCanonicalType(T: pointeeA).getUnqualifiedType(), |
155 | T2: Context.getCanonicalType(T: pointeeB).getUnqualifiedType())) { |
156 | return Diag(Loc: TheCall->getBeginLoc(), |
157 | DiagID: diag::err_typecheck_sub_ptr_compatible) |
158 | << ArgTypeA << ArgTypeB << ArgA->getSourceRange() |
159 | << ArgB->getSourceRange(); |
160 | } |
161 | } |
162 | |
163 | // at least one argument should be pointer type |
164 | if (!ArgTypeA->isAnyPointerType() && !ArgTypeB->isAnyPointerType()) |
165 | return Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_memtag_any2arg_pointer) |
166 | << ArgTypeA << ArgTypeB << ArgA->getSourceRange(); |
167 | |
168 | if (isNull(ArgA)) // adopt type of the other pointer |
169 | ArgExprA = |
170 | SemaRef.ImpCastExprToType(E: ArgExprA.get(), Type: ArgTypeB, CK: CK_NullToPointer); |
171 | |
172 | if (isNull(ArgB)) |
173 | ArgExprB = |
174 | SemaRef.ImpCastExprToType(E: ArgExprB.get(), Type: ArgTypeA, CK: CK_NullToPointer); |
175 | |
176 | TheCall->setArg(Arg: 0, ArgExpr: ArgExprA.get()); |
177 | TheCall->setArg(Arg: 1, ArgExpr: ArgExprB.get()); |
178 | TheCall->setType(Context.LongLongTy); |
179 | return false; |
180 | } |
181 | assert(false && "Unhandled ARM MTE intrinsic" ); |
182 | return true; |
183 | } |
184 | |
185 | /// BuiltinARMSpecialReg - Handle a check if argument ArgNum of CallExpr |
186 | /// TheCall is an ARM/AArch64 special register string literal. |
187 | bool SemaARM::BuiltinARMSpecialReg(unsigned BuiltinID, CallExpr *TheCall, |
188 | int ArgNum, unsigned ExpectedFieldNum, |
189 | bool AllowName) { |
190 | bool IsARMBuiltin = BuiltinID == ARM::BI__builtin_arm_rsr64 || |
191 | BuiltinID == ARM::BI__builtin_arm_wsr64 || |
192 | BuiltinID == ARM::BI__builtin_arm_rsr || |
193 | BuiltinID == ARM::BI__builtin_arm_rsrp || |
194 | BuiltinID == ARM::BI__builtin_arm_wsr || |
195 | BuiltinID == ARM::BI__builtin_arm_wsrp; |
196 | bool IsAArch64Builtin = BuiltinID == AArch64::BI__builtin_arm_rsr64 || |
197 | BuiltinID == AArch64::BI__builtin_arm_wsr64 || |
198 | BuiltinID == AArch64::BI__builtin_arm_rsr128 || |
199 | BuiltinID == AArch64::BI__builtin_arm_wsr128 || |
200 | BuiltinID == AArch64::BI__builtin_arm_rsr || |
201 | BuiltinID == AArch64::BI__builtin_arm_rsrp || |
202 | BuiltinID == AArch64::BI__builtin_arm_wsr || |
203 | BuiltinID == AArch64::BI__builtin_arm_wsrp; |
204 | assert((IsARMBuiltin || IsAArch64Builtin) && "Unexpected ARM builtin." ); |
205 | |
206 | // We can't check the value of a dependent argument. |
207 | Expr *Arg = TheCall->getArg(Arg: ArgNum); |
208 | if (Arg->isTypeDependent() || Arg->isValueDependent()) |
209 | return false; |
210 | |
211 | // Check if the argument is a string literal. |
212 | if (!isa<StringLiteral>(Val: Arg->IgnoreParenImpCasts())) |
213 | return Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_expr_not_string_literal) |
214 | << Arg->getSourceRange(); |
215 | |
216 | // Check the type of special register given. |
217 | StringRef Reg = cast<StringLiteral>(Val: Arg->IgnoreParenImpCasts())->getString(); |
218 | SmallVector<StringRef, 6> Fields; |
219 | Reg.split(A&: Fields, Separator: ":" ); |
220 | |
221 | if (Fields.size() != ExpectedFieldNum && !(AllowName && Fields.size() == 1)) |
222 | return Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_arm_invalid_specialreg) |
223 | << Arg->getSourceRange(); |
224 | |
225 | // If the string is the name of a register then we cannot check that it is |
226 | // valid here but if the string is of one the forms described in ACLE then we |
227 | // can check that the supplied fields are integers and within the valid |
228 | // ranges. |
229 | if (Fields.size() > 1) { |
230 | bool FiveFields = Fields.size() == 5; |
231 | |
232 | bool ValidString = true; |
233 | if (IsARMBuiltin) { |
234 | ValidString &= Fields[0].starts_with_insensitive(Prefix: "cp" ) || |
235 | Fields[0].starts_with_insensitive(Prefix: "p" ); |
236 | if (ValidString) |
237 | Fields[0] = Fields[0].drop_front( |
238 | N: Fields[0].starts_with_insensitive(Prefix: "cp" ) ? 2 : 1); |
239 | |
240 | ValidString &= Fields[2].starts_with_insensitive(Prefix: "c" ); |
241 | if (ValidString) |
242 | Fields[2] = Fields[2].drop_front(N: 1); |
243 | |
244 | if (FiveFields) { |
245 | ValidString &= Fields[3].starts_with_insensitive(Prefix: "c" ); |
246 | if (ValidString) |
247 | Fields[3] = Fields[3].drop_front(N: 1); |
248 | } |
249 | } |
250 | |
251 | SmallVector<int, 5> Ranges; |
252 | if (FiveFields) |
253 | Ranges.append(IL: {IsAArch64Builtin ? 1 : 15, 7, 15, 15, 7}); |
254 | else |
255 | Ranges.append(IL: {15, 7, 15}); |
256 | |
257 | for (unsigned i = 0; i < Fields.size(); ++i) { |
258 | int IntField; |
259 | ValidString &= !Fields[i].getAsInteger(Radix: 10, Result&: IntField); |
260 | ValidString &= (IntField >= 0 && IntField <= Ranges[i]); |
261 | } |
262 | |
263 | if (!ValidString) |
264 | return Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_arm_invalid_specialreg) |
265 | << Arg->getSourceRange(); |
266 | } else if (IsAArch64Builtin && Fields.size() == 1) { |
267 | // This code validates writes to PSTATE registers. |
268 | |
269 | // Not a write. |
270 | if (TheCall->getNumArgs() != 2) |
271 | return false; |
272 | |
273 | // The 128-bit system register accesses do not touch PSTATE. |
274 | if (BuiltinID == AArch64::BI__builtin_arm_rsr128 || |
275 | BuiltinID == AArch64::BI__builtin_arm_wsr128) |
276 | return false; |
277 | |
278 | // These are the named PSTATE accesses using "MSR (immediate)" instructions, |
279 | // along with the upper limit on the immediates allowed. |
280 | auto MaxLimit = llvm::StringSwitch<std::optional<unsigned>>(Reg) |
281 | .CaseLower(S: "spsel" , Value: 15) |
282 | .CaseLower(S: "daifclr" , Value: 15) |
283 | .CaseLower(S: "daifset" , Value: 15) |
284 | .CaseLower(S: "pan" , Value: 15) |
285 | .CaseLower(S: "uao" , Value: 15) |
286 | .CaseLower(S: "dit" , Value: 15) |
287 | .CaseLower(S: "ssbs" , Value: 15) |
288 | .CaseLower(S: "tco" , Value: 15) |
289 | .CaseLower(S: "allint" , Value: 1) |
290 | .CaseLower(S: "pm" , Value: 1) |
291 | .Default(Value: std::nullopt); |
292 | |
293 | // If this is not a named PSTATE, just continue without validating, as this |
294 | // will be lowered to an "MSR (register)" instruction directly |
295 | if (!MaxLimit) |
296 | return false; |
297 | |
298 | // Here we only allow constants in the range for that pstate, as required by |
299 | // the ACLE. |
300 | // |
301 | // While clang also accepts the names of system registers in its ACLE |
302 | // intrinsics, we prevent this with the PSTATE names used in MSR (immediate) |
303 | // as the value written via a register is different to the value used as an |
304 | // immediate to have the same effect. e.g., for the instruction `msr tco, |
305 | // x0`, it is bit 25 of register x0 that is written into PSTATE.TCO, but |
306 | // with `msr tco, #imm`, it is bit 0 of xN that is written into PSTATE.TCO. |
307 | // |
308 | // If a programmer wants to codegen the MSR (register) form of `msr tco, |
309 | // xN`, they can still do so by specifying the register using five |
310 | // colon-separated numbers in a string. |
311 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 1, Low: 0, High: *MaxLimit); |
312 | } |
313 | |
314 | return false; |
315 | } |
316 | |
317 | // Get the valid immediate range for the specified NEON type code. |
318 | static unsigned RFT(unsigned t, bool shift = false, bool ForceQuad = false) { |
319 | NeonTypeFlags Type(t); |
320 | int IsQuad = ForceQuad ? true : Type.isQuad(); |
321 | switch (Type.getEltType()) { |
322 | case NeonTypeFlags::Int8: |
323 | case NeonTypeFlags::Poly8: |
324 | return shift ? 7 : (8 << IsQuad) - 1; |
325 | case NeonTypeFlags::Int16: |
326 | case NeonTypeFlags::Poly16: |
327 | return shift ? 15 : (4 << IsQuad) - 1; |
328 | case NeonTypeFlags::Int32: |
329 | return shift ? 31 : (2 << IsQuad) - 1; |
330 | case NeonTypeFlags::Int64: |
331 | case NeonTypeFlags::Poly64: |
332 | return shift ? 63 : (1 << IsQuad) - 1; |
333 | case NeonTypeFlags::Poly128: |
334 | return shift ? 127 : (1 << IsQuad) - 1; |
335 | case NeonTypeFlags::Float16: |
336 | assert(!shift && "cannot shift float types!" ); |
337 | return (4 << IsQuad) - 1; |
338 | case NeonTypeFlags::Float32: |
339 | assert(!shift && "cannot shift float types!" ); |
340 | return (2 << IsQuad) - 1; |
341 | case NeonTypeFlags::Float64: |
342 | assert(!shift && "cannot shift float types!" ); |
343 | return (1 << IsQuad) - 1; |
344 | case NeonTypeFlags::BFloat16: |
345 | assert(!shift && "cannot shift float types!" ); |
346 | return (4 << IsQuad) - 1; |
347 | } |
348 | llvm_unreachable("Invalid NeonTypeFlag!" ); |
349 | } |
350 | |
351 | /// getNeonEltType - Return the QualType corresponding to the elements of |
352 | /// the vector type specified by the NeonTypeFlags. This is used to check |
353 | /// the pointer arguments for Neon load/store intrinsics. |
354 | static QualType getNeonEltType(NeonTypeFlags Flags, ASTContext &Context, |
355 | bool IsPolyUnsigned, bool IsInt64Long) { |
356 | switch (Flags.getEltType()) { |
357 | case NeonTypeFlags::Int8: |
358 | return Flags.isUnsigned() ? Context.UnsignedCharTy : Context.SignedCharTy; |
359 | case NeonTypeFlags::Int16: |
360 | return Flags.isUnsigned() ? Context.UnsignedShortTy : Context.ShortTy; |
361 | case NeonTypeFlags::Int32: |
362 | return Flags.isUnsigned() ? Context.UnsignedIntTy : Context.IntTy; |
363 | case NeonTypeFlags::Int64: |
364 | if (IsInt64Long) |
365 | return Flags.isUnsigned() ? Context.UnsignedLongTy : Context.LongTy; |
366 | else |
367 | return Flags.isUnsigned() ? Context.UnsignedLongLongTy |
368 | : Context.LongLongTy; |
369 | case NeonTypeFlags::Poly8: |
370 | return IsPolyUnsigned ? Context.UnsignedCharTy : Context.SignedCharTy; |
371 | case NeonTypeFlags::Poly16: |
372 | return IsPolyUnsigned ? Context.UnsignedShortTy : Context.ShortTy; |
373 | case NeonTypeFlags::Poly64: |
374 | if (IsInt64Long) |
375 | return Context.UnsignedLongTy; |
376 | else |
377 | return Context.UnsignedLongLongTy; |
378 | case NeonTypeFlags::Poly128: |
379 | break; |
380 | case NeonTypeFlags::Float16: |
381 | return Context.HalfTy; |
382 | case NeonTypeFlags::Float32: |
383 | return Context.FloatTy; |
384 | case NeonTypeFlags::Float64: |
385 | return Context.DoubleTy; |
386 | case NeonTypeFlags::BFloat16: |
387 | return Context.BFloat16Ty; |
388 | } |
389 | llvm_unreachable("Invalid NeonTypeFlag!" ); |
390 | } |
391 | |
392 | enum ArmSMEState : unsigned { |
393 | ArmNoState = 0, |
394 | |
395 | ArmInZA = 0b01, |
396 | ArmOutZA = 0b10, |
397 | ArmInOutZA = 0b11, |
398 | ArmZAMask = 0b11, |
399 | |
400 | ArmInZT0 = 0b01 << 2, |
401 | ArmOutZT0 = 0b10 << 2, |
402 | ArmInOutZT0 = 0b11 << 2, |
403 | ArmZT0Mask = 0b11 << 2 |
404 | }; |
405 | |
406 | bool SemaARM::ParseSVEImmChecks( |
407 | CallExpr *TheCall, SmallVector<std::tuple<int, int, int>, 3> &ImmChecks) { |
408 | // Perform all the immediate checks for this builtin call. |
409 | bool HasError = false; |
410 | for (auto &I : ImmChecks) { |
411 | int ArgNum, CheckTy, ElementSizeInBits; |
412 | std::tie(args&: ArgNum, args&: CheckTy, args&: ElementSizeInBits) = I; |
413 | |
414 | typedef bool (*OptionSetCheckFnTy)(int64_t Value); |
415 | |
416 | // Function that checks whether the operand (ArgNum) is an immediate |
417 | // that is one of the predefined values. |
418 | auto CheckImmediateInSet = [&](OptionSetCheckFnTy CheckImm, |
419 | int ErrDiag) -> bool { |
420 | // We can't check the value of a dependent argument. |
421 | Expr *Arg = TheCall->getArg(Arg: ArgNum); |
422 | if (Arg->isTypeDependent() || Arg->isValueDependent()) |
423 | return false; |
424 | |
425 | // Check constant-ness first. |
426 | llvm::APSInt Imm; |
427 | if (SemaRef.BuiltinConstantArg(TheCall, ArgNum, Result&: Imm)) |
428 | return true; |
429 | |
430 | if (!CheckImm(Imm.getSExtValue())) |
431 | return Diag(Loc: TheCall->getBeginLoc(), DiagID: ErrDiag) << Arg->getSourceRange(); |
432 | return false; |
433 | }; |
434 | |
435 | switch ((SVETypeFlags::ImmCheckType)CheckTy) { |
436 | case SVETypeFlags::ImmCheck0_31: |
437 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 0, High: 31)) |
438 | HasError = true; |
439 | break; |
440 | case SVETypeFlags::ImmCheck0_13: |
441 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 0, High: 13)) |
442 | HasError = true; |
443 | break; |
444 | case SVETypeFlags::ImmCheck1_16: |
445 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 1, High: 16)) |
446 | HasError = true; |
447 | break; |
448 | case SVETypeFlags::ImmCheck0_7: |
449 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 0, High: 7)) |
450 | HasError = true; |
451 | break; |
452 | case SVETypeFlags::ImmCheck1_1: |
453 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 1, High: 1)) |
454 | HasError = true; |
455 | break; |
456 | case SVETypeFlags::ImmCheck1_3: |
457 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 1, High: 3)) |
458 | HasError = true; |
459 | break; |
460 | case SVETypeFlags::ImmCheck1_7: |
461 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 1, High: 7)) |
462 | HasError = true; |
463 | break; |
464 | case SVETypeFlags::ImmCheckExtract: |
465 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 0, |
466 | High: (2048 / ElementSizeInBits) - 1)) |
467 | HasError = true; |
468 | break; |
469 | case SVETypeFlags::ImmCheckShiftRight: |
470 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 1, |
471 | High: ElementSizeInBits)) |
472 | HasError = true; |
473 | break; |
474 | case SVETypeFlags::ImmCheckShiftRightNarrow: |
475 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 1, |
476 | High: ElementSizeInBits / 2)) |
477 | HasError = true; |
478 | break; |
479 | case SVETypeFlags::ImmCheckShiftLeft: |
480 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 0, |
481 | High: ElementSizeInBits - 1)) |
482 | HasError = true; |
483 | break; |
484 | case SVETypeFlags::ImmCheckLaneIndex: |
485 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 0, |
486 | High: (128 / (1 * ElementSizeInBits)) - 1)) |
487 | HasError = true; |
488 | break; |
489 | case SVETypeFlags::ImmCheckLaneIndexCompRotate: |
490 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 0, |
491 | High: (128 / (2 * ElementSizeInBits)) - 1)) |
492 | HasError = true; |
493 | break; |
494 | case SVETypeFlags::ImmCheckLaneIndexDot: |
495 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 0, |
496 | High: (128 / (4 * ElementSizeInBits)) - 1)) |
497 | HasError = true; |
498 | break; |
499 | case SVETypeFlags::ImmCheckComplexRot90_270: |
500 | if (CheckImmediateInSet([](int64_t V) { return V == 90 || V == 270; }, |
501 | diag::err_rotation_argument_to_cadd)) |
502 | HasError = true; |
503 | break; |
504 | case SVETypeFlags::ImmCheckComplexRotAll90: |
505 | if (CheckImmediateInSet( |
506 | [](int64_t V) { |
507 | return V == 0 || V == 90 || V == 180 || V == 270; |
508 | }, |
509 | diag::err_rotation_argument_to_cmla)) |
510 | HasError = true; |
511 | break; |
512 | case SVETypeFlags::ImmCheck0_1: |
513 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 0, High: 1)) |
514 | HasError = true; |
515 | break; |
516 | case SVETypeFlags::ImmCheck0_2: |
517 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 0, High: 2)) |
518 | HasError = true; |
519 | break; |
520 | case SVETypeFlags::ImmCheck0_3: |
521 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 0, High: 3)) |
522 | HasError = true; |
523 | break; |
524 | case SVETypeFlags::ImmCheck0_0: |
525 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 0, High: 0)) |
526 | HasError = true; |
527 | break; |
528 | case SVETypeFlags::ImmCheck0_15: |
529 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 0, High: 15)) |
530 | HasError = true; |
531 | break; |
532 | case SVETypeFlags::ImmCheck0_255: |
533 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 0, High: 255)) |
534 | HasError = true; |
535 | break; |
536 | case SVETypeFlags::ImmCheck2_4_Mul2: |
537 | if (SemaRef.BuiltinConstantArgRange(TheCall, ArgNum, Low: 2, High: 4) || |
538 | SemaRef.BuiltinConstantArgMultiple(TheCall, ArgNum, Multiple: 2)) |
539 | HasError = true; |
540 | break; |
541 | } |
542 | } |
543 | |
544 | return HasError; |
545 | } |
546 | |
547 | SemaARM::ArmStreamingType getArmStreamingFnType(const FunctionDecl *FD) { |
548 | if (FD->hasAttr<ArmLocallyStreamingAttr>()) |
549 | return SemaARM::ArmStreaming; |
550 | if (const Type *Ty = FD->getType().getTypePtrOrNull()) { |
551 | if (const auto *FPT = Ty->getAs<FunctionProtoType>()) { |
552 | if (FPT->getAArch64SMEAttributes() & |
553 | FunctionType::SME_PStateSMEnabledMask) |
554 | return SemaARM::ArmStreaming; |
555 | if (FPT->getAArch64SMEAttributes() & |
556 | FunctionType::SME_PStateSMCompatibleMask) |
557 | return SemaARM::ArmStreamingCompatible; |
558 | } |
559 | } |
560 | return SemaARM::ArmNonStreaming; |
561 | } |
562 | |
563 | static bool checkArmStreamingBuiltin(Sema &S, CallExpr *TheCall, |
564 | const FunctionDecl *FD, |
565 | SemaARM::ArmStreamingType BuiltinType, |
566 | unsigned BuiltinID) { |
567 | SemaARM::ArmStreamingType FnType = getArmStreamingFnType(FD); |
568 | |
569 | // Check if the intrinsic is available in the right mode, i.e. |
570 | // * When compiling for SME only, the caller must be in streaming mode. |
571 | // * When compiling for SVE only, the caller must be in non-streaming mode. |
572 | // * When compiling for both SVE and SME, the caller can be in either mode. |
573 | if (BuiltinType == SemaARM::VerifyRuntimeMode) { |
574 | auto DisableFeatures = [](llvm::StringMap<bool> &Map, StringRef S) { |
575 | for (StringRef K : Map.keys()) |
576 | if (K.starts_with(Prefix: S)) |
577 | Map[K] = false; |
578 | }; |
579 | |
580 | llvm::StringMap<bool> CallerFeatureMapWithoutSVE; |
581 | S.Context.getFunctionFeatureMap(FeatureMap&: CallerFeatureMapWithoutSVE, FD); |
582 | DisableFeatures(CallerFeatureMapWithoutSVE, "sve" ); |
583 | |
584 | // Avoid emitting diagnostics for a function that can never compile. |
585 | if (FnType == SemaARM::ArmStreaming && !CallerFeatureMapWithoutSVE["sme" ]) |
586 | return false; |
587 | |
588 | llvm::StringMap<bool> CallerFeatureMapWithoutSME; |
589 | S.Context.getFunctionFeatureMap(FeatureMap&: CallerFeatureMapWithoutSME, FD); |
590 | DisableFeatures(CallerFeatureMapWithoutSME, "sme" ); |
591 | |
592 | // We know the builtin requires either some combination of SVE flags, or |
593 | // some combination of SME flags, but we need to figure out which part |
594 | // of the required features is satisfied by the target features. |
595 | // |
596 | // For a builtin with target guard 'sve2p1|sme2', if we compile with |
597 | // '+sve2p1,+sme', then we know that it satisfies the 'sve2p1' part if we |
598 | // evaluate the features for '+sve2p1,+sme,+nosme'. |
599 | // |
600 | // Similarly, if we compile with '+sve2,+sme2', then we know it satisfies |
601 | // the 'sme2' part if we evaluate the features for '+sve2,+sme2,+nosve'. |
602 | StringRef BuiltinTargetGuards( |
603 | S.Context.BuiltinInfo.getRequiredFeatures(ID: BuiltinID)); |
604 | bool SatisfiesSVE = Builtin::evaluateRequiredTargetFeatures( |
605 | RequiredFatures: BuiltinTargetGuards, TargetFetureMap: CallerFeatureMapWithoutSME); |
606 | bool SatisfiesSME = Builtin::evaluateRequiredTargetFeatures( |
607 | RequiredFatures: BuiltinTargetGuards, TargetFetureMap: CallerFeatureMapWithoutSVE); |
608 | |
609 | if ((SatisfiesSVE && SatisfiesSME) || |
610 | (SatisfiesSVE && FnType == SemaARM::ArmStreamingCompatible)) |
611 | return false; |
612 | else if (SatisfiesSVE) |
613 | BuiltinType = SemaARM::ArmNonStreaming; |
614 | else if (SatisfiesSME) |
615 | BuiltinType = SemaARM::ArmStreaming; |
616 | else |
617 | // This should be diagnosed by CodeGen |
618 | return false; |
619 | } |
620 | |
621 | if (FnType != SemaARM::ArmNonStreaming && |
622 | BuiltinType == SemaARM::ArmNonStreaming) |
623 | S.Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_attribute_arm_sm_incompat_builtin) |
624 | << TheCall->getSourceRange() << "non-streaming" ; |
625 | else if (FnType != SemaARM::ArmStreaming && |
626 | BuiltinType == SemaARM::ArmStreaming) |
627 | S.Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_attribute_arm_sm_incompat_builtin) |
628 | << TheCall->getSourceRange() << "streaming" ; |
629 | else |
630 | return false; |
631 | |
632 | return true; |
633 | } |
634 | |
635 | static bool hasArmZAState(const FunctionDecl *FD) { |
636 | const auto *T = FD->getType()->getAs<FunctionProtoType>(); |
637 | return (T && FunctionType::getArmZAState(AttrBits: T->getAArch64SMEAttributes()) != |
638 | FunctionType::ARM_None) || |
639 | (FD->hasAttr<ArmNewAttr>() && FD->getAttr<ArmNewAttr>()->isNewZA()); |
640 | } |
641 | |
642 | static bool hasArmZT0State(const FunctionDecl *FD) { |
643 | const auto *T = FD->getType()->getAs<FunctionProtoType>(); |
644 | return (T && FunctionType::getArmZT0State(AttrBits: T->getAArch64SMEAttributes()) != |
645 | FunctionType::ARM_None) || |
646 | (FD->hasAttr<ArmNewAttr>() && FD->getAttr<ArmNewAttr>()->isNewZT0()); |
647 | } |
648 | |
649 | static ArmSMEState getSMEState(unsigned BuiltinID) { |
650 | switch (BuiltinID) { |
651 | default: |
652 | return ArmNoState; |
653 | #define GET_SME_BUILTIN_GET_STATE |
654 | #include "clang/Basic/arm_sme_builtins_za_state.inc" |
655 | #undef GET_SME_BUILTIN_GET_STATE |
656 | } |
657 | } |
658 | |
659 | bool SemaARM::CheckSMEBuiltinFunctionCall(unsigned BuiltinID, |
660 | CallExpr *TheCall) { |
661 | if (const FunctionDecl *FD = SemaRef.getCurFunctionDecl()) { |
662 | std::optional<ArmStreamingType> BuiltinType; |
663 | |
664 | switch (BuiltinID) { |
665 | #define GET_SME_STREAMING_ATTRS |
666 | #include "clang/Basic/arm_sme_streaming_attrs.inc" |
667 | #undef GET_SME_STREAMING_ATTRS |
668 | } |
669 | |
670 | if (BuiltinType && |
671 | checkArmStreamingBuiltin(S&: SemaRef, TheCall, FD, BuiltinType: *BuiltinType, BuiltinID)) |
672 | return true; |
673 | |
674 | if ((getSMEState(BuiltinID) & ArmZAMask) && !hasArmZAState(FD)) |
675 | Diag(Loc: TheCall->getBeginLoc(), |
676 | DiagID: diag::warn_attribute_arm_za_builtin_no_za_state) |
677 | << TheCall->getSourceRange(); |
678 | |
679 | if ((getSMEState(BuiltinID) & ArmZT0Mask) && !hasArmZT0State(FD)) |
680 | Diag(Loc: TheCall->getBeginLoc(), |
681 | DiagID: diag::warn_attribute_arm_zt0_builtin_no_zt0_state) |
682 | << TheCall->getSourceRange(); |
683 | } |
684 | |
685 | // Range check SME intrinsics that take immediate values. |
686 | SmallVector<std::tuple<int, int, int>, 3> ImmChecks; |
687 | |
688 | switch (BuiltinID) { |
689 | default: |
690 | return false; |
691 | #define GET_SME_IMMEDIATE_CHECK |
692 | #include "clang/Basic/arm_sme_sema_rangechecks.inc" |
693 | #undef GET_SME_IMMEDIATE_CHECK |
694 | } |
695 | |
696 | return ParseSVEImmChecks(TheCall, ImmChecks); |
697 | } |
698 | |
699 | bool SemaARM::CheckSVEBuiltinFunctionCall(unsigned BuiltinID, |
700 | CallExpr *TheCall) { |
701 | if (const FunctionDecl *FD = SemaRef.getCurFunctionDecl()) { |
702 | std::optional<ArmStreamingType> BuiltinType; |
703 | |
704 | switch (BuiltinID) { |
705 | #define GET_SVE_STREAMING_ATTRS |
706 | #include "clang/Basic/arm_sve_streaming_attrs.inc" |
707 | #undef GET_SVE_STREAMING_ATTRS |
708 | } |
709 | if (BuiltinType && |
710 | checkArmStreamingBuiltin(S&: SemaRef, TheCall, FD, BuiltinType: *BuiltinType, BuiltinID)) |
711 | return true; |
712 | } |
713 | // Range check SVE intrinsics that take immediate values. |
714 | SmallVector<std::tuple<int, int, int>, 3> ImmChecks; |
715 | |
716 | switch (BuiltinID) { |
717 | default: |
718 | return false; |
719 | #define GET_SVE_IMMEDIATE_CHECK |
720 | #include "clang/Basic/arm_sve_sema_rangechecks.inc" |
721 | #undef GET_SVE_IMMEDIATE_CHECK |
722 | } |
723 | |
724 | return ParseSVEImmChecks(TheCall, ImmChecks); |
725 | } |
726 | |
727 | bool SemaARM::CheckNeonBuiltinFunctionCall(const TargetInfo &TI, |
728 | unsigned BuiltinID, |
729 | CallExpr *TheCall) { |
730 | if (const FunctionDecl *FD = SemaRef.getCurFunctionDecl()) { |
731 | |
732 | switch (BuiltinID) { |
733 | default: |
734 | break; |
735 | #define GET_NEON_BUILTINS |
736 | #define TARGET_BUILTIN(id, ...) case NEON::BI##id: |
737 | #define BUILTIN(id, ...) case NEON::BI##id: |
738 | #include "clang/Basic/arm_neon.inc" |
739 | if (checkArmStreamingBuiltin(S&: SemaRef, TheCall, FD, BuiltinType: ArmNonStreaming, |
740 | BuiltinID)) |
741 | return true; |
742 | break; |
743 | #undef TARGET_BUILTIN |
744 | #undef BUILTIN |
745 | #undef GET_NEON_BUILTINS |
746 | } |
747 | } |
748 | |
749 | llvm::APSInt Result; |
750 | uint64_t mask = 0; |
751 | unsigned TV = 0; |
752 | int PtrArgNum = -1; |
753 | bool HasConstPtr = false; |
754 | switch (BuiltinID) { |
755 | #define GET_NEON_OVERLOAD_CHECK |
756 | #include "clang/Basic/arm_fp16.inc" |
757 | #include "clang/Basic/arm_neon.inc" |
758 | #undef GET_NEON_OVERLOAD_CHECK |
759 | } |
760 | |
761 | // For NEON intrinsics which are overloaded on vector element type, validate |
762 | // the immediate which specifies which variant to emit. |
763 | unsigned ImmArg = TheCall->getNumArgs() - 1; |
764 | if (mask) { |
765 | if (SemaRef.BuiltinConstantArg(TheCall, ArgNum: ImmArg, Result)) |
766 | return true; |
767 | |
768 | TV = Result.getLimitedValue(Limit: 64); |
769 | if ((TV > 63) || (mask & (1ULL << TV)) == 0) |
770 | return Diag(Loc: TheCall->getBeginLoc(), DiagID: diag::err_invalid_neon_type_code) |
771 | << TheCall->getArg(Arg: ImmArg)->getSourceRange(); |
772 | } |
773 | |
774 | if (PtrArgNum >= 0) { |
775 | // Check that pointer arguments have the specified type. |
776 | Expr *Arg = TheCall->getArg(Arg: PtrArgNum); |
777 | if (ImplicitCastExpr *ICE = dyn_cast<ImplicitCastExpr>(Val: Arg)) |
778 | Arg = ICE->getSubExpr(); |
779 | ExprResult RHS = SemaRef.DefaultFunctionArrayLvalueConversion(E: Arg); |
780 | QualType RHSTy = RHS.get()->getType(); |
781 | |
782 | llvm::Triple::ArchType Arch = TI.getTriple().getArch(); |
783 | bool IsPolyUnsigned = Arch == llvm::Triple::aarch64 || |
784 | Arch == llvm::Triple::aarch64_32 || |
785 | Arch == llvm::Triple::aarch64_be; |
786 | bool IsInt64Long = TI.getInt64Type() == TargetInfo::SignedLong; |
787 | QualType EltTy = getNeonEltType(Flags: NeonTypeFlags(TV), Context&: getASTContext(), |
788 | IsPolyUnsigned, IsInt64Long); |
789 | if (HasConstPtr) |
790 | EltTy = EltTy.withConst(); |
791 | QualType LHSTy = getASTContext().getPointerType(T: EltTy); |
792 | Sema::AssignConvertType ConvTy; |
793 | ConvTy = SemaRef.CheckSingleAssignmentConstraints(LHSType: LHSTy, RHS); |
794 | if (RHS.isInvalid()) |
795 | return true; |
796 | if (SemaRef.DiagnoseAssignmentResult(ConvTy, Loc: Arg->getBeginLoc(), DstType: LHSTy, |
797 | SrcType: RHSTy, SrcExpr: RHS.get(), Action: Sema::AA_Assigning)) |
798 | return true; |
799 | } |
800 | |
801 | // For NEON intrinsics which take an immediate value as part of the |
802 | // instruction, range check them here. |
803 | unsigned i = 0, l = 0, u = 0; |
804 | switch (BuiltinID) { |
805 | default: |
806 | return false; |
807 | #define GET_NEON_IMMEDIATE_CHECK |
808 | #include "clang/Basic/arm_fp16.inc" |
809 | #include "clang/Basic/arm_neon.inc" |
810 | #undef GET_NEON_IMMEDIATE_CHECK |
811 | } |
812 | |
813 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: i, Low: l, High: u + l); |
814 | } |
815 | |
816 | bool SemaARM::CheckMVEBuiltinFunctionCall(unsigned BuiltinID, |
817 | CallExpr *TheCall) { |
818 | switch (BuiltinID) { |
819 | default: |
820 | return false; |
821 | #include "clang/Basic/arm_mve_builtin_sema.inc" |
822 | } |
823 | } |
824 | |
825 | bool SemaARM::CheckCDEBuiltinFunctionCall(const TargetInfo &TI, |
826 | unsigned BuiltinID, |
827 | CallExpr *TheCall) { |
828 | bool Err = false; |
829 | switch (BuiltinID) { |
830 | default: |
831 | return false; |
832 | #include "clang/Basic/arm_cde_builtin_sema.inc" |
833 | } |
834 | |
835 | if (Err) |
836 | return true; |
837 | |
838 | return CheckARMCoprocessorImmediate(TI, CoprocArg: TheCall->getArg(Arg: 0), /*WantCDE*/ true); |
839 | } |
840 | |
841 | bool SemaARM::CheckARMCoprocessorImmediate(const TargetInfo &TI, |
842 | const Expr *CoprocArg, |
843 | bool WantCDE) { |
844 | ASTContext &Context = getASTContext(); |
845 | if (SemaRef.isConstantEvaluatedContext()) |
846 | return false; |
847 | |
848 | // We can't check the value of a dependent argument. |
849 | if (CoprocArg->isTypeDependent() || CoprocArg->isValueDependent()) |
850 | return false; |
851 | |
852 | llvm::APSInt CoprocNoAP = *CoprocArg->getIntegerConstantExpr(Ctx: Context); |
853 | int64_t CoprocNo = CoprocNoAP.getExtValue(); |
854 | assert(CoprocNo >= 0 && "Coprocessor immediate must be non-negative" ); |
855 | |
856 | uint32_t CDECoprocMask = TI.getARMCDECoprocMask(); |
857 | bool IsCDECoproc = CoprocNo <= 7 && (CDECoprocMask & (1 << CoprocNo)); |
858 | |
859 | if (IsCDECoproc != WantCDE) |
860 | return Diag(Loc: CoprocArg->getBeginLoc(), DiagID: diag::err_arm_invalid_coproc) |
861 | << (int)CoprocNo << (int)WantCDE << CoprocArg->getSourceRange(); |
862 | |
863 | return false; |
864 | } |
865 | |
866 | bool SemaARM::CheckARMBuiltinExclusiveCall(unsigned BuiltinID, |
867 | CallExpr *TheCall, |
868 | unsigned MaxWidth) { |
869 | assert((BuiltinID == ARM::BI__builtin_arm_ldrex || |
870 | BuiltinID == ARM::BI__builtin_arm_ldaex || |
871 | BuiltinID == ARM::BI__builtin_arm_strex || |
872 | BuiltinID == ARM::BI__builtin_arm_stlex || |
873 | BuiltinID == AArch64::BI__builtin_arm_ldrex || |
874 | BuiltinID == AArch64::BI__builtin_arm_ldaex || |
875 | BuiltinID == AArch64::BI__builtin_arm_strex || |
876 | BuiltinID == AArch64::BI__builtin_arm_stlex) && |
877 | "unexpected ARM builtin" ); |
878 | bool IsLdrex = BuiltinID == ARM::BI__builtin_arm_ldrex || |
879 | BuiltinID == ARM::BI__builtin_arm_ldaex || |
880 | BuiltinID == AArch64::BI__builtin_arm_ldrex || |
881 | BuiltinID == AArch64::BI__builtin_arm_ldaex; |
882 | |
883 | ASTContext &Context = getASTContext(); |
884 | DeclRefExpr *DRE = |
885 | cast<DeclRefExpr>(Val: TheCall->getCallee()->IgnoreParenCasts()); |
886 | |
887 | // Ensure that we have the proper number of arguments. |
888 | if (SemaRef.checkArgCount(Call: TheCall, DesiredArgCount: IsLdrex ? 1 : 2)) |
889 | return true; |
890 | |
891 | // Inspect the pointer argument of the atomic builtin. This should always be |
892 | // a pointer type, whose element is an integral scalar or pointer type. |
893 | // Because it is a pointer type, we don't have to worry about any implicit |
894 | // casts here. |
895 | Expr *PointerArg = TheCall->getArg(Arg: IsLdrex ? 0 : 1); |
896 | ExprResult PointerArgRes = |
897 | SemaRef.DefaultFunctionArrayLvalueConversion(E: PointerArg); |
898 | if (PointerArgRes.isInvalid()) |
899 | return true; |
900 | PointerArg = PointerArgRes.get(); |
901 | |
902 | const PointerType *pointerType = PointerArg->getType()->getAs<PointerType>(); |
903 | if (!pointerType) { |
904 | Diag(Loc: DRE->getBeginLoc(), DiagID: diag::err_atomic_builtin_must_be_pointer) |
905 | << PointerArg->getType() << 0 << PointerArg->getSourceRange(); |
906 | return true; |
907 | } |
908 | |
909 | // ldrex takes a "const volatile T*" and strex takes a "volatile T*". Our next |
910 | // task is to insert the appropriate casts into the AST. First work out just |
911 | // what the appropriate type is. |
912 | QualType ValType = pointerType->getPointeeType(); |
913 | QualType AddrType = ValType.getUnqualifiedType().withVolatile(); |
914 | if (IsLdrex) |
915 | AddrType.addConst(); |
916 | |
917 | // Issue a warning if the cast is dodgy. |
918 | CastKind CastNeeded = CK_NoOp; |
919 | if (!AddrType.isAtLeastAsQualifiedAs(other: ValType)) { |
920 | CastNeeded = CK_BitCast; |
921 | Diag(Loc: DRE->getBeginLoc(), DiagID: diag::ext_typecheck_convert_discards_qualifiers) |
922 | << PointerArg->getType() << Context.getPointerType(T: AddrType) |
923 | << Sema::AA_Passing << PointerArg->getSourceRange(); |
924 | } |
925 | |
926 | // Finally, do the cast and replace the argument with the corrected version. |
927 | AddrType = Context.getPointerType(T: AddrType); |
928 | PointerArgRes = SemaRef.ImpCastExprToType(E: PointerArg, Type: AddrType, CK: CastNeeded); |
929 | if (PointerArgRes.isInvalid()) |
930 | return true; |
931 | PointerArg = PointerArgRes.get(); |
932 | |
933 | TheCall->setArg(Arg: IsLdrex ? 0 : 1, ArgExpr: PointerArg); |
934 | |
935 | // In general, we allow ints, floats and pointers to be loaded and stored. |
936 | if (!ValType->isIntegerType() && !ValType->isAnyPointerType() && |
937 | !ValType->isBlockPointerType() && !ValType->isFloatingType()) { |
938 | Diag(Loc: DRE->getBeginLoc(), DiagID: diag::err_atomic_builtin_must_be_pointer_intfltptr) |
939 | << PointerArg->getType() << 0 << PointerArg->getSourceRange(); |
940 | return true; |
941 | } |
942 | |
943 | // But ARM doesn't have instructions to deal with 128-bit versions. |
944 | if (Context.getTypeSize(T: ValType) > MaxWidth) { |
945 | assert(MaxWidth == 64 && "Diagnostic unexpectedly inaccurate" ); |
946 | Diag(Loc: DRE->getBeginLoc(), DiagID: diag::err_atomic_exclusive_builtin_pointer_size) |
947 | << PointerArg->getType() << PointerArg->getSourceRange(); |
948 | return true; |
949 | } |
950 | |
951 | switch (ValType.getObjCLifetime()) { |
952 | case Qualifiers::OCL_None: |
953 | case Qualifiers::OCL_ExplicitNone: |
954 | // okay |
955 | break; |
956 | |
957 | case Qualifiers::OCL_Weak: |
958 | case Qualifiers::OCL_Strong: |
959 | case Qualifiers::OCL_Autoreleasing: |
960 | Diag(Loc: DRE->getBeginLoc(), DiagID: diag::err_arc_atomic_ownership) |
961 | << ValType << PointerArg->getSourceRange(); |
962 | return true; |
963 | } |
964 | |
965 | if (IsLdrex) { |
966 | TheCall->setType(ValType); |
967 | return false; |
968 | } |
969 | |
970 | // Initialize the argument to be stored. |
971 | ExprResult ValArg = TheCall->getArg(Arg: 0); |
972 | InitializedEntity Entity = InitializedEntity::InitializeParameter( |
973 | Context, Type: ValType, /*consume*/ Consumed: false); |
974 | ValArg = SemaRef.PerformCopyInitialization(Entity, EqualLoc: SourceLocation(), Init: ValArg); |
975 | if (ValArg.isInvalid()) |
976 | return true; |
977 | TheCall->setArg(Arg: 0, ArgExpr: ValArg.get()); |
978 | |
979 | // __builtin_arm_strex always returns an int. It's marked as such in the .def, |
980 | // but the custom checker bypasses all default analysis. |
981 | TheCall->setType(Context.IntTy); |
982 | return false; |
983 | } |
984 | |
985 | bool SemaARM::CheckARMBuiltinFunctionCall(const TargetInfo &TI, |
986 | unsigned BuiltinID, |
987 | CallExpr *TheCall) { |
988 | if (BuiltinID == ARM::BI__builtin_arm_ldrex || |
989 | BuiltinID == ARM::BI__builtin_arm_ldaex || |
990 | BuiltinID == ARM::BI__builtin_arm_strex || |
991 | BuiltinID == ARM::BI__builtin_arm_stlex) { |
992 | return CheckARMBuiltinExclusiveCall(BuiltinID, TheCall, MaxWidth: 64); |
993 | } |
994 | |
995 | if (BuiltinID == ARM::BI__builtin_arm_prefetch) { |
996 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 1, Low: 0, High: 1) || |
997 | SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 2, Low: 0, High: 1); |
998 | } |
999 | |
1000 | if (BuiltinID == ARM::BI__builtin_arm_rsr64 || |
1001 | BuiltinID == ARM::BI__builtin_arm_wsr64) |
1002 | return BuiltinARMSpecialReg(BuiltinID, TheCall, ArgNum: 0, ExpectedFieldNum: 3, AllowName: false); |
1003 | |
1004 | if (BuiltinID == ARM::BI__builtin_arm_rsr || |
1005 | BuiltinID == ARM::BI__builtin_arm_rsrp || |
1006 | BuiltinID == ARM::BI__builtin_arm_wsr || |
1007 | BuiltinID == ARM::BI__builtin_arm_wsrp) |
1008 | return BuiltinARMSpecialReg(BuiltinID, TheCall, ArgNum: 0, ExpectedFieldNum: 5, AllowName: true); |
1009 | |
1010 | if (CheckNeonBuiltinFunctionCall(TI, BuiltinID, TheCall)) |
1011 | return true; |
1012 | if (CheckMVEBuiltinFunctionCall(BuiltinID, TheCall)) |
1013 | return true; |
1014 | if (CheckCDEBuiltinFunctionCall(TI, BuiltinID, TheCall)) |
1015 | return true; |
1016 | |
1017 | // For intrinsics which take an immediate value as part of the instruction, |
1018 | // range check them here. |
1019 | // FIXME: VFP Intrinsics should error if VFP not present. |
1020 | switch (BuiltinID) { |
1021 | default: |
1022 | return false; |
1023 | case ARM::BI__builtin_arm_ssat: |
1024 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 1, Low: 1, High: 32); |
1025 | case ARM::BI__builtin_arm_usat: |
1026 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 1, Low: 0, High: 31); |
1027 | case ARM::BI__builtin_arm_ssat16: |
1028 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 1, Low: 1, High: 16); |
1029 | case ARM::BI__builtin_arm_usat16: |
1030 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 1, Low: 0, High: 15); |
1031 | case ARM::BI__builtin_arm_vcvtr_f: |
1032 | case ARM::BI__builtin_arm_vcvtr_d: |
1033 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 1, Low: 0, High: 1); |
1034 | case ARM::BI__builtin_arm_dmb: |
1035 | case ARM::BI__builtin_arm_dsb: |
1036 | case ARM::BI__builtin_arm_isb: |
1037 | case ARM::BI__builtin_arm_dbg: |
1038 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 0, Low: 0, High: 15); |
1039 | case ARM::BI__builtin_arm_cdp: |
1040 | case ARM::BI__builtin_arm_cdp2: |
1041 | case ARM::BI__builtin_arm_mcr: |
1042 | case ARM::BI__builtin_arm_mcr2: |
1043 | case ARM::BI__builtin_arm_mrc: |
1044 | case ARM::BI__builtin_arm_mrc2: |
1045 | case ARM::BI__builtin_arm_mcrr: |
1046 | case ARM::BI__builtin_arm_mcrr2: |
1047 | case ARM::BI__builtin_arm_mrrc: |
1048 | case ARM::BI__builtin_arm_mrrc2: |
1049 | case ARM::BI__builtin_arm_ldc: |
1050 | case ARM::BI__builtin_arm_ldcl: |
1051 | case ARM::BI__builtin_arm_ldc2: |
1052 | case ARM::BI__builtin_arm_ldc2l: |
1053 | case ARM::BI__builtin_arm_stc: |
1054 | case ARM::BI__builtin_arm_stcl: |
1055 | case ARM::BI__builtin_arm_stc2: |
1056 | case ARM::BI__builtin_arm_stc2l: |
1057 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 0, Low: 0, High: 15) || |
1058 | CheckARMCoprocessorImmediate(TI, CoprocArg: TheCall->getArg(Arg: 0), |
1059 | /*WantCDE*/ false); |
1060 | } |
1061 | } |
1062 | |
1063 | bool SemaARM::CheckAArch64BuiltinFunctionCall(const TargetInfo &TI, |
1064 | unsigned BuiltinID, |
1065 | CallExpr *TheCall) { |
1066 | if (BuiltinID == AArch64::BI__builtin_arm_ldrex || |
1067 | BuiltinID == AArch64::BI__builtin_arm_ldaex || |
1068 | BuiltinID == AArch64::BI__builtin_arm_strex || |
1069 | BuiltinID == AArch64::BI__builtin_arm_stlex) { |
1070 | return CheckARMBuiltinExclusiveCall(BuiltinID, TheCall, MaxWidth: 128); |
1071 | } |
1072 | |
1073 | if (BuiltinID == AArch64::BI__builtin_arm_prefetch) { |
1074 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 1, Low: 0, High: 1) || |
1075 | SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 2, Low: 0, High: 3) || |
1076 | SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 3, Low: 0, High: 1) || |
1077 | SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 4, Low: 0, High: 1); |
1078 | } |
1079 | |
1080 | if (BuiltinID == AArch64::BI__builtin_arm_rsr64 || |
1081 | BuiltinID == AArch64::BI__builtin_arm_wsr64 || |
1082 | BuiltinID == AArch64::BI__builtin_arm_rsr128 || |
1083 | BuiltinID == AArch64::BI__builtin_arm_wsr128) |
1084 | return BuiltinARMSpecialReg(BuiltinID, TheCall, ArgNum: 0, ExpectedFieldNum: 5, AllowName: true); |
1085 | |
1086 | // Memory Tagging Extensions (MTE) Intrinsics |
1087 | if (BuiltinID == AArch64::BI__builtin_arm_irg || |
1088 | BuiltinID == AArch64::BI__builtin_arm_addg || |
1089 | BuiltinID == AArch64::BI__builtin_arm_gmi || |
1090 | BuiltinID == AArch64::BI__builtin_arm_ldg || |
1091 | BuiltinID == AArch64::BI__builtin_arm_stg || |
1092 | BuiltinID == AArch64::BI__builtin_arm_subp) { |
1093 | return BuiltinARMMemoryTaggingCall(BuiltinID, TheCall); |
1094 | } |
1095 | |
1096 | if (BuiltinID == AArch64::BI__builtin_arm_rsr || |
1097 | BuiltinID == AArch64::BI__builtin_arm_rsrp || |
1098 | BuiltinID == AArch64::BI__builtin_arm_wsr || |
1099 | BuiltinID == AArch64::BI__builtin_arm_wsrp) |
1100 | return BuiltinARMSpecialReg(BuiltinID, TheCall, ArgNum: 0, ExpectedFieldNum: 5, AllowName: true); |
1101 | |
1102 | // Only check the valid encoding range. Any constant in this range would be |
1103 | // converted to a register of the form S1_2_C3_C4_5. Let the hardware throw |
1104 | // an exception for incorrect registers. This matches MSVC behavior. |
1105 | if (BuiltinID == AArch64::BI_ReadStatusReg || |
1106 | BuiltinID == AArch64::BI_WriteStatusReg) |
1107 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 0, Low: 0, High: 0x7fff); |
1108 | |
1109 | if (BuiltinID == AArch64::BI__getReg) |
1110 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 0, Low: 0, High: 31); |
1111 | |
1112 | if (BuiltinID == AArch64::BI__break) |
1113 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 0, Low: 0, High: 0xffff); |
1114 | |
1115 | if (BuiltinID == AArch64::BI__hlt) |
1116 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: 0, Low: 0, High: 0xffff); |
1117 | |
1118 | if (CheckNeonBuiltinFunctionCall(TI, BuiltinID, TheCall)) |
1119 | return true; |
1120 | |
1121 | if (CheckSVEBuiltinFunctionCall(BuiltinID, TheCall)) |
1122 | return true; |
1123 | |
1124 | if (CheckSMEBuiltinFunctionCall(BuiltinID, TheCall)) |
1125 | return true; |
1126 | |
1127 | // For intrinsics which take an immediate value as part of the instruction, |
1128 | // range check them here. |
1129 | unsigned i = 0, l = 0, u = 0; |
1130 | switch (BuiltinID) { |
1131 | default: return false; |
1132 | case AArch64::BI__builtin_arm_dmb: |
1133 | case AArch64::BI__builtin_arm_dsb: |
1134 | case AArch64::BI__builtin_arm_isb: l = 0; u = 15; break; |
1135 | case AArch64::BI__builtin_arm_tcancel: l = 0; u = 65535; break; |
1136 | } |
1137 | |
1138 | return SemaRef.BuiltinConstantArgRange(TheCall, ArgNum: i, Low: l, High: u + l); |
1139 | } |
1140 | |
1141 | namespace { |
1142 | struct IntrinToName { |
1143 | uint32_t Id; |
1144 | int32_t FullName; |
1145 | int32_t ShortName; |
1146 | }; |
1147 | } // unnamed namespace |
1148 | |
1149 | static bool BuiltinAliasValid(unsigned BuiltinID, StringRef AliasName, |
1150 | ArrayRef<IntrinToName> Map, |
1151 | const char *IntrinNames) { |
1152 | AliasName.consume_front(Prefix: "__arm_" ); |
1153 | const IntrinToName *It = |
1154 | llvm::lower_bound(Range&: Map, Value&: BuiltinID, C: [](const IntrinToName &L, unsigned Id) { |
1155 | return L.Id < Id; |
1156 | }); |
1157 | if (It == Map.end() || It->Id != BuiltinID) |
1158 | return false; |
1159 | StringRef FullName(&IntrinNames[It->FullName]); |
1160 | if (AliasName == FullName) |
1161 | return true; |
1162 | if (It->ShortName == -1) |
1163 | return false; |
1164 | StringRef ShortName(&IntrinNames[It->ShortName]); |
1165 | return AliasName == ShortName; |
1166 | } |
1167 | |
1168 | bool SemaARM::MveAliasValid(unsigned BuiltinID, StringRef AliasName) { |
1169 | #include "clang/Basic/arm_mve_builtin_aliases.inc" |
1170 | // The included file defines: |
1171 | // - ArrayRef<IntrinToName> Map |
1172 | // - const char IntrinNames[] |
1173 | return BuiltinAliasValid(BuiltinID, AliasName, Map, IntrinNames); |
1174 | } |
1175 | |
1176 | bool SemaARM::CdeAliasValid(unsigned BuiltinID, StringRef AliasName) { |
1177 | #include "clang/Basic/arm_cde_builtin_aliases.inc" |
1178 | return BuiltinAliasValid(BuiltinID, AliasName, Map, IntrinNames); |
1179 | } |
1180 | |
1181 | bool SemaARM::SveAliasValid(unsigned BuiltinID, StringRef AliasName) { |
1182 | if (getASTContext().BuiltinInfo.isAuxBuiltinID(ID: BuiltinID)) |
1183 | BuiltinID = getASTContext().BuiltinInfo.getAuxBuiltinID(ID: BuiltinID); |
1184 | return BuiltinID >= AArch64::FirstSVEBuiltin && |
1185 | BuiltinID <= AArch64::LastSVEBuiltin; |
1186 | } |
1187 | |
1188 | bool SemaARM::SmeAliasValid(unsigned BuiltinID, StringRef AliasName) { |
1189 | if (getASTContext().BuiltinInfo.isAuxBuiltinID(ID: BuiltinID)) |
1190 | BuiltinID = getASTContext().BuiltinInfo.getAuxBuiltinID(ID: BuiltinID); |
1191 | return BuiltinID >= AArch64::FirstSMEBuiltin && |
1192 | BuiltinID <= AArch64::LastSMEBuiltin; |
1193 | } |
1194 | |
1195 | void SemaARM::handleBuiltinAliasAttr(Decl *D, const ParsedAttr &AL) { |
1196 | ASTContext &Context = getASTContext(); |
1197 | if (!AL.isArgIdent(Arg: 0)) { |
1198 | Diag(Loc: AL.getLoc(), DiagID: diag::err_attribute_argument_n_type) |
1199 | << AL << 1 << AANT_ArgumentIdentifier; |
1200 | return; |
1201 | } |
1202 | |
1203 | IdentifierInfo *Ident = AL.getArgAsIdent(Arg: 0)->Ident; |
1204 | unsigned BuiltinID = Ident->getBuiltinID(); |
1205 | StringRef AliasName = cast<FunctionDecl>(Val: D)->getIdentifier()->getName(); |
1206 | |
1207 | bool IsAArch64 = Context.getTargetInfo().getTriple().isAArch64(); |
1208 | if ((IsAArch64 && !SveAliasValid(BuiltinID, AliasName) && |
1209 | !SmeAliasValid(BuiltinID, AliasName)) || |
1210 | (!IsAArch64 && !MveAliasValid(BuiltinID, AliasName) && |
1211 | !CdeAliasValid(BuiltinID, AliasName))) { |
1212 | Diag(Loc: AL.getLoc(), DiagID: diag::err_attribute_arm_builtin_alias); |
1213 | return; |
1214 | } |
1215 | |
1216 | D->addAttr(A: ::new (Context) ArmBuiltinAliasAttr(Context, AL, Ident)); |
1217 | } |
1218 | |
1219 | static bool checkNewAttrMutualExclusion( |
1220 | Sema &S, const ParsedAttr &AL, const FunctionProtoType *FPT, |
1221 | FunctionType::ArmStateValue CurrentState, StringRef StateName) { |
1222 | auto CheckForIncompatibleAttr = |
1223 | [&](FunctionType::ArmStateValue IncompatibleState, |
1224 | StringRef IncompatibleStateName) { |
1225 | if (CurrentState == IncompatibleState) { |
1226 | S.Diag(Loc: AL.getLoc(), DiagID: diag::err_attributes_are_not_compatible) |
1227 | << (std::string("'__arm_new(\"" ) + StateName.str() + "\")'" ) |
1228 | << (std::string("'" ) + IncompatibleStateName.str() + "(\"" + |
1229 | StateName.str() + "\")'" ) |
1230 | << true; |
1231 | AL.setInvalid(); |
1232 | } |
1233 | }; |
1234 | |
1235 | CheckForIncompatibleAttr(FunctionType::ARM_In, "__arm_in" ); |
1236 | CheckForIncompatibleAttr(FunctionType::ARM_Out, "__arm_out" ); |
1237 | CheckForIncompatibleAttr(FunctionType::ARM_InOut, "__arm_inout" ); |
1238 | CheckForIncompatibleAttr(FunctionType::ARM_Preserves, "__arm_preserves" ); |
1239 | return AL.isInvalid(); |
1240 | } |
1241 | |
1242 | void SemaARM::handleNewAttr(Decl *D, const ParsedAttr &AL) { |
1243 | if (!AL.getNumArgs()) { |
1244 | Diag(Loc: AL.getLoc(), DiagID: diag::err_missing_arm_state) << AL; |
1245 | AL.setInvalid(); |
1246 | return; |
1247 | } |
1248 | |
1249 | std::vector<StringRef> NewState; |
1250 | if (const auto *ExistingAttr = D->getAttr<ArmNewAttr>()) { |
1251 | for (StringRef S : ExistingAttr->newArgs()) |
1252 | NewState.push_back(x: S); |
1253 | } |
1254 | |
1255 | bool HasZA = false; |
1256 | bool HasZT0 = false; |
1257 | for (unsigned I = 0, E = AL.getNumArgs(); I != E; ++I) { |
1258 | StringRef StateName; |
1259 | SourceLocation LiteralLoc; |
1260 | if (!SemaRef.checkStringLiteralArgumentAttr(Attr: AL, ArgNum: I, Str&: StateName, ArgLocation: &LiteralLoc)) |
1261 | return; |
1262 | |
1263 | if (StateName == "za" ) |
1264 | HasZA = true; |
1265 | else if (StateName == "zt0" ) |
1266 | HasZT0 = true; |
1267 | else { |
1268 | Diag(Loc: LiteralLoc, DiagID: diag::err_unknown_arm_state) << StateName; |
1269 | AL.setInvalid(); |
1270 | return; |
1271 | } |
1272 | |
1273 | if (!llvm::is_contained(Range&: NewState, Element: StateName)) // Avoid adding duplicates. |
1274 | NewState.push_back(x: StateName); |
1275 | } |
1276 | |
1277 | if (auto *FPT = dyn_cast<FunctionProtoType>(Val: D->getFunctionType())) { |
1278 | FunctionType::ArmStateValue ZAState = |
1279 | FunctionType::getArmZAState(AttrBits: FPT->getAArch64SMEAttributes()); |
1280 | if (HasZA && ZAState != FunctionType::ARM_None && |
1281 | checkNewAttrMutualExclusion(S&: SemaRef, AL, FPT, CurrentState: ZAState, StateName: "za" )) |
1282 | return; |
1283 | FunctionType::ArmStateValue ZT0State = |
1284 | FunctionType::getArmZT0State(AttrBits: FPT->getAArch64SMEAttributes()); |
1285 | if (HasZT0 && ZT0State != FunctionType::ARM_None && |
1286 | checkNewAttrMutualExclusion(S&: SemaRef, AL, FPT, CurrentState: ZT0State, StateName: "zt0" )) |
1287 | return; |
1288 | } |
1289 | |
1290 | D->dropAttr<ArmNewAttr>(); |
1291 | D->addAttr(A: ::new (getASTContext()) ArmNewAttr( |
1292 | getASTContext(), AL, NewState.data(), NewState.size())); |
1293 | } |
1294 | |
1295 | void SemaARM::handleCmseNSEntryAttr(Decl *D, const ParsedAttr &AL) { |
1296 | if (getLangOpts().CPlusPlus && !D->getDeclContext()->isExternCContext()) { |
1297 | Diag(Loc: AL.getLoc(), DiagID: diag::err_attribute_not_clinkage) << AL; |
1298 | return; |
1299 | } |
1300 | |
1301 | const auto *FD = cast<FunctionDecl>(Val: D); |
1302 | if (!FD->isExternallyVisible()) { |
1303 | Diag(Loc: AL.getLoc(), DiagID: diag::warn_attribute_cmse_entry_static); |
1304 | return; |
1305 | } |
1306 | |
1307 | D->addAttr(A: ::new (getASTContext()) CmseNSEntryAttr(getASTContext(), AL)); |
1308 | } |
1309 | |
1310 | void SemaARM::handleInterruptAttr(Decl *D, const ParsedAttr &AL) { |
1311 | // Check the attribute arguments. |
1312 | if (AL.getNumArgs() > 1) { |
1313 | Diag(Loc: AL.getLoc(), DiagID: diag::err_attribute_too_many_arguments) << AL << 1; |
1314 | return; |
1315 | } |
1316 | |
1317 | StringRef Str; |
1318 | SourceLocation ArgLoc; |
1319 | |
1320 | if (AL.getNumArgs() == 0) |
1321 | Str = "" ; |
1322 | else if (!SemaRef.checkStringLiteralArgumentAttr(Attr: AL, ArgNum: 0, Str, ArgLocation: &ArgLoc)) |
1323 | return; |
1324 | |
1325 | ARMInterruptAttr::InterruptType Kind; |
1326 | if (!ARMInterruptAttr::ConvertStrToInterruptType(Val: Str, Out&: Kind)) { |
1327 | Diag(Loc: AL.getLoc(), DiagID: diag::warn_attribute_type_not_supported) |
1328 | << AL << Str << ArgLoc; |
1329 | return; |
1330 | } |
1331 | |
1332 | const TargetInfo &TI = getASTContext().getTargetInfo(); |
1333 | if (TI.hasFeature(Feature: "vfp" )) |
1334 | Diag(Loc: D->getLocation(), DiagID: diag::warn_arm_interrupt_vfp_clobber); |
1335 | |
1336 | D->addAttr(A: ::new (getASTContext()) |
1337 | ARMInterruptAttr(getASTContext(), AL, Kind)); |
1338 | } |
1339 | |
1340 | } // namespace clang |
1341 | |