AVR: Provide built-ins for strlen where the string lives in some AS.

This patch adds built-in functions __builtin_avr_strlen_flash,
__builtin_avr_strlen_flashx and __builtin_avr_strlen_memx.
Purpose is that higher-level functions can use __builtin_constant_p
on strlen without raising a diagnostic due to -Waddr-space-convert.


Ok for trunk?

Johann

--

AVR: Provide built-ins for strlen where the string lives in some AS.

This patch adds built-in functions __builtin_avr_strlen_flash,
__builtin_avr_strlen_flashx and __builtin_avr_strlen_memx.
Purpose is that higher-level functions can use __builtin_constant_p
on strlen without raising a diagnostic due to -Waddr-space-convert.

gcc/
        * config/avr/builtins.def (AVR_FIRST_C_ONLY_BUILTIN_ID): New macro.
        (STRLEN_FLASH, STRLEN_FLASHX, STRLEN_MEMX): New DEF_BUILTIN's.
        * config/avr/avr-protos.h (avr_builtin_C_only_p): New.
        * config/avr/avr.cc (avr_builtin_C_only_p): New function.
        (avr_ftype_strlen): New static function.
        (avr_init_builtins): Only provide a built-in when it is supported
        for the compiled language.
        <strlen_flash_node, strlen_flashx_node, strlen_memx_node>: Provide
        new fntypes.
        (avr_fold_builtin) [AVR_BUILTIN_STRLEN_FLASH]
        [AVR_BUILTIN_STRLEN_FLASHX, AVR_BUILTIN_STRLEN_MEMX]: Fold if
        possible.
        * config/avr/avr-c.cc (avr_cpu_cpp_builtins): Only define the
        __BUILTIN_AVR_<NAME> build-in defines when a built-in function
        is available for the compiled language.
        * doc/extend.texi (AVR Built-in Functions): Document
        __builtin_avr_strlen_flash, __builtin_avr_strlen_flashx,
        __builtin_avr_strlen_memx.
libgcc/
        * config/avr/t-avr (LIB1ASMFUNCS): Add _strlen_memx.
        * config/avr/lib1funcs.S <L_strlen_memx, __strlen_memx>: Implement.
diff --git a/gcc/config/avr/avr-c.cc b/gcc/config/avr/avr-c.cc
index 53f15f2be7b..ced541a9ddc 100644
--- a/gcc/config/avr/avr-c.cc
+++ b/gcc/config/avr/avr-c.cc
@@ -500,7 +500,9 @@ avr_cpu_cpp_builtins (cpp_reader *pfile)
      not a specific builtin is available. */
 
 #define DEF_BUILTIN(NAME, N_ARGS, TYPE, CODE, LIBNAME, ATTRS) \
