This is an archive of the discontinued LLVM Phabricator instance.

[libc] Switch to use a macro which does not insert a section for every libc function.
ClosedPublic

Authored by michaelrj on Jan 6 2021, 2:35 PM.

Details

Summary

The new macro also inserts the C alias for the C++ implementations
without needing an objcopy based post processing step. The CMake
rules have been updated to reflect this. More CMake cleanup can be
taken up in future rounds and appropriate TODOs have been added for them.

Diff Detail

Event Timeline

michaelrj created this revision.Jan 6 2021, 2:35 PM
michaelrj requested review of this revision.Jan 6 2021, 2:35 PM
sivachandra added inline comments.Jan 6 2021, 2:51 PM
libc/src/__support/common.h.def
21

Make the implementation name __##name##_impl__. Just name##_impl might collide with a real thing. You will need to update the name on line 23 also.

michaelrj updated this revision to Diff 315002.Jan 6 2021, 3:01 PM
michaelrj marked an inline comment as done.

fix the alias name in common.h.def

michaelrj updated this revision to Diff 315180.Jan 7 2021, 10:43 AM

update fmaf.cpp to be in line with the rest of the functions.

mcgrathr added inline comments.Jan 7 2021, 12:59 PM
libc/src/__support/common.h.def
20

I'd argue for leaving this decl out just to enforce that you only use this when you've included the public API header that declares the public name.

michaelrj updated this revision to Diff 315253.Jan 7 2021, 3:03 PM
michaelrj marked an inline comment as done.

update the macro in common.h to force the decltypes to use the definition in the __llvm_libc namespace

submit comment

libc/src/__support/common.h.def
20

We don't always include the corresponding .h file so we don't always have the namespace-scoped name available to use with decltype. That is why I have added this declaration.

mcgrathr added inline comments.Jan 7 2021, 4:26 PM
libc/src/__support/common.h.def
20

It is dangerously wrong to define the public API function without its public API declaration in scope.

sivachandra added inline comments.Jan 7 2021, 4:36 PM
libc/src/__support/common.h.def
20

By public API function, do you mean the declaration of say strlen from the public string.h header file?
The declaration here is not declaring the public function from string.h. This macro will be used within the namespace __llvm_libc so it is declaring the internal function here.

namespace __llvm_libc {
LLVM_LIBC_FUNCTION(size_t, strlen, (...)) {
  ...
}
} // namespace __llvm_libc

If we want to avoid such a declaration, we have to include the corresponding internal header (for example, src/string/strlen.h) which we don't do with all functions.

mcgrathr added inline comments.Jan 7 2021, 5:58 PM
libc/src/__support/common.h.def
21

In the Fuchsia build the public symbol also needs a [[gnu::visibility("default")]] attribute.
So I'd recommend adding a macro LLVM_LIBC_FUNCTION_ATTR with #ifndef define it to empty, then put that macro at the front of this line.

michaelrj updated this revision to Diff 315453.Jan 8 2021, 10:59 AM
michaelrj marked an inline comment as done.

Add the macro definition for fuchsia (or others) to hook into for extra attributes on the libc functions

michaelrj updated this revision to Diff 315474.Jan 8 2021, 11:51 AM

fix the previous patch, the macro was placed on the wrong line.

mcgrathr added inline comments.Jan 8 2021, 2:27 PM
libc/src/__support/common.h.def
20

Here's an expanded example:

extern "C" void declared_function(int); // public header                        
namespace local { void declared_function(int); } // private header              
                                                                                
namespace local {                                                               
void declared_function(int);                                                    
decltype(local::declared_function) impl_name __asm__("declared_function");      
decltype(local::declared_function) declared_function [[gnu::alias("declared_function")]];                                                                      
void impl_name(int) {}                                                          
}

That's what you propose. It works fine.

Now introduce a bug:

extern "C" void declared_function(int); // public header                        
namespace local { void declared_function(int); } // private header              
                                                                                
namespace local {                                                               
void declared_function(double);                                                 
decltype(local::declared_function) impl_name __asm__("declared_function");      
decltype(local::declared_function) declared_function [[gnu::alias("declared_function")]];                                                                      
void impl_name(double) {}                                                       
}

The signature mismatch is caught as an overload resolution failure in the first decltype use. Fine.
Now fail to include the private header:

extern "C" void declared_function(int); // public header                        
//namespace local { void declared_function(int); } // private header              
                                                                                
namespace local {                                                               
void declared_function(double);                                                 
decltype(local::declared_function) impl_name __asm__("declared_function");      
decltype(local::declared_function) declared_function [[gnu::alias("declared_function")]];                                                                      
void impl_name(double) {}                                                       
}

Now it compiles fine, but is wrong.

Now consider another version of the case, where we've included the private header but not the public header and have a mismatch between the two.

//extern "C" void declared_function(int); // public header                      
namespace local { void declared_function(double); } // private header           
                                                                                
namespace local {                                                               
void declared_function(double);                                                 
decltype(local::declared_function) impl_name __asm__("declared_function");      
decltype(local::declared_function) declared_function [[gnu::alias("declared_function")]];                                                                      
void impl_name(double) {}                                                       
}

This too compiles, but is wrong.

Now what I would do: Don't declare the namespace symbol, and reference the public symbol in one case and the namespace symbol in the other.

extern "C" void declared_function(double); // public header                     
namespace local { void declared_function(double); } // private header           
                                                                                
namespace local {                                                               
decltype(local::declared_function) impl_name __asm__("declared_function");      
decltype(::declared_function) declared_function [[gnu::alias("declared_function")]];                                                                           
void impl_name(double) {}                                                       
}

This compiles fine. But if you omit the public header, it fails. If you omit the private header, it fails. If the public and private signatures don't match, it fails. There's no way to get the signature wrong and still compile.

sivachandra added inline comments.Jan 8 2021, 3:20 PM
libc/src/__support/common.h.def
20

I agree with all of this and thanks for pointing it out. For the convenience of landing safely and not disrupting downstream uses, I propose to do it in three steps:

  1. Land this change as is.
  2. In the next change, include the internal headers in all of the implementation files and remove the declaration on line 24 of this version of the patch.
  3. Last change, could be experimental or might be possible to actually land it right away: Include the public header in all of the implementation files and change line 26 of this version of the patch to use decltype of the public function.

Step 3 might show us some problems coming from interaction of the internal and pubic headers from the system libc. So, we might have to do that change at some future point in time, or attempt something case by case if they are small/trivial adjustments. For example, we use differently typedefed arg types for internal and public functions in a few cases.

mcgrathr added inline comments.Jan 8 2021, 3:22 PM
libc/src/__support/common.h.def
20

I like this plan.

sivachandra accepted this revision.Jan 8 2021, 3:36 PM

Accepting now so that we can start our 3 step approach to arrive at the final state @mcgrathr proposed.

This revision is now accepted and ready to land.Jan 8 2021, 3:36 PM