On Mon, 2025-11-17 at 12:34 +0800, WANG Xuerui wrote:
> From: WANG Xuerui <[email protected]>
>
> The option was introduced a few days ago to provide more control over
> the code generation of __builtin_trap(), but the current form does not
> provide room for further extension should other means of trapping become
> available on future generations of LoongArch cores. Moreover, using
> out-of-range values to ask for something that's not a "break" is
> counter-intuitive, at least for an option named "break"-something.
>
> To improve the command-line UX, rename the option to
> -mbuiltin-trap-impl= to better reflect the intent from a user's
> perspective, and make it accept a string that resembles real LoongArch
> assembly if the user were asked to write the trap snippet themselves.
>
> For now, the accepted forms are "break-CODE" where CODE is an integer
> literal between 0 and 32767 inclusive, and "amswap.w". Both deviate
> from usual assembly forms somehow for practical reasons:
>
> 1. Spaces are notorious in CLI argument handling, so for something
> expected to be passed around often in an unstructured way (read:
> CFLAGS), it is prudent to use something else.
> 2. The usage of "amswap.w" here (or any other AM* instruction) is
> exotic, as the LoongArch reference manual does not provide any other
> way to trigger an INE (instruction non-existent) exception for sure.
> In this case, the register operands are intentionally illegal, so
> having them fully spelled out only serves to confuse any reader. In
> addition, the exact register usage is irrelevant to the option's
> purpose.
>
> Since this is now freeform string, any future evolution of the option's
> usage is made possible.
>
> gcc/ChangeLog:
>
> * config/loongarch/genopts/loongarch.opt.in: Rework
> -mbreak-code= into -mbuiltin-trap-impl=.
> * config/loongarch/loongarch-opts.cc
> (loongarch_parse_mbuiltin_trap_impl): New function.
> (loongarch_init_misc_options): Parse the new option.
> * config/loongarch/loongarch.opt: Regenerate.
loongarch.opt.urls needs to be regenerated too.
> * doc/invoke.texi: Amend documentation.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.target/loongarch/trap-1.c: Move to...
> * gcc.target/loongarch/builtin-trap-impl-break-1.c: ...here and
> migrate to new option.
> * gcc.target/loongarch/trap-default.c: Move to...
> * gcc.target/loongarch/builtin-trap-impl-default.c: ...here and
> migrate to new option.
> * gcc.target/loongarch/builtin-trap-impl-amswap-w.c: New test.
> * gcc.target/loongarch/builtin-trap-impl-break-0.c: New test.
> * gcc.target/loongarch/builtin-trap-impl-break-32767.c: New test.
> * gcc.target/loongarch/builtin-trap-impl-err-1.c: New test.
> * gcc.target/loongarch/builtin-trap-impl-err-2.c: New test.
> * gcc.target/loongarch/builtin-trap-impl-test.c: New common test
> code.
> ---
> gcc/config/loongarch/genopts/loongarch.opt.in | 11 ++++--
> gcc/config/loongarch/loongarch-opts.cc | 38 ++++++++++++++++++
> gcc/config/loongarch/loongarch.opt | 11 ++++--
> gcc/doc/invoke.texi | 39 +++++++++++++------
> .../loongarch/builtin-trap-impl-amswap-w.c | 6 +++
> .../loongarch/builtin-trap-impl-break-0.c | 6 +++
> .../{trap-1.c => builtin-trap-impl-break-1.c} | 9 ++---
> .../loongarch/builtin-trap-impl-break-32767.c | 6 +++
> ...-default.c => builtin-trap-impl-default.c} | 7 +---
> .../loongarch/builtin-trap-impl-err-1.c | 7 ++++
> .../loongarch/builtin-trap-impl-err-2.c | 7 ++++
> .../loongarch/builtin-trap-impl-test.c | 9 +++++
> 12 files changed, 127 insertions(+), 29 deletions(-)
> create mode 100644
> gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-amswap-w.c
> create mode 100644
> gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-break-0.c
> rename gcc/testsuite/gcc.target/loongarch/{trap-1.c =>
> builtin-trap-impl-break-1.c} (58%)
> create mode 100644
> gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-break-32767.c
> rename gcc/testsuite/gcc.target/loongarch/{trap-default.c =>
> builtin-trap-impl-default.c} (71%)
> create mode 100644
> gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-err-1.c
> create mode 100644
> gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-err-2.c
> create mode 100644
> gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-test.c
>
> diff --git a/gcc/config/loongarch/genopts/loongarch.opt.in
> b/gcc/config/loongarch/genopts/loongarch.opt.in
> index f0c089a928e..82f6106992a 100644
> --- a/gcc/config/loongarch/genopts/loongarch.opt.in
> +++ b/gcc/config/loongarch/genopts/loongarch.opt.in
> @@ -205,9 +205,14 @@ mmax-inline-memcpy-size=
> Target Joined RejectNegative UInteger Var(la_max_inline_memcpy_size)
> Init(1024) Save
> -mmax-inline-memcpy-size=SIZE Set the max size of memcpy to inline,
> default is 1024.
>
> -mbreak-code=
> -Target Joined UInteger Var(la_break_code) Init(-1) Save
> --mbreak-code=CODE Use 'break CODE' for traps supposed to be
> unrecoverable, or an 'amswap.w' instruction leading to INE if CODE is out of
> range.
> +TargetVariable
> +int la_break_code = -1
> +
> +mbuiltin-trap-impl=
> +Target Joined RejectNegative Var(la_builtin_trap_impl) Init("") Save
> +Control how __builtin_trap is code-generated and its runtime behavior.
> +Possible values are \"break-CODE\" (0 <= CODE <= 32767) for a BRK with the
> +given CODE, or \"amswap.w\" for a documented INE.
>
> Enum
> Name(explicit_relocs) Type(int)
> diff --git a/gcc/config/loongarch/loongarch-opts.cc
> b/gcc/config/loongarch/loongarch-opts.cc
> index cacfe370345..3668788b07e 100644
> --- a/gcc/config/loongarch/loongarch-opts.cc
> +++ b/gcc/config/loongarch/loongarch-opts.cc
> @@ -1045,6 +1045,40 @@ loongarch_target_option_override (struct
> loongarch_target *target,
> }
>
>
> +/* Parser for -mbuiltin-trap-impl=STRATEGY. */
> +int
> +loongarch_parse_mbuiltin_trap_impl (const char *val)
> +{
> + if (!val || *val == '\0' || !strcmp (val, "amswap.w"))
> + return -1;
> +
> + if (!strncmp (val, "break-", 6))
> + {
> + const char *code_substr = val + 6;
> + int err = 0;
> + HOST_WIDE_INT r = integral_argument (code_substr, &err, false);
> + if (err || r < 0 || r > 0x7fff)
> + {
> + error ("code %qs invalid for the %qs instruction", code_substr,
> + "break");
> + inform (UNKNOWN_LOCATION,
> + "code for the %qs instruction must be an integer between "
> + "%d and %d, inclusive",
> + "break", 0, 0x7fff);
> + return -1;
> + }
> + return (int) r;
> + }
> +
> + error ("unrecognized strategy for %<-mbuiltin-trap-impl%>: %qs", val);
> + inform (UNKNOWN_LOCATION,
> + "valid values are %<break-CODE%> (CODE between %d and %d, "
> + "inclusive) and %<amswap.w%>",
> + 0, 0x7fff);
> + return -1;
> +}
> +
> +
> /* Resolve options that's not covered by la_target. */
> void
> loongarch_init_misc_options (struct gcc_options *opts,
> @@ -1056,6 +1090,10 @@ loongarch_init_misc_options (struct gcc_options *opts,
> /* -mrecip options. */
> opts->x_recip_mask = loongarch_parse_mrecip_scheme (opts->x_la_recip_name);
>
> + /* -mbuiltin-trap-impl. */
> + opts->x_la_break_code
> + = loongarch_parse_mbuiltin_trap_impl (opts->x_la_builtin_trap_impl);
> +
> #define INIT_TARGET_FLAG(NAME, INIT) \
> { \
> if (!(opts_set->x_target_flags & MASK_##NAME)) \
> diff --git a/gcc/config/loongarch/loongarch.opt
> b/gcc/config/loongarch/loongarch.opt
> index 628eabe8d59..12d9c538dc5 100644
> --- a/gcc/config/loongarch/loongarch.opt
> +++ b/gcc/config/loongarch/loongarch.opt
> @@ -213,9 +213,14 @@ mmax-inline-memcpy-size=
> Target Joined RejectNegative UInteger Var(la_max_inline_memcpy_size)
> Init(1024) Save
> -mmax-inline-memcpy-size=SIZE Set the max size of memcpy to inline,
> default is 1024.
>
> -mbreak-code=
> -Target Joined UInteger Var(la_break_code) Init(-1) Save
> --mbreak-code=CODE Use 'break CODE' for traps supposed to be
> unrecoverable, or an 'amswap.w' instruction leading to INE if CODE is out of
> range.
> +TargetVariable
> +int la_break_code = -1
> +
> +mbuiltin-trap-impl=
> +Target Joined RejectNegative Var(la_builtin_trap_impl) Init("") Save
> +Control how __builtin_trap is code-generated and its runtime behavior.
> +Possible values are \"break-CODE\" (0 <= CODE <= 32767) for a BRK with the
> +given CODE, or \"amswap.w\" for a documented INE.
>
> Enum
> Name(explicit_relocs) Type(int)
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index df4331fbad0..060f70c4d91 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -1155,7 +1155,7 @@ Objective-C and Objective-C++ Dialects}.
> -mfpu=@var{fpu-type} -msimd=@var{simd-type}
> -msoft-float -msingle-float -mdouble-float -mlsx -mno-lsx -mlasx -mno-lasx
> -mbranch-cost=@var{n} -maddr-reg-reg-cost=@var{n} -mcheck-zero-division
> --mno-check-zero-division -mbreak-code=@var{code}
> +-mno-check-zero-division -mbuiltin-trap-impl=@var{strategy}
> -mcond-move-int -mno-cond-move-int
> -mcond-move-float -mno-cond-move-float
> -memcpy -mno-memcpy -mstrict-align -mno-strict-align -G @var{num}
> @@ -16333,7 +16333,7 @@ as a parameter of @code{free} call or compared with
> @code{NULL}. If
> with @option{-fmalloc-dce=2} also comparisons with @code{NULL} pointer are
> considered safe to remove.
>
> -The default is @option{-fmalloc-dce=2}. See also @option{-fallocation-dce}.
> +The default is @option{-fmalloc-dce=2}. See also @option{-fallocation-dce}.
>
> @opindex fmove-loop-invariants
> @item -fmove-loop-invariants
> @@ -28757,16 +28757,31 @@ Trap (do not trap) on integer division by zero.
> The default is
> @option{-mcheck-zero-division} for @option{-O0} or @option{-Og}, and
> @option{-mno-check-zero-division} for other optimization levels.
>
> -@opindex mbreak-code
> -@item -mbreak-code=@var{code}
> -Emit a @code{break} @var{code} instruction for irrecoverable traps
> -from @code{__builtin_trap} or inserted by the compiler (for example
> -an erroneous path isolated with
> -@option{-fisolate-erroneous-paths-dereference}), or an
> -@code{amswap.w $r0, $r1, $r0} instruction which will cause the hardware
> -to trigger an Instruction Not-defined Exception if @var{code} is negative
> -or greater than 32767. The default is -1, meaning to use the
> -@code{amswap.w} instruction.
> +@opindex mbuiltin-trap-impl
> +@item -mbuiltin-trap-impl=@var{strategy}
> +Control the code generated for irrecoverable traps from
> +@code{__builtin_trap} or inserted by the compiler (for example an
> +erroneous path isolated with
> +@option{-fisolate-erroneous-paths-dereference}).
> +
> +The following forms are supported for @var{strategy}:
> +
> +@itemize @bullet
> +@item
> +@code{amswap.w}
> +Use @code{amswap.w $r0, $r1, $r0} for traps, which is a documented
> +illegal use of the instruction. An @code{INE} (Instruction
> +non-existent) exception will be triggered at runtime.
> +
> +@item
> +@code{break-CODE}
> +Use @code{break} @var{CODE} for traps. @var{CODE} must be an integer
> +literal between 0 and 32767 inclusive, due to the @code{break}
> +instruction's encoding. A @code{BRK} (Breakpoint) exception will be
> +triggered at runtime.
> +@end itemize
> +
> +The default is to use the @code{amswap.w} strategy.
>
> @opindex mcond-move-int
> @item -mcond-move-int
> diff --git a/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-amswap-w.c
> b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-amswap-w.c
> new file mode 100644
> index 00000000000..2b4d48088ba
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-amswap-w.c
> @@ -0,0 +1,6 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -w -fisolate-erroneous-paths-dereference
> -mbuiltin-trap-impl=amswap.w" } */
> +/* { dg-final { scan-assembler "amswap\\.w\\t\\\$r0,\\\$r1,\\\$r0" } } */
> +
> +#define BUILTIN_TRAP_IMPL_TEST
> +#include "builtin-trap-impl-test.c"
> diff --git a/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-break-0.c
> b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-break-0.c
> new file mode 100644
> index 00000000000..a20960f0a1c
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-break-0.c
> @@ -0,0 +1,6 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -w -fisolate-erroneous-paths-dereference
> -mbuiltin-trap-impl=break-0" } */
> +/* { dg-final { scan-assembler "break\\t0" } } */
> +
> +#define BUILTIN_TRAP_IMPL_TEST
> +#include "builtin-trap-impl-test.c"
> diff --git a/gcc/testsuite/gcc.target/loongarch/trap-1.c
> b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-break-1.c
> similarity index 58%
> rename from gcc/testsuite/gcc.target/loongarch/trap-1.c
> rename to gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-break-1.c
> index 8936f60cce2..fe6a63c777a 100644
> --- a/gcc/testsuite/gcc.target/loongarch/trap-1.c
> +++ b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-break-1.c
> @@ -1,9 +1,6 @@
> /* { dg-do compile } */
> -/* { dg-options "-O2 -w -fisolate-erroneous-paths-dereference
> -mbreak-code=1" } */
> +/* { dg-options "-O2 -w -fisolate-erroneous-paths-dereference
> -mbuiltin-trap-impl=break-1" } */
> /* { dg-final { scan-assembler "break\\t1" } } */
>
> -int
> -bug (void)
> -{
> - return *(int *)0;
> -}
> +#define BUILTIN_TRAP_IMPL_TEST
> +#include "builtin-trap-impl-test.c"
> diff --git
> a/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-break-32767.c
> b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-break-32767.c
> new file mode 100644
> index 00000000000..3ae5b0a68f7
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-break-32767.c
> @@ -0,0 +1,6 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -w -fisolate-erroneous-paths-dereference
> -mbuiltin-trap-impl=break-0x7fff" } */
> +/* { dg-final { scan-assembler "break\\t32767" } } */
> +
> +#define BUILTIN_TRAP_IMPL_TEST
> +#include "builtin-trap-impl-test.c"
> diff --git a/gcc/testsuite/gcc.target/loongarch/trap-default.c
> b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-default.c
> similarity index 71%
> rename from gcc/testsuite/gcc.target/loongarch/trap-default.c
> rename to gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-default.c
> index 32948d4c822..50a17cfc1d5 100644
> --- a/gcc/testsuite/gcc.target/loongarch/trap-default.c
> +++ b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-default.c
> @@ -2,8 +2,5 @@
> /* { dg-options "-O2 -w -fisolate-erroneous-paths-dereference" } */
> /* { dg-final { scan-assembler "amswap\\.w\\t\\\$r0,\\\$r1,\\\$r0" } } */
>
> -int
> -bug (void)
> -{
> - return *(int *)0;
> -}
> +#define BUILTIN_TRAP_IMPL_TEST
> +#include "builtin-trap-impl-test.c"
> diff --git a/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-err-1.c
> b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-err-1.c
> new file mode 100644
> index 00000000000..857e5951158
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-err-1.c
> @@ -0,0 +1,7 @@
> +/* { dg-do compile } */
> +/* { dg-options "-mbuiltin-trap-impl=break" } */
> +
> +int dummy;
> +
> +/* { dg-error "unrecognized strategy for '-mbuiltin-trap-impl': 'break'" ""
> { target { "loongarch*-*-*" } } 0 } */
> +/* { dg-message "note: valid values are 'break-CODE' \\(CODE between 0 and
> 32767, inclusive\\) and 'amswap.w'" "" { target { "loongarch*-*-*" } } 0 } */
> diff --git a/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-err-2.c
> b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-err-2.c
> new file mode 100644
> index 00000000000..e6ffd00a535
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-err-2.c
> @@ -0,0 +1,7 @@
> +/* { dg-do compile } */
> +/* { dg-options "-mbuiltin-trap-impl=break-32768" } */
> +
> +int dummy;
> +
> +/* { dg-error "code '32768' invalid for the 'break' instruction" "" { target
> { "loongarch*-*-*" } } 0 } */
> +/* { dg-message "note: code for the 'break' instruction must be an integer
> between 0 and 32767, inclusive" "" { target { "loongarch*-*-*" } } 0 } */
> diff --git a/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-test.c
> b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-test.c
> new file mode 100644
> index 00000000000..a3daf81b478
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/builtin-trap-impl-test.c
> @@ -0,0 +1,9 @@
> +#ifdef BUILTIN_TRAP_IMPL_TEST
> +int
> +bug (void)
> +{
> + return *(int *)0;
> +}
> +#endif
> +
> +int dummy;
--
Xi Ruoyao <[email protected]>