-  cpp_define (pfile, "__BUILTIN_AVR_" #NAME);
+  if (lang_GNU_C ()					      \
+      || ! avr_builtin_C_only_p (AVR_BUILTIN_ ## NAME))	      \
+    cpp_define (pfile, "__BUILTIN_AVR_" #NAME);
 #include "builtins.def"
 #undef DEF_BUILTIN
 
diff --git a/gcc/config/avr/avr-protos.h b/gcc/config/avr/avr-protos.h
index 83137c7f6f6..72eb5163cce 100644
--- a/gcc/config/avr/avr-protos.h
+++ b/gcc/config/avr/avr-protos.h
@@ -21,6 +21,7 @@
 
 extern bool avr_function_arg_regno_p (int r);
 extern void avr_cpu_cpp_builtins (cpp_reader * pfile);
+extern bool avr_builtin_C_only_p (unsigned id);
 extern enum reg_class avr_regno_reg_class (int r);
 extern void asm_globalize_label (FILE *file, const char *name);
 extern void avr_adjust_reg_alloc_order (void);
diff --git a/gcc/config/avr/avr.cc b/gcc/config/avr/avr.cc
index 656d3e7389b..c33beb9b95d 100644
--- a/gcc/config/avr/avr.cc
+++ b/gcc/config/avr/avr.cc
@@ -15689,6 +15689,17 @@ avr_bdesc[AVR_BUILTIN_COUNT] =
   };
 
 
+/* Some of our built-in function are available for GNU-C only:
+   - Built-ins that use named address-spaces.
+   - Built-ins that use fixed-point types.  */
+
+bool
+avr_builtin_C_only_p (unsigned id)
+{
+  return id >= AVR_FIRST_C_ONLY_BUILTIN_ID;
+}
+
+
 /* Implement `TARGET_BUILTIN_DECL'.  */
 
 static tree
@@ -15714,6 +15725,25 @@ avr_init_builtin_int24 (void)
 }
 
 
+/* Return a function signature type similar to strlen, but where
+   the address is qualified by named address-space AS.  */
+
+static tree
+avr_ftype_strlen (addr_space_t as)
+{
+  tree const_AS_char_node
+    = build_qualified_type (char_type_node,
+			    TYPE_QUAL_CONST | ENCODE_QUAL_ADDR_SPACE (as));
+  tree const_AS_ptr_type_node
+    = build_pointer_type_for_mode (const_AS_char_node,
+				   avr_addr_space_pointer_mode (as), false);
+  tree size_ftype_const_AS_char_ptr
+    = build_function_type_list (size_type_node, const_AS_ptr_type_node, NULL);
+
+  return size_ftype_const_AS_char_ptr;
+}
+
+
 /* Implement `TARGET_INIT_BUILTINS' */
 /* Set up all builtin functions for this target.  */
 
@@ -15771,6 +15801,10 @@ avr_init_builtins (void)
 				const_memx_ptr_type_node,
 				NULL);
 
+  tree strlen_flash_node = avr_ftype_strlen (ADDR_SPACE_FLASH);
+  tree strlen_flashx_node = avr_ftype_strlen (ADDR_SPACE_FLASHX);
+  tree strlen_memx_node = avr_ftype_strlen (ADDR_SPACE_MEMX);
+
 #define ITYP(T)                                                         \
   lang_hooks.types.type_for_size (TYPE_PRECISION (T), TYPE_UNSIGNED (T))
 
@@ -15885,8 +15919,15 @@ avr_init_builtins (void)
   tree attr_const = tree_cons (get_identifier ("const"), NULL, NULL);
 
 #define DEF_BUILTIN(NAME, N_ARGS, TYPE, CODE, LIBNAME, ATTRS)		\
-  {									\
+  do {									\
     int id = AVR_BUILTIN_ ## NAME;					\
+    if (! lang_GNU_C ()							\
+	&& avr_builtin_C_only_p (id))					\
+      {									\
+	avr_bdesc[id].fndecl = NULL_TREE;				\
+	break;								\
+      }									\
+									\
     const char *Name = "__builtin_avr_" #NAME;				\
     char *name = (char *) alloca (1 + strlen (Name));			\
 									\
@@ -15894,7 +15935,7 @@ avr_init_builtins (void)
     avr_bdesc[id].fndecl						\
       = add_builtin_function (avr_tolower (name, Name), TYPE, id,	\
 			      BUILT_IN_MD, LIBNAME, ATTRS);		\
-  }
+  } while (0);
 #include "builtins.def"
 #undef DEF_BUILTIN
 
@@ -16161,6 +16202,13 @@ avr_fold_builtin (tree fndecl, int /*n_args*/, tree *arg, bool /*ignore*/)
 			    build_int_cst (val_type, 4));
       }
 
+    case AVR_BUILTIN_STRLEN_FLASH:
+    case AVR_BUILTIN_STRLEN_FLASHX:
+    case AVR_BUILTIN_STRLEN_MEMX:
+      if (tree len = c_strlen (arg[0], 0))
+	return len;
+      break;
+
     case AVR_BUILTIN_ABSHR:
     case AVR_BUILTIN_ABSR:
     case AVR_BUILTIN_ABSLR:
