}
+/* Read a non-negative decimal number from a string starting at START.
+ Set *END to one position past the last digit of the number. */
+
+static int
+avr_read_number (const char *start, const char **end)
+{
+ int num = 0;
+ for (*end = start; **end >= '0' && **end <= '9'; ++(*end))
+ num = 10 * num + **end - '0';
+
+ return num;
+}
+
+
+/* Return the sum over all [[len=<words>]] annotations in inline asm INSN.
+ TPL is the asm template, N_XOP the number of operands, or -1 when the
+ asm has no operands (not even zero operands). XOP[] are these operands.
+ LOC is the location of the asm, and DEFAULT_LEN is the code length
+ in words as determined by the middle-end from the number of logical and
+ physical line breaks in TPL.
+
+ When an invalid %-reference or an unrecognized [[len=... is found,
+ then return -1. Otherwise, return the sum over all [[len=<words>]]
+ annotations, where:
+
+ <words> = [0-9]+
+ Specifies a non-negative decimal integer.
+
+ <words> = %[0-9]+
+ <words> = %[<name>] # Already resolved to %[0-9]+ by the middle-end.
+ Refers to the respective asm operand, which must be CONST_INT.
+
+ <words> = lds
+ <words> = sts
+ Specifies the length of a LDS or STS instruction, i.e.
+ 1 word if AVR_TINY, and 2 words otherwise.
+
+ <words> = %~
+ <words> = %~call
+ <words> = %~jmp
+ Specifies the length of a %~call resp. %~jmp instruction, i.e.
+ 2 words if AVR_HAVE_JMP_CALL, and 1 word otherwise.
+
+ In order to observe the assigned lengths, see -fdump-rtl-shorten or the
+ asm output with -mlog=insn_addresses. */
+
+static int
+avr_length_of_asm (rtx_insn *insn, int default_len, const char *tpl,
+ int n_xop, rtx *xop, location_t loc)
+{
+ const char *const len_begin = "[[len=";
+ const char *const len_end = "]]";
+ const char *pos = strstr (tpl, len_begin);
+ int len = 0;
+
+ if (! pos)
+ return -1;
+
+ avr_dump ("\n;;; Calculating length of asm insn (default=%d, warn=%d):\n"
+ "%r\n", default_len, avropt_warn_asmlen_notes, insn);
+
+ for (; pos; pos = strstr (pos, len_begin))
+ {
+ const char *const pos_begin = pos;
+ const char *const pos_end = strstr (pos, len_end);
+
+ pos += strlen (len_begin);
+ const bool percent_p = pos[0] == '%' && pos[1] != '~';
+ int inc = -1;
+
+ if (startswith (pos, "%~")
+ // %-resolution only applies to asm with operands.
+ && n_xop >= 0)
+ {
+ // "%~" specifies the length of %~jmp / %~call.
+ inc = AVR_HAVE_JMP_CALL ? 2 : 1;
+ pos += 2;
+
+ // Allow an optional suffix of "jmp" or "call".
+ if (startswith (pos, "jmp"))
+ pos += 3;
+ else if (startswith (pos, "call"))
+ pos += 4;
+ }
+ else if (startswith (pos, "lds")
+ || startswith (pos, "sts"))
+ {
+ // "lds" specifies the length of LDS or STS.
+ inc = AVR_TINY ? 1 : 2;
+ pos += 3;
+ }
+ else if (! percent_p
+ // %-resolution only applies to asm with operands.
+ || n_xop >= 0)
+ {
+ // A plain decimal number <num>, or %<num> to refer to the
+ // <num>-th asm operand. Notice that the middle-end has already
+ // resolved named operand references like %[name] to the respective
+ // operand number (provided such a named operand exists).
+ pos += percent_p;
+ const char *end;
+ const int num = avr_read_number (pos, &end);
+ inc = end > pos ? num : -1;
+ pos = end;
+ }
+
+ /* Prepare a code quote so we have it handy in maybe diagnostics. */
+
+ // Only show the gist of an unrecognized annotation.
+ // Recognized notes are short, so show only the relevant
+ // part and display the rest as ...'s.
+ const char *dots = "...";
+ const size_t l_pos_begin = strlen (pos_begin);
+ const size_t l_pos = pos_end ? pos_end - pos_begin : l_pos_begin;
+ const size_t l_show = pos - pos_begin + 5;
+ char *msg = XALLOCAVEC (char, 1 + strlen (tpl) + strlen (dots));
+ strcpy (msg, pos_begin);
+ if (l_pos > l_show + 2 * strlen (dots))
+ {
+ msg[l_show] = '\0';
+ strcat (msg, dots);
+ if (pos_end)
+ strncat (msg, pos_end - 4, 4);
+ }
+ if (pos_end)
+ strcat (msg, len_end);
+
+ /* Now use inc or diagnose a problem. */
+
+ if (inc >= 0 && pos == pos_end)
+ {
+ pos += strlen (len_end);
+
+ avr_dump (";;; len = %s + %s%d",
+ len ? "len" : "0", percent_p ? "%" : "", inc);
+ if (percent_p)
+ {
+ // Map inc to the value of the inc-th asm operand,
+ // which must be CONST_INT.
+
+ if (inc >= n_xop)
+ {
+ // For asm with >= 0 operands, the middle-end will issue
+ // an error message.
+ avr_dump ("\n;;; Ignored: op %d does not exist\n\n", inc);
+ return -1;
+ }
+ if (! CONST_INT_P (xop[inc]))
+ {
+ avr_dump ("\n;;; Ignored: op %d is not CONST_INT\n\n", inc);
+ warning_at (loc, OPT_Wasm_len_notes, "%<asm%> len annotation"
+ " ignored: operand %d is not a compile-time"
+ " integer constant: %qs", inc, msg);
+ return -1;
+ }
+ inc = UINTVAL (xop[inc]) > 1000000 ? 1000000 : INTVAL (xop[inc]);
+ avr_dump (" = %s + %d", len ? "len" : "0", inc);
+ } // %
+
+ len += inc;
+ avr_dump (" = %d\n", len);
+ }
+ else // inc < 0 || pos != pos_end
+ {
+ // FIXME: Diagnosing is void in LTO mode, except when compiled
+ // with `-ffat-lto-objects' (in which case cc1[plus] is diagnosing,
+ // not lto1).
+ if (pos_end)
+ {
+ avr_dump (";;; Ignored: %s\n\n", msg);
+ warning_at (loc, OPT_Wasm_len_notes, "%<asm%> len annotation"
+ " ignored: %qs", msg);
+ }
+ else
+ {
+ avr_dump (";;; Ignored (no %s): %s\n\n", len_end, msg);
+ warning_at (loc, OPT_Wasm_len_notes, "%<asm%> len annotation"
+ " ignored: misses closing %qs: %qs", len_end, msg);
+ }
+ return -1;
+ }
+ }
+ avr_dump ("\n");
+
+ return len;
+}
+
+
+/* A helper for the function below. Recognize [[len=<words>]] annotations
+ in the inline asm template of INSN, so the user can specify the length
+ of an asm. This can solve two problems:
+
+ - Cases where the expanded asm is longer than determined from the number
+ of physical and logical line breaks. Such cases can lead to errors
+ when a jump that uses a too optimistic jump offset is crossing an asm.
+
+ - Better code generation for jumps that are crossing an asm. The default
+ length of an asm is (1 + NL) * 2 words, where NL denotes the sum of
+ physical and logical line breaks. However, almost all AVR instructions
+ occupy only one 16-bit word.
+
+ LEN is the length in units of 16-bit words as determined by the middle-end.
+ When no annotation has been found or a diagnostic occurred, return -1. */
+
+static int
+avr_maybe_length_of_asm (rtx_insn *insn, int len)
+{
+ if (avropt_asmlen_notes
+ && NONDEBUG_INSN_P (insn))
+ {
+ rtx body = PATTERN (insn);
+
+ if (asm_noperands (body) >= 0)
+ {
+ // An `asm' construct with operands.
+
+ location_t loc;
+ int n_ops = asm_noperands (body);
+ rtx *ops = XALLOCAVEC (rtx, n_ops);
+ const char *tpl = decode_asm_operands (body, ops, NULL, NULL, NULL,
+ &loc);
+ return avr_length_of_asm (insn, len, tpl, n_ops, ops, loc);
+ }
+ else if (GET_CODE (body) == ASM_INPUT)
+ {
+ // An `asm' construct without operands.
+
+ return avr_length_of_asm (insn, len, XSTR (body, 0), -1, nullptr,
+ ASM_INPUT_SOURCE_LOCATION (body));
+ }
+ }
+
+ return -1;
+}
+
+
/* Worker function for `ADJUST_INSN_LENGTH'. */
-/* Modifies the length assigned to instruction INSN
+/* Modifies the length assigned to instruction INSN.
LEN is the initially computed length of the insn. */
int
It is easier to state this in an insn attribute "adjust_len" than
to clutter up code here... */
- if (!NONDEBUG_INSN_P (insn) || recog_memoized (insn) == -1)
+ if (!NONDEBUG_INSN_P (insn))
+ return len;
+
+ if (recog_memoized (insn) == -1)
{
- return len;
+ // Let the user specify the length of the asm with [[len=<words>]]'s.
+ const int len_asm = avr_maybe_length_of_asm (insn, len);
+ return len_asm < 0 ? len : len_asm;
}
/* Read from insn attribute "adjust_len" if/how length is to be adjusted. */
instead of just @code{asm}, then for inlining purposes the size of the asm
is taken as the minimum size, ignoring how many instructions GCC thinks it is.
+@subsubheading Specifying the size of an @code{asm} on AVR
+
+Apart from the default size calculation explained above, since GCC@tie{}17
+there is a way to specify the exact size of an @code{asm} by means of
+@code{[[len=@var{spec}]]} annotations. The annotations must be placed
+in assembly comments in the @code{asm} template:
+In a @code{/* ... */} block comment, or after a @samp{;} that starts
+a single-line comment.
+
+The following @code{@var{spec}}'s are supported. They specify the
+size of the @code{asm} --- or parts of it --- in units of 16-bit words:
+
+@table @code
+@item [0-9]+
+The decimal representation of a non-negative integer.
+
+@item %[0-9]+
+@itemx %[@var{name}]
+The value of the referred operand, which must evaluate to a
+compile-time integer constant.
+
+@item %~
+@itemx %~jmp
+@itemx %~call
+The length of a @code{%~call} or @code{%~jmp} instruction before relaxing.
+
+@item lds
+@itemx sts
+The length of a @code{lds} resp.@: @code{sts} instruction.
+@end table
+
+The following rules apply:
+
+@itemize
+@item All such annotations will be ignored with option
+@ref{avr_masm_len_notes,@option{-mno-asm-len-notes}}. Diagnosing can be
+adjusted with option @ref{avr_Wasm_len_notes,@option{-Wasm-len-notes}}.
+
+@item The length of the @code{asm} is the sum over all
+@code{[[len=@var{spec}]]} notes in the @code{asm} template. When an invalid
+or unrecognizable note is found, then the default length (as calculated from
+the number of physical and logical line breaks) applies.
+
+@item No white-spaces are recognized inside @code{[[...]]} notes, and all
+letters must be lower case. Otherwise, all notes in that asm are ignored,
+and the default length of the @code{asm} is used.
+@end itemize
+
+The actual instruction lengths can be inferred from @samp{;; ADDR = ...}
+assembly comments as generated with, say
+@option{-save-temps -mlog=insn_addresses}.
+Length calculations are also shown in the RTL dump file(s) as generated with
+@option{-fdump-rtl-shorten}.
+
+Lengths annotations can solve two issues:
+
+@itemize
+@item Cases where the expanded assembly code is longer than determined from the
+number of physical and logical line breaks. Such cases can lead to errors when
+a jump that uses a too optimistic jump offset is crossing such assembly code.
+
+@item Better code generation for jumps that are crossing an @code{asm}.
+The default length of an asm is (1 + @var{NL}) * 2 words, where @var{NL}
+denotes the sum of physical and logical line breaks. However, almost
+all AVR instructions occupy only a single 16-bit word.
+@end itemize
+
+
@node Syntax Extensions
@section Other Extensions to C Syntax
-mvectorize-with-neon-quad -mvectorize-with-neon-double}
@emph{AVR Options} (@ref{AVR Options})
-@gccoptlist{-mmcu=@var{mcu} -mabsdata -maccumulate-args -mcvt
--mbranch-cost=@var{cost} -mfuse-add=@var{level} -mfuse-move=@var{level}
+@gccoptlist{-mmcu=@var{mcu} -mabsdata -maccumulate-args -masm-len-notes
+-mcvt -mbranch-cost=@var{cost} -mfuse-add=@var{level} -mfuse-move=@var{level}
-mfuse-move2 -mcall-prologues -mgas-isr-prologues -mint8 -mflmap
-mdouble=@var{bits} -mlong-double=@var{bits} -mno-call-main
-mn_flash=@var{size} -mfract-convert-truncate -mno-interrupts
-mrmw -mstrict-X -mtiny-stack
-mrodata-in-ram -msplit-bit-shift -msplit-ldst -mshort-calls
-mskip-bug -muse-nonzero-bits -nodevicelib -nodevicespecs
--Waddr-space-convert -Wmisspelled-isr}
+-Wasm-len-notes -Waddr-space-convert -Wmisspelled-isr}
@emph{Blackfin Options} (@ref{Blackfin Options})
@gccoptlist{-mcpu=@var{cpu}@r{[}-@var{sirevision}@r{]}
ATtiny40. See also the @code{absdata}
@ref{AVR Attributes,variable attribute}.
+@anchor{avr_masm_len_notes}
+@opindex masm-len-notes
+@item -masm-len-notes
+@itemx -mno-asm-len-notes
+Recognize resp.@: ignore @code{[[len=@var{spec}]]} notes that are placed in
+(comments in) inline assembly code templates. They allow to specify
+the exact @ref{Size of an asm,size of @code{asm} constructs}.
+This option is on per default.
+
@opindex mcvt
@item -mcvt
Use a @emph{compact vector table}. Some devices support a CVT
Warn about conversions between address spaces in the case where the
resulting address space is not contained in the incoming address space.
+@anchor{avr_Wasm_len_notes}
+@opindex Wasm-len-notes
+@item -Wasm-len-notes
+Warn about unrecognized @code{[[len=@var{spec}]]}
+@ref{Size of an asm,length} annotations in inline assembly code templates.
+This @ref{Warning Options,warning} is on per default.
+Notice that in LTO mode, the code must be compiled with option
+@option{-ffat-lto-objects} in order for these diagnoses to appear.
+
@opindex Wmisspelled-isr
@opindex Wno-misspelled-isr
@item -Wmisspelled-isr
{
extern void f7_classify_asm (void);
register uint8_t rclass __asm ("r24");
- __asm ("%~call %x[f]"
+ __asm ("%~call %x[f] ; [[len=%~call]]"
: "=r" (rclass)
: [f] "i" (f7_classify_asm), "z" (aa));
return rclass;
void f7_clr (f7_t *cc)
{
extern void f7_clr_asm (void);
- __asm ("%~call %x[f]"
+ __asm ("%~call %x[f] ; [[len=%~call]]"
:
: [f] "i" (f7_clr_asm), "z" (cc)
: "memory");
f7_t* f7_copy (f7_t *cc, const f7_t *aa)
{
extern void f7_copy_asm (void);
- __asm ("%~call %x[f]"
+ __asm ("%~call %x[f] ; [[len=%~call]]"
:
: [f] "i" (f7_copy_asm), "z" (cc), "x" (aa)
: "memory");
f7_t* f7_copy_P (f7_t *cc, const f7_t *aa)
{
extern void f7_copy_P_asm (void);
- __asm ("%~call %x[f]"
+ __asm ("%~call %x[f] ; [[len=%~call]]"
:
: [f] "i" (f7_copy_P_asm), "x" (cc), "z" (aa)
: "memory");
void f7_copy_mant (f7_t *cc, const f7_t *aa)
{
extern void f7_copy_mant_asm (void);
- __asm ("%~call %x[f]"
+ __asm ("%~call %x[f] ; [[len=%~call]]"
:
: [f] "i" (f7_copy_mant_asm), "z" (cc), "x" (aa)
: "memory");
{
extern void f7_cmp_mant_asm (void);
register int8_t r24 __asm ("r24");
- __asm ("%~call %x[f] ;; %1 %3"
+ __asm ("%~call %x[f] ;; %1 %3 ; [[len=%~call]]"
: "=r" (r24)
: [f] "i" (f7_cmp_mant_asm), "x" (aa), "z" (bb));
return r24;
extern void f7_store_expo_asm (void);
register bool r24 __asm ("r24");
register int16_t rexpo __asm ("r24") = expo;
- __asm ("%~call %x[f] ;; %0 %2 %3"
+ __asm ("%~call %x[f] ;; %0 %2 %3 ; [[len=%~call]]"
: "=r" (r24)
: [f] "i" (f7_store_expo_asm), "z" (cc), "r" (rexpo));
return r24;