time of inline assembler evaluation in template specialization

I tried to use template specialization for selecting
the right inline assembly code.

Example:

No. A program is ill-formed, no diagnostic required, if it contains
a template specialization with no valid instantiations. You cannot
rely on (non-dependent) code in dead template specializations
to not be checked for validity.

John.

Clear, but elegantly bypassing my question :wink:
What is - in this context - validity, then?
Why is it not the mere syntactical correctness of the asm statement?
asm statements are a nonstandard extension, anyway; so clang is free
to do it this or the other way.
If clang would handle it like gcc the combination of template specialization
and assembly language would work; and I'd recommend to follow gcc
here since there is no other standard.

Since you cut away my second question:
clang already behaves inconsistently (from the user perspective)
because it analyzes the register lists but not the statement itself.
That this might be (don't know if it can be with clang) explained
by the different stages of the compiler / assembler is no excuse.

And this behaviour is also present when the assembly code is dependent on
some type parameter.

What do you think?

Regards
Titus

Diagnosing ill-formed code within a template definition is a quality-of-implementation issue. Clang's philosophy is to diagnose everything that is allowed and reasonable. It's certainly reasonable to check the constraints, but our architecture makes it a bit hard to check the actual statement. That's a QoI issue that could conceivably be fixed in the future.

Besides, in the example you give, we're not even talking about a template definition: this is actual code that presumably only works in GCC because one of the inline functions isn't ever emitted. I don't think it makes any sense whatsoever to accept this code.

  - Doug

Sorry to insist, and sorry if I was unclear.
There is no "ill formed", "invalid" or whatsoever bad C++ code in my example.
It's like in any template instantiation / specialization.
It only works because the right (specialized) template code gets selected and emitted.

There is no standard for inline asm, thus doing it like gcc (which is
what http://clang.llvm.org/compatibility.html#inline-asm claims to be clang's intention)
might be a good idea.
gcc analyzes the syntactical correctness of the asm
statement during template code parsing, and analyzes the content of
the asm statement during template instantiation / code generation
(the statement itself is even passed through to gas, not being analyzed by gcc).
With gcc you can write "r4", "blah" or "eax" in the register lists as long as e.g.
the input / output lists are well formed (which can be analyzed at the "C++ code level").
That the register lists take the form of string constants already points at
the different levels of analysis. The register names are no special words
at the "C++ code level" (sorry, have no better phrase)
which is what I feel clang is erroneously trying to make sure.

All I want to say is:
Doing it the gcc way is what I'd suggest for clang. And you already said that it's
"a bit hard" to get clang consistent the other way round: to analyze
the complete content of the asm statement during (template) parsing
before code actually gets emitted.

The sense to accept this code is because one gains the option of
using template specialization instead of having to rely on the preprocessor
to select the right (assembly) code which is perfectly reasonable.
The software I'm working on contains quite a bit of platform specific
(specialized) template code that gets selected based on CPU, OS, and the like.
Being able to use assembly inside template specializations surely isn't
an everyday usage case but definitely can make "sense".
In the case that brought up my question it's a specialized lock that also
works on platforms that lack std::atomic_flag (e.g. MacOS/clang :wink:

Maybe it's helpful looking at clang's code?
When time permits I'd try to figure out what would be needed
to get clang gcc compatible.

Regards
Titus

+1, from C++ point of view assembly is just a const char * passed to built-in asm() call.

Diagnosing ill-formed code within a template definition is a quality-of-implementation issue. Clang's philosophy is to diagnose everything that is allowed and reasonable. It's certainly reasonable to check the constraints, but our architecture makes it a bit hard to check the actual statement. That's a QoI issue that could conceivably be fixed in the future.

Besides, in the example you give, we're not even talking about a template definition: this is actual code that presumably only works in GCC because one of the inline functions isn't ever emitted. I don't think it makes any sense whatsoever to accept this code.

Sorry to insist, and sorry if I was unclear.
There is no "ill formed", "invalid" or whatsoever bad C++ code in my example.

There is ill-formed use of a GCC/Clang extension: you have a clobber list that contains registers not valid for the current platform. The fact that it happens to be in an inline function of a template specialization is irrelevant here.

It's like in any template instantiation / specialization.
It only works because the right (specialized) template code gets selected and emitted.

That's not how templates work.

There is no standard for inline asm,

Yes, there is. It's just that this particular form is an extension of the standard form.

  thus doing it like gcc (which is
what Language Compatibility claims to be clang's intention)
might be a good idea.
gcc analyzes the syntactical correctness of the asm
statement during template code parsing, and analyzes the content of
the asm statement during template instantiation / code generation

More precisely, GCC only validates the asm constraints when actually generating code. It has nothing to do with templates. The only interesting thing happening here is that GCC doesn't emit the code for the inline function if it isn't used.

(the statement itself is even passed through to gas, not being analyzed by gcc).
With gcc you can write "r4", "blah" or "eax" in the register lists as long as e.g.
the input / output lists are well formed (which can be analyzed at the "C++ code level").
That the register lists take the form of string constants already points at
the different levels of analysis. The register names are no special words
at the "C++ code level" (sorry, have no better phrase)
which is what I feel clang is erroneously trying to make sure.

Asm constraints are a compiler extension. Why shouldn't the compiler frontend validate them? Because they are in strings? Consider this:
extern "FooBar" void f();
Should the compiler not complain about this function unless it is used?

All I want to say is:
Doing it the gcc way is what I'd suggest for clang.

So you don't want to know about bad asm constraints if you just run an -fsyntax-only check over the file? Because that's what delaying the check to code generation means. Well, I want to get an error then. So I vote for not delaying the check.

  And you already said that it's
"a bit hard" to get clang consistent the other way round: to analyze
the complete content of the asm statement during (template) parsing
before code actually gets emitted.

Forget templates. They have nothing to do with this.

The sense to accept this code is because one gains the option of
using template specialization instead of having to rely on the preprocessor
to select the right (assembly) code which is perfectly reasonable.

What makes the preprocessor a worse choice than templates here?

Maybe it's helpful looking at clang's code?
When time permits I'd try to figure out what would be needed
to get clang gcc compatible.

You're welcome to try. But unless an -fsyntax-only run still detects genuinely invalid uses, I will oppose any patches.

Sebastian

There is no "ill formed", "invalid" or whatsoever bad C++ code in my example.

There is ill-formed use of a GCC/Clang extension: you have a clobber
list that contains registers not valid for the current platform. The
fact that it happens to be in an inline function of a template
specialization is irrelevant here.

No, it is relevant here because that's the use case.

It's like in any template instantiation / specialization.
It only works because the right (specialized) template code gets selected and emitted.

That's not how templates work.

How else should they work?
std::copy is perfectly valid until I instantiate it with e.g. an int instead of
something which has an interface of an iterator.
Analogous to this code.

There is no standard for inline asm,

Yes, there is. It's just that this particular form is an extension of
the standard form.

The standard I'm aware of says
"The asm declaration is conditionally-supported; its meaning is implementation-defined."
which I - admittedly - shortened to "there is no standard".
What standard do you mean?

More precisely, GCC only validates the asm constraints when actually
generating code. It has nothing to do with templates. The only
interesting thing happening here is that GCC doesn't emit the code for
the inline function if it isn't used.

Same with clang. It does not emit code for the unused template specialization.
Otherwise it would/could detect that "ldr r4,=1" is invalid for x86 assembler.
And, of course, it has to do with templates because this is my use case, and one
of the few use case I'm aware of where code analysis and code generation fall
that "far" apart.

Asm constraints are a compiler extension. Why shouldn't the compiler
frontend validate them? Because they are in strings? Consider this:
extern "FooBar" void f();
Should the compiler not complain about this function unless it is used?

To again quote from the standard (I'm aware of):
"Use of a string-literal other than "C" or "C++" is conditionally supported,
with implementation-defined semantics."
If the compiler magically (!) knows already during syntax analysis that "C" is the only
external linkage spec supported, then it *might* ("implementation-defined")
bail out at this stage.
If your fancy super OS happens to have a "COBOL" or even "FooBar" standard,
whatever that might be, porting clang to this OS would mean to make
it emit code with this linkage standard.
In short, yes, I'd rather see it not as part of the fsyntax-only check.
That's why I guess this is a string constant: to mark it as "beyond of C++"
or "implementation defined".
Same in asm statements. Here, clang is not ported but should be constructed
compatible with gcc.

All I want to say is:
Doing it the gcc way is what I'd suggest for clang.

So you don't want to know about bad asm constraints if you just run an
-fsyntax-only check over the file? Because that's what delaying the
check to code generation means. Well, I want to get an error then. So I
vote for not delaying the check.

You're not really trying to tell me that an intended use
case of clang is to fsyntax-only-check asm() register declarations?
With gcc -fsyntax-only, this is not possible, anyway:
int main(void)
{
  register int a=1, b=2;
  asm("xor %1,%0\n\t" : "=&r"(b) : "r"(a) : "blah");
  return b;
}

goes very well through gcc. BUT g++ -fsyntax-only:

  asm("xor %1,%0\n\t" : "=&r"(blah) : "r"(a) : "eax");

gets the right error
asmtst2.cpp: In function ‘int main()’:
asmtst2.cpp:5: error: ‘blah’ was not declared in this scope
asmtst2.cpp:5: error: lvalue required in asm statement
asmtst2.cpp:5: error: invalid lvalue in asm output 0

And exactly this is the behaviour I'd recommend
to make clang compatible with. It is consistent because
what can be analyzed with a syntax check on "C++ level"
get's done, and *all* of the rest goes to code generation phase.
And I expect that to be less "hard to implement" than
making it consistent the other way round.

The sense to accept this code is because one gains the option of
using template specialization instead of having to rely on the preprocessor
to select the right (assembly) code which is perfectly reasonable.

What makes the preprocessor a worse choice than templates here?

First, this is a matter of taste, and thus is not really an argument.
Second because there *is* existing code which relies on gcc's (consistent)
different levels of inline assembly checking.
It can be compared to Stroustrup's known dislike of the preprocessor but
- of course - keeping it in the language definition.

There is no "ill formed", "invalid" or whatsoever bad C++ code in my example.

There is ill-formed use of a GCC/Clang extension: you have a clobber
list that contains registers not valid for the current platform. The
fact that it happens to be in an inline function of a template
specialization is irrelevant here.

No, it is relevant here because that's the use case.

The "use case" argument is separate from the "it's a template" argument.

It's like in any template instantiation / specialization.
It only works because the right (specialized) template code gets selected and emitted.

That's not how templates work.

How else should they work?
std::copy is perfectly valid until I instantiate it with e.g. an int instead of
something which has an interface of an iterator.
Analogous to this code.

It is not analogous.

In your std::copy() example, the expressions that become ill-formed when instantiating std::copy with an 'int' were valid at template definition time, because they depend on a template parameter. For example, this template definition is fine:

  template<typename T> void f1(T x, T y) { x + y; } // x + y depends on a template parameter

but instantiating it with int* would make the program ill-formed. In the other hand, this template definition

  template<typename T> void f1(int* x, int *y) { x + y; } // ill-formed, x + y doesn't depend on a template parameter

is ill-formed per C++ 14.6p8 (since there are no valid instantiations of this template), but that implementations are not *required* to diagnose the problem.

Note that with your asm example:

  asm("ldr r4,=1\n\t" ::: "r4");

the clobber list does not depend on a template parameter, so if we extend C++ standard semantics to the "asm" statement in the natural way, this asm statement is ill-formed regardless of whether it occurs in a template or not.

That's the first technical issue with the "it's a template" argument.

The second technical issue with the "it's a template" argument is that your example is in fact not a template:

template<>
struct A<8> {
void f(void) {
   asm("ldr r4,=1\n\t" ::: "r4");
}
};

That's a class template specialization, but it's not a template because there are no template parameters. Hence, the compiler is required to fully type-check the definition of this class template specialization and it's members, so the discussion above is moot.

All I want to say is:
Doing it the gcc way is what I'd suggest for clang.

So you don't want to know about bad asm constraints if you just run an
-fsyntax-only check over the file? Because that's what delaying the
check to code generation means. Well, I want to get an error then. So I
vote for not delaying the check.

You're not really trying to tell me that an intended use
case of clang is to fsyntax-only-check asm() register declarations?

There's an important invariant here that if a program passes semantic analysis, it builds a well-formed abstract syntax tree (AST). At this point, generating code from that AST should always succeed, and other tools and analyses know they are working with a well-formed program. At this point, code generation should not be able to fail.

The sense to accept this code is because one gains the option of
using template specialization instead of having to rely on the preprocessor
to select the right (assembly) code which is perfectly reasonable.

What makes the preprocessor a worse choice than templates here?

First, this is a matter of taste, and thus is not really an argument.

Opinion is a valid argument; it just has the unfortunate aspect of allowing everyone to be right and still disagree :slight_smile:

There are two important pieces here: (1) using the preprocessor is a solution, and (2) you find it cleaner to use template specialization.

Second because there *is* existing code which relies on gcc's (consistent)
different levels of inline assembly checking.

This is a (differ) valid argument: that Clang should accept this code to be compatible with GCC.

GCC compatibility is a compelling argument, but we always weigh the pros and cons of a particular GCC extension.

How much code will be broken by this?
  Apparently very little, since this is the only report we've seen about this problem since we turned on the integrated assembler more than a year ago.

Is the code that will be broken reasonable?
  This code is (intentionally) ill-formed code that GCC only accepts because it does checking of the clobbers at code generation time. This has more of the characteristics of a GCC bug than a language feature.

Is accepting this code detrimental to the language or other users?
  Yes, since moving this checking to code generation time makes Clang accept ill-formed programs. For example, users in an environment that provide type-checking via Clang (without using code generation) would no longer diagnose errors in asm clobbers.

Is there a workaround?
  Yes, through the use of the preprocessor.

This extension fails basically all of the criteria we use to decide when to implement an extension in Clang.

  - Doug

There is no "ill formed", "invalid" or whatsoever bad C++ code in my example.

There is ill-formed use of a GCC/Clang extension: you have a clobber
list that contains registers not valid for the current platform. The
fact that it happens to be in an inline function of a template
specialization is irrelevant here.

No, it is relevant here because that's the use case.

But it doesn't change the behavior. Not of Clang, and not of GCC either, I would venture. (Try it. Put an unused static inline function containing an invalid asm constraint in a source file and compile it with GCC. My bet is that there won't be an error, even though no templates are involved.)

It's like in any template instantiation / specialization.
It only works because the right (specialized) template code gets selected and emitted.

That's not how templates work.

How else should they work?
std::copy is perfectly valid until I instantiate it with e.g. an int instead of
something which has an interface of an iterator.
Analogous to this code.

No, because std::copy is a template. Your code consists of full template specializations, which are much closer to normal classes/functions than to templates.

In other words, you just argued that this is supposed to work:

template <typename InIt, typename OutIt>
class CopyAsClass {
OutIt operator()(InIt first, InIt last, OutIt dest) {
for (; first != last; ++first, ++dest) *dest = *first; // fine, dependent operations, late checked
return dest;
}
};
template <>
class CopyAsClass<int, int> {
int operator()(int first, int last, int dest) {
for (; first != last; ++first, ++dest) *dest = *first; // error, int doesn't support dereference
return dest;
}
};

Template specializations are not late-checked.

There is no standard for inline asm,

Yes, there is. It's just that this particular form is an extension of
the standard form.

The standard I'm aware of says
"The asm declaration is conditionally-supported; its meaning is implementation-defined."
which I - admittedly - shortened to "there is no standard".
What standard do you mean?

The standard that says that there exists a construct
'asm' string-literal
which is conditionally supported, with implementation-defined meaning.
So there is a standard, and even though it doesn't say what should happen, it still requires the compiler to document what happens.
But I admit it was a nitpicky point, not really relevant to the discussion.

More precisely, GCC only validates the asm constraints when actually
generating code. It has nothing to do with templates. The only
interesting thing happening here is that GCC doesn't emit the code for
the inline function if it isn't used.

Same with clang. It does not emit code for the unused template specialization.

It does not emit code for the unused inline function. That's a difference as far as the processing is concerned.

Otherwise it would/could detect that "ldr r4,=1" is invalid for x86 assembler.

Only if it does full syntactic analysis of assembly. Which would be nice to have, actually, but is quite a bit of effort that is better spent elsewhere. That is not to say that having it is a *bad thing*.

And, of course, it has to do with templates because this is my use case, and one
of the few use case I'm aware of where code analysis and code generation fall
that "far" apart.

It has nothing to do with templates because the fact that your functions are members of template specializations does not affect processing in *any way whatsoever*. Changing that would actually be a huge disruption in how Clang works.

Asm constraints are a compiler extension. Why shouldn't the compiler
frontend validate them? Because they are in strings? Consider this:
extern "FooBar" void f();
Should the compiler not complain about this function unless it is used?

To again quote from the standard (I'm aware of):
"Use of a string-literal other than "C" or "C++" is conditionally supported,
with implementation-defined semantics."
If the compiler magically (!) knows already during syntax analysis that "C" is the only
external linkage spec supported, then it *might* ("implementation-defined")
bail out at this stage.

What's magical about this? The compiler has lots of target-dependent information during semantic analysis. For example, it knows the size of datatypes.

int ar[sizeof(long) / 8]; // compile error during semantic analysis if sizeof(long) < 8

So why shouldn't the compiler know supported linkage types during semantic analysis?

If your fancy super OS happens to have a "COBOL" or even "FooBar" standard,
whatever that might be, porting clang to this OS would mean to make
it emit code with this linkage standard.
In short, yes, I'd rather see it not as part of the fsyntax-only check.

I don't see an argument here.

That's why I guess this is a string constant: to mark it as "beyond of C++"
or "implementation defined".

No, it's a string constant to allow arbitrary characters, regardless of C++ tokenization rules. This is what makes it not a nightmare to specify "C++" as a valid linkage name.

All I want to say is:
Doing it the gcc way is what I'd suggest for clang.

So you don't want to know about bad asm constraints if you just run an
-fsyntax-only check over the file? Because that's what delaying the
check to code generation means. Well, I want to get an error then. So I
vote for not delaying the check.

You're not really trying to tell me that an intended use
case of clang is to fsyntax-only-check asm() register declarations?

Why not? I would like it.

With gcc -fsyntax-only, this is not possible, anyway:
int main(void)
{
   register int a=1, b=2;
   asm("xor %1,%0\n\t" : "=&r"(b) : "r"(a) : "blah");
   return b;
}

goes very well through gcc.

A shortcoming of GCC, IMO. For example, consider an IDE that runs the compiler to check for syntax errors while you type, and then underlines the errors in your source. I would want that IDE to highlight the invalid "blah" constraint. I would also want that IDE to use -fsyntax-only to save some processing time, because I have no use for it generating code. So I could not use GCC as the compiler to run for this check.

  BUT g++ -fsyntax-only:

   asm("xor %1,%0\n\t" : "=&r"(blah) : "r"(a) : "eax");

gets the right error
asmtst2.cpp: In function ‘int main()’:
asmtst2.cpp:5: error: ‘blah’ was not declared in this scope
asmtst2.cpp:5: error: lvalue required in asm statement
asmtst2.cpp:5: error: invalid lvalue in asm output 0

And exactly this is the behaviour I'd recommend
to make clang compatible with. It is consistent because
what can be analyzed with a syntax check on "C++ level"
get's done, and *all* of the rest goes to code generation phase.

I just don't see that a pair of quotes is such a magical thing that we can't do semantic analysis inside. So no, I don't see this as consistent behavior at all.
We do other semantic analysis in quotes as well:

printf("%d");

emits a warning, because we do semantic analysis inside the quotes.

"\z"

emits an error (or warning), because we do semantic analysis inside the quotes.

For neither of these is it sensible to delay the checking beyond semantic analysis. What makes asm constraints different?

And I expect that to be less "hard to implement" than
making it consistent the other way round.

Well, we can delay the validity check for asm constraints to the Clang CodeGen phase, where we generate LLVM instructions. The diagnostic subsystem would allow that. Of course, we now have broken modularity, by putting a semantic check into CodeGen.
A more reasonable thing to do would be to delay the check to marking a function as used, but then we either need to recheck the function for inline asm (prohibitively expensive) or dedicate a bit in our FunctionDecl AST node to remembering whether there was any inline asm in the function - a rather weak effect for actually changing the AST.
So yes, it's less hard. It's still not easy, and more importantly, it's really unclean.

The preprocessor and careful selection of source files have always been the tools of choice for writing platform specific code. Templates are not. Why should that be different for inline assembly than for any other code? Do you use templates for deciding between a call to a POSIX function or a Win32 function? If so, how do you use templates to decide which header to include?

Sebastian

I see and understand all the points (especially the one with the
AST that always should be translateable into code), but I disagree
labelling this as a "GCC bug" being "detrimental to the language"
because it's not (to my knowledge) violating a "language feature"
in the scope of the standard.
I simply like the gcc way better for the reasons I lengthily explained.
But, of course, I see that I myself rely on a nonstandard compiler feature,
and I'll rewrite my code then to use macros.

Thanks a lot for the time to explain!

Regards
Titus