This patch extends support for the ARMv8-M Security Extensions
'cmse_nonsecure_entry' attribute to safeguard against leak of
information through unbanked registers.

When returning from a nonsecure entry function we clear all caller-saved
registers that are not used to pass return values, by writing either the
LR, in case of general purpose registers, or the value 0, in case of FP
registers. We use the LR to write to APSR and FPSCR too. We currently do
not support entry functions that pass arguments or return variables on
the stack and we diagnose this. This patch relies on the existing code
to make sure callee-saved registers used in cmse_nonsecure_entry
functions are saved and restored thus retaining their nonsecure mode
value, this should be happening already as it is required by AAPCS.

This patch also clears padding bits for cmse_nonsecure_entry functions
with struct and union return types. For unions a bit is only considered
a padding bit if it is an unused bit in every field of that union. The
function that calculates these is used in a later patch to do the same
for arguments of cmse_nonsecure_call's.

*** gcc/ChangeLog ***
2016-07-25  Andre Vieira        <andre.simoesdiasvie...@arm.com>
            Thomas Preud'homme  <thomas.preudho...@arm.com>

        * config/arm/arm.c (output_return_instruction): Clear
        registers.
        (thumb2_expand_return): Likewise.
        (thumb1_expand_epilogue): Likewise.
        (thumb_exit): Likewise.
        (arm_expand_epilogue): Likewise.
        (cmse_nonsecure_entry_clear_before_return): New.
        (comp_not_to_clear_mask_str_un): New.
        (compute_not_to_clear_mask): New.
        * config/arm/thumb1.md (*epilogue_insns): Change length attribute.
        * config/arm/thumb2.md (*thumb2_return): Likewise.

*** gcc/testsuite/ChangeLog ***
2016-07-25  Andre Vieira        <andre.simoesdiasvie...@arm.com>
            Thomas Preud'homme  <thomas.preudho...@arm.com>

        * gcc.target/arm/cmse/cmse.exp: Test different multilibs separate.
        * gcc.target/arm/cmse/struct-1.c: New.
        * gcc.target/arm/cmse/bitfield-1.c: New.
        * gcc.target/arm/cmse/bitfield-2.c: New.
        * gcc.target/arm/cmse/bitfield-3.c: New.
        * gcc.target/arm/cmse/baseline/cmse-2.c: Test that registers are
cleared.
        * gcc.target/arm/cmse/mainline/soft/cmse-5.c: New.
        * gcc.target/arm/cmse/mainline/hard/cmse-5.c: New.
        * gcc.target/arm/cmse/mainline/hard-sp/cmse-5.c: New.
        * gcc.target/arm/cmse/mainline/softfp/cmse-5.c: New.
        * gcc.target/arm/cmse/mainline/softfp-sp/cmse-5.c: New.
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 
9fba371768b1eba3a11dc8aa5d6acf8cc30f464d..81a9d9a6fb29d0956a661734d60dd2e44cb554b8
 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -17473,6 +17473,279 @@ note_invalid_constants (rtx_insn *insn, HOST_WIDE_INT 
address, int do_pushes)
   return;
 }
 
