Index: clang/lib/CodeGen/CGCall.cpp =================================================================== --- clang/lib/CodeGen/CGCall.cpp +++ 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" @@ -2870,6 +2871,212 @@ 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. Use little-endian layout, i.e.`Bits[0]` is the LSB. +static void setBitRange(SmallVectorImpl &Bits, int BitOffset, + int BitWidth, int CharWidth) { + assert(CharWidth <= 64); + assert(static_cast(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`. Use target endian +// layout. +static void setBitRange(SmallVectorImpl &Bits, int StorageOffset, + int StorageSize, int BitOffset, int BitWidth, + int CharWidth, bool BigEndian) { + + SmallVector 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 &); + +// 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 &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, + F->getBitWidthValue(Context), 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 &Bits) { + const ASTContext &Context = CGM.getContext(); + + QualType ETy = Context.getBaseElementType(ATy); + int Size = Context.getTypeSizeInChars(ETy).getQuantity(); + SmallVector 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 &Bits) { + if (const auto *RTy = QTy->getAs()) + 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 &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 Bits(Size); + setUsedBits(CGM, QTy->getAs(), 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 Bits(Size); + setUsedBits(CGM, QTy->getAs(), 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) { @@ -3036,6 +3243,21 @@ llvm::Instruction *Ret; if (RV) { + if (CurFuncDecl && CurFuncDecl->hasAttr()) { + // 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(RV->getType()); + if (ITy != nullptr && isa(RetTy.getCanonicalType())) + RV = EmitCMSEClearRecord(RV, ITy, RetTy); + } + } EmitReturnValueCheck(RV); Ret = Builder.CreateRet(RV); } else { @@ -4331,8 +4553,25 @@ } 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(Load->getType()); + if (ATy != nullptr && isa(I->Ty.getCanonicalType())) + Load = EmitCMSEClearRecord(Load, ATy, I->Ty); + } + } + IRCallArgs[FirstIRArg] = Load; } break; Index: clang/lib/CodeGen/CodeGenFunction.h =================================================================== --- clang/lib/CodeGen/CodeGenFunction.h +++ clang/lib/CodeGen/CodeGenFunction.h @@ -3876,6 +3876,11 @@ 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, Index: clang/test/CodeGen/cmse-clear-arg.c =================================================================== --- /dev/null +++ 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 Index: clang/test/CodeGen/cmse-clear-fp16.c =================================================================== --- /dev/null +++ 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]]) Index: clang/test/CodeGen/cmse-clear-return.c =================================================================== --- /dev/null +++ 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