diff mbox

[v2] implement constant-folding in __builtin_bswap*()

Message ID 20161117095503.8754-1-johannes@sipsolutions.net (mailing list archive)
State Mainlined, archived
Headers show

Commit Message

Johannes Berg Nov. 17, 2016, 9:55 a.m. UTC
Since gcc does this, it's apparently valid to write

 switch (x) {
 case __builtin_bswap16(12):
   break;
 }

but sparse will flag it as an error today.

The constant folding used to be done in the kernel's htons() and
friends, but due to gcc bugs that isn't done anymore since
commit 7322dd755e7d ("byteswap: try to avoid __builtin_constant_p
gcc bug").

To get rid of the sparse errors on every such instance now, just
add constant folding to __builtin_bswap*() in sparse.

Signed-off-by: Johannes Berg <johannes@sipsolutions.net>
---
 lib.c                               | 35 ++++++++++++++++++++++++++++++++---
 validation/bswap-constant-folding.c | 28 ++++++++++++++++++++++++++++
 2 files changed, 60 insertions(+), 3 deletions(-)
 create mode 100644 validation/bswap-constant-folding.c

Comments

Christopher Li Nov. 21, 2016, 2:38 a.m. UTC | #1
On Thu, Nov 17, 2016 at 5:55 PM, Johannes Berg
<johannes@sipsolutions.net> wrote:
> Since gcc does this, it's apparently valid to write
>
>  switch (x) {
>  case __builtin_bswap16(12):
>    break;
>  }
>

Applied. Thanks for the patch. That will definite stop a lot of warning.

There is other possible __builtin_xxx() function gcc expected to get a
const result
when the input arguments are constant.  Ideally, we should be able to
mark __builtin_bswap16
as a special constant "pass through" function. So during the
evaluation and constant propagation,
sparse can pass through the native __builtin_xxx() call to get the
const result. In other words,
sparse shouldn't need to implement "__builtin_bswap16()" by itself. It
can just call to the gcc
one to get the result. Then we can treat all other similar
__builtin_xxx function the same way.

That is a separate patch though.

Chris
--
To unsubscribe from this list: send the line "unsubscribe linux-sparse" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Johannes Berg Nov. 21, 2016, 9:45 a.m. UTC | #2
> There is other possible __builtin_xxx() function gcc expected to get
> a const result when the input arguments are constant.  Ideally, we
> should be able to mark __builtin_bswap16 as a special constant "pass
> through" function. So during the evaluation and constant propagation,
> sparse can pass through the native __builtin_xxx() call to get the
> const result. In other words, sparse shouldn't need to implement
> "__builtin_bswap16()" by itself. It can just call to the gcc
> one to get the result. Then we can treat all other similar
> __builtin_xxx function the same way.

Yes, that'd make some sense. I have no idea how to pull it off though
:)

johannes
--
To unsubscribe from this list: send the line "unsubscribe linux-sparse" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christopher Li Nov. 22, 2016, 11:13 a.m. UTC | #3
On Mon, Nov 21, 2016 at 5:45 PM, Johannes Berg
<johannes@sipsolutions.net> wrote:
> Yes, that'd make some sense. I have no idea how to pull it off though

I take a brief look.

It seems that you need to implement a symbol with ".expand" implement as
"expand_bswap", similar to "expand_constant_p".

Later in in "eval_init_table", just add one entry for __builtin_bswap.
eval_init_table[] = {
{ "__builtin_constant_p", &builtin_fn_type, MOD_TOPLEVEL, &constant_p_op },
{ "__builtin_safe_p", &builtin_fn_type, MOD_TOPLEVEL, &safe_p_op },
{ "__builtin_warning", &builtin_fn_type, MOD_TOPLEVEL, &warning_op },
{ "__builtin_expect", &builtin_fn_type, MOD_TOPLEVEL, &expect_op },
{ "__builtin_choose_expr", &builtin_fn_type, MOD_TOPLEVEL, &choose_op },
{ NULL, NULL, 0 }
};

As you can see in "expand_call", the cost of arguments is zero
if all arguments are constant.

The function "expand_symbol_call" even have the comment about expanding
the builtins there.

In side the "expand_bswap", it should abort if cost is not zero.
It means arguments are not constant, this expression can't
expand into a constant value.

Otherwise, expand_bswap should proceed to calculate the return
value of the bswap call and rewrite the call expression into a constant.
Some what similar to the "__builtin_constant_p".

I will see if I can hack up some thing very quick.

Chris
--
To unsubscribe from this list: send the line "unsubscribe linux-sparse" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Christopher Li Nov. 22, 2016, 11:39 a.m. UTC | #4
On Tue, Nov 22, 2016 at 7:13 PM, Christopher Li <sparse@chrisli.org> wrote:
> On Mon, Nov 21, 2016 at 5:45 PM, Johannes Berg
> <johannes@sipsolutions.net> wrote:
>> Yes, that'd make some sense. I have no idea how to pull it off though
>
> I take a brief look.
>
> It seems that you need to implement a symbol with ".expand" implement as
> "expand_bswap", similar to "expand_constant_p".

Never mind that, I just find out expand stage happen after the evaluation
stage. The warning happen in the evaluation stage. It will need to make some
plan change to get it right.