+/* This function computes the clear mask and PADDING_BITS_TO_CLEAR for structs
+   and unions in the context of ARMv8-M Security Extensions.  It is used as a
+   helper function for both 'cmse_nonsecure_call' and 'cmse_nonsecure_entry'
+   functions.  The PADDING_BITS_TO_CLEAR pointer can be the base to either one
+   or four masks, depending on whether it is being computed for a
+   'cmse_nonsecure_entry' return value or a 'cmse_nonsecure_call' argument
+   respectively.  The tree for the type of the argument or a field within an
+   argument is passed in ARG_TYPE, the current register this argument or field
+   starts in is kept in the pointer REGNO and updated accordingly, the bit this
+   argument or field starts at is passed in STARTING_BIT and the last used bit
+   is kept in LAST_USED_BIT which is also updated accordingly.  */
+
+static unsigned HOST_WIDE_INT
+comp_not_to_clear_mask_str_un (tree arg_type, int * regno,
+                              uint32_t * padding_bits_to_clear,
+                              unsigned starting_bit, int * last_used_bit)
+
+{
+  unsigned HOST_WIDE_INT not_to_clear_reg_mask = 0;
+
+  if (TREE_CODE (arg_type) == RECORD_TYPE)
+    {
+      unsigned current_bit = starting_bit;
+      tree field;
+      long int offset, size;
+
+
+      field = TYPE_FIELDS (arg_type);
+      while (field)
+       {
+         /* The offset within a structure is always an offset from
+            the start of that structure.  Make sure we take that into the
+            calculation of the register based offset that we use here.  */
+         offset = starting_bit;
+         offset += TREE_INT_CST_ELT (DECL_FIELD_BIT_OFFSET (field), 0);
+         offset %= 32;
+
+         /* This is the actual size of the field, for bitfields this is the
+            bitfield width and not the container size.  */
+         size = TREE_INT_CST_ELT (DECL_SIZE (field), 0);
+
+         if (*last_used_bit != offset)
+           {
+             if (offset < *last_used_bit)
+               {
+                 /* This field's offset is before the 'last_used_bit', that
+                    means this field goes on the next register.  So we need to
+                    pad the rest of the current register and increase the
+                    register number.  */
+                 uint32_t mask;
+                 mask  = UINT32_MAX - ((uint32_t) 1 << *last_used_bit);
+                 mask++;
+
+                 *(padding_bits_to_clear + *regno) |= mask;
+                 not_to_clear_reg_mask |= HOST_WIDE_INT_1U << *regno;
+                 (*regno)++;
+               }
+             else
+               {
+                 /* Otherwise we pad the bits between the last field's end and
+                    the start of the new field.  */
+                 uint32_t mask;
+
+                 mask = UINT32_MAX >> (32 - offset);
+                 mask -= ((uint32_t) 1 << *last_used_bit) - 1;
+                 *(padding_bits_to_clear + *regno) |= mask;
+               }
+             current_bit = offset;
+           }
+
+         /* Calculate further padding bits for inner structs/unions too.  */
+         if (RECORD_OR_UNION_TYPE_P (TREE_TYPE (field)))
+           {
+             *last_used_bit = current_bit;
+             not_to_clear_reg_mask
+               |= comp_not_to_clear_mask_str_un (TREE_TYPE (field), regno,
+                                                 padding_bits_to_clear, offset,
+                                                 last_used_bit);
+           }
+         else
+           {
+             /* Update 'current_bit' with this field's size.  If the
+                'current_bit' lies in a subsequent register, update 'regno' and
+                reset 'current_bit' to point to the current bit in that new
+                register.  */
+             current_bit += size;
+             while (current_bit >= 32)
+               {
+                 current_bit-=32;
+                 not_to_clear_reg_mask |= HOST_WIDE_INT_1U << *regno;
+                 (*regno)++;
+               }
+             *last_used_bit = current_bit;
+           }
+
+         field = TREE_CHAIN (field);
+       }
+      not_to_clear_reg_mask |= HOST_WIDE_INT_1U << *regno;
+    }
+  else if (TREE_CODE (arg_type) == UNION_TYPE)
+    {
+      tree field, field_t;
+      int i, regno_t, field_size;
+      int max_reg = -1;
+      int max_bit = -1;
+      uint32_t mask;
+      uint32_t padding_bits_to_clear_res[NUM_ARG_REGS]
+       = {UINT32_MAX, UINT32_MAX, UINT32_MAX, UINT32_MAX};
+
+      /* To compute the padding bits in a union we only consider bits as
+        padding bits if they are always either a padding bit or fall outside a
+        fields size for all fields in the union.  */
+      field = TYPE_FIELDS (arg_type);
+      while (field)
+       {
+         uint32_t padding_bits_to_clear_t[NUM_ARG_REGS]
+           = {0U, 0U, 0U, 0U};
+         int last_used_bit_t = *last_used_bit;
+         regno_t = *regno;
+         field_t = TREE_TYPE (field);
+
+         /* If the field's type is either a record or a union make sure to
+            compute their padding bits too.  */
+         if (RECORD_OR_UNION_TYPE_P (field_t))
+           not_to_clear_reg_mask
+             |= comp_not_to_clear_mask_str_un (field_t, &regno_t,
+                                               &padding_bits_to_clear_t[0],
+                                               starting_bit, &last_used_bit_t);
+         else
+           {
+             field_size = TREE_INT_CST_ELT (DECL_SIZE (field), 0);
+             regno_t = (field_size / 32) + *regno;
+             last_used_bit_t = (starting_bit + field_size) % 32;
+           }
+
+         for (i = *regno; i < regno_t; i++)
+           {
+             /* For all but the last register used by this field only keep the
+                padding bits that were padding bits in this field.  */
+             padding_bits_to_clear_res[i] &= padding_bits_to_clear_t[i];
+           }
+
+           /* For the last register, keep all padding bits that were padding
+              bits in this field and any padding bits that are still valid
+              as padding bits but fall outside of this field's size.  */
+           mask = (UINT32_MAX - ((uint32_t) 1 << last_used_bit_t)) + 1;
+           padding_bits_to_clear_res[regno_t]
+             &= padding_bits_to_clear_t[regno_t] | mask;
+
+         /* Update the maximum size of the fields in terms of registers used
+            ('max_reg') and the 'last_used_bit' in said register.  */
+         if (max_reg < regno_t)
+           {
+             max_reg = regno_t;
+             max_bit = last_used_bit_t;
+           }
+         else if (max_reg == regno_t && max_bit < last_used_bit_t)
+           max_bit = last_used_bit_t;
+
+         field = TREE_CHAIN (field);
+       }
+
+      /* Update the current padding_bits_to_clear using the intersection of the
+        padding bits of all the fields.  */
+      for (i=*regno; i < max_reg; i++)
+       padding_bits_to_clear[i] |= padding_bits_to_clear_res[i];
+
+      /* Do not keep trailing padding bits, we do not know yet whether this
+        is the end of the argument.  */
+      mask = ((uint32_t) 1 << max_bit) - 1;
+      padding_bits_to_clear[max_reg]
+       |= padding_bits_to_clear_res[max_reg] & mask;
+
+      *regno = max_reg;
+      *last_used_bit = max_bit;
+    }
+  else
+    /* This function should only be used for structs and unions.  */
+    gcc_unreachable ();
+
+  return not_to_clear_reg_mask;
+}
+
+/* In the context of ARMv8-M Security Extensions, this function is used for 
both
+   'cmse_nonsecure_call' and 'cmse_nonsecure_entry' functions to compute what
+   registers are used when returning or passing arguments, which is then
+   returned as a mask.  It will also compute a mask to indicate padding/unused
+   bits for each of these registers, and passes this through the
+   PADDING_BITS_TO_CLEAR pointer.  The tree of the argument type is passed in
+   ARG_TYPE, the rtl representation of the argument is passed in ARG_RTX and
+   the starting register used to pass this argument or return value is passed
+   in REGNO.  It makes use of 'comp_not_to_clear_mask_str_un' to compute these
+   for struct and union types.  */
+
+static unsigned HOST_WIDE_INT
+compute_not_to_clear_mask (tree arg_type, rtx arg_rtx, int regno,
+                            uint32_t * padding_bits_to_clear)
+
+{
+  int last_used_bit = 0;
+  unsigned HOST_WIDE_INT not_to_clear_mask;
+
+  if (RECORD_OR_UNION_TYPE_P (arg_type))
+    {
+      not_to_clear_mask
+       = comp_not_to_clear_mask_str_un (arg_type, &regno,
+                                        padding_bits_to_clear, 0,
+                                        &last_used_bit);
+
+
+      /* If the 'last_used_bit' is not zero, that means we are still using a
+        part of the last 'regno'.  In such cases we must clear the trailing
+        bits.  Otherwise we are not using regno and we should mark it as to
+        clear.  */
+      if (last_used_bit != 0)
+       *(padding_bits_to_clear + regno)
+         |= UINT32_MAX - ((uint32_t) 1 << last_used_bit) + 1;
+      else
+       not_to_clear_mask &= ~(HOST_WIDE_INT_1U << regno);
+    }
+  else
+    {
+      not_to_clear_mask = 0;
+      /* We are not dealing with structs nor unions.  So these arguments may be
+        passed in floating point registers too.  In some cases a BLKmode is
+        used when returning or passing arguments in multiple VFP registers.  */
+      if (GET_MODE (arg_rtx) == BLKmode)
+       {
+         int i, arg_regs;
+         rtx reg;
+
+         /* This should really only occur when dealing with the hard-float
+            ABI.  */
+         gcc_assert (TARGET_HARD_FLOAT_ABI && TARGET_VFP);
+
+         for (i = 0; i < XVECLEN (arg_rtx, 0); i++)
+           {
+             reg = XEXP (XVECEXP (arg_rtx, 0, i), 0);
+             gcc_assert (REG_P (reg));
+
+             not_to_clear_mask |= HOST_WIDE_INT_1U << REGNO (reg);
+
+             /* If we are dealing with DF mode, make sure we don't
+                clear either of the registers it addresses.  */
+             arg_regs = ARM_NUM_REGS (GET_MODE (reg));
+             if (arg_regs > 1)
+               {
+                 unsigned HOST_WIDE_INT mask;
+                 mask = HOST_WIDE_INT_1U << (REGNO (reg) + arg_regs);
+                 mask -= HOST_WIDE_INT_1U << REGNO (reg);
+                 not_to_clear_mask |= mask;
+               }
+           }
+       }
+      else
+       {
+         /* Otherwise we can rely on the MODE to determine how many registers
+            are being used by this argument.  */
+         int arg_regs = ARM_NUM_REGS (GET_MODE (arg_rtx));
+         not_to_clear_mask |= HOST_WIDE_INT_1U << REGNO (arg_rtx);
+         if (arg_regs > 1)
+           {
+             unsigned HOST_WIDE_INT
+             mask = HOST_WIDE_INT_1U << (REGNO (arg_rtx) + arg_regs);
+             mask -= HOST_WIDE_INT_1U << REGNO (arg_rtx);
+             not_to_clear_mask |= mask;
+           }
+       }
+    }
+
+  return not_to_clear_mask;
+}
+
 /* Rewrite move insn into subtract of 0 if the condition codes will
    be useful in next conditional jump insn.  */
 
