return cast_or_null<RecordDecl>(TagDecl::getDefinition());
}
+ /// Returns whether this record is a union, or contains (at any nesting level)
+ /// a union member. This is used by CMSE to warn about possible information
+ /// leaks.
+ bool isOrContainsUnion() const;
+
// Iterator access to field members. The field iterator only visits
// the non-static data members of this class, ignoring any static
// data members, functions, constructors, destructors, etc.
def warn_attribute_cmse_entry_static : Warning<
"'cmse_nonsecure_entry' cannot be applied to functions with internal linkage">,
InGroup<IgnoredAttributes>;
+def warn_cmse_nonsecure_union : Warning<
+ "passing union across security boundary via %select{parameter %1|return value}0 "
+ "may leak information">,
+ InGroup<DiagGroup<"cmse-union-leak">>;
def err_attribute_weak_static : Error<
"weak declaration cannot have internal linkage">;
def err_attribute_selectany_non_extern_data : Error<
addAttr(CapturedRecordAttr::CreateImplicit(getASTContext()));
}
+bool RecordDecl::isOrContainsUnion() const {
+ if (isUnion())
+ return true;
+
+ if (const RecordDecl *Def = getDefinition()) {
+ for (const FieldDecl *FD : Def->fields()) {
+ const RecordType *RT = FD->getType()->getAs<RecordType>();
+ if (RT && RT->getDecl()->isOrContainsUnion())
+ return true;
+ }
+ }
+
+ return false;
+}
+
RecordDecl::field_iterator RecordDecl::field_begin() const {
if (hasExternalLexicalStorage() && !hasLoadedFieldsFromExternalStorage())
LoadFieldsFromExternalStorage();
#include "CGBlocks.h"
#include "CGCXXABI.h"
#include "CGCleanup.h"
+#include "CGRecordLayout.h"
#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "TargetInfo.h"
return store;
}
+// Helper functions for EmitCMSEClearRecord
+
+// Set the bits corresponding to a field having width `BitWidth` and located at
+// offset `BitOffset` (from the least significant bit) within a storage unit of
+// `Bits.size()` bytes. Each element of `Bits` corresponds to one target byte.
+// Use little-endian layout, i.e.`Bits[0]` is the LSB.
+static void setBitRange(SmallVectorImpl<uint64_t> &Bits, int BitOffset,
+ int BitWidth, int CharWidth) {
+ assert(CharWidth <= 64);
+ assert(static_cast<unsigned>(BitWidth) <= Bits.size() * CharWidth);
+
+ int Pos = 0;
+ if (BitOffset >= CharWidth) {
+ Pos += BitOffset / CharWidth;
+ BitOffset = BitOffset % CharWidth;
+ }
+
+ const uint64_t Used = (uint64_t(1) << CharWidth) - 1;
+ if (BitOffset + BitWidth >= CharWidth) {
+ Bits[Pos++] |= (Used << BitOffset) & Used;
+ BitWidth -= CharWidth - BitOffset;
+ BitOffset = 0;
+ }
+
+ while (BitWidth >= CharWidth) {
+ Bits[Pos++] = Used;
+ BitWidth -= CharWidth;
+ }
+
+ if (BitWidth > 0)
+ Bits[Pos++] |= (Used >> (CharWidth - BitWidth)) << BitOffset;
+}
+
+// Set the bits corresponding to a field having width `BitWidth` and located at
+// offset `BitOffset` (from the least significant bit) within a storage unit of
+// `StorageSize` bytes, located at `StorageOffset` in `Bits`. Each element of
+// `Bits` corresponds to one target byte. Use target endian layout.
+static void setBitRange(SmallVectorImpl<uint64_t> &Bits, int StorageOffset,
+ int StorageSize, int BitOffset, int BitWidth,
+ int CharWidth, bool BigEndian) {
+
+ SmallVector<uint64_t, 8> TmpBits(StorageSize);
+ setBitRange(TmpBits, BitOffset, BitWidth, CharWidth);
+
+ if (BigEndian)
+ std::reverse(TmpBits.begin(), TmpBits.end());
+
+ for (uint64_t V : TmpBits)
+ Bits[StorageOffset++] |= V;
+}
+
+static void setUsedBits(CodeGenModule &, QualType, int,
+ SmallVectorImpl<uint64_t> &);
+
+// Set the bits in `Bits`, which correspond to the value representations of
+// the actual members of the record type `RTy`. Note that this function does
+// not handle base classes, virtual tables, etc, since they cannot happen in
+// CMSE function arguments or return. The bit mask corresponds to the target
+// memory layout, i.e. it's endian dependent.
+static void setUsedBits(CodeGenModule &CGM, const RecordType *RTy, int Offset,
+ SmallVectorImpl<uint64_t> &Bits) {
+ ASTContext &Context = CGM.getContext();
+ int CharWidth = Context.getCharWidth();
+ const RecordDecl *RD = RTy->getDecl()->getDefinition();
+ const ASTRecordLayout &ASTLayout = Context.getASTRecordLayout(RD);
+ const CGRecordLayout &Layout = CGM.getTypes().getCGRecordLayout(RD);
+
+ int Idx = 0;
+ for (auto I = RD->field_begin(), E = RD->field_end(); I != E; ++I, ++Idx) {
+ const FieldDecl *F = *I;
+
+ if (F->isUnnamedBitfield() || F->isZeroLengthBitField(Context) ||
+ F->getType()->isIncompleteArrayType())
+ continue;
+
+ if (F->isBitField()) {
+ const CGBitFieldInfo &BFI = Layout.getBitFieldInfo(F);
+ setBitRange(Bits, Offset + BFI.StorageOffset.getQuantity(),
+ BFI.StorageSize / CharWidth, BFI.Offset,
+ BFI.Size, CharWidth,
+ CGM.getDataLayout().isBigEndian());
+ continue;
+ }
+
+ setUsedBits(CGM, F->getType(),
+ Offset + ASTLayout.getFieldOffset(Idx) / CharWidth, Bits);
+ }
+}
+
+// Set the bits in `Bits`, which correspond to the value representations of
+// the elements of an array type `ATy`.
+static void setUsedBits(CodeGenModule &CGM, const ConstantArrayType *ATy,
+ int Offset, SmallVectorImpl<uint64_t> &Bits) {
+ const ASTContext &Context = CGM.getContext();
+
+ QualType ETy = Context.getBaseElementType(ATy);
+ int Size = Context.getTypeSizeInChars(ETy).getQuantity();
+ SmallVector<uint64_t, 4> TmpBits(Size);
+ setUsedBits(CGM, ETy, 0, TmpBits);
+
+ for (int I = 0, N = Context.getConstantArrayElementCount(ATy); I < N; ++I) {
+ auto Src = TmpBits.begin();
+ auto Dst = Bits.begin() + Offset + I * Size;
+ for (int J = 0; J < Size; ++J)
+ *Dst++ |= *Src++;
+ }
+}
+
+// Set the bits in `Bits`, which correspond to the value representations of
+// the type `QTy`.
+static void setUsedBits(CodeGenModule &CGM, QualType QTy, int Offset,
+ SmallVectorImpl<uint64_t> &Bits) {
+ if (const auto *RTy = QTy->getAs<RecordType>())
+ return setUsedBits(CGM, RTy, Offset, Bits);
+
+ ASTContext &Context = CGM.getContext();
+ if (const auto *ATy = Context.getAsConstantArrayType(QTy))
+ return setUsedBits(CGM, ATy, Offset, Bits);
+
+ int Size = Context.getTypeSizeInChars(QTy).getQuantity();
+ if (Size <= 0)
+ return;
+
+ std::fill_n(Bits.begin() + Offset, Size,
+ (uint64_t(1) << Context.getCharWidth()) - 1);
+}
+
+static uint64_t buildMultiCharMask(const SmallVectorImpl<uint64_t> &Bits,
+ int Pos, int Size, int CharWidth,
+ bool BigEndian) {
+ assert(Size > 0);
+ uint64_t Mask = 0;
+ if (BigEndian) {
+ for (auto P = Bits.begin() + Pos, E = Bits.begin() + Pos + Size; P != E;
+ ++P)
+ Mask = (Mask << CharWidth) | *P;
+ } else {
+ auto P = Bits.begin() + Pos + Size, End = Bits.begin() + Pos;
+ do
+ Mask = (Mask << CharWidth) | *--P;
+ while (P != End);
+ }
+ return Mask;
+}
+
+// Emit code to clear the bits in a record, which aren't a part of any user
+// declared member, when the record is a function return.
+llvm::Value *CodeGenFunction::EmitCMSEClearRecord(llvm::Value *Src,
+ llvm::IntegerType *ITy,
+ QualType QTy) {
+ assert(Src->getType() == ITy);
+ assert(ITy->getScalarSizeInBits() <= 64);
+
+ const llvm::DataLayout &DataLayout = CGM.getDataLayout();
+ int Size = DataLayout.getTypeStoreSize(ITy);
+ SmallVector<uint64_t, 4> Bits(Size);
+ setUsedBits(CGM, QTy->getAs<RecordType>(), 0, Bits);
+
+ int CharWidth = CGM.getContext().getCharWidth();
+ uint64_t Mask =
+ buildMultiCharMask(Bits, 0, Size, CharWidth, DataLayout.isBigEndian());
+
+ return Builder.CreateAnd(Src, Mask, "cmse.clear");
+}
+
+// Emit code to clear the bits in a record, which aren't a part of any user
+// declared member, when the record is a function argument.
+llvm::Value *CodeGenFunction::EmitCMSEClearRecord(llvm::Value *Src,
+ llvm::ArrayType *ATy,
+ QualType QTy) {
+ const llvm::DataLayout &DataLayout = CGM.getDataLayout();
+ int Size = DataLayout.getTypeStoreSize(ATy);
+ SmallVector<uint64_t, 16> Bits(Size);
+ setUsedBits(CGM, QTy->getAs<RecordType>(), 0, Bits);
+
+ // Clear each element of the LLVM array.
+ int CharWidth = CGM.getContext().getCharWidth();
+ int CharsPerElt =
+ ATy->getArrayElementType()->getScalarSizeInBits() / CharWidth;
+ int MaskIndex = 0;
+ llvm::Value *R = llvm::UndefValue::get(ATy);
+ for (int I = 0, N = ATy->getArrayNumElements(); I != N; ++I) {
+ uint64_t Mask = buildMultiCharMask(Bits, MaskIndex, CharsPerElt, CharWidth,
+ DataLayout.isBigEndian());
+ MaskIndex += CharsPerElt;
+ llvm::Value *T0 = Builder.CreateExtractValue(Src, I);
+ llvm::Value *T1 = Builder.CreateAnd(T0, Mask, "cmse.clear");
+ R = Builder.CreateInsertValue(R, T1, I);
+ }
+
+ return R;
+}
+
+// Emit code to clear the padding bits when returning or passing as an argument
+// a 16-bit floating-point value.
+llvm::Value *CodeGenFunction::EmitCMSEClearFP16(llvm::Value *Src) {
+ llvm::Type *RetTy = Src->getType();
+ assert(RetTy->isFloatTy() ||
+ RetTy->isIntegerTy() && RetTy->getIntegerBitWidth() == 32);
+ if (RetTy->isFloatTy()) {
+ llvm::Value *T0 = Builder.CreateBitCast(Src, Builder.getIntNTy(32));
+ llvm::Value *T1 = Builder.CreateAnd(T0, 0xffff, "cmse.clear");
+ return Builder.CreateBitCast(T1, RetTy);
+ }
+ return Builder.CreateAnd(Src, 0xffff, "cmse.clear");
+}
+
void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI,
bool EmitRetDbgLoc,
SourceLocation EndLoc) {
llvm::Instruction *Ret;
if (RV) {
+ if (CurFuncDecl && CurFuncDecl->hasAttr<CmseNSEntryAttr>()) {
+ // For certain return types, clear padding bits, as they may reveal
+ // sensitive information.
+ const Type *RTy = RetTy.getCanonicalType().getTypePtr();
+ if (RTy->isFloat16Type() || RTy->isHalfType()) {
+ // 16-bit floating-point types are passed in a 32-bit integer or float,
+ // with unspecified upper bits.
+ RV = EmitCMSEClearFP16(RV);
+ } else {
+ // Small struct/union types are passed as integers.
+ auto *ITy = dyn_cast<llvm::IntegerType>(RV->getType());
+ if (ITy != nullptr && isa<RecordType>(RetTy.getCanonicalType()))
+ RV = EmitCMSEClearRecord(RV, ITy, RetTy);
+ }
+ }
EmitReturnValueCheck(RV);
Ret = Builder.CreateRet(RV);
} else {
} else {
// In the simple case, just pass the coerced loaded value.
assert(NumIRArgs == 1);
- IRCallArgs[FirstIRArg] =
- CreateCoercedLoad(Src, ArgInfo.getCoerceToType(), *this);
+ llvm::Value *Load =
+ CreateCoercedLoad(Src, ArgInfo.getCoerceToType(), *this);
+
+ if (CallInfo.isCmseNSCall()) {
+ // For certain parameter types, clear padding bits, as they may reveal
+ // sensitive information.
+ const Type *PTy = I->Ty.getCanonicalType().getTypePtr();
+ // 16-bit floating-point types are passed in a 32-bit integer or
+ // float, with unspecified upper bits.
+ if (PTy->isFloat16Type() || PTy->isHalfType()) {
+ Load = EmitCMSEClearFP16(Load);
+ } else {
+ // Small struct/union types are passed as integer arrays.
+ auto *ATy = dyn_cast<llvm::ArrayType>(Load->getType());
+ if (ATy != nullptr && isa<RecordType>(I->Ty.getCanonicalType()))
+ Load = EmitCMSEClearRecord(Load, ATy, I->Ty);
+ }
+ }
+ IRCallArgs[FirstIRArg] = Load;
}
break;
llvm::Value *EmitARMCDEBuiltinExpr(unsigned BuiltinID, const CallExpr *E,
ReturnValueSlot ReturnValue,
llvm::Triple::ArchType Arch);
+ llvm::Value *EmitCMSEClearRecord(llvm::Value *V, llvm::IntegerType *ITy,
+ QualType RTy);
+ llvm::Value *EmitCMSEClearRecord(llvm::Value *V, llvm::ArrayType *ATy,
+ QualType RTy);
+ llvm::Value *EmitCMSEClearFP16(llvm::Value *V);
llvm::Value *EmitCommonNeonBuiltinExpr(unsigned BuiltinID,
unsigned LLVMIntrinsic,
return;
}
- if (cast<FunctionDecl>(D)->getStorageClass() == SC_Static) {
+ const auto *FD = cast<FunctionDecl>(D);
+ if (!FD->isExternallyVisible()) {
S.Diag(AL.getLoc(), diag::warn_attribute_cmse_entry_static);
return;
}
if (NDecl)
DiagnoseSentinelCalls(NDecl, LParenLoc, Args);
+ // Warn for unions passing across security boundary (CMSE).
+ if (FuncT != nullptr && FuncT->getCmseNSCallAttr()) {
+ for (unsigned i = 0, e = Args.size(); i != e; i++) {
+ if (const auto *RT =
+ dyn_cast<RecordType>(Args[i]->getType().getCanonicalType())) {
+ if (RT->getDecl()->isOrContainsUnion())
+ Diag(Args[i]->getBeginLoc(), diag::warn_cmse_nonsecure_union)
+ << 0 << i;
+ }
+ }
+ }
+
// Do special checking on direct calls to functions.
if (FDecl) {
if (CheckFunctionCall(FDecl, TheCall, Proto))
if (isa<CXXBoolLiteralExpr>(RetValExp))
Diag(ReturnLoc, diag::warn_main_returns_bool_literal)
<< RetValExp->getSourceRange();
+ if (FD->hasAttr<CmseNSEntryAttr>() && RetValExp) {
+ if (const auto *RT = dyn_cast<RecordType>(FnRetType.getCanonicalType())) {
+ if (RT->getDecl()->isOrContainsUnion())
+ Diag(RetValExp->getBeginLoc(), diag::warn_cmse_nonsecure_union) << 1;
+ }
+ }
} else if (ObjCMethodDecl *MD = getCurMethodDecl()) {
FnRetType = MD->getReturnType();
isObjCMethod = true;
--- /dev/null
+// RUN: %clang_cc1 -triple thumbv8m.main -O0 -mcmse -S -emit-llvm %s -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-LE,CHECK-SOFTFP
+// RUN: %clang_cc1 -triple thumbebv8m.main -O0 -mcmse -S -emit-llvm %s -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-BE,CHECK-SOFTFP
+// RUN: %clang_cc1 -triple thumbv8m.main -O2 -mcmse -S -emit-llvm %s -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-LE,CHECK-SOFTFP
+// RUN: %clang_cc1 -triple thumbebv8m.main -O2 -mcmse -S -emit-llvm %s -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-BE,CHECK-SOFTFP
+// RUN: %clang_cc1 -triple thumbv8m.main -O0 -mcmse -mfloat-abi hard \
+// RUN: -S -emit-llvm %s -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-LE,CHECK-HARDFP
+
+// We don't really need to repeat *all* the test cases from cmse-clear-return.c
+// as it won't increase test coverage.
+
+// : Memory layout | Mask
+// LE: .......1 ........ ........ ........ | 0x00000001/1
+// BE: 1....... ........ ........ ........ | 0x80000000/-2147483648
+typedef struct T0 {
+ int a : 1, : 31;
+} T0;
+
+void __attribute__((cmse_nonsecure_call)) (*g0)(T0);
+
+T0 t0;
+void f0() { g0(t0); }
+// CHECK: define {{.*}} @f0()
+// CHECK-LE: %[[V0:.*]] = and i32 {{.*}}, 1
+// CHECK-BE: %[[V0:.*]] = and i32 {{.*}}, -2147483648
+// CHECK: %[[V1:.*]] = insertvalue [1 x i32] undef, i32 %[[V0]], 0
+// CHECK: call {{.*}} void %0([1 x i32] %[[V1]])
+
+// LE: 11111111 111111.. 11111111 11111111 0xfffffcff/-769
+// BE: 11111111 ..111111 11111111 11111111 0xff3fffff/-12582913
+typedef struct T8 {
+ struct T80 {
+ char a;
+ char : 2, b : 6;
+ } a;
+ short b;
+} T8;
+
+T8 t8;
+void __attribute__((cmse_nonsecure_call)) (*g8)(T8);
+void f8() { g8(t8); }
+// CHECK: define {{.*}} @f8()
+// CHECK-LE: %[[V0:.*]] = and i32 {{.*}}, -769
+// CHECK-BE: %[[V0:.*]] = and i32 {{.*}}, -12582913
+// CHECK: %[[V1:.*]] = insertvalue [1 x i32] undef, i32 %[[V0]], 0
+// CHECK: call {{.*}} void %0([1 x i32] %[[V1]])
+
+// LE(0): 11111111 ........ 11111111 11111111 0xffff00ff/-65281
+// LE(4): ...111.. 11111... 11111111 .....111 0x7fff81c/134215708
+// BE(0): 11111111 ........ 11111111 11111111 0xff00ffff/-16711681
+// BE(4): ..111... ...11111 11111111 111..... 0x381fffe0/941621216
+typedef struct T15 {
+ char a;
+ short b;
+ int : 2, c : 3, : 6, d : 16;
+} T15;
+
+T15 t15;
+
+void __attribute__((cmse_nonsecure_call)) (*g15_0)(T15);
+void f15_0() {
+ g15_0(t15);
+}
+// CHECK: define {{.*}}@f15_0()
+// CHECK: %[[FN:.*]] = load {{.*}} @g15_0
+// CHECK-LE: %cmse.clear = and i32 {{.*}}, -65281
+// CHECK-BE: %cmse.clear = and i32 {{.*}}, -16711681
+// CHECK: %[[R0:.*]] = insertvalue [2 x i32] undef, i32 %cmse.clear, 0
+// CHECK-LE: %cmse.clear1 = and i32 {{.*}}, 134215708
+// CHECK-BE: %cmse.clear1 = and i32 {{.*}}, 941621216
+// CHECK: %[[R1:.*]] = insertvalue [2 x i32] %[[R0]], i32 %cmse.clear1, 1
+// CHECK: call {{.*}} void %[[FN]]([2 x i32] %[[R1]])
+
+void __attribute__((cmse_nonsecure_call)) (*g15_1)(int, int, int, T15);
+void f15_1() {
+ g15_1(0, 1, 2, t15);
+}
+// CHECK: define {{.*}}@f15_1()
+// CHECK: %[[FN:.*]] = load {{.*}} @g15_1
+// CHECK-LE: %cmse.clear = and i32 {{.*}}, -65281
+// CHECK-BE: %cmse.clear = and i32 {{.*}}, -16711681
+// CHECK: %[[R0:.*]] = insertvalue [2 x i32] undef, i32 %cmse.clear, 0
+// CHECK-LE: %cmse.clear1 = and i32 {{.*}}, 134215708
+// CHECK-BE: %cmse.clear1 = and i32 {{.*}}, 941621216
+// CHECK: %[[R1:.*]] = insertvalue [2 x i32] %[[R0]], i32 %cmse.clear1, 1
+// CHECK: call {{.*}} void %[[FN]](i32 0, i32 1, i32 2, [2 x i32] %[[R1]])
+
+// LE: 11111111 ........ 11111111 11111111 1111.... ...11111 ........ .111111.
+// LE: 0xff00fffff01f007e/9079291968726434047
+// BE: 11111111 ........ 11111111 11111111 ....1111 11111... ........ .111111.
+// BE: 0xff00ffff0ff8007e/-71776123088273282
+
+typedef struct T16 {
+ char a;
+ short b;
+ long long : 4, c : 9, : 12, d : 6;
+} T16;
+
+T16 t16;
+
+void __attribute__((cmse_nonsecure_call)) (*g16_0)(T16);
+void f16_0() {
+ g16_0(t16);
+}
+// CHECK: define {{.*}} @f16_0()
+// CHECK: %[[FN:.*]] = load {{.*}} @g16_0
+// CHECK-LE: %cmse.clear = and i64 {{.*}}, 9079291968726434047
+// CHECK-BE: %cmse.clear = and i64 {{.*}}, -71776123088273282
+// CHECK: %[[R:.*]] = insertvalue [1 x i64] undef, i64 %cmse.clear, 0
+// CHECK: call {{.*}} void %0([1 x i64] %[[R]])
+
+
+// LE0: 1111..11 .......1 1111..11 .......1 1111..11 .......1 1111..11 .......1
+// LE4: 1111..11 .......1 1111..11 .......1 11111111 11111111 11111111 ........
+// LE : 0x01f301f3/32702963 * 3 + 0x00ffffff/16777215
+// BE0: 11..1111 1....... 11..1111 1....... 11..1111 1....... 11..1111 1.......
+// BE4: 11..1111 1....... 11..1111 1....... 11111111 11111111 11111111 ........
+// BE : 0xcf80cf80/-813641856 * 3 + 0xffffff00/-256
+
+typedef struct T18 {
+ struct T180 {
+ short a : 2;
+ short : 2, b : 5;
+ } a[2][3];
+ char b[3];
+ char c[];
+} T18;
+
+T18 t18;
+
+void __attribute__((cmse_nonsecure_call)) (*g18)(T18);
+void f18() {
+ g18(t18);
+}
+// CHECK: define {{.*}} @f18()
+// CHECK: %[[FN:.*]] = load {{.*}} @g18
+// CHECK-LE: %cmse.clear = and i32 {{.*}}, 32702963
+// CHECK-BE: %cmse.clear = and i32 {{.*}}, -813641856
+// CHECK: %[[R0:.*]] = insertvalue [4 x i32] undef, i32 %cmse.clear, 0
+// CHECK-LE: %cmse.clear1 = and i32 {{.*}}, 32702963
+// CHECK-BE: %cmse.clear1 = and i32 {{.*}}, -813641856
+// CHECK: %[[R1:.*]] = insertvalue [4 x i32] %[[R0]], i32 %cmse.clear1, 1
+// CHECK-LE: %cmse.clear2 = and i32 {{.*}}, 32702963
+// CHECK-BE: %cmse.clear2 = and i32 {{.*}}, -813641856
+// CHECK: %[[R2:.*]] = insertvalue [4 x i32] %[[R1]], i32 %cmse.clear2, 2
+// CHECK-LE: %cmse.clear3 = and i32 {{.*}}, 16777215
+// CHECK-BE: %cmse.clear3 = and i32 {{.*}}, -256
+// CHECK: %[[R3:.*]] = insertvalue [4 x i32] %[[R2]], i32 %cmse.clear3, 3
+// CHECK: call {{.*}} void %[[FN]]([4 x i32] %[[R3]])
+
+// LE: 11111111 11111111 ..111... ..111... 0x3838ffff/943259647
+// BE: 11111111 11111111 ...111.. ...111.. 0xffff1c1c/-58340
+typedef union T19 {
+ short a;
+ struct T190 {
+ char : 3, a : 3;
+ } b[4];
+} T19;
+
+T19 t19;
+void __attribute__((cmse_nonsecure_call)) (*g19)(T19);
+void f19() {
+ g19(t19);
+}
+// CHECK: define {{.*}} @f19()
+// CHECK: %[[FN:.*]] = load {{.*}} @g19
+// CHECK-LE: %cmse.clear = and i32 {{.*}}, 943259647
+// CHECK-BE: %cmse.clear = and i32 {{.*}}, -58340
+// CHECK: %[[R:.*]] = insertvalue [1 x i32] undef, i32 %cmse.clear, 0
+// CHECK: call {{.*}} void %[[FN]]([1 x i32] %[[R]])
+
+
+typedef struct T20 {
+ float a[2];
+} T20;
+
+T20 t20;
+void __attribute__((cmse_nonsecure_call)) (*g20)(T20);
+void f20() {
+ g20(t20);
+}
+// CHECK: define {{.*}} @f20()
+// CHECK: %[[FN:.*]] = load {{.*}} @g20
+// CHECK-SOFTFP: call arm_aapcscc void %[[FN]]([2 x i32]
+// CHECK-HARDFP: call arm_aapcs_vfpcc void %[[FN]](%struct.T20
--- /dev/null
+// RUN: %clang_cc1 -triple thumbv8m.main -O0 -mcmse -S -emit-llvm \
+// RUN: -fallow-half-arguments-and-returns %s -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-NOPT-SOFT
+// RUN: %clang_cc1 -triple thumbv8m.main -O2 -mcmse -S -emit-llvm \
+// RUN: -fallow-half-arguments-and-returns %s -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-OPT-SOFT
+// RUN: %clang_cc1 -triple thumbv8m.main -O0 -mcmse -S -emit-llvm \
+// RUN: -fallow-half-arguments-and-returns -mfloat-abi hard %s -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-NOPT-HARD
+// RUN: %clang_cc1 -triple thumbv8m.main -O2 -mcmse -S -emit-llvm \
+// RUN: -fallow-half-arguments-and-returns -mfloat-abi hard %s -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-OPT-HARD
+
+__fp16 g0();
+__attribute__((cmse_nonsecure_entry)) __fp16 f0() {
+ return g0();
+}
+// CHECK: define {{.*}}@f0()
+
+// CHECK-NOPT-SOFT: %[[V0:.*]] = load i32
+// CHECK-NOPT-SOFT: %[[V1:.*]] = and i32 %[[V0]], 65535
+// CHECK-NOPT-SOFT: ret i32 %[[V1]]
+
+// CHECK-OPT-SOFT: %[[V0:.*]] = tail call {{.*}} @g0
+// CHECK-OPT-SOFT: %[[V1:.*]] = and i32 %[[V0]], 65535
+// CHECK-OPT-SOFT: ret i32 %[[V1]]
+
+// CHECK-NOPT-HARD: %[[V0:.*]] = bitcast float {{.*}} to i32
+// CHECK-NOPT-HARD: %[[V1:.*]] = and i32 %[[V0]], 65535
+// CHECK-NOPT-HARD: %[[V2:.*]] = bitcast i32 %[[V1]] to float
+// CHECK-NOPT-HARD: ret float %[[V2]]
+
+// CHECK-OPT-HARD: %[[V0:.*]] = bitcast float {{.*}} to i32
+// CHECK-OPT-HARD: %[[V1:.*]] = and i32 %[[V0]], 65535
+// CHECK-OPT-HARD: %[[V2:.*]] = bitcast i32 %[[V1]] to float
+// CHECK-OPT-HARD: ret float %[[V2]]
+
+void __attribute__((cmse_nonsecure_call)) (*g1)(__fp16);
+__fp16 x;
+void f1() {
+ g1(x);
+}
+// CHECK: define {{.*}}@f1()
+
+// CHECK-NOPT-SOFT: %[[V0:.*]] = load i32
+// CHECK-NOPT-SOFT: %[[V1:.*]] = and i32 %[[V0]], 65535
+// CHECK-NOPT-SOFT: call {{.*}} void {{.*}}(i32 %[[V1]])
+
+// CHECK-OPT-SOFT: %[[V1:.*]] = zext i16 {{.*}} to i32
+// CHECK-OPT-SOFT: call {{.*}} void {{.*}}(i32 %[[V1]])
+
+// CHECK-NOPT-HARD: %[[V0:.*]] = bitcast float {{.*}} to i32
+// CHECK-NOPT-HARD: %[[V1:.*]] = and i32 %[[V0]], 65535
+// CHECK-NOPT-HARD: %[[V2:.*]] = bitcast i32 %[[V1]] to float
+// CHECK-NOPT-HARD: call {{.*}}(float %[[V2]])
+
+// CHECK-OPT-HARD: %[[V0:.*]] = zext i16 {{.*}} to i32
+// CHECK-OPT-HARD: %[[V1:.*]] = bitcast i32 %[[V0]] to float
+// CHECK-OPT-HARD: call {{.*}}(float %[[V1]])
--- /dev/null
+// RUN: %clang_cc1 -triple thumbv8m.main -O0 -mcmse -S -emit-llvm %s -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-LE,CHECK-LE-NOPT,CHECK-SOFT
+// RUN: %clang_cc1 -triple thumbebv8m.main -O0 -mcmse -S -emit-llvm %s -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-BE,CHECK-BE-NOPT,CHECK-SOFT
+// RUN: %clang_cc1 -triple thumbv8m.main -O2 -mcmse -S -emit-llvm %s -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-LE,CHECK-LE-OPT,CHECK-SOFT
+// RUN: %clang_cc1 -triple thumbebv8m.main -O2 -mcmse -S -emit-llvm %s -o - | \
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-BE,CHECK-BE-OPT,CHECK-SOFT
+// RUN: %clang_cc1 -triple thumbv8m.main -O0 -mcmse -S -emit-llvm %s -o - \
+// RUN: -mfloat-abi hard | \
+// RUN: FileCheck %s --check-prefixes=CHECK,CHECK-LE,CHECK-LE-NOPT,CHECK-HARD
+
+
+// : Memory layout | Mask
+// LE: .......1 ........ ........ ........ | 0x00000001/1
+// BE: 1....... ........ ........ ........ | 0x80000000/-2147483648
+typedef struct T0 {
+ int a : 1, : 31;
+} T0;
+
+T0 t0;
+__attribute__((cmse_nonsecure_entry)) T0 f0() { return t0; }
+// CHECK: define {{.*}} @f0()
+// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 1
+// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, -2147483648
+// CHECK: ret i32 %[[R]]
+
+// LE: ......1. ........ ........ ........ 0x00000002/2
+// BE: .1...... ........ ........ ........ 0x40000000/1073741824
+typedef struct T1 {
+ int : 1, a : 1, : 30;
+} T1;
+
+T1 t1;
+__attribute__((cmse_nonsecure_entry)) T1 f1() { return t1; }
+// CHECK: define {{.*}} @f1()
+// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 2
+// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, 1073741824
+// CHECK: ret i32 %[[R]]
+
+// LE: ........ .......1 ........ ........ 0x00000100/256
+// BE: ........ 1....... ........ ........ 0x00800000/8388608
+typedef struct T2 {
+ int : 8, a : 1, : 23;
+} T2;
+
+T2 t2;
+__attribute__((cmse_nonsecure_entry)) T2 f2() { return t2; }
+// CHECK: define {{.*}} @f2()
+// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 256
+// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, 8388608
+// CHECK: ret i32 %[[R]]
+
+// LE: ........ .....1.. ........ ........ 0x00000400/1024
+// BE: ........ ..1..... ........ ........ 0x00200000/2097152
+typedef struct T3 {
+ int : 10, a : 1;
+} T3;
+
+T3 t3;
+__attribute__((cmse_nonsecure_entry)) T3 f3() { return t3; }
+// CHECK: define {{.*}} @f3()
+// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 1024
+// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, 2097152
+// CHECK: ret i32 %[[R]]
+
+// LE: 11111111 ........ ........ ........ 0x000000ff/255
+// BE: 11111111 ........ ........ ........ 0xff000000/-16777216
+typedef struct T4 {
+ int a : 8, : 24;
+} T4;
+
+T4 t4;
+__attribute__((cmse_nonsecure_entry)) T4 f4() { return t4; }
+// CHECK: define {{.*}} @f4()
+// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 255
+// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, -16777216
+// CHECK: ret i32 %[[R]]
+
+// LE: 1111111. .......1 ........ ........ 0x000001fe/510
+// BE: .1111111 1....... ........ ........ 0x7f800000/2139095040
+typedef struct T5 {
+ int : 1, a : 8, : 23;
+} T5;
+
+T5 t5;
+__attribute__((cmse_nonsecure_entry)) T5 f5() { return t5; }
+// CHECK: define {{.*}} @f5()
+// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 510
+// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, 2139095040
+// CHECK: ret i32 %[[R]]
+
+// LE: 1111111. 11111111 ........ ........ 0x0000fffe/65534
+// BE: .1111111 11111111 ........ ........ 0x7fff0000/2147418112
+typedef struct T6 {
+ int : 1, a : 15, : 16;
+} T6;
+
+T6 t6;
+__attribute__((cmse_nonsecure_entry)) T6 f6() { return t6; }
+// CHECK: define {{.*}} @f6()
+// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 65534
+// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, 2147418112
+// CHECK: ret i32 %[[R]]
+
+// LE: 1111111. 11111111 .......1 ........ 0x0001fffe/131070
+// BE: .1111111 11111111 1....... ........ 0x7fff8000/2147450880
+typedef struct T7 {
+ int : 1, a : 16, : 15;
+} T7;
+
+T7 t7;
+__attribute__((cmse_nonsecure_entry)) T7 f7() { return t7; }
+// CHECK: define {{.*}} @f7()
+// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 131070
+// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, 2147450880
+// CHECK: ret i32 %[[R]]
+
+// LE: 11111111 111111.. 11111111 11111111 0xfffffcff/-769
+// BE: 11111111 ..111111 11111111 11111111 0xff3fffff/-12582913
+typedef struct T8 {
+ struct T80 {
+ char a;
+ char : 2, b : 6;
+ } a;
+ short b;
+} T8;
+
+T8 t8;
+__attribute__((cmse_nonsecure_entry)) T8 f8() { return t8; }
+// CHECK: define {{.*}} @f8()
+// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, -769
+// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, -12582913
+// CHECK: ret i32 %[[R]]
+
+// LE: ......11 ..111111 ...11111 ........ 0x001f3f03/2047747
+// BE: 11...... 111111.. 11111... ........ 0xc0fcf800/-1057163264
+typedef struct T9 {
+ struct T90 {
+ char a : 2;
+ char : 0;
+ short b : 6;
+ } a;
+ int b : 5;
+} T9;
+
+T9 t9;
+__attribute__((cmse_nonsecure_entry)) T9 f9() { return t9; }
+// CHECK: define {{.*}} @f9()
+// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 2047747
+// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, -1057163264
+// CHECK: ret i32 %[[R]]
+
+T9 f91() { return t9; }
+// CHECK: define {{.*}} @f91()
+// CHECK: %[[R:.*]] = load i32
+// CHECK: ret i32 %[[R]]
+
+// LE: 11111111 ........ 11111111 11111111 0xffff00ff/-65281
+// BE: 11111111 ........ 11111111 11111111 0xff00ffff/16711681
+typedef struct T10 {
+ char a;
+ short b;
+} T10;
+
+T10 t10;
+__attribute__((cmse_nonsecure_entry)) T10 f10() { return t10; }
+// CHECK: define {{.*}} @f10()
+// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, -65281
+// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, -16711681
+// CHECK: ret i32 %[[R]]
+
+// LE: 11111111 11111111 11111111 ........ 0x00ffffff/16777215
+// BE: 11111111 11111111 11111111 ........ 0xffffff00/-256
+typedef struct T11 {
+ short a;
+ char b;
+} T11;
+
+T11 t11;
+__attribute__((cmse_nonsecure_entry)) T11 f11() { return t11; }
+// CHECK: define {{.*}} @f11()
+// CHECK-LE: %[[R:.*]] = and i32 %{{.*}}, 16777215
+// CHECK-BE: %[[R:.*]] = and i32 %{{.*}}, -256
+// CHECK: ret i32 %[[R]]
+
+// LE: 11111111 11111111 11111111 ........ 0x00ffffff/16777215
+// BE: 11111111 11111111 11111111 ........ 0xffffff00/-256
+typedef struct T12 {
+ char a[3];
+} T12;
+
+T12 t12;
+__attribute__((cmse_nonsecure_entry)) T12 f12() { return t12; }
+// CHECK: define {{.*}} @f12()
+// CHECK-LE-OPT: %[[V0:.*]] = load i24, i24* bitcast (%struct.T12* @t12
+// CHECK-LE-OPT: %[[R:.*]] = zext i24 %[[V0]] to i32
+// CHECK-LE-NOPT: %[[R:.*]] = and i32 %{{.*}}, 16777215
+
+// CHECK-BE-OPT: %[[V0:.*]] = load i24, i24* bitcast (%struct.T12* @t12
+// CHECK-BE-OPT: %[[V1:.*]] = zext i24 %[[V0]] to i32
+// CHECK-BE-OPT: %[[R:.*]] = shl nuw i32 %[[V1]], 8
+// CHECK: ret i32 %[[R]]
+
+// LE: 11111111 11111111 11111111 ........ 0x00ffffff/16777215
+// BE: 11111111 11111111 11111111 ........ 0xffffff00/-256
+typedef struct __attribute__((packed)) T13 {
+ char a;
+ short b;
+} T13;
+
+T13 t13;
+__attribute__((cmse_nonsecure_entry)) T13 f13() { return t13; }
+// CHECK: define {{.*}} @f13()
+// CHECK-LE-OPT: %[[V0:.*]] = load i24, i24* bitcast (%struct.T13* @t13
+// CHECK-LE-OPT: %[[R:.*]] = zext i24 %[[V0]] to i32
+// CHECK-LE-NOPT: %[[R:.*]] = and i32 %{{.*}}, 16777215
+
+// CHECK-BE-OPT: %[[V0:.*]] = load i24, i24* bitcast (%struct.T13* @t13
+// CHECK-BE-OPT: %[[V1:.*]] = zext i24 %[[V0]] to i32
+// CHECK-BE-OPT: %[[R:.*]] = shl nuw i32 %[[V1]], 8
+// CHECK: ret i32 %[[R]]
+
+typedef struct __attribute__((packed)) T14 {
+ short a;
+ short b;
+} T14;
+
+T14 t14;
+__attribute__((cmse_nonsecure_entry)) T14 f14() { return t14; }
+// CHECK: define {{.*}} @f14()
+// CHECK: %[[R:.*]] = load
+// CHECK: ret i32 %[[R]]
+
+// LE: 1111..11 1111..11 11111111 11111111 0xfffff3f3/-3085
+// BE: 11..1111 11..1111 11111111 11111111 0xcfcfffff/-808452097
+typedef struct T17 {
+ struct T170 {
+ char a : 2;
+ char : 2, b : 4;
+ } a[2];
+ char b[2];
+ char c[];
+} T17;
+
+T17 t17;
+__attribute__((cmse_nonsecure_entry)) T17 f17() { return t17; }
+// CHECK: define {{.*}} @f17()
+// CHECK-LE: %[[R:.*]] = and i32 {{.*}}, -3085
+// CHECK-BE: %[[R:.*]] = and i32 {{.*}}, -808452097
+// CHECK: ret i32 %[[R]]
+
+typedef struct T21 {
+ float a;
+} T21;
+
+T21 t21;
+__attribute__((cmse_nonsecure_entry)) T21 f21() { return t21; }
+// CHECK: define {{.*}} @f21()
+// CHECK-SOFT: ret i32
+// CHECK-HARD: ret %struct.T21
+
+__attribute__((cmse_nonsecure_entry)) float f22() { return 1.0f; }
+// CHECK: define {{.*}} @f22()
+// CHECK: ret float
--- /dev/null
+// RUN: %clang_cc1 -triple thumbv8m.base-none-eabi -mcmse -verify -Wno-cmse-union-leak %s
+// expected-no-diagnostics
+
+union U { unsigned n; char b[4]; } u;
+
+void (*fn2)(int, union U) __attribute__((cmse_nonsecure_call));
+
+union U xyzzy() __attribute__((cmse_nonsecure_entry)) {
+ fn2(0, u);
+ return u;
+}
void fn1() __attribute__((cmse_nonsecure_entry(1))); // expected-error {{'cmse_nonsecure_entry' attribute takes no arguments}}
typedef void (*fn2_t)() __attribute__((cmse_nonsecure_call("abc"))); // expected-error {{'cmse_nonsecure_call' attribute takes no argument}}
+
+union U { unsigned n; char b[4]; } u;
+
+union U xyzzy() __attribute__((cmse_nonsecure_entry)) {
+ return u; // expected-warning {{passing union across security boundary via return value may leak information}}
+}
+
+void (*fn2)(int, union U) __attribute__((cmse_nonsecure_call));
+void (*fn3)() __attribute__ ((cmse_nonsecure_call));
+
+struct S {
+ int t;
+ union {
+ char b[4];
+ unsigned w;
+ };
+} s;
+
+void qux() {
+ fn2(1,
+ u); // expected-warning {{passing union across security boundary via parameter 1 may leak information}}
+
+ fn3(
+ u, // expected-warning {{passing union across security boundary via parameter 0 may leak information}}
+ 1,
+ s); // expected-warning {{passing union across security boundary via parameter 2 may leak information}}
+}