3

I'm building an installer/downloader that detects the architecture and downloads the version of the main executable compiled with that architecture. If it doesn't exist yet, it'll be compiled on the server on the fly.

I can easily get the CPU name, e.g. "Intel(R) Core(TM) i9-10900KF CPU @ 3.70GHz", but how do I convert this to the correct string for the -march and -mtune settings for MinGW?

I cannot rely on any dependencies, it need to be able to determine this using a standalone script or executable that can be run by the installer to determine the correct string. Or I could use a massive list of all CPU names converted to -march strings, but where would I find that?

EDIT: I cannot use -march=native because the compiler is not on the client machine. What I'm doing is detecting the architecture on the client machine by an installer, then compiling for their architecture on the server. So I cannot use -march=native.

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
Alasdair
  • 13,348
  • 18
  • 82
  • 138
  • You're looking for `gcc -march=native` (which also implies `-mtune=native`, but it doesn't do any harm do to that manually). – Peter Cordes Apr 08 '23 at 05:31
  • The only thing you could possibly gain over that is with very new CPUs that your GCC version doesn't know about; then `-march=native` still enables all the ISA extensions the CPU has, but it'll use `-mtune=generic` rather than e.g. using Ice Lake tuning for Alder Lake. (This was a bigger deal when `-mtune=generic` still included some stuff that was good for really old CPUs but hurt on newer ones, like splitting 256-bit loads not known to be aligned: [Why doesn't gcc resolve \_mm256\_loadu\_pd as single vmovupd?](https://stackoverflow.com/q/52626726)) – Peter Cordes Apr 08 '23 at 05:31
  • 1
    @PeterCordes no I need to get that value from a client computer without GCC installed, by an installer, then POST that to a server, which will then compile for the client system. – Alasdair Apr 08 '23 at 05:33
  • Ah, I see. And you don't have GCC at all on the client, so you just have a model string from CPUID, not the output of `gcc -march=native -v -c empty.c` or something (which on my Skylake system includes `cc1 ... -march=skylake`) – Peter Cordes Apr 08 '23 at 05:36
  • Something like this maybe? https://github.com/vusec/revanc/blob/master/source/cpuid/intel/cpuid.c – Brecht Sanders Apr 11 '23 at 12:35
  • @BrechtSanders thanks! Only problem is it's not been updated in 7 years. Most CPUs are newer than that. – Alasdair Apr 12 '23 at 14:43
  • 1
    Is it an option to capture and forward some CPUID subvalues other than the model string? – Ben Voigt Apr 12 '23 at 14:54
  • @BenVoigt sure, doesn't have to be the model string. – Alasdair Apr 14 '23 at 02:32

2 Answers2

3

If you can distribute MinGW's gcc.exe compiler driver (just that small file and the libraries it depends on, without the multi-megabyte language front-ends cc1.exe, cc1plus.exe, etc) then you can run it on the client machine with the -### option like this:

gcc.exe -### -xc - -E -march=native

and take the various -m and --param options from the cc1.exe line in the output.

To learn how -march=native works, have a look at driver-i386.cc and common/config/i386 in GCC source code. If you copy from there, I'd suggest to make the result a standalone tool that you can distribute under the GPL, respecting the original licensing of those files.

amonakov
  • 2,324
  • 11
  • 23
  • Alright, I isolated gcc.exe and ran it with your command. It gives me a long list of instruction tuning `"-march=skylake" -mmmx -mpopcnt -msse -msse2 -msse3 -mssse3 -msse4.1 -msse4.2 -mavx -mavx2 -mno-sse4a -mno-fma4 -mno-xop -mfma -mno-avx512f`. It also ends with `fatal error: '-fuse-linker-plugin', but liblto_plugin.dll not found` but I guess that's okay as I already got the tuning strings? So I could just copy the string I get from gcc.exe and then compile using that on another machine. Is that right? – Alasdair Apr 14 '23 at 03:10
  • @Alasdair Regarding the error, it's better to add `-E` to command line instead, I edited the example in the post. Otherwise yes, collect the `-m` and `--param` flags and you should be good. – amonakov Apr 14 '23 at 06:08
  • Interesting, `-###` prints the commands but doesn't run them, so it doesn't actually read from stdin despite running `gcc -`. So no need to redirect from an empty file or `echo | gcc -` or anything. – Peter Cordes Apr 14 '23 at 06:13
0

you can use the cpuid instruction to query the CPU for information about its features. here's most of features with corresponding flags.

std::vector<std::string> get_cpu_flags() {
    std::vector<std::string> flags;

    int info[4];
    __cpuid(info, 1);

    std::bitset<32> edx_features(info[3]);
    std::bitset<32> ecx_features(info[2]);

    if (edx_features[23]) flags.push_back("mmx");
    if (edx_features[25]) flags.push_back("sse");
    if (edx_features[26]) flags.push_back("sse2");
    if (ecx_features[0])  flags.push_back("sse3");
    if (ecx_features[9])  flags.push_back("ssse3");
    if (ecx_features[19]) flags.push_back("sse4_1");
    if (ecx_features[20]) flags.push_back("sse4_2");
    if (ecx_features[28]) flags.push_back("avx");
    if (ecx_features[12]) flags.push_back("fma");

    __cpuid(info, 7);

    std::bitset<32> ebx_features(info[1]);
    std::bitset<32> ecx_features_level7(info[2]);

    if (ebx_features[5])  flags.push_back("avx2");
    if (ebx_features[16]) flags.push_back("avx512f");
    if (ebx_features[17]) flags.push_back("avx512dq");
    if (ebx_features[21]) flags.push_back("avx512ifma");
    if (ebx_features[26]) flags.push_back("avx512pf");
    if (ebx_features[27]) flags.push_back("avx512er");
    if (ebx_features[28]) flags.push_back("avx512cd");
    if (ebx_features[30]) flags.push_back("avx512bw");
    if (ebx_features[31]) flags.push_back("avx512vl");

    if (ecx_features_level7[1]) flags.push_back("avx512vbmi");
    if (ecx_features_level7[14]) flags.push_back("avx512vbmi2");
    if (ecx_features_level7[30]) flags.push_back("avx512_vnni");

    return flags;
}
SolessChong
  • 3,370
  • 8
  • 40
  • 67
  • 1
    `-msse4_1` isn't a GCC option. Maybe you meant `"sse4.1"`? But regardless, this answer is missing a way to set `-mtune=` to go with this combination of `-m...` options. Also, you forgot `-mbmi` and `-mbmi2`, `-mcx`, and probably other useful options that would be implied by `-march=icelake-client` or `-march=znver4`. – Peter Cordes Apr 15 '23 at 04:56
  • yes this is a very complicated case. I'm not sure if the settings listed is complete. This will help OP determining the features. – SolessChong Apr 15 '23 at 05:51