[RFC] Passing Options to Different Parts of the Compiler Using Attributes

Hi!

This is a proposal to expand the Attributes class to support many different options that will be accessible by all parts of the compiler. Please read and give any feedback you may have.

Thanks!
-bw

             Passing Options to Different Parts of the Compiler

Problem

Couple of quick questions:

First, what are the valid types for ? Are they always strings which the target must interpret, or will numeric literals and booleans also be supported? Also, in the proposal, attributes are sometimes quoted and sometimes they are not. I know this is nit-picking, but what is the preferred syntax?

Second, would it make sense to fold the calling convention into this set of attributes? For correctness, attributes cannot be stripped from the IR anyway (as I understand it). The existing calling convention API could be implemented using the new attributes. The old way of specifying calling conventions (before the function name instead of after) would need to be supported until the next break in IR compatibility, but this doesn’t seem like a big issue. Perhaps throw an error if both are present and conflict. This may also mean the calling convention specifier at the call site needs to go away, but is this really needed?

That's a very interesting idea, and might be a great extension after the basic pieces Bill is proposing goes in and settles!

-Chris

Couple of quick questions:

First, what are the valid types for <value>? Are they always strings which the target must interpret, or will numeric literals and booleans also be supported?

We should be able to support all types of values: strings, integers, boolean, etc.

Also, in the proposal, attributes are sometimes quoted and sometimes they are not. I know this is nit-picking, but what is the preferred syntax?

I made a distinction (perhaps artificial) between target independent and target dependent attributes. The independent ones need no quotes while the dependent ones do. This reflects how they are stored and accessed via the Attributes class. I said "perhaps artificial" because there's no real reason why the target dependent options cannot be specified via the attributes enum in the Attributes class. Except, perhaps, to avoid placing target dependent cruft in a general class. :slight_smile: I'm open to arguments against the original design.

Second, would it make sense to fold the calling convention into this set of attributes? For correctness, attributes cannot be stripped from the IR anyway (as I understand it). The existing calling convention API could be implemented using the new attributes. The old way of specifying calling conventions (before the function name instead of after) would need to be supported until the next break in IR compatibility, but this doesn't seem like a big issue. Perhaps throw an error if both are present and conflict. This may also mean the calling convention specifier at the call site needs to go away, but is this really needed?

As Chris mentioned, this sounds like a good idea. We can explore that after the initial implementation. :slight_smile:

-bw

Querying

--------

The attributes are attached to the function. It's therefore trivial to
access
the attributes within the middle- and the back-ends. Here's an example of
how
attributes are queried:

Just had a thought, what about compile options that change
alignment/layout/section placement of globals etc? For example the -G <num>
option in gcc that the mips guys will want to support some day (there are
probably better options but this was the first the came to mind).

"-G num
Put definitions of externally-visible data in a small data section if that
data is no bigger than num bytes. GCC can then access the data more
efficiently; see -mgpopt for details."

So we'll probably want to put attributes on all top level entities and not
just functions.

-eric

IR Changes
----------

The attributes will be specified within the IR. This allows us to generate code
that the user wants. This also has the advantage that it will no longer be
necessary to specify all of the command line options when compiling the bit code
(via 'llc' or 'clang'). E.g., '-mcpu=cortex-a8' will be an attribute and won't
be required on llc's command line. However, explicit flags (like `-mcpu') on the
llc command line will override flags specified in the module.

The core of this proposal is the idea of an "attribute group". As the name
implies, it's a group of attributes that are then referenced by objects within
the IR. An attribute group is a module-level object. The BNF of the syntax is:

attribute_group := attrgroup <attrgroup_id> = { <attribute_list> }
attrgroup_id := #<number>
attribute_list := <attribute> (, <attribute>)*
attribute := <name> (= <value>)?

To use an attribute group, an object references the attribute group's ID:

attribute_group_ref := attrgroup(<attrgroup_id>)

This is an example of an attribute group for a function that should always be
inlined, has stack alignment of 4, and doesn't unwind:

attrgroup #1 = { alwaysinline, nounwind, alignstack=4 }

