
Introducing a new, advanced Visual C++ code optimizer - nikbackm
https://blogs.msdn.microsoft.com/vcblog/2016/05/04/new-code-optimizer/
======
kibwen
Undefined behavior notice:

    
    
      > Historically, Visual C++ did not take advantage of the 
      > fact that the C and C++ standards consider the result 
      > of overflowing signed operations undefined. Other 
      > compilers are very aggressive in this regard, which 
      > motivated the decision to implement some patterns which 
      > take advantage of undefined integer overflow behavior. 
      > We implemented the ones we thought were safe and didn’t 
      > impose any unnecessary security risks in generated code.
    
      > A new undocumented compiler flag has been added to 
      > disable these optimizations, in case an application 
      > that is not standard-conformant fails: 
      > -d2SSAOptimizerUndefinedIntOverflow-. Due to security 
      > concerns, we have seen cases where these patterns 
      > should not be optimized, even though following the C 
      > and C++ standards allows us to [...]
    

It sounds like they're not doing anything that GCC/Clang aren't already, but
if you've only ever compiled with MSVC then be careful not to overlook this.

~~~
protomikron
Interesting. Can anybody explain in a few sentences (if this is possible), why
we have undefined behaviour in the first place? I know there are optimization
reasons, but do we have data that confirms that turning on stricter flags (I
think all major compilers let you avoid undefined behaviour) breaks lots of
software?

~~~
PeCaN
(Not guaranteed to be correct)

Long story short: historical accident. Modern safe systems languages like Rust
and Ada¹ avoid undefined behavior unless you explicitly ask for unsafe code,
and even then there aren't many _undefined_ things you can do (violate strict
aliasing I guess).

Most undefined behavior could be implementation-defined, but the edge cases
from porting C+Unix from the PDP-11 (which I understand to have been...
quirky) to many other systems happened before the spec, then the spec authors
had to make the Ideal C Machine a sort of subset of all the existing
behaviors. Thus, some things that could've been implementation-defined ended
up totally undefined (god knows what computers did on signed overflow back
then—the compiler may not have been able to guarantee anything: hence
undefined) and we've kinda stuck that way ever since.

That said, the people who shame the compiler writers for "exploiting undefined
behavior to optimize microbenchmarks and look good" annoy me to no end. That's
just how the spec turned out, put up or shut up (or use a nicer language that
isn't trying to stab you in the back as soon as you let down your
vigilance...).

