diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 286d8ba..c167606 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: os: [ubuntu-24.04, windows-2022] configuration: [Debug, Release] runs-on: ${{ matrix.os }} - timeout-minutes: 45 + timeout-minutes: 60 steps: - uses: actions/checkout@v4 @@ -104,7 +104,7 @@ jobs: - name: Test (Ubuntu) id: test_linux if: runner.os == 'Linux' - timeout-minutes: 20 + timeout-minutes: 35 shell: bash run: | set -o pipefail @@ -171,6 +171,10 @@ jobs: llvm-config-${ABSOLUTE_LLVM_VERSION} --version ninja --version || true node --version || true + # The backend selects a target CPU from the host model, so a runner whose + # advertised model implies features the VM masks off is worth spotting in + # the log rather than inferring from a SIGILL. + lscpu || true } > ci-logs/toolchain.txt 2>&1 - name: Record toolchain versions (Windows) diff --git a/Absolute-CodeGen/include/codegen_pch.h b/Absolute-CodeGen/include/codegen_pch.h index 9793703..3e61fdd 100644 --- a/Absolute-CodeGen/include/codegen_pch.h +++ b/Absolute-CodeGen/include/codegen_pch.h @@ -34,8 +34,10 @@ #include #if LLVM_VERSION_MAJOR >= 18 #include +#include #else #include +#include #endif #include diff --git a/Absolute-CodeGen/src/codegen_module.cpp b/Absolute-CodeGen/src/codegen_module.cpp index e650ac5..140c958 100644 --- a/Absolute-CodeGen/src/codegen_module.cpp +++ b/Absolute-CodeGen/src/codegen_module.cpp @@ -1263,6 +1263,7 @@ namespace Absolute { llvm::TargetOptions options; std::string cpu = "generic"; + std::string features; llvm::Reloc::Model reloc = llvm::Reloc::PIC_; if (IsWebAssemblyTriple(triple)) { // Wasm objects use a generic CPU; PIC is the usual reloc model. @@ -1270,12 +1271,32 @@ namespace Absolute { reloc = llvm::Reloc::PIC_; } else { + // The host CPU name only identifies a microarchitecture, it does not say + // which of that microarchitecture's features the machine actually exposes. + // A virtualised host can report a model whose default feature set includes + // instructions the hypervisor masks off, and selecting by name alone then + // emits code the CPU refuses to execute. Pin the feature string to what the + // host really advertises so generated objects stay runnable on that host. const std::string hostCpu = llvm::sys::getHostCPUName().str(); if (!hostCpu.empty()) cpu = hostCpu; + +#if LLVM_VERSION_MAJOR >= 19 + const llvm::StringMap hostFeatures = llvm::sys::getHostCPUFeatures(); + const bool detectedFeatures = !hostFeatures.empty(); +#else + llvm::StringMap hostFeatures; + const bool detectedFeatures = llvm::sys::getHostCPUFeatures(hostFeatures); +#endif + if (detectedFeatures) { + llvm::SubtargetFeatures hostSubtarget; + for (const llvm::StringMapEntry& feature : hostFeatures) + hostSubtarget.AddFeature(feature.getKey(), feature.getValue()); + features = hostSubtarget.getString(); + } } std::unique_ptr targetMachine(target->createTargetMachine( - tripleName, cpu, "", options, reloc, + tripleName, cpu, features, options, reloc, std::nullopt, LlvmCodeGenOptimizationLevel(optimizationLevel))); if (!targetMachine) Fail("cannot create target machine for '" + tripleName + "'");