@@ -19891,7 +20164,25 @@ output_return_instruction (rtx operand, bool 
really_return, bool reverse,
 
        default:
          if (IS_CMSE_ENTRY (func_type))
-           snprintf (instr, sizeof (instr), "bxns%s\t%%|lr", conditional);
+           {
+             /* Check if we have to clear the 'GE bits' which is only used if
+                parallel add and subtraction instructions are available.  */
+             if (TARGET_INT_SIMD)
+               snprintf (instr, sizeof (instr),
+                         "msr%s\tAPSR_nzcvqg, %%|lr", conditional);
+             else
+               snprintf (instr, sizeof (instr),
+                         "msr%s\tAPSR_nzcvq, %%|lr", conditional);
+
+             output_asm_insn (instr, & operand);
+             if (TARGET_HARD_FLOAT && TARGET_VFP)
+               {
+                 snprintf (instr, sizeof (instr), "vmsr%s\tfpscr, %%|lr",
+                           conditional);
+                 output_asm_insn (instr, & operand);
+               }
+             snprintf (instr, sizeof (instr), "bxns%s\t%%|lr", conditional);
+           }
          /* Use bx if it's available.  */
          else if (arm_arch5 || arm_arch4t)
            sprintf (instr, "bx%s\t%%|lr", conditional);
@@ -24143,7 +24434,20 @@ thumb_exit (FILE *f, int reg_containing_return_addr)
        asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, ARM_EH_STACKADJ_REGNUM);
 
       if (IS_CMSE_ENTRY (arm_current_func_type ()))
