>
>
>
64-bits for C++ Developers: from /Wp64 …

Andrey Karpov
Articles: 673

64-bits for C++ Developers: from /Wp64 to Viva64

The development of the 64-bit solutions market has given some new tasks in the field of their verification and testing. The article describes one of such tools - Viva64. It's a lint-like static code analyzer assigned for exposure of errors related with the peculiarities of the 64-bit platforms. The prerequisites for the creation of such an analyzer and its connection with the "Detect 64-Bit Portability Issues" mode in C++ compiler Visual Studio 2005 are covered in the article.

Viva64 tool became a part of PVS-Studio product and is no longer distributed separately. All the abilities of searching for specific errors related to developing 64-bit applications, as well as porting code from 32-bit to 64-bit platform are now available within PVS-Studio analyzer.

One of the most frequent questions I've been asked by the developers of C++ programs is why do we need Viva64 analyzer if there is a built-in means of diagnostics of a code which is being ported to the 64-bit architecture in Visual C++ 2005. This diagnostic tool can be turned on using the /Wp64 compiler key and is called "Detect 64-Bit Portability Issues". The forum answers gave birth to this article. It gives a brief account of what was the prerequisite for the creation of the static code analyzer, Viva64, and what is its distinction from other means of verification and code quality improvement.

The compiler key /Wp64 (Detect 64-Bit Portability Issues) is certainly a good feature for detection of errors related to the migration of application to the 64-bit system. It is able to point to many lines of code which may cause incorrect behavior. But there is an important detail behind all this. Many of the widely-spread C++ language constructions are potentially dangerous from the point of view of 64 bits, but the compiler is not able to display warning messages for them because in most cases they are absolutely correct. Further on, by means of the examples this aspect will be uncovered in detail. Viva64 analyzer carries out a more profound and detailed analysis, discovers potentially dangerous code and makes proper diagnostics. It is not an analogue or a substitute of a /Wp64. It is its expansion and supplement!

Before the Viva64 analyzer release I have participated in the porting of a rather large application to the 64-bit platform, and it was as the following: the first couple of days were spent on compilation of the project at the new architecture. Then a week more was spent on correction of all the dangerous places (at least they seemed to be all), they were diagnosed by the /W4 and /Wp64 keys. As a result, in a week and a half we got the 64-bit variant of the program. The entire source code, except the external libraries, was compiled when the options /W4 and /Wp64 were on, without a single warning. It is also worth mentioning that as this project was developed for several platforms, so, for example, with Linux it was compiled by gcc compiler without warnings with - Wall key. Our team was satisfied and believed that the migration is almost done. The application pretended to work. And we started testing.

Now the trouble began... Errors appeared in the most unexpected locations. As a consequence of this we spent more than two months for debug and correction of the errors found. The corrections were very difficult to make. It turned out that there were no specialized programs for searching errors of such type. The existing lint-like code analyzers were of little help and required many efforts to be set.

You may ask "And what about unit-tests?". They were to narrow down the field of search. Unfortunately, the project has existed for many years, and on the early stages the use of unit-tests was not in practice. As a consequence, the code is covered by them rather fragmentarily. But unfortunately, the lack of unit-tests in our situation had the following consequences: the tests did not cover cases of processing more than 4 gigabytes of data. It is explainable because such processing was just impossible before. Even now the use of such tests is embarrassing. Realization of unit tests with such big arrays leads to enormous time wastes. But the porting to the 64-bit platform was started exactly for the big arrays.

There was nothing to do, but keep on testing and use our eyes for the analysis of the code, and correct it. Later on all this led to the idea of the development of a specialized static analyzer. This analyzer should be aimed at the search of errors which appear when C++ code is being ported to the 64-bit platform. Now let us consider some situations which can be diagnosed by Viva64 analyzer.

The first example is my favourite one. It's the alteration of the virtual function behavior. It can show up very simply - the help system in an MFC application suddenly stops working on the 64-bit platform. Here is the code which illustrates the problem:

class CWinApp { 
  virtual void WinHelp(DWORD_PTR dwData, UINT nCmd); 
};
class CMyApp : public CWinApp { 
  // Don't called in x64 mode
  virtual void WinHelp(DWORD dwData, UINT nCmd); 
};