void @foo() attrgroup(#1) { ret void }

An object may refer to more than one attribute group. In that situation, the
attributes are merged.

Attribute groups are important for keeping `.ll' files readable, because a lot
of functions will use the same attributes. In the degenerative case of a `.ll'
file that corresponds to a single `.c' file, the single `attrgroup' will capture
the command line flags used to build that file.

A few comments on the new syntax:

   1. I think most folks will understand what 'attrgroup' means, but it is a little cryptic.
      How about just 'attributes'? The following reads easier to my eyes:

         attributes #1 = { alwaysinline, nounwind, alignstack=4 }
         void @foo() attributes(#1) { ret void }

   2. Are group references allowed in all attribute contexts (parameter, return value, function)?
      I think the answer should be yes. Also, it might be worth considering using the same attribute
      list syntax in the current context and the new attribute group definition (i.e. comma-separated
      v.s. space-separated). This way we have a consistent syntax for groups of attributes and the
      main addition this proposal adds is to give a name to those attributes for later reference.
      
   3. Can attribute groups and single attributes be inter-mixed?
      For example:
     
         void @foo attrgroup(#1) alwaysinline attrgroup(#2) nounwind

   4. Do we really want the attribute references limited to a number? Code will be more readable
      if you can use actual names that indicate the intent. For example:

         attrgroup #compile_options = { … }
         void @foo attrgroup(#compile_options)

   5. Can attributes be nested? For example:

         attrgroup #1 = { foo, bar }
         attrgroup #2 = { #1, baz }

      Might be nice.

   6. Do we really need to specify the attrgroup keyword twice? (Once in the group definition and once in the use)
      ISTM, that the hash-mark is enough to announce a group reference in the use. For example:

         void @foo #1 alwaysinline #2 no unwind

In other words, I think something like the following might be nicer:

attribute_group := attributes <attrgroup_id> = { <attribute_list> }
attrgroup_id := #<id>
attribute_list := <attribute> ( <attribute>)*
attribute := <name> (= <value>)?
                 > <attribuge_id>

function_def := <attribute_list> <result_type> @<id> ([argument_list]) <attribute_list>

Target-Dependent Attributes in IR
---------------------------------

The front-end is responsible for knowing which target-dependent options are
interesting to the target. Target-dependent attributes are specified as strings,
which are understood by the target's back-end. E.g.:

attrgroup #0 = { "long-calls", "cpu=cortex-a8", "thumb" }

define void @func() attrgroup(#0) { ret void }

The ARM back-end is the only target that knows about these options and what to
do with them.

Some of the `cl::opt' options in the backend could move into attribute groups.
This will clean up the compiler.

Isn't calling these "target-dependent" a little artificial? Surely there are many uses
for string attributes one of which is for target-specific data. I think organizing the
proposal to add these new arbitrary string attributes and using the target-specific bits
as examples will be clearer.

Updating IR
-----------

The current attributes that are specified on functions will be moved into an
attribute group. The LLVM assembly reader will still honor those but when the
assembly file is emitted, those attributes will be output as an attribute group
by the assembly writer. As usual, LLVM 3.3 will be able to read and auto-upgrade
previous bitcode and `.ll' files.

Querying
--------

The attributes are attached to the function. It's therefore trivial to access
the attributes within the middle- and the back-ends. Here's an example of how
attributes are queried:

Attributes &A = F.getAttributes();

// Target-independent attribute query.
A.hasAttribute(Attributes::NoInline);

// Target-dependent attribute query.
A.hasAttribute("no-sse");

// Retrieving value of a target-independent attribute.
int Alignment = A.getIntValue(Attributes::Alignment);

// Retrieving value of a target-dependent attribute.
StringRef CPU = A.getStringValue("cpu");

Maybe some set attribute examples too?

Overall, I think this is a nice addition!

IR Changes
----------

The attributes will be specified within the IR. This allows us to generate code
that the user wants. This also has the advantage that it will no longer be
necessary to specify all of the command line options when compiling the bit code
(via 'llc' or 'clang'). E.g., '-mcpu=cortex-a8' will be an attribute and won't
be required on llc's command line. However, explicit flags (like `-mcpu') on the
llc command line will override flags specified in the module.

The core of this proposal is the idea of an "attribute group". As the name
implies, it's a group of attributes that are then referenced by objects within
the IR. An attribute group is a module-level object. The BNF of the syntax is:

attribute_group := attrgroup <attrgroup_id> = { <attribute_list> }
attrgroup_id := #<number>
attribute_list := <attribute> (, <attribute>)*
attribute := <name> (= <value>)?

To use an attribute group, an object references the attribute group's ID:

attribute_group_ref := attrgroup(<attrgroup_id>)

This is an example of an attribute group for a function that should always be
inlined, has stack alignment of 4, and doesn't unwind:

attrgroup #1 = { alwaysinline, nounwind, alignstack=4 }

void @foo() attrgroup(#1) { ret void }

An object may refer to more than one attribute group. In that situation, the
attributes are merged.

Attribute groups are important for keeping `.ll' files readable, because a lot
of functions will use the same attributes. In the degenerative case of a `.ll'
file that corresponds to a single `.c' file, the single `attrgroup' will capture
the command line flags used to build that file.

A few comments on the new syntax:

   1. I think most folks will understand what 'attrgroup' means, but it is a little cryptic.
      How about just 'attributes'? The following reads easier to my eyes:

         attributes #1 = { alwaysinline, nounwind, alignstack=4 }
         void @foo() attributes(#1) { ret void }

   2. Are group references allowed in all attribute contexts (parameter, return value, function)?
      I think the answer should be yes. Also, it might be worth considering using the same attribute
      list syntax in the current context and the new attribute group definition (i.e. comma-separated
      v.s. space-separated). This way we have a consistent syntax for groups of attributes and the
      main addition this proposal adds is to give a name to those attributes for later reference.

   3. Can attribute groups and single attributes be inter-mixed?
      For example:

         void @foo attrgroup(#1) alwaysinline attrgroup(#2) nounwind

   4. Do we really want the attribute references limited to a number? Code will be more readable
      if you can use actual names that indicate the intent. For example:

         attrgroup #compile_options = { … }
         void @foo attrgroup(#compile_options)

   5. Can attributes be nested? For example:

         attrgroup #1 = { foo, bar }
         attrgroup #2 = { #1, baz }

      Might be nice.

   6. Do we really need to specify the attrgroup keyword twice? (Once in the group definition and once in the use)
      ISTM, that the hash-mark is enough to announce a group reference in the use. For example:

         void @foo #1 alwaysinline #2 no unwind

In other words, I think something like the following might be nicer:

attribute_group := attributes <attrgroup_id> = { <attribute_list> }

Well, if we're picking paints for this bikeshed, are the braces here
necessary? We don't have braces for the other places we have
<attribute_list>s.

attributes #attrs = nounwind alwaysinline noreturn
i8 @foo() #attrs

... as a synonym for ...

i8 @foo() nounwind alwaysinline noreturn

... seems quite clean to me.

All of those can be directly represented in LLVM IR today, but if there were a good reason to, I can see extending attributes to work on globals someday.

-Chris

Querying

--------

The attributes are attached to the function. It's therefore trivial to
access
the attributes within the middle- and the back-ends. Here's an example of
how
attributes are queried:

Just had a thought, what about compile options that change
alignment/layout/section placement of globals etc? For example the -G <num>
option in gcc that the mips guys will want to support some day (there are
probably better options but this was the first the came to mind).

All of those can be directly represented in LLVM IR today, but if there
were a good reason to, I can see extending attributes to work on globals
someday.

I'm a bit worried about this creating an arbitrary line between annotations
that are defined as attributes and annotations that are first-class
keywords in the IR language. It may become a source of confusion for
people. If attributes are only applied to functions, the obvious questions
for users is "why do I need to do things differently for functions vs.
globals?" I'm not saying it's a big issue, but it does seem a bit
inconsistent. If we're implementing these attributes anyway, why not unify
the handling of annotations on functions and global variables?

I’m not saying I’m opposed to it - I’m saying that it is a completely different topic than what Bill is proposing.

-Chris

Querying

--------

The attributes are attached to the function. It's therefore trivial to
access
the attributes within the middle- and the back-ends. Here's an example of
how
attributes are queried:

Just had a thought, what about compile options that change
alignment/layout/section placement of globals etc? For example the -G <num>
option in gcc that the mips guys will want to support some day (there are
probably better options but this was the first the came to mind).

All of those can be directly represented in LLVM IR today, but if there
were a good reason to, I can see extending attributes to work on globals
someday.

Right, I couldn't think of anything that wasn't handled already, but I
wasn't sure if we wanted this to be a single way of encapsulating
information for globals. No opinion either way :slight_smile:

-eric

That's not a bad idea. It may be better to have that as a future expansion, though. Just to get the more incremental development done... But on the whole, I don't see anything wrong with it though.

-bw

IR Changes
----------

The attributes will be specified within the IR. This allows us to generate code
that the user wants. This also has the advantage that it will no longer be
necessary to specify all of the command line options when compiling the bit code
(via 'llc' or 'clang'). E.g., '-mcpu=cortex-a8' will be an attribute and won't
be required on llc's command line. However, explicit flags (like `-mcpu') on the
llc command line will override flags specified in the module.

The core of this proposal is the idea of an "attribute group". As the name
implies, it's a group of attributes that are then referenced by objects within
the IR. An attribute group is a module-level object. The BNF of the syntax is:

attribute_group := attrgroup <attrgroup_id> = { <attribute_list> }
attrgroup_id := #<number>
attribute_list := <attribute> (, <attribute>)*
attribute := <name> (= <value>)?

To use an attribute group, an object references the attribute group's ID:

attribute_group_ref := attrgroup(<attrgroup_id>)

This is an example of an attribute group for a function that should always be
inlined, has stack alignment of 4, and doesn't unwind:

attrgroup #1 = { alwaysinline, nounwind, alignstack=4 }

void @foo() attrgroup(#1) { ret void }

An object may refer to more than one attribute group. In that situation, the
attributes are merged.

Attribute groups are important for keeping `.ll' files readable, because a lot
of functions will use the same attributes. In the degenerative case of a `.ll'
file that corresponds to a single `.c' file, the single `attrgroup' will capture
the command line flags used to build that file.

A few comments on the new syntax:

  1. I think most folks will understand what 'attrgroup' means, but it is a little cryptic.
     How about just 'attributes'? The following reads easier to my eyes:

        attributes #1 = { alwaysinline, nounwind, alignstack=4 }
        void @foo() attributes(#1) { ret void }

I don't have a very strong opinion on this.

  2. Are group references allowed in all attribute contexts (parameter, return value, function)?
     I think the answer should be yes.

It would seem a natural expansion of the attribute groups concept. But I want to make these changes incrementally. So at the beginning this won't happen.

Also, it might be worth considering using the same attribute
     list syntax in the current context and the new attribute group definition (i.e. comma-separated
     v.s. space-separated). This way we have a consistent syntax for groups of attributes and the
     main addition this proposal adds is to give a name to those attributes for later reference.

I also prefer comma separated lists of things. But this could cause some confusion if we expand the concept to parameter attributes. But see below for a potential alternative syntax for the attribute groups.

  3. Can attribute groups and single attributes be inter-mixed?
     For example:

        void @foo attrgroup(#1) alwaysinline attrgroup(#2) nounwind

This will be necessary for backwards compatibility. However, running this through this sequence:

  $ llvm-as < foo.ll | llvm-dis

would produce:

  attrgroup #1 = { ... }
  attrgroup #2 = { ... }
  attrgroup #3 = { alwaysinline, nounwind }

  void @foo() attrgroup(#1) attrgroup(#2) attrgroup(#3)

This is because of how the attributes will be represented internally to LLVM. Let me know if you have strong objections to this.

  4. Do we really want the attribute references limited to a number? Code will be more readable
     if you can use actual names that indicate the intent. For example:

        attrgroup #compile_options = { … }
        void @foo attrgroup(#compile_options)

The problem with this is it limits the number of attribute groups to a specific set -- compile options, non-compile options, etc.. There could be many different attribute groups involved, especially during LTO. I realize that the names will be uniqued. But that just adds a number to the existing name. I also want to avoid partitioning of the attributes into arbitrary groups -- i.e., groups with specific names which imply their usage or type.

  5. Can attributes be nested? For example:

        attrgroup #1 = { foo, bar }
        attrgroup #2 = { #1, baz }

     Might be nice.

I'm not a big fan of this idea. This could open it up to circular attribute groups:

  attrgroup #1 = { foo, #2 }
  attrgroup #2 = { #1, bar }

which I'm opposed to on moral groups. :wink: A less compelling (but IMHO valid) argument is that it makes the internal representation of attributes that much more complex.

  6. Do we really need to specify the attrgroup keyword twice? (Once in the group definition and once in the use)
     ISTM, that the hash-mark is enough to announce a group reference in the use. For example:

        void @foo #1 alwaysinline #2 no unwind

Looking at my example above, my syntax can get a bit wordy. How about this alternative representation?

  define void @foo() attrgroup(#1, #2, #3) { ret void }

I don't have a strong opinion though. You're correct that the hash-number combo unambiguously defines an attribute group's use. If others are amenable to this, I can drop the keyword here.

In other words, I think something like the following might be nicer:

attribute_group := attributes <attrgroup_id> = { <attribute_list> }
attrgroup_id := #<id>
attribute_list := <attribute> ( <attribute>)*
attribute := <name> (= <value>)?
                > <attribuge_id>

function_def := <attribute_list> <result_type> @<id> ([argument_list]) <attribute_list>

So something like this (no references inside of the 'attributes' statement allowed, cf. above)?

  attributes #1 = { noinline, alignstack=4 }
  attributes #2 = { "no-sse" }

  define void @foo() #1 #2 { ret void }

This seems reasonable to me.

Target-Dependent Attributes in IR
---------------------------------

The front-end is responsible for knowing which target-dependent options are
interesting to the target. Target-dependent attributes are specified as strings,
which are understood by the target's back-end. E.g.:

attrgroup #0 = { "long-calls", "cpu=cortex-a8", "thumb" }

define void @func() attrgroup(#0) { ret void }

The ARM back-end is the only target that knows about these options and what to
do with them.

Some of the `cl::opt' options in the backend could move into attribute groups.
This will clean up the compiler.

Isn't calling these "target-dependent" a little artificial? Surely there are many uses
for string attributes one of which is for target-specific data. I think organizing the
proposal to add these new arbitrary string attributes and using the target-specific bits
as examples will be clearer.

It's a bit artificial. I basically want to make a small distinction here where anything not target-specific will be defined inside of LangRef.html. So anything that could be used by all targets should be defined there.

Updating IR
-----------

The current attributes that are specified on functions will be moved into an
attribute group. The LLVM assembly reader will still honor those but when the
assembly file is emitted, those attributes will be output as an attribute group
by the assembly writer. As usual, LLVM 3.3 will be able to read and auto-upgrade
previous bitcode and `.ll' files.

Querying
--------

The attributes are attached to the function. It's therefore trivial to access
the attributes within the middle- and the back-ends. Here's an example of how
attributes are queried:

Attributes &A = F.getAttributes();

// Target-independent attribute query.
A.hasAttribute(Attributes::NoInline);

// Target-dependent attribute query.
A.hasAttribute("no-sse");

// Retrieving value of a target-independent attribute.
int Alignment = A.getIntValue(Attributes::Alignment);

// Retrieving value of a target-dependent attribute.
StringRef CPU = A.getStringValue("cpu");

Maybe some set attribute examples too?

That would be done through the current AttrBuilder class:

  AttrBuilder B;

  // Add a target-independent attribute.
  B.addAttribute(Attributes::NoInline);

  // Add a target-dependent attribute.
  B.addAttribute("no-sse");

  // Create the attribute object.
  Attributes A = Attributes::get(Context, B);

Overall, I think this is a nice addition!

Thanks!

-bw

I prefer them, because it makes it clearer (to me) which attributes are added to the group. It's possible that the list could get long.

-bw

Now that I think about it, this isn't the output. Here's what it would look like:

a.ll:
  attributes #1 = { "no-sse" }
  attributes #2 = { noredzone }

  define void @foo() #1 #2 alwaysinline nounwind { ret void }

Here's the output:

  $ llvm-as < a.ll | llvm-dis

  attributes #1 = { "no-sse" }
  attributes #2 = { noredzone }
  attributes #3 = { "no-sse", noredzone, alwaysinline, nounwind }

  define void @foo() #3 { ret void }

This is because all of the attribute groups that a function references will be merged into one attribute object. When we output the attribute object, we don't know that the original function referred to two attribute groups and had a couple of extra attributes defined.

In practice, I expect this to happen rarely in non-LTO mode.

-bw

4. Do we really want the attribute references limited to a number? Code will be more readable
    if you can use actual names that indicate the intent. For example:

       attrgroup #compile_options = { … }
       void @foo attrgroup(#compile_options)

The problem with this is it limits the number of attribute groups to a specific set -- compile options, non-compile options, etc.. There could be many different attribute groups involved, especially during LTO. I realize that the names will be uniqued. But that just adds a number to the existing name. I also want to avoid partitioning of the attributes into arbitrary groups -- i.e., groups with specific names which imply their usage or type.

My main concern is that I see no reason to limit the id to just numbers in the *language definition*.
That doesn't mean you can't always generate #<number> (in the same way that we do for variable names).
This way it leaves open the possibility of hand-writing nice names.

5. Can attributes be nested? For example:

       attrgroup #1 = { foo, bar }
       attrgroup #2 = { #1, baz }

    Might be nice.

I'm not a big fan of this idea. This could open it up to circular attribute groups:

  attrgroup #1 = { foo, #2 }
  attrgroup #2 = { #1, bar }

which I'm opposed to on moral groups. :wink: A less compelling (but IMHO valid) argument is that it makes the internal representation of attributes that much more complex.

Fair enough.

In other words, I think something like the following might be nicer:

attribute_group := attributes <attrgroup_id> = { <attribute_list> }
attrgroup_id := #<id>
attribute_list := <attribute> ( <attribute>)*
attribute := <name> (= <value>)?
               > <attribuge_id>

function_def := <attribute_list> <result_type> @<id> ([argument_list]) <attribute_list>

So something like this (no references inside of the 'attributes' statement allowed, cf. above)?

  attributes #1 = { noinline, alignstack=4 }
  attributes #2 = { "no-sse" }

  define void @foo() #1 #2 { ret void }

This seems reasonable to me.

Me too. This seem pretty close to what was implemented in the patches posted on
llvm-commits. I review those in a bit.

4. Do we really want the attribute references limited to a number? Code will be more readable
   if you can use actual names that indicate the intent. For example:

      attrgroup #compile_options = { … }
      void @foo attrgroup(#compile_options)

The problem with this is it limits the number of attribute groups to a specific set -- compile options, non-compile options, etc.. There could be many different attribute groups involved, especially during LTO. I realize that the names will be uniqued. But that just adds a number to the existing name. I also want to avoid partitioning of the attributes into arbitrary groups -- i.e., groups with specific names which imply their usage or type.

My main concern is that I see no reason to limit the id to just numbers in the *language definition*.
That doesn't mean you can't always generate #<number> (in the same way that we do for variable names).
This way it leaves open the possibility of hand-writing nice names.

Okay. It shouldn't be too difficult to do that.

In other words, I think something like the following might be nicer:

attribute_group := attributes <attrgroup_id> = { <attribute_list> }
attrgroup_id := #<id>
attribute_list := <attribute> ( <attribute>)*
attribute := <name> (= <value>)?
              > <attribuge_id>

function_def := <attribute_list> <result_type> @<id> ([argument_list]) <attribute_list>

So something like this (no references inside of the 'attributes' statement allowed, cf. above)?

  attributes #1 = { noinline, alignstack=4 }
  attributes #2 = { "no-sse" }

  define void @foo() #1 #2 { ret void }

This seems reasonable to me.

Me too. This seem pretty close to what was implemented in the patches posted on
llvm-commits. I review those in a bit.

I made one change. I made the attributes in the attribute groups non-comma separated. Otherwise, it's pretty much the same.

-bw