[clang] 102b410 - [CMSE] Clear padding bits of struct/unions/fp16 passed by value

Momchil Velikov via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 28 09:06:21 PDT 2020


Author: Momchil Velikov
Date: 2020-04-28T17:05:58+01:00
New Revision: 102b4105e3fd568ed2c758ed7e6fd266a819d6db

URL: https://github.com/llvm/llvm-project/commit/102b4105e3fd568ed2c758ed7e6fd266a819d6db
DIFF: https://github.com/llvm/llvm-project/commit/102b4105e3fd568ed2c758ed7e6fd266a819d6db.diff

LOG: [CMSE] Clear padding bits of struct/unions/fp16 passed by value

When passing a value of a struct/union type from secure to non-secure
state (that is returning from a CMSE entry function or passing an
argument to CMSE-non-secure call), there is a potential sensitive
information leak via the padding bits in the structure. It is not
possible in the general case to ensure those bits are cleared by using
Standard C/C++.

This patch makes the compiler emit code to clear such padding
bits. Since type information is lost in LLVM IR, the code generation
is done by Clang.

For each interesting record type, we build a bitmask, in which all the
bits, corresponding to user declared members, are set. Values of
record types are returned by coercing them to an integer. After the
coercion, the coerced value is masked (with bitwise AND) and then
returned by the function. In a similar manner, values of record types
are passed as arguments by coercing them to an array of integers, and
the coerced values themselves are masked.

For union types, we effectively clear only bits, which aren't part of
any member, since we don't know which is the currently active one.
The compiler will issue a warning, whenever a union is passed to
non-secure state.

Values of half-precision floating-point types are passed in the least
significant bits of a 32-bit register (GPR or FPR) with the most
significant bits unspecified. Since this is also a potential leak of
sensitive information, this patch also clears those unspecified bits.

Differential Revision: https://reviews.llvm.org/D76369

Added: 
    clang/test/CodeGen/cmse-clear-arg.c
    clang/test/CodeGen/cmse-clear-fp16.c
    clang/test/CodeGen/cmse-clear-return.c
    clang/test/Sema/arm-cmse-no-diag.c

Modified: 
    clang/include/clang/AST/Decl.h
    clang/include/clang/Basic/DiagnosticSemaKinds.td
    clang/lib/AST/Decl.cpp
    clang/lib/CodeGen/CGCall.cpp
    clang/lib/CodeGen/CodeGenFunction.h
    clang/lib/Sema/SemaDeclAttr.cpp
    clang/lib/Sema/SemaExpr.cpp
    clang/lib/Sema/SemaStmt.cpp
    clang/test/Sema/arm-cmse.c

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/Decl.h b/clang/include/clang/AST/Decl.h
index 2af1189511a3..7db74e0803ce 100644
--- a/clang/include/clang/AST/Decl.h
+++ b/clang/include/clang/AST/Decl.h
@@ -3961,6 +3961,11 @@ class RecordDecl : public TagDecl {
     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.

diff  --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 9c1b6c593857..c0316f5d7834 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -3123,6 +3123,10 @@ def warn_weak_identifier_undeclared : Warning<
 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<

diff  --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp
index 055b9c6d37ba..9c1b99d30e78 100644
--- a/clang/lib/AST/Decl.cpp
+++ b/clang/lib/AST/Decl.cpp
@@ -4410,6 +4410,21 @@ void RecordDecl::setCapturedRecord() {
   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();

diff  --git a/clang/lib/CodeGen/CGCall.cpp b/clang/lib/CodeGen/CGCall.cpp
index e0e895f202c2..7672d95219fa 100644
--- a/clang/lib/CodeGen/CGCall.cpp
+++ b/clang/lib/CodeGen/CGCall.cpp
@@ -16,6 +16,7 @@
 #include "CGBlocks.h"
 #include "CGCXXABI.h"
 #include "CGCleanup.h"
+#include "CGRecordLayout.h"
 #include "CodeGenFunction.h"
 #include "CodeGenModule.h"
 #include "TargetInfo.h"
@@ -2871,6 +2872,213 @@ static llvm::StoreInst *findDominatingStoreToReturnValue(CodeGenFunction &CGF) {
   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) {
@@ -3037,6 +3245,21 @@ void CodeGenFunction::EmitFunctionEpilog(const CGFunctionInfo &FI,
 
   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 {
@@ -4332,8 +4555,25 @@ RValue CodeGenFunction::EmitCall(const CGFunctionInfo &CallInfo,
       } 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;

diff  --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h
index 1af83b837cb7..7ea89eb3f6f9 100644
--- a/clang/lib/CodeGen/CodeGenFunction.h
+++ b/clang/lib/CodeGen/CodeGenFunction.h
@@ -3877,6 +3877,11 @@ class CodeGenFunction : public CodeGenTypeCache {
   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,

diff  --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp
index 29c3b8d8567b..883ab2ad0505 100644
--- a/clang/lib/Sema/SemaDeclAttr.cpp
+++ b/clang/lib/Sema/SemaDeclAttr.cpp
@@ -1998,7 +1998,8 @@ static void handleCmseNSEntryAttr(Sema &S, Decl *D, const ParsedAttr &AL) {
     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;
   }

diff  --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 5a0b67f0cd4a..5b28015dba03 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -6583,6 +6583,18 @@ ExprResult Sema::BuildResolvedCallExpr(Expr *Fn, NamedDecl *NDecl,
   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))

diff  --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp
index 291f7641c910..aa0d89ac09c3 100644
--- a/clang/lib/Sema/SemaStmt.cpp
+++ b/clang/lib/Sema/SemaStmt.cpp
@@ -3631,6 +3631,12 @@ StmtResult Sema::BuildReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
       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;

diff  --git a/clang/test/CodeGen/cmse-clear-arg.c b/clang/test/CodeGen/cmse-clear-arg.c
new file mode 100644
index 000000000000..1447eb61b66e
--- /dev/null
+++ b/clang/test/CodeGen/cmse-clear-arg.c
@@ -0,0 +1,189 @@
+// 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

diff  --git a/clang/test/CodeGen/cmse-clear-fp16.c b/clang/test/CodeGen/cmse-clear-fp16.c
new file mode 100644
index 000000000000..a52190d39c30
--- /dev/null
+++ b/clang/test/CodeGen/cmse-clear-fp16.c
@@ -0,0 +1,59 @@
+// 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]])

diff  --git a/clang/test/CodeGen/cmse-clear-return.c b/clang/test/CodeGen/cmse-clear-return.c
new file mode 100644
index 000000000000..0174e2475ec0
--- /dev/null
+++ b/clang/test/CodeGen/cmse-clear-return.c
@@ -0,0 +1,265 @@
+// 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

diff  --git a/clang/test/Sema/arm-cmse-no-diag.c b/clang/test/Sema/arm-cmse-no-diag.c
new file mode 100644
index 000000000000..0ad8cc3970ac
--- /dev/null
+++ b/clang/test/Sema/arm-cmse-no-diag.c
@@ -0,0 +1,11 @@
+// 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;
+}

diff  --git a/clang/test/Sema/arm-cmse.c b/clang/test/Sema/arm-cmse.c
index 2148cc1aeb96..2a32256aaf09 100644
--- a/clang/test/Sema/arm-cmse.c
+++ b/clang/test/Sema/arm-cmse.c
@@ -28,3 +28,30 @@ void fn0() __attribute__((cmse_nonsecure_entry));
 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}}
+}


        


More information about the cfe-commits mailing list