Once, prototype of the WinHelp virtual function in Visual C++ took a variable of DWORD type as the first argument. And it is quite logical, you also used the DWORD type to override this function at that time. Then the function prototype in the header files of Visual C++ changed and the first argument came to the DWORD_PTR type. On the 32-bit platform everything will keep working properly. But it won't on a 64-bit platform. There will be just two different functions, that's it. No one is to be blamed, and one error has already been found.

If you've got classes with composite inheritance hierarchy and virtual functions, similar errors might hide there. Accordingly, Viva64 finds and diagnoses errors of this type. The compiler with the /Wp64 key remains silent because from its point of view everything is correct.

The second example is an infinite loop.

size_t n = bigValue;
for (unsigned i = 0; i != n; ++i) { ... }

Here is the example of a classical infinite loop if the value of the bigValue variable exceeds the value of UINT_MAX. Even with /Wp64 the compiler has to remain silent because it is a widely-spread operation of comparison of two variables, each of which has different digit capacity in bits. The code is completely correct when bigValue<=UINT_MAX. But when we are developing a 64-bit application we often mean processing of great amount of elements. In this case it is necessary to find and analyze such operations. That's exactly what Viva64 analyzer does. It marks all the comparison operations between 32-bit types and types which become 64-bit ones on a 64-bit platform.

The third example is an incorrect explicit type conversion. Often the errors of clipping of 64-bit types to 32-bit types hide behind the explicit type conversion. The reasons of the existence of such locations in the code may be different. The compiler here has no reasons to show warning. One and the same explicit type conversion can be written in many ways:

size_t a;
int b = (int)a;
int b = (int)(a);     
int b = int(a);
int b = static_cast<int>(a);

The search for the explicit type conversion can be a rather laborious task. And what's more, one needs looking for not all the explicit type conversions, but only dangerous ones from the point of view of program migration.

Here Viva64 analyzer can be helpful again, if it is run in the corresponding search mode.

The fourth example is an incorrect array indexing.

size_t n = bigValue;
unsigned index = 0;
for (size_t i = 0; i != n; ++i)
  array[index++] = 10;

Unfortunately, it is more habitual to use int and unsigned types for array indexing. Never do this! Use only ptrdiff_t and size_t! If you work with arrays containing more than UINT_MAX elements, as we have in the example, the behavior of the algorithm will not be correct.

Unfortunately /Wp64 is unable to help, too. If the compiler starts to warn about the usage of a 32-bit type for indexing, it will reject a large part of your perfectly correct code as defective. The error will occur only when you work with huge arrays, and there may be no such arrays in your program. But if you've got ones, it will be difficult to find similar errors.

Viva64 analyzer allows you to look through each usage of 32-bit variables for indexing an array elements in the program and to make corrections if it is necessary. At the same time it is smart enough not to draw your attention to constructions of the following type:

enum NUM { ZERO, ONE, TWO };
array[0] = array[ONE];

We'd like to suggest you one more reason to appreciate the advantages of Viva64. Imagine that you've got an old "dirty" code of a third-party developer. You compile it with the warnings switched off because there is no sense to correct it. Now imagine that you have to port this code to a 64-bit platform. If you leave the warnings switched off, you'll get a 64-bit code which is unable to work. If you switch on the warnings, you'll spend weeks and months looking through them. It's a realistic but very sad situation. If you use Viva64, you can look through ONLY THOSE parts of the code which are potentially dangerous in the 64-bit context, and you don't have to pay any attention to secondary warnings. This may save your time to a greater extent.

Of course, it's by no means everything that the analyzer can do. But I hope I've given some general estimate to it. I'm sure it can save several millions of nerve cells for somebody allowing to release a 64-bit product in time, and not with a two-months delay, like the company described in the example.

This analyzer is an excellent addition for some other means of verification and enhancement of applications. The examples of such means are /Wp64 in Visual Studio, Gimpel Software's static analyzer PC-Lint or Compuware's BoundsChecker. All these and some other tools are able to lighten the burden for a developer and to speed up software development. I hope that Viva64 will contribute much to that.

I wish you good luck in mastering 64-bit systems!