meson icon indicating copy to clipboard operation
meson copied to clipboard

Recognise `__attribute__ ((malloc, malloc (fclose, 1)))` in `compiler.has_function_attribute()`

Open thesamesam opened this issue 3 years ago • 5 comments

Describe the bug

Meson doesn't have a way to check for whether the compiler supports "malloc attributes with an argument" like __attribute__ ((malloc, malloc (fclose, 1))). Checking for malloc (which is supported as an atribute already) works but gives a false-positive for support on Clang which does not yet support this extended type (https://github.com/llvm/llvm-project/issues/53152).

(The generic, already-supported malloc attribute simply marks a function as a malloc, while with an argument, it indicates how to free memory allocated by it).

Worked around this in https://github.com/OpenRC/openrc/commit/17de4e5dfdf614332091b856bc05837b2e204a5a.

To Reproduce

project('foo', 'c',
  version : '0.45',
  license: 'BSD-2'
)

cc = meson.get_compiler('c')

# Meson's has_function_attribute doesn't know about GCC's extended
# version (with arguments), so we have to build a test program instead.
#malloc_attribute_test = '''#include<stdlib.h>
#__attribute__ ((malloc (free, 1)))
#int func() { return 0; }
#'''
#if cc.compiles(malloc_attribute_test, name : 'malloc attribute with arguments')
#  add_project_arguments('-DHAVE_MALLOC_EXTENDED_ATTRIBUTE', language: 'c')
#endif

if cc.has_function_attribute('malloc')
    add_project_arguments('-DHAVE_MALLOC_EXTENDED_ATTRIBUTE', language: 'c')
endif

foolib = shared_library('foo', ['foo.c'])

foo.c:

void *foo_free(void* foo);

#ifdef HAVE_MALLOC_EXTENDED_ATTRIBUTE
__attribute__ ((malloc (foo_free, 1)))
#endif
void *foo_new(void);

The build fails with Clang as HAVE_MALLOC_EXTENDED_ATTRIBUTE is defined when it shouldn't be (has_function_attribute is working correctly, it's just that there's no argument to give it for detecting what I want):

$ CC=clang meson compile -C build
ninja: Entering directory `/tmp/foo2/build'
[1/2] Compiling C object libfoo.so.p/foo.c.o
FAILED: libfoo.so.p/foo.c.o
clang -Ilibfoo.so.p -I. -I.. -fcolor-diagnostics -D_FILE_OFFSET_BITS=64 -Wall -Winvalid-pch -Wextra -Wpedantic -std=c99 -O0 -g -DHAVE_MALLOC_EXTENDED_ATTRIBUTE -fPIC -MD -MQ libfoo.so.p/foo.c.o -MF libfoo.so.p/foo.c.o.d -o libfoo.so.p/foo.c.o -c ../foo.c
../foo.c:4:17: error: 'malloc' attribute takes no arguments
__attribute__ ((malloc (foo_free, 1)))
                ^
1 error generated.
ninja: build stopped: subcommand failed.

Expected behavior

compiler.has_function_attribute() should allow specifying malloc-with-argument or similar to detect new enough GCC versions which support this and exclude Clang.

system parameters

  • Plain native build.
  • Gentoo (rolling).
  • Python 3.10.6
  • meson --version: 0.63.1
  • ninja --version: 1.11.0

thesamesam avatar Aug 23 '22 21:08 thesamesam

I would look at the visibility:xxx attributes for inspiration on how to name your function attribute since malloc is already taken.

tristan957 avatar Aug 23 '22 21:08 tristan957

See e.g. commit 5d438b6aedbb074c06f6f9fa2f7972b422ccd1bd for general guidance on how to implement new options for has_function_attribute.

eli-schwartz avatar Sep 04 '22 23:09 eli-schwartz

The visibility:xxx method doesn't seem all that scalable. Here is the code I wrote just now for checking attributes for NumPy (there are quite a few more targets):

# Optional compiler attributes
optional_function_attributes = [
  ['optimize("unroll-loops")', 'OPTIMIZE_UNROLL_LOOPS'],
  ['optimize("O3")', 'OPTIMIZE_OPT_3'],
  ['optimize("O2")', 'OPTIMIZE_OPT_2'],
  ['optimize("nonnull (1)")', 'NONNULL'],
  ]
if host_machine.cpu_family() in ['x86', 'x86_64']
  optional_function_attributes += [
    ['target("avx")', 'TARGET_AVX'],
    ['target("avx2")', 'TARGET_AVX2'],
    ['target("avx512f")', 'TARGET_AVX512F'],
    ['target("avx512f,avx512dq,avx512bw,avx512vl,avx512cd")', 'TARGET_AVX512_SKX'],
  ]
endif
foreach attr: optional_function_attributes
  if cc.has_function_attribute(attr[0])
    cdata.set10('HAVE_ATTRIBUTE_' + attr[1], true)
  endif
endforeach

It seems like for (malloc, malloc (xxx, 1)), optimize("xxx") and target("xxx"), the list of things to check can be arbitrarily long. Would it make sense to allow:

cc.has_function_attribute('malloc:xxx')
cc.has_function_attribute('optimize:xxx')
cc.has_function_attribute('target:xxx')

where xxx can be arbitrary and just does the right thing?

Or should that use cc.compiles instead? If so, where's the line for what to add as builtin attributes?

rgommers avatar Nov 19 '22 20:11 rgommers

I think that either allowing malloc:xxx where xxx is an arbatrary thing, or cc.has_function_attribute('malloc', arguments : 'foo_free, 1') would be fine solutions. There are obviously advantages to both, so I'm not sure which would be better.

In the latter case you'd have to do some interpreter validation, which stinks

The visibility was kinda a hack because macos... It really only works because the chances of getting any sort of new visibility is very low

dcbaker avatar Nov 21 '22 18:11 dcbaker

I like the arguments variant better. Seems more forward compatible.

tristan957 avatar Nov 21 '22 19:11 tristan957