-       asm_fprintf (f, "\tbxns\t%r\n", reg_containing_return_addr);
+       {
+         /* Check if we have to clear the 'GE bits' which is only used if
+            parallel add and subtraction instructions are available.  */
+         if (TARGET_INT_SIMD)
+           asm_fprintf (f, "\tmsr\tAPSR_nzcvqg, %r\n",
+                        reg_containing_return_addr);
+         else
+           asm_fprintf (f, "\tmsr\tAPSR_nzcvq, %r\n",
+                        reg_containing_return_addr);
+
+         if (TARGET_HARD_FLOAT && TARGET_VFP)
+           asm_fprintf (f, "\tvmsr\tfpscr, %r\n", reg_containing_return_addr);
+         asm_fprintf (f, "\tbxns\t%r\n", reg_containing_return_addr);
+       }
       else
        asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr);
       return;
@@ -24379,7 +24683,20 @@ thumb_exit (FILE *f, int reg_containing_return_addr)
 
   /* Return to caller.  */
   if (IS_CMSE_ENTRY (arm_current_func_type ()))
-    asm_fprintf (f, "\tbxns\t%r\n", reg_containing_return_addr);
+    {
+      /* Check if we have to clear the 'GE bits' which is only used if
+        parallel add and subtraction instructions are available.  */
+      if (TARGET_INT_SIMD)
+       asm_fprintf (f, "\tmsr\tAPSR_nzcvqg, %r\n",
+                    reg_containing_return_addr);
+      else
+       asm_fprintf (f, "\tmsr\tAPSR_nzcvq, %r\n",
+                    reg_containing_return_addr);
+
+      if (TARGET_HARD_FLOAT && TARGET_VFP)
+       asm_fprintf (f, "\tvmsr\tfpscr, %r\n", reg_containing_return_addr);
+      asm_fprintf (f, "\tbxns\t%r\n", reg_containing_return_addr);
+    }
   else
     asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr);
 }
@@ -25241,6 +25558,119 @@ thumb1_expand_prologue (void)
     cfun->machine->lr_save_eliminated = 0;
 }
 
+/* Clear caller saved registers not used to pass return values and leaked
+   condition flags before exiting a cmse_nonsecure_entry function.  */
+
+void
+cmse_nonsecure_entry_clear_before_return (void)
+{
+  uint64_t to_clear_mask;
+  uint32_t padding_bits_to_clear = 0;
+  uint32_t * padding_bits_to_clear_ptr = &padding_bits_to_clear;
+  int regno, maxregno = IP_REGNUM;
+  tree result_type;
+  rtx result_rtl;
+
+  to_clear_mask = (1ULL << (NUM_ARG_REGS)) - 1;
+  to_clear_mask |= (1ULL << IP_REGNUM);
+  /* If we are not dealing with -mfloat-abi=soft we will need to clear VFP
+     registers.  We also check TARGET_VFP to make sure these are present.  */
+  if (TARGET_HARD_FLOAT && TARGET_VFP)
+    {
+      uint64_t float_mask = (1ULL << (D7_VFP_REGNUM + 1)) - 1;
+      float_mask &= ~((1ULL << FIRST_VFP_REGNUM) - 1);
+      to_clear_mask |= float_mask;
+      maxregno = LAST_VFP_REGNUM;
+    }
+
+  /* If the user has defined registers to be caller saved, these are no longer
+     restored by the function before returning and must thus be cleared for
+     security purposes.  */
+  for (regno = NUM_ARG_REGS; regno < LAST_VFP_REGNUM; regno++)
+    {
+      /* We do not touch registers that can be used to pass arguments as per
+        the AAPCS, since these should never be made callee-saved by user
+        options.  */
+      if (regno >= FIRST_VFP_REGNUM && regno <= D7_VFP_REGNUM)
+       continue;
+      if (regno >= IP_REGNUM && regno <= PC_REGNUM)
+       continue;
+      if (call_used_regs[regno])
+       to_clear_mask |= (1ULL << regno);
+    }
+
+  /* Make sure we do not clear the registers used to return the result in.  */
+  result_type = TREE_TYPE (DECL_RESULT (current_function_decl));
+  if (!VOID_TYPE_P (result_type))
+    {
+      result_rtl = arm_function_value (result_type, current_function_decl, 0);
+
+      /* No need to check that we return in registers, because we don't
+        support returning on stack yet.  */
+      to_clear_mask
+       &= ~compute_not_to_clear_mask (result_type, result_rtl, 0,
+                                      padding_bits_to_clear_ptr);
+    }
+
+  if (padding_bits_to_clear != 0)
+    {
+      rtx reg_rtx;
+      /* Padding bits to clear is not 0 so we know we are dealing with
+        returning a composite type, which only uses r0.  Let's make sure that
+        r1-r3 is cleared too, we will use r1 as a scratch register.  */
+      gcc_assert ((to_clear_mask & 0xe) == 0xe);
+
+      reg_rtx = gen_rtx_REG (SImode, 1);
+
+      /* Fill the lower half of the negated padding_bits_to_clear.  */
+      emit_move_insn (reg_rtx,
+                     GEN_INT ((((~padding_bits_to_clear) << 16u) >> 16u)));
+
+      /* Also fill the top half of the negated padding_bits_to_clear.  */
+      if (((~padding_bits_to_clear) >> 16) > 0)
+       emit_insn (gen_rtx_SET (gen_rtx_ZERO_EXTRACT (SImode, reg_rtx,
+                                                     GEN_INT (16),
+                                                     GEN_INT (16)),
+                               GEN_INT ((~padding_bits_to_clear) >> 16)));
+
+      emit_insn (gen_andsi3 (gen_rtx_REG (SImode, 0),
+                          gen_rtx_REG (SImode, 0),
+                          reg_rtx));
+    }
+
+  for (regno = 0; regno <= maxregno; regno++)
+    {
+      if (!(to_clear_mask & (1ULL << regno)))
+       continue;
+
+      if (IS_VFP_REGNUM (regno))
+       {
+         /* If regno is an even vfp register and its successor is also to
+            be cleared, use vmov.  */
+         if (TARGET_VFP_DOUBLE
+             && VFP_REGNO_OK_FOR_DOUBLE (regno)
+             && to_clear_mask & (1ULL << (regno + 1)))
+           {
+             emit_move_insn (gen_rtx_REG (DFmode, regno++),
+                             CONST1_RTX (DFmode));
+             emit_use (gen_rtx_REG (DFmode, regno));
+           }
+         else
+           {
+             emit_move_insn (gen_rtx_REG (SFmode, regno),
+                             CONST1_RTX (SFmode));
+             emit_use (gen_rtx_REG (SFmode, regno));
+           }
+       }
+      else
+       {
+         emit_move_insn (gen_rtx_REG (SImode, regno),
+                         gen_rtx_REG (SImode, LR_REGNUM));
+         emit_use (gen_rtx_REG (SImode, regno));
+       }
+    }
+}
+
 /* Generate pattern *pop_multiple_with_stack_update_and_return if single
    POP instruction can be generated.  LR should be replaced by PC.  All
    the checks required are already done by  USE_RETURN_INSN ().  Hence,
@@ -25290,6 +25720,8 @@ thumb2_expand_return (bool simple_return)
     }
   else
     {
+      if (IS_CMSE_ENTRY (arm_current_func_type ()))
+       cmse_nonsecure_entry_clear_before_return ();
       emit_jump_insn (simple_return_rtx);
     }
 }
@@ -25348,6 +25780,10 @@ thumb1_expand_epilogue (void)
 
   if (! df_regs_ever_live_p (LR_REGNUM))
     emit_use (gen_rtx_REG (SImode, LR_REGNUM));
+
+  /* Clear all caller-saved regs that are not used to return.  */
+  if (IS_CMSE_ENTRY (arm_current_func_type ()))
+    cmse_nonsecure_entry_clear_before_return ();
 }
 
 /* Epilogue code for APCS frame.  */
