summaryrefslogtreecommitdiffstats
path: root/util/x86simdgen/3rdparty/x86simd_generate.pl
blob: 99236b6eec2317f275e9f51f7f56aeabe4c008d5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
#!/usr/bin/env perl

# Copyright 2022 Intel Corporation.
# SPDX-License-Identifier: Apache-2.0

use strict;
$\ = "\n";
$/ = "\n";
my $debug = 0;
my %leaves = (
    Leaf01ECX           => "CPUID Leaf 1, ECX",
    Leaf07_00EBX        => "CPUID Leaf 7, Sub-leaf 0, EBX",
    Leaf07_00ECX        => "CPUID Leaf 7, Sub-leaf 0, ECX",
    Leaf07_00EDX        => "CPUID Leaf 7, Sub-leaf 0, EDX",
    Leaf07_01EAX        => "CPUID Leaf 7, Sub-leaf 1, EAX",
    Leaf13_01EAX        => "CPUID Leaf 13, Sub-leaf 1, EAX",
    Leaf80000001hECX    => "CPUID Leaf 80000001h, ECX",
    Leaf80000008hEBX    => "CPUID Leaf 80000008h, EBX",
);
my @leafNames = sort keys %leaves;

# out of order (we want it first)
unshift @leafNames, "Leaf01EDX";
$leaves{Leaf01EDX} = "CPUID Leaf 1, EDX";

# Read input from file specified by first argument
my $input_conf_file = shift @ARGV;
open(FH, '<', $input_conf_file) or die $!;

my $i = 0;
my @features;
my %feature_ids;
my @architecture_names;
my %architectures;
my @xsaveStates;
my $maxarchnamelen = 0;
while (<FH>) {
    chomp $_;
    m/#\s*(.*)\s*/;
    my $comment = $1;

    s/#.*$//;
    s/^\s+//;
    next if $_ eq "";

    if (s/^arch=//) {
        my ($arch, $based, $f) = split /\s+/;
        die("Unknown base architecture \"$based\"")
            unless $based eq "<>" or grep {$_ eq $based} @architecture_names;
        my $id = lc($arch);
        $id =~ s/[^A-Za-z0-9_]/_/g;

        my $prettyname = $arch;
        $prettyname =~ s/\B([A-Z])/ $1/g;
        $prettyname =~ s/-(\w+)/ ($1)/g;
        $maxarchnamelen = length($prettyname) if length($prettyname) > $maxarchnamelen;

        my @basefeatures;
        my @extrafeatures;
        @basefeatures = @{$architectures{$based}->{allfeatures}} if $based ne "<>";
        @extrafeatures = @{$architectures{$arch}{features}} if defined($architectures{$arch});
        @extrafeatures = (@extrafeatures, split(',', $f));
        my @allfeatures = sort { $feature_ids{$a} <=> $feature_ids{$b} } (@basefeatures, @extrafeatures);

        $architectures{$arch} = {
            name => $arch,
            prettyname => $prettyname,
            id => $id,
            base => $based,
            features => \@extrafeatures,
            allfeatures => \@allfeatures,
            comment => $comment
        };
        push @architecture_names, $arch
            unless grep {$_ eq $arch} @architecture_names;
    } elsif (s/^xsave=//) {
        my ($name, $value, $required) = split /\s+/;
        push @xsaveStates,
            { id => $name, value => $value, required_for => $required, comment => $comment };
    } else {
        my ($name, $function, $bit, $depends) = split /\s+/;
        die("Unknown CPUID function \"$function\"")
            unless grep {$_ eq $function} @leafNames;
        if (my @match = grep { $_->{name} eq $name } @features) {
            die("internal error") if scalar @match != 1;
            next if $match[0]->{function} eq $function &&
                $match[0]->{bit} eq $bit && $match[0]->{depends} eq $depends;
            die("Duplicate feature \"$name\" with different details. " .
                "Previously was $match[0]->{function} bit $match[0]->{bit}.");
        }

        my $id = uc($name);
        $id =~ s/[^A-Z0-9_]/_/g;
        push @features,
        { name => $name, depends => $depends, id => $id, bit => $bit, leaf => $function, comment => $comment };
        $feature_ids{$name} = $i;
        ++$i;
        die("Too many features to fit a 64-bit integer") if $i > 64;
    }
}
close FH;

