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) .