@@ -25782,6 +26218,14 @@ arm_expand_epilogue (bool really_return)
                                   stack_pointer_rtx, stack_pointer_rtx);
     }
 
+    /* Clear all caller-saved regs that are not used to return.  */
+    if (IS_CMSE_ENTRY (arm_current_func_type ()))
+      {
+       /* CMSE_ENTRY always returns.  */
+       gcc_assert (really_return);
+       cmse_nonsecure_entry_clear_before_return ();
+      }
+
   if (!really_return)
     return;
 
diff --git a/gcc/config/arm/thumb1.md b/gcc/config/arm/thumb1.md
index 
cd98de7dcb40de483a9f93c0674bd216f4b0c56a..c3d4b4e71193ce347f1e8a931b66705619d9b0c5
 100644
--- a/gcc/config/arm/thumb1.md
+++ b/gcc/config/arm/thumb1.md
@@ -1843,8 +1843,15 @@
   "*
     return thumb1_unexpanded_epilogue ();
   "
-  ; Length is absolute worst case
-  [(set_attr "length" "44")
+  ; Length is absolute worst case, when using CMSE and if this is an entry
+  ; function an extra 4 (MSR) to 8 (VMSR) bytes might be added.
+  [(set (attr "length")
+       (if_then_else
+        (match_test "IS_CMSE_ENTRY (arm_current_func_type ())")
+        (if_then_else (match_test "TARGET_HARD_FLOAT && TARGET_VFP")
+         (const_int 52)
+         (const_int 48))
+        (const_int 44)))
    (set_attr "type" "block")
    ;; We don't clobber the conditions, but the potential length of this
    ;; operation is sufficient to make conditionalizing the sequence
diff --git a/gcc/config/arm/thumb2.md b/gcc/config/arm/thumb2.md
index 
ab08288413c3e64911e8d7a8199b9809e0282d8e..3227c4226738a23b56a59cdf7a0052111317678c
 100644
--- a/gcc/config/arm/thumb2.md
+++ b/gcc/config/arm/thumb2.md
@@ -1118,7 +1118,15 @@
   "TARGET_THUMB2"
   "* return output_return_instruction (const_true_rtx, true, false, true);"
   [(set_attr "type" "branch")
-   (set_attr "length" "4")]
+  ; If this is a return from a cmse_nonsecure_entry function then code will be
+  ; added to clear the APSR and potentially the FPSCR if VFP is available, so
+  ; we adapt the length accordingly.
+   (set (attr "length")
+    (if_then_else (match_test "IS_CMSE_ENTRY (arm_current_func_type ())")
+     (if_then_else (match_test "TARGET_HARD_FLOAT && TARGET_VFP")
+      (const_int 12)
+      (const_int 8))
+     (const_int 4)))]
 )
 
 (define_insn_and_split "thumb2_eh_return"
diff --git a/gcc/testsuite/gcc.target/arm/cmse/baseline/cmse-2.c 
b/gcc/testsuite/gcc.target/arm/cmse/baseline/cmse-2.c
new file mode 100644
index 
0000000000000000000000000000000000000000..4c6354c7a8cfead895e6c62d61c3f2a2bca1a1f2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/baseline/cmse-2.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v8m_base_ok } */
+/* { dg-add-options arm_arch_v8m_base } */
+/* { dg-options "-mcmse" }  */
+
+extern float bar (void);
+
+float __attribute__ ((cmse_nonsecure_entry))
+foo (void)
+{
+  return bar ();
+}
+/* { dg-final { scan-assembler "mov\tr1, lr" } } */
+/* { dg-final { scan-assembler "mov\tr2, lr" } } */
+/* { dg-final { scan-assembler "mov\tr3, lr" } } */
+/* { dg-final { scan-assembler "mov\tip, lr" } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, r1" } } */
+/* { dg-final { scan-assembler "bxns" } } */
diff --git a/gcc/testsuite/gcc.target/arm/cmse/bitfield-1.c 
b/gcc/testsuite/gcc.target/arm/cmse/bitfield-1.c
new file mode 100644
index 
0000000000000000000000000000000000000000..fccc51d5c82f7955ee4cb8256c1dd38f9ff2670d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/bitfield-1.c
@@ -0,0 +1,39 @@
+/* { dg-do run } */
+/* { dg-options "--save-temps -mcmse 
-Wl,--section-start,.gnu.sgstubs=0x20400000" } */
+
+typedef struct
+{
+  unsigned short  a : 6;
+  unsigned char          b : 3;
+  unsigned char          c;
+  unsigned short  d : 8;
+} test_st;
+
+test_st __attribute__ ((cmse_nonsecure_entry)) foo (void)
+{
+  test_st t;
+  t.a = 63u;
+  t.b = 7u;
+  t.c = 255u;
+  t.d = 255u;
+  return t;
+}
+
+int
+main (void)
+{
+  test_st t;
+  t = foo ();
+  if (t.a != 63u
+      || t.b != 7u
+      || t.c != 255u
+      || t.d != 255u)
+    __builtin_abort ();
+  return 0;
+}
+
+/* { dg-final { scan-assembler "movw\tr1, #1855" } } */
+/* { dg-final { scan-assembler "movt\tr1, 65535" } } */
+/* { dg-final { scan-assembler "ands\tr0(, r0)?, r1" } } */
+/* { dg-final { scan-assembler "bxns" } } */
+
diff --git a/gcc/testsuite/gcc.target/arm/cmse/bitfield-2.c 
b/gcc/testsuite/gcc.target/arm/cmse/bitfield-2.c
new file mode 100644
index 
0000000000000000000000000000000000000000..e6aee3c4c022d50baec8ab16443130897540e703
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/bitfield-2.c
@@ -0,0 +1,36 @@
+/* { dg-do run } */
+/* { dg-options "--save-temps -mcmse 
-Wl,--section-start,.gnu.sgstubs=0x20400000" } */
+
+typedef struct
+{
+  short              a : 7;
+  signed char b : 3;
+  short              c : 11;
+} test_st;
+
+test_st __attribute__ ((cmse_nonsecure_entry)) foo (void)
+{
+  test_st t;
+  t.a = -64;
+  t.b = -4 ;
+  t.c = -1024;
+  return t;
+}
+
+int
+main (void)
+{
+  test_st t;
+  t = foo ();
+  if (t.a != -64
+      || t.b != -4
+      || t.c != -1024)
+    __builtin_abort ();
+  return 0;
+}
+
+/* { dg-final { scan-assembler "movw\tr1, #1919" } } */
+/* { dg-final { scan-assembler "movt\tr1, 2047" } } */
+/* { dg-final { scan-assembler "ands\tr0(, r0)?, r1" } } */
+/* { dg-final { scan-assembler "bxns" } } */
+
diff --git a/gcc/testsuite/gcc.target/arm/cmse/bitfield-3.c 
b/gcc/testsuite/gcc.target/arm/cmse/bitfield-3.c
new file mode 100644
index 
0000000000000000000000000000000000000000..285a2b92f64c1913ef585b8daa4d27c6da0a3d2f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/bitfield-3.c
@@ -0,0 +1,37 @@
+/* { dg-do run } */
+/* { dg-options "--save-temps -mcmse 
-Wl,--section-start,.gnu.sgstubs=0x20400000" } */
+
+typedef struct
+{
+  short              a;
+  signed char b : 2;
+  short                : 1;
+  signed char c : 3;
+} test_st;
+
+test_st __attribute__ ((cmse_nonsecure_entry)) foo (void)
+{
+  test_st t;
+  t.a = -32768;
+  t.b = -2;
+  t.c = -4;
+  return t;
+}
+
+int
+main (void)
+{
+  test_st t;
+  t = foo ();
+  if (t.a != -32768
+      || t.b != -2
+      || t.c != -4)
+    __builtin_abort ();
+  return 0;
+}
+
+/* { dg-final { scan-assembler "movw\tr1, #65535" } } */
+/* { dg-final { scan-assembler "movt\tr1, 63" } } */
+/* { dg-final { scan-assembler "ands\tr0(, r0)?, r1" } } */
+/* { dg-final { scan-assembler "bxns" } } */
+
diff --git a/gcc/testsuite/gcc.target/arm/cmse/cmse.exp 
b/gcc/testsuite/gcc.target/arm/cmse/cmse.exp
index 
f797dba1901720e04249d61078c1cbf2a3e436a9..38f18414c2fefec56161e6ac3f7291b03a3b29a3
 100644
