restrict – a key new language feature introduced with C++ AMP
As part of the C99 standard, C has defined the keyword restrict . That feature is not what this blog post is about.
restrict(cpu)
In the Visual C++ compiler shipping with Visual Studio 11 (the one after VS2010), we are making the restrict modifier apply to functions (including lambdas, of course) in the way described below.
You can think of all functions by default to be (invisibly) decorated with this keyword in the following manner:
void myFunction(int a) restrict(cpu)
{
// code that can execute on the CPU
}
For all your existing code that you recompile with the next version, you can think of that as a no-op. You can even add yourself the restrict modifier with the cpu specifier and recompile - you'll see no difference.
Beyond cpu, there is only one other specifier/option (in this first release), that you can specify between the parenthesis following the restrict keyword. In the future we could add more options, but for now there is only "cpu" (without the quotes, of course) plus one more. As a developer, you cannot currently add your own options to the restrict keyword, the usage is reserved for the compiler vendor. So what is the other option you can specify?
restrict(amp)
Aside from cpu, in the next version of the Visual C++ compiler, the only other option you can specify is amp, e.g.
void myFunction(int a) restrict(amp)
{
// Code that can execute on a amp accelerator
// This function cannot be called from restrict(cpu) functions.
}
It is not a reserved word or part of the language in any other way - it is simply contextual. Same applies for cpu.
Note that you can combine restriction specifiers, e.g.
void myFunction(int a) restrict(amp, cpu) //or restrict(cpu, amp)
{
// Code that can execute on the CPU
// and
// can execute on amp accelerators
}
So what does it really mean for a function to be annotated with restrict(amp)?
The initial motivation for this feature was the new C++ AMP technology that enables heterogeneous computing by allowing you to target, in the first release, DirectX devices along with the cpu. In that scenario, the developer needs to be able to declare their intent to the compiler ("I want this code to be able to execute on the GPU") so the compiler can
- enforce the correct subset of the language
- enforce any limitations of the underlying implementation (e.g. Direct3D aka DirectX 11)
- perform special code generation as appropriate
- perform optimizations as appropriate
- provide compile time checking/reporting of the above
The more astute among you will be wondering how can a restrict(amp) function ever be called since, according to the rules above, it cannot be called from a restrict(cpu) function? How does it ever execute on a amp accelerator? The answer is that, in the first release, there is a single exception to that rule: the entry point to C++ AMP, the new parallel_for_each overload, accepts and executes a restrict(amp) lambda – read more about it here.
restrict is truly part of the signature
This new modifier really is part of the language and becomes part of the method signature so, for example, you can overload on it:
double func_A(double) restrict(cpu, amp); // 1: same code for both
double func_B(double); // 2a: general code
double func_B(double) restrict(amp); // 2b: specific code
This is also one of the reasons we couldn't achieve our goal with something like attributes, because we concluded that overloading is a key scenario to enable.
versioning
An FAQ seems to be around versioning. Let's say that in the version after the next version, we wanted to relax the restrictions or change them in some other way, for a specifier, e.g. for restrict(amp). We could then introduce a versioning scheme so you can type e.g. restrict(amp:2), and now you get the v2 set of restrictions. Furthermore, if we wanted to change what version the default restrict(amp) binds to, we could offer a compiler option so you could set it globally instead of changing every occurrence in the source. We could use a combination of versioning specified in the code and compiler options, etc. We have various alternative designs, but none of them need to worry you at this stage, since this is a v1. When the time is right, we will blog about those too.
possible future directions
Another FAQ is if the restrict feature is just for C++ AMP, or if there will be additional specifiers at some point. I stated earlier that the initial motivation was C++ AMP, but the design is certainly generic enough to fit other contexts. The most common example we give for future usage is restrict(pure) which you could apply to a function so the compiler could check if it is free of side effects. Another example is restrict(cloud) that would do magical stuff for you. To be clear, we are not offering any of that in this release, but it is an example of the generality of the feature.
restrictions
If you are wondering, follow this link to read the exact list of restrictions and limitations for restrict(amp) .
Comments
Anonymous
September 06, 2011
Very interesting. When can we get our hands at some kind of pre-release?Anonymous
September 06, 2011
Unfortunately your blog "The Moth" does not allow to leave comments. I hope classes like index<N>, extent<N> and others will also provide compile-time coordinate accessors in addition to operator [], which obviously only accepts run-time value. Something like member function get<I> or free-function get<I,N>(const index<N> &) and others would be great. It seems natural to have those in classes defined this way and will probably simplify generic code that would need to initialize, return or work with those classes.Anonymous
September 06, 2011
x86 already has multiple calling convensions, it is possible to just introduce '__gpu' c.c ( void __gpu myFunction(int a) { } ). It would made function distinguishable in a regular manner.Anonymous
September 07, 2011
Alexander, you should have no problem posting comments on The Moth, can you try posting again there please and let me (daniel.moth@youknowwhere.com) know what the error you are seeing is? All, I'll get to the other questions soon.Anonymous
September 07, 2011
Yuriy, these restriction specifiers are different than calling-convention keywords. As Daniel indicates, we allow overloading on restriction specifiers, but not on calling conventions. The calling-convention syntax is also very awkward for lambdas.Anonymous
September 07, 2011
Axel, when the pre-release bits are available, we will blog about it here.Anonymous
September 07, 2011
Yuriy: I don't think you can overload functions by calling conventions, like with this restrict feature.