Chris
--
To unsubscribe from this list: send the line "unsubscribe linux-sparse" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Luc Van Oostenryck Nov. 22, 2016, 1:15 p.m. UTC | #5
On Tue, Nov 22, 2016 at 07:13:11PM +0800, Christopher Li wrote:
> 
> Otherwise, expand_bswap should proceed to calculate the return
> value of the bswap call and rewrite the call expression into a constant.
> Some what similar to the "__builtin_constant_p".
> 
> I will see if I can hack up some thing very quick.
> 
> Chris
> --

I think it would be best to do any change related tos handling
of constant expressions on to of Nicolai Stange's serie:
	http://marc.info/?l=linux-sparse&m=145429372932235

Not only this serie fixes several issues but it handle the
expression constantnes in a much more systematic way.
Also, I wouldn't like that changes in this area make it
more difficult to integrate this serie (which is already waiting
since July or August 2015).

Luc
--
To unsubscribe from this list: send the line "unsubscribe linux-sparse" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/lib.c b/lib.c
index d5b56b012a21..aa2af68bf8d2 100644
--- a/lib.c
+++ b/lib.c
@@ -818,9 +818,38 @@  void declare_builtin_functions(void)
 	add_pre_buffer("extern int __builtin_popcountll(unsigned long long);\n");
 
 	/* And byte swaps.. */
-	add_pre_buffer("extern unsigned short __builtin_bswap16(unsigned short);\n");
-	add_pre_buffer("extern unsigned int __builtin_bswap32(unsigned int);\n");
-	add_pre_buffer("extern unsigned long long __builtin_bswap64(unsigned long long);\n");
+	add_pre_buffer("extern unsigned short ____builtin_bswap16(unsigned short);\n");
+	add_pre_buffer("extern unsigned int ____builtin_bswap32(unsigned int);\n");
+	add_pre_buffer("extern unsigned long long ____builtin_bswap64(unsigned long long);\n");
+	add_pre_buffer("#define __sparse_constant_swab16(x) ((unsigned short)("
+		       "	(((unsigned short)(x) & (unsigned short)0x00ffU) << 8) |"
+		       "	(((unsigned short)(x) & (unsigned short)0xff00U) >> 8)))\n");
+	add_pre_buffer("#define __sparse_constant_swab32(x) ((unsigned int)("
+		       "	(((unsigned int)(x) & (unsigned int)0x000000ffUL) << 24) |"
+		       "	(((unsigned int)(x) & (unsigned int)0x0000ff00UL) <<  8) |"
+		       "	(((unsigned int)(x) & (unsigned int)0x00ff0000UL) >>  8) |"
+		       "	(((unsigned int)(x) & (unsigned int)0xff000000UL) >> 24)))\n");
+	add_pre_buffer("#define __sparse_constant_swab64(x) ((unsigned long long)("
+		       "	(((unsigned long long)(x) & (unsigned long long)0x00000000000000ffULL) << 56) |"
+		       "	(((unsigned long long)(x) & (unsigned long long)0x000000000000ff00ULL) << 40) |"
+		       "	(((unsigned long long)(x) & (unsigned long long)0x0000000000ff0000ULL) << 24) |"
+		       "	(((unsigned long long)(x) & (unsigned long long)0x00000000ff000000ULL) <<  8) |"
+		       "	(((unsigned long long)(x) & (unsigned long long)0x000000ff00000000ULL) >>  8) |"
+		       "	(((unsigned long long)(x) & (unsigned long long)0x0000ff0000000000ULL) >> 24) |"
+		       "	(((unsigned long long)(x) & (unsigned long long)0x00ff000000000000ULL) >> 40) |"
+		       "	(((unsigned long long)(x) & (unsigned long long)0xff00000000000000ULL) >> 56)))\n");
+	add_pre_buffer("#define __builtin_bswap16(x)"
+		       "	(__builtin_constant_p((unsigned short)(x)) ?"
+		       "	__sparse_constant_swab16(x) :"
+		       "	____builtin_bswap16(x))\n");
+	add_pre_buffer("#define __builtin_bswap32(x)"
+		       "	(__builtin_constant_p((unsigned int)(x)) ?"
+		       "	__sparse_constant_swab32(x) :"
+		       "	____builtin_bswap32(x))\n");
+	add_pre_buffer("#define __builtin_bswap64(x)"
+		       "	(__builtin_constant_p((unsigned long long)(x)) ?"
+		       "	__sparse_constant_swab64(x) :"
+		       "	____builtin_bswap64(x))\n");
 
 	/* And atomic memory access functions.. */
 	add_pre_buffer("extern int __sync_fetch_and_add(void *, ...);\n");
diff --git a/validation/bswap-constant-folding.c b/validation/bswap-constant-folding.c
new file mode 100644
index 000000000000..c6511fe62041
--- /dev/null
+++ b/validation/bswap-constant-folding.c
@@ -0,0 +1,28 @@ 
+typedef unsigned short __be16;
+typedef unsigned short __u16;
+typedef unsigned short u16;
+#define __force
+
+#define __swab16(x) (__u16)__builtin_bswap16((__u16)(x))
+/* the test behaves as though it's always on a little-endian machine */
+#define __cpu_to_be16(x) ((__force __be16)__swab16((x)))
+#define ___htons(x) __cpu_to_be16(x)
+#define htons(x) ___htons(x)
+
+#define ETH_P_IPV6 0x86DD
+
+static u16 protocol;
+
+static void test(void)
+{
+	switch (protocol) {
+	case htons(ETH_P_IPV6):
+		break;
+	}
+}
+
+/*
+ * check-name: constant folding in bswap builtins
+ * check-error-start
+ * check-error-end
+ */