diff --git a/gcc/config/avr/builtins.def b/gcc/config/avr/builtins.def
index 61dbc3a6c1b..c9610897784 100644
--- a/gcc/config/avr/builtins.def
+++ b/gcc/config/avr/builtins.def
@@ -34,6 +34,8 @@
    ATTRS:   Function attributes like "attr_const" for the `const' attribute
             or "NULL_TREE" for no attribute.  */
 
+#define AVR_FIRST_C_ONLY_BUILTIN_ID AVR_BUILTIN_FLASH_SEGMENT
+
 /* Mapped to respective instruction.  */
 
 DEF_BUILTIN (NOP,  -1, void_ftype_void, nothing, NULL, NULL_TREE)
@@ -56,8 +58,19 @@ DEF_BUILTIN (DELAY_CYCLES, -1, void_ftype_uintSI, nothing, NULL, NULL_TREE)
 DEF_BUILTIN (NOPS,         -1, void_ftype_uintSI, nothing, NULL, NULL_TREE)
 DEF_BUILTIN (MASK1,       2, uintQI_ftype_uintQI_uintQI, gen_mask1, NULL, attr_const)
 DEF_BUILTIN (INSERT_BITS, 3, uintQI_ftype_uintSI_uintQI_uintQI, insert_bits, NULL, attr_const)
+
+/* All following built-ins are C only, see avr.cc::avr_builtin_C_only_p()
+ * since they are using named address-spaces or fixed-point types, none
+ * of which are supported for C++.  */
+
 DEF_BUILTIN (FLASH_SEGMENT, 1, intQI_ftype_const_memx_ptr, flash_segment, NULL, attr_const)
 
+/* strlen for ASes so that __builtin_constant_p can be used wthout raising
+   a diagnostic from -Waddr-space-convert in some AVR-LibC headers.  */
+DEF_BUILTIN (STRLEN_FLASH,  1, strlen_flash_node,  nothing, "__strlen_P", attr_const) // AVR-LibC
+DEF_BUILTIN (STRLEN_FLASHX, 1, strlen_flashx_node, nothing, "strlen_PF",  attr_const) // AVR-LibC
+DEF_BUILTIN (STRLEN_MEMX,   1, strlen_memx_node,   nothing, "__strlen_memx", NULL_TREE)
+
 /* ISO/IEC TR 18037 "Embedded C"
    The following builtins are undocumented and used by stdfix.h.  */
 
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 2764597a479..172239216fd 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -17369,14 +17369,6 @@ might increase delay time. @var{ticks} must be a compile-time
 integer constant; delays with a variable number of cycles are not supported.
 @enddefbuiltin
 
-@defbuiltin{int8_t __builtin_avr_flash_segment (const __memx void*)}
-This built-in takes a byte address to the 24-bit
-@ref{AVR Named Address Spaces,address space} @code{__memx} and returns
-the number of the flash segment (the 64 KiB chunk) where the address
-points to.  Counting starts at @code{0}.
-If the address does not point to flash memory, return @code{-1}.
-@enddefbuiltin
-
 @defbuiltin{uint8_t __builtin_avr_insert_bits (uint32_t @var{map}, uint8_t @var{bits}, uint8_t @var{val})}
 Insert bits from @var{bits} into @var{val} and return the resulting
 value. The nibbles of @var{map} determine how the insertion is
@@ -17445,6 +17437,25 @@ Insert @var{count} @code{NOP} instructions.
 The number of instructions must be a compile-time integer constant.
 @enddefbuiltin
 
+@b{All of the following built-in functions are only available for GNU-C}
+
+@defbuiltin{int8_t __builtin_avr_flash_segment (const __memx void*)}
+This built-in takes a byte address to the 24-bit
+@ref{AVR Named Address Spaces,named address space} @code{__memx} and returns
+the number of the flash segment (the 64 KiB chunk) where the address
+points to.  Counting starts at @code{0}.
+If the address does not point to flash memory, return @code{-1}.
+@enddefbuiltin
+
+@defbuiltin{size_t __builtin_avr_strlen_flash (const __flash char*)}
+@defbuiltinx{size_t __builtin_avr_strlen_flashx (const __flashx char*)}
+@defbuiltinx{size_t __builtin_avr_strlen_memx (const __memx char*)}
+These built-ins return the length of a string located in
+named address-space @code{__flash}, @code{__flashx} or @code{__memx},
+respectively.  They are used to support functions like @code{strlen_F} from
+@w{@uref{https://avrdudes.github.io/avr-libc/avr-libc-user-manual/,AVR-LibC}}.
+@enddefbuiltin
+
 @noindent
 There are many more AVR-specific built-in functions that are used to
 implement the ISO/IEC TR 18037 ``Embedded C'' fixed-point functions of
diff --git a/libgcc/config/avr/lib1funcs.S b/libgcc/config/avr/lib1funcs.S
index 580f511eb94..96f20ca8e64 100644
--- a/libgcc/config/avr/lib1funcs.S
+++ b/libgcc/config/avr/lib1funcs.S
@@ -2755,6 +2755,24 @@ DEFUN __fload_4
 #endif /* L_fload_{1|2|3|4} */
 #endif /* if !defined (__AVR_TINY__) */
 
+
+#if !defined (__AVR_TINY__)
+#if defined (L_strlen_memx)
+DEFUN __strlen_memx
+#ifdef __AVR_ERRATA_SKIP_JMP_CALL__
+    tst     r24
+    brmi 1f
+#else
+    sbrs    r24, 7
+#endif
+    XJMP    strlen_PF           ; AVR-LibC
+1:  wmov    24, 22
+    XJMP    strlen              ; AVR-LibC
+ENDF __strlen_memx
+#endif /* L_strlen_memx */
+#endif /* if !defined (__AVR_TINY__) */
+
+
 #if !defined (__AVR_TINY__)
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; memcopy from Address Space __memx to RAM
diff --git a/libgcc/config/avr/t-avr b/libgcc/config/avr/t-avr
index e9fdb98d776..f98d48fc013 100644
--- a/libgcc/config/avr/t-avr
+++ b/libgcc/config/avr/t-avr
@@ -33,7 +33,8 @@ LIB1ASMFUNCS = \
 	_popcountsi2 \
 	_popcountqi2 \
 	_bswapsi2 \
-	_fmul _fmuls _fmulsu
+	_fmul _fmuls _fmulsu \
+	_strlen_memx
 
 # The below functions either use registers that are not present
 # in tiny core, or use a different register convention (don't save

Reply via email to