From patchwork Tue Mar 29 16:49:45 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: "Andre Vieira (lists)" X-Patchwork-Id: 603075 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3qZGwr1d6Rz9sD1 for ; Wed, 30 Mar 2016 03:50:20 +1100 (AEDT) Authentication-Results: ozlabs.org; dkim=pass (1024-bit key; unprotected) header.d=gcc.gnu.org header.i=@gcc.gnu.org header.b=UWEg/PXG; dkim-atps=neutral DomainKey-Signature: a=rsa-sha1; c=nofws; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :subject:to:references:from:message-id:date:mime-version :in-reply-to:content-type; q=dns; s=default; b=UhqEBT4WhZ3G+2Wtl laXCP0F24Th/keQYdYkHcSb7djttIffvrD7PwX3jQhwfss1bOMq4JfkRQQYh+z9F 3ScWgj6Or4tXym4g/vGM+NWYMz/sZrj6Aj5h8K2sQwcDZejm54PBj27xYAgGr021 MKo20dHliFURLG7z4dSWMHOwAs= DKIM-Signature: v=1; a=rsa-sha1; c=relaxed; d=gcc.gnu.org; h=list-id :list-unsubscribe:list-archive:list-post:list-help:sender :subject:to:references:from:message-id:date:mime-version :in-reply-to:content-type; s=default; bh=3XKf+O/DkOP9Y/lXbYeVjsP xLKg=; b=UWEg/PXGbfjq2L8W/AH3FGdnuBsEqF7CX7jQ9+Z0DTLmsA5yuJuGYXU dzeODOL2CyHmqe8Hx9Idclcyu1i0adfL5A3weU6N0xGumOzuwr+SMaUsQBRgfKs3 Ahh13eVyAcsbpYWkGK5zgB3cB4HWrfHtxUxc1Q8sadPJECqv6Z5k= Received: (qmail 84165 invoked by alias); 29 Mar 2016 16:50:07 -0000 Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Received: (qmail 84133 invoked by uid 89); 29 Mar 2016 16:50:06 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=2.1 required=5.0 tests=BAYES_50, KAM_LOTSOFHASH, RP_MATCHES_RCVD, SPF_PASS, UNSUBSCRIBE_BODY autolearn=no version=3.3.2 spammy=msr, cmse, UD:gnu.sgstubs, UD:sgstubs X-HELO: foss.arm.com Received: from foss.arm.com (HELO foss.arm.com) (217.140.101.70) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with ESMTP; Tue, 29 Mar 2016 16:49:48 +0000 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id 13DBD28 for ; Tue, 29 Mar 2016 09:48:40 -0700 (PDT) Received: from [10.2.206.221] (e107157-lin.cambridge.arm.com [10.2.206.221]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPSA id 079A63F25F for ; Tue, 29 Mar 2016 09:49:46 -0700 (PDT) Subject: Re: [RFC][PATCH v2, ARM 5/8] ARMv8-M Security Extension's cmse_nonsecure_entry: clear registers To: GCC Patches References: <001701d13f80$5022d980$f0688c80$@foss.arm.com> <56AB9C58.6000208@arm.com> From: "Andre Vieira (lists)" Message-ID: <56FAB229.5050005@arm.com> Date: Tue, 29 Mar 2016 17:49:45 +0100 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:38.0) Gecko/20100101 Thunderbird/38.2.0 MIME-Version: 1.0 In-Reply-To: <56AB9C58.6000208@arm.com> X-IsSubscribed: yes On 29/01/16 17:07, Andre Vieira (lists) wrote: > On 26/12/15 01:54, Thomas Preud'homme wrote: >> [Sending on behalf of Andre Vieira] >> >> Hello, >> >> 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 only support 32 FP registers as in we only >> clear D0-D7. >> 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. >> >> >> *** gcc/ChangeLog *** >> 2015-10-27 Andre Vieira >> Thomas Preud'homme >> >> * gcc/config/arm/arm.c (output_return_instruction): Clear >> registers. >> (thumb2_expand_return): Likewise. >> (thumb1_expand_epilogue): Likewise. >> (arm_expand_epilogue): Likewise. >> (cmse_nonsecure_entry_clear_before_return): New. >> * gcc/config/arm/arm.h (TARGET_DSP_ADD): New macro define. >> * gcc/config/arm/thumb1.md (*epilogue_insns): Change length >> attribute. >> * gcc/config/arm/thumb2.md (*thumb2_return): Likewise. >> >> *** gcc/testsuite/ChangeLog *** >> 2015-10-27 Andre Vieira >> Thomas Preud'homme >> >> * gcc.target/arm/cmse/cmse.exp: Test different multilibs >> separate. >> * 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.h b/gcc/config/arm/arm.h >> index >> f12e3c93bbe24b10ed8eee6687161826773ef649..b06e0586a3da50f57645bda13629bc4dbd3d53b7 >> 100644 >> --- a/gcc/config/arm/arm.h >> +++ b/gcc/config/arm/arm.h >> @@ -230,6 +230,9 @@ extern void >> (*arm_lang_output_object_attributes_hook)(void); >> /* Integer SIMD instructions, and extend-accumulate instructions. */ >> #define TARGET_INT_SIMD \ >> (TARGET_32BIT && arm_arch6 && (arm_arch_notm || arm_arch7em)) >> +/* Parallel addition and subtraction instructions. */ >> +#define TARGET_DSP_ADD \ >> + (TARGET_ARM_ARCH >= 6 && (arm_arch_notm || arm_arch7em)) >> >> /* Should MOVW/MOVT be used in preference to a constant pool. */ >> #define TARGET_USE_MOVT \ >> diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c >> index >> e530b772e3cc053c16421a2a2861d815d53ebb01..0700478ca38307f35d0cb01f83ea182802ba28fa >> 100644 >> --- a/gcc/config/arm/arm.c >> +++ b/gcc/config/arm/arm.c >> @@ -19755,6 +19755,24 @@ output_return_instruction (rtx operand, bool >> really_return, bool reverse, >> default: >> if (IS_CMSE_ENTRY (func_type)) >> { >> + char flags[12] = "APSR_nzcvq"; >> + /* Check if we have to clear the 'GE bits' which is only >> used if >> + parallel add and subtraction instructions are available. */ >> + if (TARGET_DSP_ADD) >> + { >> + /* If so also clear the ge flags. */ >> + flags[10] = 'g'; >> + flags[11] = '\0'; >> + } >> + snprintf (instr, sizeof (instr), "msr%s\t%s, %%|lr", >> conditional, >> + flags); >> + 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. */ >> @@ -23999,6 +24017,17 @@ thumb_pop (FILE *f, unsigned long mask) >> static void >> thumb1_cmse_nonsecure_entry_return (FILE *f, int >> reg_containing_return_addr) >> { >> + char flags[12] = "APSR_nzcvq"; >> + /* Check if we have to clear the 'GE bits' which is only used if >> + parallel add and subtraction instructions are available. */ >> + if (TARGET_DSP_ADD) >> + { >> + flags[10] = 'g'; >> + flags[11] = '\0'; >> + } >> + asm_fprintf (f, "\tmsr\t%s, %r\n", flags, 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); >> } >> >> @@ -25140,6 +25169,139 @@ 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; >> + int regno, maxregno = IP_REGNUM; >> + tree result_type; >> + rtx result_rtl; >> + >> + to_clear_mask = (1LL << (NUM_ARG_REGS)) - 1; >> + to_clear_mask |= (1LL << IP_REGNUM); >> + /* If we are not dealing with -mfloat-abi=softfp 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 = (1LL << (D7_VFP_REGNUM + 1)) - 1; >> + float_mask &= ~((1LL << 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 leave registers traditionally used to pass arguments >> untouched, >> + since if these should not be made callee-saved by the user. */ >> + 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 |= (1LL << regno); >> + } >> + >> + /* Make sure we do not clear the registers used to pass the result >> in. */ >> + result_type = TREE_TYPE (DECL_RESULT (current_function_decl)); >> + if (!VOID_TYPE_P (result_type)) >> + { >> + rtx reg; >> + >> + 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. */ >> + switch (GET_MODE (result_rtl)) >> + { >> + case BLKmode: >> + /* We are dealing with a return in multiple VFP registers. */ >> + { >> + int i; >> + /* This should really only occur when dealing with an hard float >> + abi. */ >> + gcc_assert (TARGET_HARD_FLOAT_ABI && TARGET_VFP); >> + >> + for (i = 0; i < XVECLEN (result_rtl, 0); i++) >> + { >> + reg = XEXP (XVECEXP (result_rtl, 0, i), 0); >> + gcc_assert (REG_P (reg)); >> + /* If we are dealing with DF mode, make sure you don't clear >> + either registers it addresses. */ >> + if (GET_MODE (reg) == DFmode) >> + to_clear_mask &= ~(1LL << (REGNO (reg) + 1)); >> + to_clear_mask &= ~(1LL << REGNO (reg)); >> + } >> + } >> + break; >> + case DImode: >> + to_clear_mask &= ~2; /* Don't clear r0 and r1. */ >> + case SImode: >> + to_clear_mask &= ~1; /* Don't clear r0. */ >> + break; >> + case DFmode: >> + /* If we have -mfloat-abi=hard, do not clear registers used to >> pass >> + return value. */ >> + if (TARGET_HARD_FLOAT_ABI && TARGET_VFP) >> + /* Don't clear s0 and s1. */ >> + to_clear_mask &= ~(1LL << (FIRST_VFP_REGNUM + 1)); >> + else >> + to_clear_mask &= ~2; /* Don't clear r0, r1. */ >> + case SFmode: >> + /* If we have -mfloat-abi=hard, do not clear registers used to >> pass >> + return value. */ >> + if (TARGET_HARD_FLOAT_ABI && TARGET_VFP) >> + /* Don't clear s0. */ >> + to_clear_mask &= ~(1LL << FIRST_VFP_REGNUM); >> + else >> + to_clear_mask &= ~1; /* Don't clear r0. */ >> + break; >> + default: >> + /* We are missing a mode, though AAPCS says we may only return in >> + r0 and r1, s0-d15/d0-d7 so something went wrong here. */ >> + gcc_unreachable (); >> + break; >> + } >> + } >> + >> + for (regno = 0; regno <= maxregno; regno++) >> + { >> + if (!(to_clear_mask & (1LL << regno))) >> + continue; >> + >> + /* If regno is a vfp register, even and its successor is also to >> + be cleared, use vmov. */ >> + if (IS_VFP_REGNUM (regno)) >> + { >> + if (TARGET_VFP_DOUBLE >> + && VFP_REGNO_OK_FOR_DOUBLE (regno) >> + && to_clear_mask & (1LL << (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, >> @@ -25189,6 +25351,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); >> } >> } >> @@ -25247,6 +25411,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. */ >> @@ -25681,6 +25849,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 >> d2a0420a2e9c71bb954d134fb88a86d694cf786d..cdcf397b0cab0ea7f246ad830ad28b07fed71d8b >> 100644 >> --- a/gcc/config/arm/thumb1.md >> +++ b/gcc/config/arm/thumb1.md >> @@ -1757,8 +1757,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) extra 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 >> a724752a39cf2862c68a53ef28a239de2ae1ed96..0c07df91fe8844a7e7437ef5b7f00f74815d63f4 >> 100644 >> --- a/gcc/config/arm/thumb2.md >> +++ b/gcc/config/arm/thumb2.md >> @@ -1106,7 +1106,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..7cef73372fd5f5be080bdfe30999694564deaa9a >> >> --- /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\tr1" } } */ >> diff --git a/gcc/testsuite/gcc.target/arm/cmse/cmse.exp >> b/gcc/testsuite/gcc.target/arm/cmse/cmse.exp >> index >> 6d4bfec1d12bc575218e0b8c33559d0a4da9ec70..592349ad07257f8074f9443a599ae08c36136a80 >> 100644 >> --- a/gcc/testsuite/gcc.target/arm/cmse/cmse.exp >> +++ b/gcc/testsuite/gcc.target/arm/cmse/cmse.exp >> @@ -38,6 +38,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..ab607aa1b7210acc7983bfb401e95117eb8c0d17 >> >> --- /dev/null >> +++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard-sp/cmse-5.c >> @@ -0,0 +1,37 @@ >> +/* { 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 } } } } */ >> 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..5ed7e1d6404b8f17d630895eb06df5921a1a965f >> >> --- /dev/null >> +++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard/cmse-5.c >> @@ -0,0 +1,30 @@ >> +/* { 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 } } } } */ >> 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..d24683f52eba11df5b41eef0e5a8e365fb206fe8 >> >> --- /dev/null >> +++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/soft/cmse-5.c >> @@ -0,0 +1,22 @@ >> +/* { 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 } } } } */ >> 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" } } */ >> >> We welcome any comments. >> >> Cheers, >> >> Andre >> > Ping. > Hi there, This is an updated version of the previous patch to clear the unused bits when returning a compound type. As per the ABI these have undefined values and thus may leak security sensitive information. Any comments? Cheers, Andre *** ChangeLog *** 2016-03-29 Andre Vieira Thomas Preud'homme * gcc/config/arm/arm.c (output_return_instruction): Clear registers. (thumb2_expand_return): Likewise. (thumb1_expand_epilogue): Likewise. (arm_expand_epilogue): Likewise. (cmse_nonsecure_entry_clear_before_return): New. * gcc/config/arm/thumb1.md (*epilogue_insns): Change length attribute. * gcc/config/arm/thumb2.md (*thumb2_return): Likewise. *** gcc/testsuite/ChangeLog *** 2016-03-29 Andre Vieira Thomas Preud'homme * 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 40e6a786b933b09dbfd89b64449acb0356c66f24..7e250a489e0cf3b6588ae3f816ad77233f9be370 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -19808,6 +19808,23 @@ output_return_instruction (rtx operand, bool really_return, bool reverse, default: if (IS_CMSE_ENTRY (func_type)) { + char flags[12] = "APSR_nzcvq"; + /* 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) + { + flags[10] = 'g'; + flags[11] = '\0'; + } + snprintf (instr, sizeof (instr), "msr%s\t%s, %%|lr", conditional, + flags); + 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. */ @@ -24045,6 +24062,17 @@ thumb_pop (FILE *f, unsigned long mask) static void thumb1_cmse_nonsecure_entry_return (FILE *f, int reg_containing_return_addr) { + char flags[12] = "APSR_nzcvq"; + /* 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) + { + flags[10] = 'g'; + flags[11] = '\0'; + } + asm_fprintf (f, "\tmsr\t%s, %r\n", flags, 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); } @@ -25186,6 +25214,235 @@ 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; + int regno, maxregno = IP_REGNUM; + tree result_type; + rtx result_rtl; + + to_clear_mask = (1LL << (NUM_ARG_REGS)) - 1; + to_clear_mask |= (1LL << IP_REGNUM); + /* If we are not dealing with -mfloat-abi=softfp 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 = (1LL << (D7_VFP_REGNUM + 1)) - 1; + float_mask &= ~((1LL << 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 leave registers traditionally used to pass arguments untouched, + since if these should not be made callee-saved by the user. */ + 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 |= (1LL << regno); + } + + /* Make sure we do not clear the registers used to pass the result in. */ + result_type = TREE_TYPE (DECL_RESULT (current_function_decl)); + if (!VOID_TYPE_P (result_type)) + { + rtx reg; + + 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. */ + if (RECORD_OR_UNION_TYPE_P (result_type)) + { + unsigned current_addr, cont_width, alignment, bitfield_width, + next_addr; + tree field, field_t; + + /* May only contain one register as the AAPCS limits the + returning of composite types to one register. */ + gcc_assert (REG_P (result_rtl)); + reg = result_rtl; + + /* Which must thus be r0. */ + gcc_assert (REG_P (reg) && REGNO (reg) == 0); + to_clear_mask &= ~1LL; + + /* Clear unused bits as per the AAPCS these have unspecified + values and may thus leak secret information. We use the algorithm + specified in Section 7.1.7.1 "Bit-fields no larger than their + container" of the Procedure Call Standard for the ARM Architecture + to determine which bits are unused. */ + field = TYPE_FIELDS (result_type); + /* 'current_addr' is the current bit address. */ + current_addr = 0; + while (field && TREE_CODE (field) == FIELD_DECL) + { + field_t = TREE_TYPE (field); + if (DECL_BIT_FIELD (field)) + { + tree bitfield_t = DECL_BIT_FIELD_TYPE (field); + /* 'cont_width' is the container's width. */ + cont_width = TREE_INT_CST_ELT (TYPE_SIZE (bitfield_t), 0); + /* 'bitfield_width' is the width of the bitfield. */ + bitfield_width = TREE_INT_CST_ELT (DECL_SIZE (field), 0); + /* 'alignment' is the alignment of the bitfield type. */ + alignment = TYPE_ALIGN (bitfield_t); + } + else + { + /* For non-bitfields 'bitfield_width' and 'cont_width' are + the same, the size of the field member type. */ + cont_width = TREE_INT_CST_ELT (TYPE_SIZE (field_t), 0); + bitfield_width = cont_width; + alignment = TYPE_ALIGN (field_t); + } + + if ((bitfield_width == 0) + || (bitfield_width > + (cont_width - (current_addr % alignment)))) + { + /* 'next_addr' is the next container bit address. */ + next_addr = + alignment * ((current_addr + alignment - 1)/alignment); + /* We make sure we clear all bits between the current and + next address. */ + padding_bits_to_clear |= (unsigned) + (((uint64_t)UINT_MAX >> (32 - (next_addr))) + 1u) + - (1u << current_addr); + current_addr = next_addr; + } + current_addr += bitfield_width; + field = TREE_CHAIN (field); + } + + /* Don't forget to clear all unused trailing bits in r0. */ + if (current_addr < 32) + padding_bits_to_clear |= UINT_MAX - (1u << current_addr) + 1u; + } + else + { + switch (GET_MODE (result_rtl)) + { + case BLKmode: + { + /* We are dealing with a return in multiple VFP registers. */ + int i; + /* This should really only occur when dealing with an hard + float abi. */ + gcc_assert (TARGET_HARD_FLOAT_ABI && TARGET_VFP); + + for (i = 0; i < XVECLEN (result_rtl, 0); i++) + { + reg = XEXP (XVECEXP (result_rtl, 0, i), 0); + gcc_assert (REG_P (reg)); + /* If we are dealing with DF mode, make sure you don't + clear either registers it addresses. */ + if (GET_MODE (reg) == DFmode) + to_clear_mask &= ~(1LL << (REGNO (reg) + 1)); + to_clear_mask &= ~(1LL << REGNO (reg)); + } + } + break; + case DImode: + to_clear_mask &= ~2; /* Don't clear r0 and r1. */ + case SImode: + to_clear_mask &= ~1; /* Don't clear r0. */ + break; + case DFmode: + /* If we have -mfloat-abi=hard, do not clear registers used to + pass return value. */ + if (TARGET_HARD_FLOAT_ABI && TARGET_VFP) + /* Don't clear s0 and s1. */ + to_clear_mask &= ~(1LL << (FIRST_VFP_REGNUM + 1)); + else + to_clear_mask &= ~2; /* Don't clear r0, r1. */ + case SFmode: + /* If we have -mfloat-abi=hard, do not clear registers used to + pass return value. */ + if (TARGET_HARD_FLOAT_ABI && TARGET_VFP) + /* Don't clear s0. */ + to_clear_mask &= ~(1LL << FIRST_VFP_REGNUM); + else + to_clear_mask &= ~1; /* Don't clear r0. */ + break; + default: + /* We are missing a mode, though AAPCS says we may only return in + r0 and r1, s0-d15/d0-d7 so something went wrong here. */ + gcc_unreachable (); + break; + } + } + } + + 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); + + 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 & (1LL << regno))) + continue; + + /* If regno is a vfp register, even and its successor is also to + be cleared, use vmov. */ + if (IS_VFP_REGNUM (regno)) + { + if (TARGET_VFP_DOUBLE + && VFP_REGNO_OK_FOR_DOUBLE (regno) + && to_clear_mask & (1LL << (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, @@ -25235,6 +25492,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); } } @@ -25293,6 +25552,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. */ @@ -25727,6 +25990,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 1b01ef6ce731fe3ff37c3d8c048fb9d5e7829b35..e895fc77d5dc68cc723f1daf7e80b8b83ce3b20b 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) extra 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 992536593d6c0a8b8fe5a324f32e279c69746157..7161571fbbd08af739c64d78119ee28032aaa412 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..7cef73372fd5f5be080bdfe30999694564deaa9a --- /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\tr1" } } */ 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..4c33b00a826ecd69ff770f3ede91aca6f96f828d --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/cmse/bitfield-1.c @@ -0,0 +1,41 @@ +/* { dg-do run } */ +/* { dg-require-effective-target arm_cmse_ok } */ +/* { dg-options "--save-temps -mcmse -Wl,--section-start,.gnu.sgstubs=0x20400000" } */ +#include +#include + +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 = UCHAR_MAX; + t.d = 255u; + return t; +} + +int +main (void) +{ + test_st t; + t = foo (); + if (t.a != 63u + || t.b != 7u + || t.c != UCHAR_MAX + || t.d != 255u) + 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" } } */ + 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..5e732d3dd0778fb67140ac004c2df24d9a352fb2 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/cmse/bitfield-2.c @@ -0,0 +1,38 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target arm_cmse_ok } */ +/* { dg-options "--save-temps -mcmse -Wl,--section-start,.gnu.sgstubs=0x20400000" } */ +#include + +typedef struct +{ + short a : 7; + 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) + 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" } } */ + + 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..9b2b2f90c28fb5d910795b99e6395feeb7b134b0 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/cmse/bitfield-3.c @@ -0,0 +1,40 @@ +/* { dg-do compile } */ +/* { dg-require-effective-target arm_cmse_ok } */ +/* { dg-options "--save-temps -mcmse -Wl,--section-start,.gnu.sgstubs=0x20400000" } */ +#include +#include + +typedef struct +{ + short a; + char b : 2; + short : 1; + char c : 3; +} test_st; + +test_st __attribute__ ((cmse_nonsecure_entry)) foo (void) +{ + test_st t; + t.a = SHRT_MIN; + t.b = -2; + t.c = -4; + return t; +} + +int +main (void) +{ + test_st t; + t = foo (); + if (t.a != SHRT_MIN + || t.b != -2 + || t.c != -4) + 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" } } */ + + diff --git a/gcc/testsuite/gcc.target/arm/cmse/cmse.exp b/gcc/testsuite/gcc.target/arm/cmse/cmse.exp index d42235174203100f8015fe1785108f70efe1c805..b22fb81774897ff4582fa656413f86a4aec2ff9e 100644 --- a/gcc/testsuite/gcc.target/arm/cmse/cmse.exp +++ b/gcc/testsuite/gcc.target/arm/cmse/cmse.exp @@ -38,6 +38,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..ab607aa1b7210acc7983bfb401e95117eb8c0d17 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard-sp/cmse-5.c @@ -0,0 +1,37 @@ +/* { 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 } } } } */ 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..5ed7e1d6404b8f17d630895eb06df5921a1a965f --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/hard/cmse-5.c @@ -0,0 +1,30 @@ +/* { 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 } } } } */ 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..d24683f52eba11df5b41eef0e5a8e365fb206fe8 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/cmse/mainline/soft/cmse-5.c @@ -0,0 +1,22 @@ +/* { 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 } } } } */ 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..16258d1417556e558989b7c3ad192338a781f503 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/cmse/struct-1.c @@ -0,0 +1,34 @@ +/* { dg-do run } */ +/* { dg-require-effective-target arm_cmse_ok } */ +/* { dg-options "--save-temps -mcmse -Wl,--section-start,.gnu.sgstubs=0x20400000" } */ +#include +#include +typedef struct +{ + unsigned char a; + unsigned short b; +} test_st; + +test_st __attribute__ ((cmse_nonsecure_entry)) foo (void) +{ + test_st t; + t.a = UCHAR_MAX; + t.b = USHRT_MAX; + return t; +} + +int +main (void) +{ + test_st t; + t = foo (); + if (t.a != UCHAR_MAX || t.b != USHRT_MAX) + 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" } } */ + +