--- a/gcc/testsuite/gcc.target/arm/cmse/cmse.exp
+++ b/gcc/testsuite/gcc.target/arm/cmse/cmse.exp
@@ -43,6 +43,26 @@ set LTO_TORTURE_OPTIONS ""
 gcc-dg-runtest [lsort [glob $srcdir/$subdir/*.c]] \
        "" $DEFAULT_CFLAGS
 
+if {[check_effective_target_arm_arch_v8m_base_ok]} then {
+    # Baseline only
+    gcc-dg-runtest [lsort [glob $srcdir/$subdir/baseline/*.c]] \
+           "" $DEFAULT_CFLAGS
+}
+
+if {[check_effective_target_arm_arch_v8m_main_ok]} then {
+    # Mainline -mfloat-abi=soft
+    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/soft/*.c]] \
+           "-mfloat-abi=soft" $DEFAULT_CFLAGS
+    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/softfp/*.c]] \
+           "" $DEFAULT_CFLAGS
+    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/softfp-sp/*.c]] \
+           "" $DEFAULT_CFLAGS
+    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/hard/*.c]] \
+           "" $DEFAULT_CFLAGS
+    gcc-dg-runtest [lsort [glob $srcdir/$subdir/mainline/hard-sp/*.c]] \
+           "" $DEFAULT_CFLAGS
+}
+
 set LTO_TORTURE_OPTIONS ${saved-lto_torture_options}
 set dg-do-what-default ${saved-dg-do-what-default}
 
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/hard-sp/cmse-5.c 
b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard-sp/cmse-5.c
new file mode 100644
index 
0000000000000000000000000000000000000000..f18c8fc98fb95618ab9ddafba0f0b7f0ccef2839
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard-sp/cmse-5.c
@@ -0,0 +1,38 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v8m_main_ok } */
+/* { dg-add-options arm_arch_v8m_main } */
+/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} 
{"-mfloat-abi=soft" -mfloat-abi=softfp } {""} } */
+/* { dg-skip-if "Skip these if testing double precision" {*-*-*} 
{"-mfpu=fpv[4-5]-d16"} {""} } */
+/* { dg-options "-mcmse -mfloat-abi=hard -mfpu=fpv5-sp-d16" }  */
+
+extern float bar (void);
+
+float __attribute__ ((cmse_nonsecure_entry))
+foo (void)
+{
+  return bar ();
+}
+/* { dg-final { scan-assembler "mov\tr0, lr" } } */
+/* { dg-final { scan-assembler "mov\tr1, lr" } } */
+/* { dg-final { scan-assembler "mov\tr2, lr" } } */
+/* { dg-final { scan-assembler "mov\tr3, lr" } } */
+/* { dg-final { scan-assembler "mov\tip, lr" } } */
+/* { dg-final { scan-assembler-not "vmov\.f32\ts0, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts1, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts2, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts3, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts4, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts5, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts6, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts7, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts8, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts9, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts10, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts11, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts12, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts13, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts14, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts15, #1\.0" } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { 
arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { 
arm_arch_v8m_main_ok && arm_dsp } } } } */
+/* { dg-final { scan-assembler "bxns" } } */
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/hard/cmse-5.c 
b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard/cmse-5.c
new file mode 100644
index 
0000000000000000000000000000000000000000..a1199dca8ca7a49c6f764e2007b575ace40414e1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard/cmse-5.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v8m_main_ok } */
+/* { dg-add-options arm_arch_v8m_main } */
+/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} 
{"-mfloat-abi=soft" -mfloat-abi=softfp } {""} } */
+/* { dg-skip-if "Skip these if testing single precision" {*-*-*} 
{"-mfpu=*-sp-*"} {""} } */
+/* { dg-options "-mcmse -mfloat-abi=hard -mfpu=fpv5-d16" }  */
+
+extern float bar (void);
+
+float __attribute__ ((cmse_nonsecure_entry))
+foo (void)
+{
+  return bar ();
+}
+/* { dg-final { scan-assembler "mov\tr0, lr" } } */
+/* { dg-final { scan-assembler "mov\tr1, lr" } } */
+/* { dg-final { scan-assembler "mov\tr2, lr" } } */
+/* { dg-final { scan-assembler "mov\tr3, lr" } } */
+/* { dg-final { scan-assembler "mov\tip, lr" } } */
+/* { dg-final { scan-assembler-not "vmov\.f32\ts0, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts1, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td1, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td2, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td3, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td4, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td5, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td6, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td7, #1\.0" } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { 
arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { 
arm_arch_v8m_main_ok && arm_dsp } } } } */
+/* { dg-final { scan-assembler "bxns" } } */
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/soft/cmse-5.c 
b/gcc/testsuite/gcc.target/arm/cmse/mainline/soft/cmse-5.c
new file mode 100644
index 
0000000000000000000000000000000000000000..a7229ea8eb2da1da264f58f8518daf303d1bdeda
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/soft/cmse-5.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v8m_main_ok } */
+/* { dg-add-options arm_arch_v8m_main } */
+/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} 
{"-mfloat-abi=hard" -mfloat-abi=softfp } {""} } */
+/* { dg-options "-mcmse -mfloat-abi=soft" }  */
+
+extern float bar (void);
+
+float __attribute__ ((cmse_nonsecure_entry))
+foo (void)
+{
+  return bar ();
+}
+
+/* { dg-final { scan-assembler "mov\tr1, lr" } } */
+/* { dg-final { scan-assembler "mov\tr2, lr" } } */
+/* { dg-final { scan-assembler "mov\tr3, lr" } } */
+/* { dg-final { scan-assembler "mov\tip, lr" } } */
+/* { dg-final { scan-assembler-not "vmov" } } */
+/* { dg-final { scan-assembler-not "vmsr" } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { 
arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { 
arm_arch_v8m_main_ok && arm_dsp } } } } */
+/* { dg-final { scan-assembler "bxns" } } */
+
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp-sp/cmse-5.c 
b/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp-sp/cmse-5.c
new file mode 100644
index 
0000000000000000000000000000000000000000..c22775ace8361729c36130422475522fbaca05b5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp-sp/cmse-5.c
@@ -0,0 +1,39 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v8m_main_ok } */
+/* { dg-add-options arm_arch_v8m_main } */
+/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} 
{"-mfloat-abi=soft" -mfloat-abi=hard } {""} } */
+/* { dg-skip-if "Skip these if testing double precision" {*-*-*} 
{"-mfpu=fpv[4-5]-d16"} {""} } */
+/* { dg-options "-mcmse -mfloat-abi=softfp -mfpu=fpv5-sp-d16" }  */
+
+extern float bar (void);
+
+float __attribute__ ((cmse_nonsecure_entry))
+foo (void)
+{
+  return bar ();
+}
+/* { dg-final { scan-assembler "__acle_se_foo:" } } */
+/* { dg-final { scan-assembler-not "mov\tr0, lr" } } */
+/* { dg-final { scan-assembler "mov\tr1, lr" } } */
+/* { dg-final { scan-assembler "mov\tr2, lr" } } */
+/* { dg-final { scan-assembler "mov\tr3, lr" } } */
+/* { dg-final { scan-assembler "mov\tip, lr" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts0, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts1, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts2, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts3, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts4, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts5, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts6, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts7, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts8, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts9, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts10, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts11, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts12, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts13, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts14, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f32\ts15, #1\.0" } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { 
arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { 
arm_arch_v8m_main_ok && arm_dsp } } } } */
+/* { dg-final { scan-assembler "bxns" } } */
diff --git a/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp/cmse-5.c 
b/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp/cmse-5.c
new file mode 100644
index 
0000000000000000000000000000000000000000..28b05c391081bd42407b4e56a1e84daa549fa06e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/softfp/cmse-5.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target arm_arch_v8m_main_ok } */
+/* { dg-add-options arm_arch_v8m_main } */
+/* { dg-skip-if "Do not combine float-abi= hard | soft | softfp" {*-*-*} 
{"-mfloat-abi=soft" -mfloat-abi=hard } {""} } */
+/* { dg-skip-if "Skip these if testing single precision" {*-*-*} 
{"-mfpu=*-sp-*"} {""} } */
+/* { dg-options "-mcmse -mfloat-abi=softfp -mfpu=fpv5-d16" }  */
+
+extern float bar (void);
+
+float __attribute__ ((cmse_nonsecure_entry))
+foo (void)
+{
+  return bar ();
+}
+/* { dg-final { scan-assembler "__acle_se_foo:" } } */
+/* { dg-final { scan-assembler-not "mov\tr0, lr" } } */
+/* { dg-final { scan-assembler "mov\tr1, lr" } } */
+/* { dg-final { scan-assembler "mov\tr2, lr" } } */
+/* { dg-final { scan-assembler "mov\tr3, lr" } } */
+/* { dg-final { scan-assembler "mov\tip, lr" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td0, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td1, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td2, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td3, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td4, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td5, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td6, #1\.0" } } */
+/* { dg-final { scan-assembler "vmov\.f64\td7, #1\.0" } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvq, lr" { target { 
arm_arch_v8m_main_ok && { ! arm_dsp } } } } } */
+/* { dg-final { scan-assembler "msr\tAPSR_nzcvqg, lr" { target { 
arm_arch_v8m_main_ok && arm_dsp } } } } */
+/* { dg-final { scan-assembler "bxns" } } */
diff --git a/gcc/testsuite/gcc.target/arm/cmse/struct-1.c 
b/gcc/testsuite/gcc.target/arm/cmse/struct-1.c
new file mode 100644
index 
0000000000000000000000000000000000000000..2d366a944df692f29ab44e3ee4d33c777b126223
--- /dev/null
+++ b/gcc/testsuite/gcc.target/arm/cmse/struct-1.c
@@ -0,0 +1,33 @@
+/* { dg-do run } */
+/* { dg-options "--save-temps -mcmse 
-Wl,--section-start,.gnu.sgstubs=0x20400000" } */
+
+typedef struct
+{
+  unsigned char          a;
+  unsigned short  b;
+} test_st;
+
+test_st __attribute__ ((cmse_nonsecure_entry)) foo (void)
+{
+  test_st t;
+  t.a = 255u;
+  t.b = 32767u;
+  return t;
+}
+
+int
+main (void)
+{
+  test_st t;
+  t = foo ();
+  if (t.a != 255u || t.b != 32767u)
+    __builtin_abort ();
+  return 0;
+}
+
+/* { dg-final { scan-assembler "movs\tr1, #255" } } */
+/* { dg-final { scan-assembler "movt\tr1, 65535" } } */
+/* { dg-final { scan-assembler "ands\tr0(, r0)?, r1" } } */
+/* { dg-final { scan-assembler "bxns" } } */
+
+

Reply via email to