Why can't everyone error like cargo?

Please don't let bare metal beat me.

I'm working on a UEFI rust bootloader/minimal OS, set up a base NoiseIK draft and had no problems with builds completing until this morning. I done SOMETHING before I went to sleep and now I'm getting build failures only on x86_64-unknown-uefi (aarch64-unknown-uefi has no problems).

"rustc-LLVM ERROR: Do not know how to split the result of this operator!" would be enough if it gave me an operator to work with, but we have nothing and a cargo

My question is not if one if one the intimidatingly skilled people here can solve the problem, can anyone point me to the right tools for debugging non-obvious LLVM issues?

(p.s. if someone wants to tell me what I'm doing wrong it I would not complain, it's the crypto stack isn't it?)

rustup 1.28.2 (e4f3ad6f8 2025-04-28)
info: This is the version for the rustup toolchain manager, not the rustc compiler.
info: The currently active rustc version is rustc 1.95.0-nightly (873d4682c 2026-01-25)

2024 edition
no_std

[dependencies]
log = "0.4.29"
uefi = { version = "0.36.1",default-features = false, features = ["logger", "alloc", "panic_handler"] }
#Link HW Rand to SW access
rand_core = { version = "0.6.4", default-features = false }
#Verify Hash
sha2 = { version = "0.10.9", default-features = false, features = ["force-soft"] }
#KeyGen
blake2 = { version = "0.10.6", default-features = false }

#Comms
chacha20poly1305 = { version = "0.10.1", default-features = false, features = ["alloc"] }

ed25519-compact = { version = "2.2.0", default-features = false, features = ["x25519"] }

Force these trait crates to stay no_std

ed25519 = { version = "2.2.3", default-features = false, features = ["alloc"] }
signature = { version = "2.2.0", default-features = false, features = ["alloc"] }

Ensure panics abort (no unwinding) in a no_std UEFI environment.

[profile.dev]
panic = "abort"

[profile.release]
panic = "abort"

[build]

optional; only if you want a default target

[target.x86_64-unknown-uefi]
linker = "rust-lld"
rustflags = [
"--cfg=poly1305_force_soft",
"--cfg=curve25519_dalek_backend="serial"",
"-Ctarget-feature=-avx,-avx2,-avx512f",
]

[target.aarch64-unknown-uefi]
rustflags = [
"--cfg", "curve25519_dalek_backend="serial"",
]

1 Like

That looks suspicious. Are you sure you are allowed to use avx2 in UEFI? I would try disabling all extra CPU features. Also, do you have any arch-specific assembler blocks or intrinsics?

OP is actually disabling avx features here. -C target-feature uses + symbol to enable given feature, and - to disable it. So -avx disables codegen for AVX. Enabling it would require passing +avx.

Ref: rustc manual.

2 Likes

There has been other people who ran into similar issues over the years... GitHub · Where software is built

FWIW AVX is already disabled on this target, so the -Ctarget-feature has no effect. And if it was enabled, without build-std you did still get AVX instructions.

1 Like

Undo that.

You should bold this. Your on the wrong forum for that. Though many are also here. Maybe a good place to start at is contributing.

I've found the issue! The specifics aren't too important but I've included a script to point "rustc-LLVM ERROR: Do not know how to split the result of this operator!" outputs with no "split operator" into a .rs caller in the project.

It may not be fancy but if it saves someone else a couple hours of headaches

#!/bin/bash
set -euo pipefail

# Allow manual creation of a list of targets
#targets=("x86_64-unknown-uefi")
#targets=("aarch64-unknown-uefi")
targets=("x86_64-unknown-uefi" "aarch64-unknown-uefi")

# Define the error string to search for
error_string="rustc-LLVM ERROR: Do not know how to split the result of this operator!"

# Helper to check for command availability
have_cmd() { command -v "$1" >/dev/null 2>&1; }

# Demangle a Rust symbol
demangle_symbol() {
  local sym="$1"
  if have_cmd rustfilt; then
    echo "$sym" | rustfilt
  elif have_cmd rustc-demangle; then
    echo "$sym" | rustc-demangle
  else
    echo "$sym"
  fi
}

# Resolve the caller's Rust source location using !dbg metadata attached to the call site
resolve_caller_location() {
  local ll_file="$1"
  local sym="$2"

  # Find call sites of the failing symbol
  local call_lines
  call_lines=$(grep -nE "call .*@\"?${sym}\"?" "$ll_file" | cut -d: -f1 || true)

  if [[ -z "$call_lines" ]]; then
    echo "  [caller] No direct call sites found for $sym (might be dead code or inlined)."
    return 0
  fi

  for line_num in $call_lines; do
    local call_content
    call_content=$(sed -n "${line_num}p" "$ll_file")

    # Extract the !dbg !ID
    local dbg_id
    dbg_id=$(echo "$call_content" | sed -nE 's/.*!dbg !([0-9]+).*/\1/p')

    if [[ -n "$dbg_id" ]]; then
      local curr_id="$dbg_id"
      local file_id=""
      local src_line=""

      # 1. Get initial line from DILocation
      local loc_line
      loc_line=$(grep -E "^!${curr_id}[[:space:]]*=" "$ll_file" | head -n 1 || true)
      src_line=$(echo "$loc_line" | sed -nE 's/.*line: ([0-9]+).*/\1/p')
      local scope_id
      scope_id=$(echo "$loc_line" | sed -nE 's/.*scope: !([0-9]+).*/\1/p')

      # 2. Follow scope chain to find file_id
      while [[ -n "$scope_id" && -z "$file_id" ]]; do
        local scope_line
        scope_line=$(grep -E "^!${scope_id}[[:space:]]*=" "$ll_file" | head -n 1 || true)
        file_id=$(echo "$scope_line" | sed -nE 's/.*file: !([0-9]+).*/\1/p')
        if [[ -z "$file_id" ]]; then
           scope_id=$(echo "$scope_line" | sed -nE 's/.*scope: !([0-9]+).*/\1/p')
        fi
      done

      # 3. Resolve file_id to path
      if [[ -n "$file_id" ]]; then
        local difile_line
        difile_line=$(grep -E "^!${file_id}[[:space:]]*=" "$ll_file" | head -n 1 || true)
        local directory filename
        directory=$(echo "$difile_line" | sed -nE 's/.*directory: "([^"]+)".*/\1/p')
        filename=$(echo "$difile_line" | sed -nE 's/.*filename: "([^"]+)".*/\1/p')

        local fullpath="${directory:+$directory/}$filename"
        echo "  [caller] Called from: ${fullpath}:${src_line:-?}"
      fi
    fi
  done
}

# Resolve a mangled function symbol to its definition location
resolve_dbg_location() {
  local ll_file="$1"
  local sym="$2"

  local def_line
  def_line=$(grep -nE "^[[:space:]]*define .*@${sym}\b" "$ll_file" | head -n 1 || true)
  if [[ -z "$def_line" ]]; then
    def_line=$(grep -nE "^[[:space:]]*define .*@\"${sym}\"" "$ll_file" | head -n 1 || true)
  fi

  if [[ -z "$def_line" ]]; then return 0; fi

  local dbg_id
  dbg_id=$(echo "$def_line" | sed -nE 's/.*!dbg !([0-9]+).*/\1/p' | head -n 1 || true)
  if [[ -n "$dbg_id" ]]; then
    local subp_line
    subp_line=$(grep -E "^!${dbg_id}[[:space:]]*=" "$ll_file" | head -n 1 || true)
    if [[ -n "$subp_line" ]]; then
      local file_id src_line
      file_id=$(echo "$subp_line" | sed -nE 's/.*file: !([0-9]+).*/\1/p')
      src_line=$(echo "$subp_line" | sed -nE 's/.*line: ([0-9]+).*/\1/p')

      if [[ -n "$file_id" ]]; then
        local difile_line
        difile_line=$(grep -E "^!${file_id}[[:space:]]*=" "$ll_file" | head -n 1 || true)
        local directory filename
        directory=$(echo "$difile_line" | sed -nE 's/.*directory: "([^"]+)".*/\1/p')
        filename=$(echo "$difile_line" | sed -nE 's/.*filename: "([^"]+)".*/\1/p')
        if [[ -n "$filename" ]]; then
          echo "  [dbg] Definition Source: ${directory:+$directory/}$filename${src_line:+:$src_line}"
        fi
      fi
    fi
  fi
}

extract_llc_function_symbol() {
  sed -nE "s/.*on function '@([^']+)'.*/\1/p" | head -n 1
}

for target in "${targets[@]}"; do
    echo "Processing target: $target"
    build_output=$(RUSTFLAGS="--emit=llvm-ir -Ccodegen-units=1 -Cdebuginfo=2" cargo build -vv --target "$target" 2>&1) || true

    if echo "$build_output" | grep -q "$error_string"; then
        echo "--- LLVM Error Detected ---"
        deps_dir="target/$target/debug/deps"
        ll_files=$(find "$deps_dir" -name "*.ll" | sort)

        for ll_file in $ll_files; do
            # Verify file structure
            if opt -passes=verify -S "$ll_file" -disable-output >/dev/null 2>&1; then
                llc_out=""
                # Run llc and capture stderr/stdout
                if ! llc_out=$(llc -mtriple="$target" -filetype=null "$ll_file" -o /dev/null 2>&1); then
                    echo "------------------------------------------------------------"
                    echo "FAILURE in: $(basename "$ll_file")"
                    echo "------------------------------------------------------------"
                    # Output the complete LLVM failure log for this file
                    echo "$llc_out"
                    echo "------------------------------------------------------------"

                    mangled_sym=$(echo "$llc_out" | extract_llc_function_symbol || true)
                    if [[ -n "$mangled_sym" ]]; then
                        echo "  [isel] Mangled Symbol: $mangled_sym"
                        echo "  [isel] Demangled:     $(demangle_symbol "$mangled_sym")"
                        resolve_dbg_location "$ll_file" "$mangled_sym"
                        resolve_caller_location "$ll_file" "$mangled_sym"
                    fi
                    echo "" # Visual separator
                fi
            fi
        done
    fi
done
1 Like