In the kernel, the architecture s390 uses these builtins to implement __atomic_cmpxchg() and friends. These builtins are polymorphic, so they need some special evaluation. These builtins are known to sparse but with a return type of 'int' and the argument's types being ignored. A problem occurs when used on a pointer type: the expected type doesn't match 'int' and it can give warnings like: warning: non size-preserving integer to pointer cast So, improve the support for these builtins by: *) checking the number of arguments *) extract the type from the 1st argument *) set the returned type to this type if needed *) finally, do the typechecking by calling evaluate_arguments() Reported-by: kernel test robot <lkp@xxxxxxxxx> Link: https://lore.kernel.org/lkml/202008072005.Myrby1lg%25lkp@xxxxxxxxx/ Signed-off-by: Luc Van Oostenryck <luc.vanoostenryck@xxxxxxxxx> --- builtin.c | 60 +++++++++++++++++++++++++++++++++-- validation/builtin-sync-cas.c | 1 - 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/builtin.c b/builtin.c index f29b4a8d518c..2e9be8be8adb 100644 --- a/builtin.c +++ b/builtin.c @@ -355,6 +355,62 @@ static struct symbol_op overflow_p_op = { }; +static int eval_sync_compare_and_swap(struct expression *expr) +{ + struct symbol_list *types = NULL; + struct symbol *ctype = NULL; + struct expression *arg; + int n = 0; + + /* the first arg is a pointer type; we'd already verified that */ + FOR_EACH_PTR(expr->args, arg) { + struct symbol *t = arg->ctype; + + if (!t) + return 0; + + // 2nd & 3rd args must be a basic integer type or a pointer + // 1st arg must be a pointer to such a type. + if (++n == 1) { + if (t->type == SYM_NODE) + t = t->ctype.base_type; + if (!t) + return 0; + if (t->type != SYM_PTR) + goto err; + t = t->ctype.base_type; + if (!t) + return 0; + if (t->type == SYM_NODE) + t = t->ctype.base_type; + if (!t) + return 0; + if (t->type != SYM_PTR && t->ctype.base_type != &int_type) + goto err; + ctype = t; + add_ptr_list(&types, arg->ctype); + } else { + add_ptr_list(&types, ctype); + } + } END_FOR_EACH_PTR(arg); + + if (!expr->ctype) // __sync_val_compare_and_swap() + expr->ctype = ctype; + return evaluate_arguments(types, expr->args); + +err: + sparse_error(arg->pos, "invalid type for argument %d:", n); + info(arg->pos, " %s", show_typename(arg->ctype)); + expr->ctype = &bad_ctype; + return 0; +} + +static struct symbol_op sync_compare_and_swap_op = { + .args = args_triadic, + .evaluate = eval_sync_compare_and_swap, +}; + + /* * Builtin functions */ @@ -548,7 +604,7 @@ static const struct builtin_fn builtins_common[] = { { "__sync_add_and_fetch", &int_ctype, 1, { &ptr_ctype }}, { "__sync_and_and_fetch", &int_ctype, 1, { &ptr_ctype }}, - { "__sync_bool_compare_and_swap", &int_ctype, 1, { &ptr_ctype }}, + { "__sync_bool_compare_and_swap", &bool_ctype, 1, { &ptr_ctype }, .op = &sync_compare_and_swap_op}, { "__sync_fetch_and_add", &int_ctype, 1, { &ptr_ctype }}, { "__sync_fetch_and_and", &int_ctype, 1, { &ptr_ctype }}, { "__sync_fetch_and_nand", &int_ctype, 1, { &ptr_ctype }}, @@ -561,7 +617,7 @@ static const struct builtin_fn builtins_common[] = { { "__sync_or_and_fetch", &int_ctype, 1, { &ptr_ctype }}, { "__sync_sub_and_fetch", &int_ctype, 1, { &ptr_ctype }}, { "__sync_synchronize", &void_ctype, 0 }, - { "__sync_val_compare_and_swap", &int_ctype, 1, { &ptr_ctype }}, + { "__sync_val_compare_and_swap", NULL, 1, { &ptr_ctype }, .op = &sync_compare_and_swap_op }, { "__sync_xor_and_fetch", &int_ctype, 1, { &ptr_ctype }}, { } diff --git a/validation/builtin-sync-cas.c b/validation/builtin-sync-cas.c index e289eba2949b..846e21bb2fbb 100644 --- a/validation/builtin-sync-cas.c +++ b/validation/builtin-sync-cas.c @@ -16,7 +16,6 @@ static _Bool boz(_Bool *ptr) /* * check-name: builtin-sync-cas - * check-known-to-fail * * check-error-start builtin-sync-cas.c:9:49: warning: incorrect type in argument 2 (different base types) -- 2.28.0