compiler: improve target features support

This commit is contained in:
Laytan Laats
2024-05-02 00:59:52 +02:00
parent ff0973e0f5
commit 25f1d0906d
19 changed files with 1067 additions and 205 deletions
+28
View File
@@ -0,0 +1,28 @@
# Featuregen
This directory contains a python and CPP script that generates the needed information
for features regarding microarchitecture and target features of the compiler.
It is not pretty! But LLVM has no way to query this information with their C API.
It generates these globals (intended for `src/build_settings.cpp`:
- `target_microarch_list`: an array of strings indexed by the architecture, each string is a comma-seperated list of microarchitectures available on that architecture
- `target_features_list`: an array of strings indexed by the architecture, each string is a comma-seperated list of target features available on that architecture
- `target_microarch_counts`: an array of ints indexed by the architecture, each int represents the amount of microarchitectures available on that target, intended for easier iteration of the next global
- `microarch_features_list`: an array of a tuple like struct where the first string is a microarchitecture and the second is a comma-seperated list of all features that are enabled by default for it
In order to get the default features for a microarchitecture there is a small CPP program that takes
a target triple and microarchitecture and spits out the default features, this is then parsed by the python script.
This should be ran each time we update LLVM to stay in sync.
If there are minor differences (like the Odin user using LLVM 14 and this table being generated on LLVM 17) it
does not impact much at all, the only thing it will do is make LLVM print a message that the feature is ignored (if it was added between 14 and 17 in this case).
## Usage
1. Make sure the table of architectures at the top of the python script is up-to-date (the triple can be any valid triple for the architecture)
1. `./build.sh`
1. `python3 featuregen.py`
1. Copy the output into `src/build_settings.cpp`
+37
View File
@@ -0,0 +1,37 @@
#include <llvm/MC/MCSubtargetInfo.h>
#include <llvm/MC/TargetRegistry.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/ADT/ArrayRef.h>
#include <llvm/Support/InitLLVM.h>
#include <llvm/Support/TargetSelect.h>
// Dumps the default set of supported features for the given microarch.
int main(int argc, char **argv) {
if (argc < 3) {
llvm::errs() << "Error: first arg should be triple, second should be microarch\n";
return 1;
}
llvm::InitializeAllTargets();
llvm::InitializeAllTargetMCs();
std::string error;
const llvm::Target* target = llvm::TargetRegistry::lookupTarget(argv[1], error);
if (!target) {
llvm::errs() << "Error: " << error << "\n";
return 1;
}
auto STI = target->createMCSubtargetInfo(argv[1], argv[2], "");
std::string plus = "+";
llvm::ArrayRef<llvm::SubtargetFeatureKV> features = STI->getAllProcessorFeatures();
for (const auto& feature : features) {
if (STI->checkFeatures(plus + feature.Key)) {
llvm::outs() << feature.Key << "\n";
}
}
return 0;
}
+116
View File
@@ -0,0 +1,116 @@
import subprocess
import tempfile
import os
import sys
archs = [
("amd64", "linux_amd64", "x86_64-pc-linux-gnu", [], []),
("i386", "linux_i386", "i386-pc-linux-gnu", [], []),
("arm32", "linux_arm32", "arm-linux-gnu", [], []),
("arm64", "linux_arm64", "aarch64-linux-elf", [], []),
("wasm32", "js_wasm32", "wasm32-js-js", [], []),
("wasm64p32", "js_wasm64p32","wasm32-js-js", [], []),
];
SEEKING_CPUS = 0
PARSING_CPUS = 1
PARSING_FEATURES = 2
with tempfile.NamedTemporaryFile(suffix=".odin", delete=True) as temp_file:
temp_file.write(b"package main\n")
for arch, target, triple, cpus, features in archs:
cmd = ["odin", "build", temp_file.name, "-file", "-build-mode:llvm", "-out:temp", "-target-features:\"help\"", f"-target:\"{target}\""]
process = subprocess.Popen(cmd, stderr=subprocess.PIPE, text=True)
state = SEEKING_CPUS
for line in process.stderr:
if state == SEEKING_CPUS:
if line == "Available CPUs for this target:\n":
state = PARSING_CPUS
elif state == PARSING_CPUS:
if line == "Available features for this target:\n":
state = PARSING_FEATURES
continue
parts = line.split(" -", maxsplit=1)
if len(parts) < 2:
continue
cpu = parts[0].strip()
cpus.append(cpu)
elif state == PARSING_FEATURES:
if line == "\n" and len(features) > 0:
break
parts = line.split(" -", maxsplit=1)
if len(parts) < 2:
continue
feature = parts[0].strip()
features.append(feature)
process.wait()
if process.returncode != 0:
print(f"odin build returned with non-zero exit code {process.returncode}")
sys.exit(1)
os.remove("temp.ll")
def print_default_features(triple, microarch):
cmd = ["./featuregen", triple, microarch]
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, text=True)
first = True
for line in process.stdout:
print("" if first else ",", line.strip(), sep="", end="")
first = False
process.wait()
if process.returncode != 0:
print(f"featuregen returned with non-zero exit code {process.returncode}")
sys.exit(1)
print("// Generated with the featuregen script in `misc/featuregen`")
print("gb_global String target_microarch_list[TargetArch_COUNT] = {")
print("\t// TargetArch_Invalid:")
print('\tstr_lit(""),')
for arch, target, triple, cpus, features in archs:
print(f"\t// TargetArch_{arch}:")
print(f'\tstr_lit("{','.join(cpus)}"),')
print("};")
print("")
print("// Generated with the featuregen script in `misc/featuregen`")
print("gb_global String target_features_list[TargetArch_COUNT] = {")
print("\t// TargetArch_Invalid:")
print('\tstr_lit(""),')
for arch, target, triple, cpus, features in archs:
print(f"\t// TargetArch_{arch}:")
print(f'\tstr_lit("{','.join(features)}"),')
print("};")
print("")
print("// Generated with the featuregen script in `misc/featuregen`")
print("gb_global int target_microarch_counts[TargetArch_COUNT] = {")
print("\t// TargetArch_Invalid:")
print("\t0,")
for arch, target, triple, cpus, feature in archs:
print(f"\t// TargetArch_{arch}:")
print(f"\t{len(cpus)},")
print("};")
print("")
print("// Generated with the featuregen script in `misc/featuregen`")
print("gb_global MicroarchFeatureList microarch_features_list[] = {")
for arch, target, triple, cpus, features in archs:
print(f"\t// TargetArch_{arch}:")
for cpu in cpus:
print(f'\t{{ str_lit("{cpu}"), str_lit("', end="")
print_default_features(triple, cpu)
print('") },')
print("};")