diff options
Diffstat (limited to 'util/x86simdgen/3rdparty/x86simd_generate.pl')
-rwxr-xr-x | util/x86simdgen/3rdparty/x86simd_generate.pl | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/util/x86simdgen/3rdparty/x86simd_generate.pl b/util/x86simdgen/3rdparty/x86simd_generate.pl new file mode 100755 index 0000000000..66bb1bbd9c --- /dev/null +++ b/util/x86simdgen/3rdparty/x86simd_generate.pl @@ -0,0 +1,354 @@ +#!/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", + Leaf07_01EDX => "CPUID Leaf 7, Sub-leaf 1, EDX", + 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 keys %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 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 ""; |