Here's a fun history of C:
[http://pastebin.com/UAQaWuWG](http://pastebin.com/UAQaWuWG)

¹ Strictly speaking, I recall the Ada spec has some wording similar to
undefined behavior, but it's not something you hit often.

~~~
kibwen

      > even then there aren't many _undefined_ things you can do
    

There's actually plenty of things you can do in an `unsafe` block in Rust that
counts as undefined behavior (I don't know how you "explicitly ask for unsafe
code" in Ada, but I'm confident the same is true there as well). Some of this
falls out of the fact that Rust compiles down to LLVM IR, which itself has
undefined behavior, but others are inherent (like demanding the preservation
of the aliasing guarantees, as you mentioned).

For anyone curious about seriously using unsafe code in Rust (which is to say,
using unsafe code in Rust at all, in any capacity), I recommend reading
through the Rustonomicon: [http://doc.rust-
lang.org/nightly/nomicon/](http://doc.rust-lang.org/nightly/nomicon/)

~~~
pjmlp
> I don't know how you "explicitly ask for unsafe code" in Ada

You need to import a virtual package and some times also make use of specific
pragmas.

Ada, and Modula-3 are very much "into your face" in what concerns unsafe code.

For example, if you intend to do a conversion between data types without
guarantee that the target can can hold a valid data representation, you need
to import Ada.Unchecked_Conversion.

And do something like this:

[http://www.adaic.org/resources/add_content/docs/95style/html...](http://www.adaic.org/resources/add_content/docs/95style/html/sec_5/5-9-1.html)

Same applies to everything else deemed unsafe.

------
cokernel_hacker
So they finally got SSA? Neat, right up there with LLVM and GCC.

It also sounds like they have something like LLVM's InstCombine pass, it'll be
interesting to compare which cases they handle. Despite it being a peephole
pass, InstCombine is actually one of the most important scalar optimizations
in LLVM's arsenal.

I also wonder if they form SSA in the face of C++ and/or SEH exceptions.

Like if you have something like:

    
    
      bool f() {
        int x = 2;
        try {
          throw 0;
        } catch (...) {
          x = 4;
        }
        return x & 1;
      }
    

Will they insert a PHI of 2 and 4? The MSVC of today cannot optimize the
return to a constant.

~~~
inDigiNeous
First time I'm hearing about SSA. Is it something me as a coder should be
thinking about while writing code, or is it purely a compiler phase ? Thanks.

~~~
cokernel_hacker
This is not something you should think about writing code. Unless that code
comprises a compiler :)

SSA is a program representation which is easy for compilers to analyze and
optimize. See
[https://en.wikipedia.org/wiki/Static_single_assignment_form](https://en.wikipedia.org/wiki/Static_single_assignment_form)
for more.

------
mrich
Thumbs up to Microsoft for investing that much into their C/C++ toolchain.

~~~
jjawssd
I believe it is just a C++ toolchain and the C part is neglected. Has
something changed?

~~~
cremno
Since VS2013 some useful and popular C99 language features are supported. And
then there's Clang/C2 but it can't be used via command-line yet.

Their standard library (besides being intentionally not fully compliant) is
missing some C11 features though. I guess we have to wait at least for C++17:

[http://www.open-
std.org/jtc1/sc22/wg21/docs/papers/2016/p006...](http://www.open-
std.org/jtc1/sc22/wg21/docs/papers/2016/p0063r2.html)

~~~
xvilka
Not all of them, and today is 2016, important C11 feature, like _Generic still
not supported. And it would be awesome, if MSVC will support GNU extensions,
like Clang does. For now I recommend to use clang-cl wrapper:
[http://clang.llvm.org/docs/MSVCCompatibility.html](http://clang.llvm.org/docs/MSVCCompatibility.html)

------
zoren
I believe the Bit Estimator example could be better:

    
    
      int test(unsigned char a) {
        short b = a;    // b: 00000000________, a: ________ 
        b <<= 4;        // b: 0000________0000 
        b |= 3;         // b: 0000________0011
        return b != 0;  // -> return true   
      }
    

The analyzer doesn't need any information about the two low bits of b to know
b != 0. Had it been b ^= 3 instead of b |= 3, it would be a different matter.

~~~
gratilup
I tried to have a simple example, maybe it was too simple. You are right, an
OR with a constant is enough to know it is not zero. For this case the Bit
Estimator also knows b = [3, 4083], writing something that takes advantage of
this info would have been more interesting.

------
AaronFriel
The tl;dr of this seems to be:

    
    
        * Visual C++ now has very aggressive SSA-based optimization rules. 
    
        * One of these optimizations is the bit estimator, which tracks the state of each bit of local variables.
    

I'm not aware of LLVM/GCC or other compilers using a bit estimator. I found
some hits in the CS literature but I'm not aware of any other compilers using
it, and it seems like a pretty potent optimization!

~~~
theresistor
LLVM has had the same thing for a very long time, under the name
SimplifyDemandedBits

------
dkopi
Love the a % 2 != 0 ? 4 : 2; optimization. This used to be one of my favorite
interview questions for low-level positions - conditional assignment without
branches.

~~~
wmu
Nice question, I had to think for a while. :)

------
m4dc4pXXX
It bothers me that they say implementing SSA eliminates the need for
"complicated" data-flow analysis. Data-flow has a really nice theory behind it
and enables a lot of optimizations. For example, dead-code elimination. But
otherwise, kudos to MS!

~~~
pbiggar
Except they are exactly right!

~~~
pbiggar
I should add more context here. A data-flow analysis framework has significant
overlap with an SSA-based analysis framework, except that the SSA-based one
leads to simpler analyses which are more powerful.

A good comparison is CCP (conditional copy propagation) vs SCCP (sparse CCP) -
the SCCP version is naturally flow-sensitive (that's a property of SSA form),
so it can eliminate entire branches, leading to more dead code being
eliminated.

~~~
rayiner
Aren't SSA analyses generally only "kinda flow-sensitive" since values are
only renamed where control flow merges, not where it splits? E.g.

    
    
      int x = ...
      if(x > 0) {
        use(x)
      else {
        use(x)
      }
    

In a DFA where you track information about each variable at each program
point, you could push the constraint implied by the condition into each
branch. If you do a sparse analysis on SSA form, that doesn't come naturally,
right?

~~~
nikic
To handle this case some SSA implementations add a concept of "pi" nodes,
which are used to artificially split variables on branches that establish some
useful data flow property.

    
    
        x1 = ...
        if (x1 > 0) {
            x2 = pi(x1 & RANGE[1..])
            use(x2)
        } else {
            x3 = pi(x1 & RANGE[..0])
            use(x3)
        }
        x4 = phi(x2, x3) // if used
    

I have placed the pi nodes in the blocks, but semantically they are placed
along the control edge.

Ref e.g. e-SSA in the ABCD value range inference algorithm.

------
polskibus
I wonder what is the level of .NET's CLR optimization in comparison to this
new optimizer for C++. I hope they will reuse some of those ideas to make .NET
better.

~~~
micahbright
Hopefully, they will just recompile the binaries, and the CLR will just get
faster...

~~~
daeken
That's not really the way it works. The CLR compiles .NET code to native
machine code (either at runtime or ahead of time), which means you need to
implement these optimizations at the code generation layer of the CLR. You can
make the CLR implementation itself faster, but that's negligible compared to
the run time of the generated code.

~~~
micahbright
Are you saying the "code generation layer" of the CLR isn't itself written in
c++?

All of this stuff trickles down without a doubt. I realize it isn't going to
magically optimize the bytecode, however, the thing that runs the bytecode
will be faster.

~~~
ynik
The CLR doesn't have any bytecode interpreter that could get faster with this
change. It's a JIT compiler that compiles the bytecode to native code.

~~~
micahbright
Interpreter or not, the JIT compiler will certainly get faster with the c++
optimizations. The libraries that are linked against will get faster. The
underlying OS components will get faster. Since .NET assemblies are mostly
just glue code between framework code, which is mostly c++, everything should
get a speedup out of this.

------
_RPM
Why do they call it "Visual C++", What does the term "Visual" mean to them?
Also, is their compiler standard compliant? (c++11, c++98)

~~~
Strom
_Visual C++_ is just the product name, it's branding. It has no functional
meaning beyond that.

As for the standards, there's pretty good partial support for C++11/14/17\.
One fairly recent blog post by them has some tables. [1]

[1]
[https://blogs.msdn.microsoft.com/vcblog/2016/01/22/vs-2015-u...](https://blogs.msdn.microsoft.com/vcblog/2016/01/22/vs-2015-update-2s-stl-
is-c17-so-far-feature-complete/)

~~~
StephanTLavavej
Fun fact: the compiler's version (19) is larger than the IDE's version (14)
because the compiler predated the addition of "Visual" to the branding. (And
the IDE skipped 13, while the compiler didn't.)