# Print the header output
my $headername = "";
my $headerguard = "";
if ($headername = shift @ARGV) {

    $headerguard = uc($headername);
    $headerguard =~ s/[^A-Z0-9_]/_/g;

    print qq|// This is a generated file. DO NOT EDIT.
// Please see $0
#ifndef $headerguard
#define $headerguard

#include <stdint.h>|;
} else {
    $debug = 1;
}

# Print the feature list
my $lastleaf;
for (my $i = 0; $i < scalar @features; ++$i) {
    my $feature = $features[$i];
    # Leaf header:
    printf "\n// in %s:\n", $leaves{$feature->{leaf}}
        if $feature->{leaf} ne $lastleaf;
    $lastleaf = $feature->{leaf};

    # Feature
    printf "#define cpu_feature_%-31s (UINT64_C(1) << %d)\n", lc($feature->{id}), $i;
}

# Print the architecture list
print "\n// CPU architectures";
for (@architecture_names) {
    my $arch = $architectures{$_};
    my $base = $arch->{base};
    if ($base eq "<>") {
        $base = "0";
    } else {
        $base =~ s/[^A-Za-z0-9_]/_/g;
        $base = "cpu_" . $base;
    }

    printf "#define cpu_%-19s (%s", lc($arch->{id}), lc($base);

    for my $f (@{$arch->{features}}) {
        my @match = grep { $_->{name} eq $f } @features;
        if (scalar @match == 1) {
            printf " \\\n%33s| cpu_feature_%s", " ", lc($match[0]->{id});
        } else {
            printf STDERR "%s: unknown feature '%s' for CPU '%s'\n", $0, $f, $arch->{name}
                if $debug;
        }
    }
    print ")";
}

print "\n// __attribute__ target strings for GCC and Clang";
for (my $i = 0; $i < scalar @features; ++$i) {
    my $feature = $features[$i];
    my $str = $feature->{name} . ',' . $feature->{depends};
    $str =~ s/,$//;
    printf "#define QT_FUNCTION_TARGET_STRING_%-17s \"%s\"\n",
        $feature->{id}, $str;
}
for (@architecture_names) {
    my $arch = $architectures{$_};
    my $base = $arch->{base};
    my $featurestr = "";
    if ($base ne "<>") {
        $featurestr = "QT_FUNCTION_TARGET_STRING_ARCH_" . uc($base);
    }

    my @features = @{$arch->{features}};
    #@features = map { defined($feature_ids{$_}) ? $_ : () } @features;
    if (scalar @features) {
        $featurestr .= ' ",' if length $featurestr;
        $featurestr .= '"' unless length $featurestr;
        $featurestr .= join(',', @features);
        $featurestr .= '"';
    }
    printf "#define QT_FUNCTION_TARGET_STRING_ARCH_%-12s %s\n", uc($arch->{id}), $featurestr;
}

print q{
static const uint64_t _compilerCpuFeatures = 0};

# And print the compiler-enabled features part:
for (my $i = 0; $i < scalar @features; ++$i) {
    my $feature = $features[$i];
    printf
        "#ifdef __%s__\n" .
        "         | cpu_feature_%s\n" .
        "#endif\n",
        $feature->{id}, lc($feature->{id});
}

print '        ;';
if ($headerguard ne "") {
    print q|
#if (defined __cplusplus) && __cplusplus >= 201103L
enum X86CpuFeatures : uint64_t {|;

    for (@features) {
        my $line = sprintf "CpuFeature%s = cpu_feature_%s,", $_->{id}, lc($_->{id});
        if ($_->{comment} ne "") {
            printf "    %-56s ///< %s\n", $line, $_->{comment};
        } else {
            print "    $line";
        }
    }

print qq|}; // enum X86CpuFeatures

enum X86CpuArchitectures : uint64_t {|;

    for (@architecture_names) {
        my $arch = $architectures{$_};
        my $name = $arch->{name};
        $name =~ s/[^A-Za-z0-9]//g;
        my $line = sprintf "CpuArch%s = cpu_%s,", $name, lc($arch->{id});
        if ($arch->{comment} ne "") {
            printf "    %-56s ///< %s\n", $line, $arch->{comment};
        } else {
            print "    $line";
        }
    }

    print qq|}; // enum X86cpuArchitectures
#endif /* C++11 */\n|;
};

