[cfe-dev] time of inline assembler evaluation in template specialization

Sebastian Redl sebastian.redl at getdesigned.at
Fri Jun 24 07:51:53 PDT 2011


On 24.06.2011 15:09, Titus von Boxberg wrote:
> Am Fr, 24.06.2011, 11:51 schrieb Sebastian Redl:
>> On 24.06.2011 10:09, Titus von Boxberg wrote:
>>> 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



More information about the cfe-dev mailing list