print "// -- implementation start --\n";
# Now generate the string table and bit-location array
my $offset = 0;
my @offsets;
print "static const char features_string[] =";
for my $feature (@features) {
    print "    \" $feature->{name}\\0\"";
    push @offsets, $offset;
    $offset += 2 + length($feature->{name});
}
print "    \"\\0\";";

# Print the string offset table
printf "\nstatic const %s features_indices[] = {",
    $offset > 255 ? "uint16_t" : "uint8_t";
for (my $j = 0; $j < scalar @offsets; ++$j) {
    printf "%s%3d,",
        $j % 8 ? " " : "\n    ", $offsets[$j];
}
print "\n};";

# Print the locator enum and table
print "\nenum X86CpuidLeaves {";
map { print "    $_," } @leafNames;
print "    X86CpuidMaxLeaf\n};";

my $type = scalar %leaves > 8 ? "uint16_t" : "uint8_t";
printf "\nstatic const %s x86_locators[] = {\n",
    $type, $type;
for (my $j = 0; $j < scalar @features; ++$j) {
    my $feature = $features[$j];
    printf "    %s*32 + %2d, %s// %s\n",
        $feature->{leaf}, $feature->{bit}, ' ' x (24 - length($feature->{leaf})), $feature->{name};
}
print '};';

# Generate the processor name listing, sorted by feature length
my %sorted_archs;
for (@architecture_names) {
    my $arch = $architectures{$_};
    my $key = sprintf "%02d_%s", scalar(@{$arch->{allfeatures}}), join(',', @{$arch->{allfeatures}});
    $sorted_archs{$key} = $arch;
}
print qq|
struct X86Architecture
{
    uint64_t features;
    char name[$maxarchnamelen + 1];
};

static const struct X86Architecture x86_architectures[] = {|;
for (sort { $b <=> $a } keys %sorted_archs) {
    my $arch = $sorted_archs{$_};
    next if $arch->{base} eq "<>";
    printf "    { cpu_%s, \"%s\" },\n", $arch->{id}, $arch->{prettyname};
}
print "};";

# Produce the list of XSAVE states
print "\nenum XSaveBits {";
my $xsaveEnumPrefix = "XSave_";
for my $state (@xsaveStates) {
    my $value = $state->{value};
    unless ($value =~ /^0x/) {
        # Compound value
        $value = join(" | ", map { $xsaveEnumPrefix . $_ } split(/\|/, $value));
    }
    printf "    %s%-12s = %s,", $xsaveEnumPrefix, $state->{id}, $value;
    printf "%s// %s", ' ' x (18 - length($value)), $state->{comment}
        if $state->{comment} ne '';
    printf "\n";
};
print "};";

# Produce a list of features require extended XSAVE state
my $xsaveRequirementMapping;
for my $state (@xsaveStates) {
    my $xsaveReqPrefix = "XSaveReq_";
    my @required_for = split /,/, $state->{required_for};
    next unless scalar @required_for;

    my $prefix = sprintf "\n// List of features requiring %s%s\nstatic const uint64_t %s%s = 0",
        $xsaveEnumPrefix, $state->{id}, $xsaveReqPrefix, $state->{id};

    # match either the feature name or one of its requirements against list
    # of features that this state is required for
    for my $feature (@features) {
        my $id = lc($feature->{id});
        my $required = 0;
        for my $requirement (@required_for) {
            my @depends = split /,/, "$id," . $feature->{depends};
            $required = grep { $_ eq $requirement } @depends;
            last if $required;
        }
        printf "$prefix\n        | cpu_feature_%s", $id if $required;
        $prefix = "" if $required;
    }

    if ($prefix eq "") {
        # we printed something
        print ";";
        $xsaveRequirementMapping .= sprintf "    { %s%s, %s%s },\n",
            $xsaveReqPrefix, $state->{id}, $xsaveEnumPrefix, $state->{id};
    }
}

# Finally, make a table
printf qq|
struct XSaveRequirementMapping
{
    uint64_t cpu_features;
    uint64_t xsave_state;
};

static const struct XSaveRequirementMapping xsave_requirements[] = {
%s};

// -- implementation end --
#endif /* $headerguard */\n|, $xsaveRequirementMapping if $xsaveRequirementMapping ne "";