diff --git a/.cursorrules b/.cursorrules index 98c2aea49..de54d54f4 100644 --- a/.cursorrules +++ b/.cursorrules @@ -4,10 +4,31 @@ # Commits: Commit frequently with small incremental changes -# Commits: Keep commit messages short (one line when possible) +# Commits: Use Conventional Commits format: `(): ` +# - Types: feat, fix, chore, docs, etc. +# - Scope: sbs, osmt, wrappers, etc. (or component name) +# - Description: short description of the commit +# - Body: optional, include only if changes are not obvious from description +# - Body format: bulleted list of changes made, each item a single line # Commits: Keep code compiling and tests passing between commits (when possible) +# Formatting: Run `cargo +nightly fmt` on all changes before committing + +# Code Organization: Place helper utility functions at the bottom of files +# Code Organization: Place more abstract things, entry points, and tests first +# Code Organization: Keep related functionality grouped together + +# Tests: Tests should be in `mod tests` at the top of the module +# Tests: Tests should be short and concise, use utility functions to avoid duplication +# Tests: Utility functions should be at the bottom of the test module +# Tests: Use builder pattern to create test data + +# Language: Keep language professional and restrained +# Language: Avoid overly optimistic language like "comprehensive", "fully production ready", "complete solution" +# Language: Avoid emoticons +# Language: Use measured, factual descriptions of what was implemented + # Style: Follow existing cranelift code style # Style: Use `#![no_std]` where appropriate diff --git a/.github/workflows/pre-merge.yml b/.github/workflows/pre-merge.yml new file mode 100644 index 000000000..4714c35ad --- /dev/null +++ b/.github/workflows/pre-merge.yml @@ -0,0 +1,49 @@ +name: CI + +on: + pull_request: + branches: ["feature/*"] + push: + branches: ["feature/*"] + +env: + RUST_BACKTRACE: 1 + RISC_V_TARGET: riscv32imac-unknown-none-elf + +jobs: + test: + name: Test + runs-on: ubuntu-24.04-arm + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Install RISC-V target + run: rustup target add ${{ env.RISC_V_TARGET }} + + - name: Cache cargo registry + uses: actions/cache@v4 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + target/ + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-cargo- + + - name: Install just + uses: extractions/setup-just@v3 + + - name: Validate + # Note: we don't run tests on ci right now because jit tests fail on arm + # due to memory protection, and don't run on x64 due to some cranelift + # x64 build issue. + run: just check build diff --git a/.gitignore b/.gitignore index 9cb2e34e7..3fb717536 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ vendor *.smt2 *.cwasm /artifacts +.ci-artifacts testcase*.wat testcase*.wasm testcase*.dna diff --git a/.idea/lp2025.iml b/.idea/lp2025.iml index 11c7218d0..29a094286 100644 --- a/.idea/lp2025.iml +++ b/.idea/lp2025.iml @@ -32,6 +32,7 @@ + diff --git a/.vscode/keybindings.json b/.vscode/keybindings.json deleted file mode 100644 index 0f04c07e9..000000000 --- a/.vscode/keybindings.json +++ /dev/null @@ -1,6 +0,0 @@ -[ - { - "key": "cmd+k", - "command": "workbench.action.terminal.clear" - } -] diff --git a/Cargo.lock b/Cargo.lock index e6f7c37ee..ec3e63b7f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -982,7 +982,7 @@ dependencies = [ [[package]] name = "cranelift-bforest" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" dependencies = [ "cranelift-entity", ] @@ -990,12 +990,12 @@ dependencies = [ [[package]] name = "cranelift-bitset" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" [[package]] name = "cranelift-codegen" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" dependencies = [ "anyhow", "bumpalo", @@ -1022,7 +1022,7 @@ dependencies = [ [[package]] name = "cranelift-codegen-meta" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" dependencies = [ "cranelift-codegen-shared", "cranelift-srcgen", @@ -1032,17 +1032,17 @@ dependencies = [ [[package]] name = "cranelift-codegen-shared" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" [[package]] name = "cranelift-control" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" [[package]] name = "cranelift-entity" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" dependencies = [ "cranelift-bitset", ] @@ -1050,7 +1050,7 @@ dependencies = [ [[package]] name = "cranelift-frontend" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" dependencies = [ "cranelift-codegen", "hashbrown 0.15.5", @@ -1062,7 +1062,7 @@ dependencies = [ [[package]] name = "cranelift-interpreter" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" dependencies = [ "cranelift-codegen", "cranelift-entity", @@ -1075,12 +1075,12 @@ dependencies = [ [[package]] name = "cranelift-isle" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" [[package]] name = "cranelift-jit" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" dependencies = [ "anyhow", "cranelift-codegen", @@ -1100,7 +1100,7 @@ dependencies = [ [[package]] name = "cranelift-module" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" dependencies = [ "anyhow", "cranelift-codegen", @@ -1111,7 +1111,7 @@ dependencies = [ [[package]] name = "cranelift-native" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" dependencies = [ "cranelift-codegen", "libc", @@ -1121,7 +1121,7 @@ dependencies = [ [[package]] name = "cranelift-object" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" dependencies = [ "anyhow", "cranelift-codegen", @@ -1135,7 +1135,7 @@ dependencies = [ [[package]] name = "cranelift-reader" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" dependencies = [ "anyhow", "cranelift-codegen", @@ -1146,7 +1146,7 @@ dependencies = [ [[package]] name = "cranelift-srcgen" version = "0.127.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" [[package]] name = "crc32fast" @@ -4113,9 +4113,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.105" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "535d180e0ecab6268a3e718bb9fd44db66bbbc256257165fc699dadf70d16fe7" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -4827,18 +4827,6 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "test-structreturn" -version = "0.1.0" -dependencies = [ - "cranelift-codegen", - "cranelift-frontend", - "cranelift-jit", - "cranelift-module", - "lp-jit-util", - "target-lexicon", -] - [[package]] name = "thiserror" version = "1.0.69" @@ -5274,7 +5262,7 @@ dependencies = [ [[package]] name = "wasmtime-internal-jit-icache-coherence" version = "40.0.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" dependencies = [ "anyhow", "cfg-if", @@ -5285,7 +5273,7 @@ dependencies = [ [[package]] name = "wasmtime-internal-math" version = "40.0.0" -source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#51f256af3cb77d49cb95d84fe40c1309cc1ad51c" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" dependencies = [ "libm", ] diff --git a/Cargo.toml b/Cargo.toml index cc824b513..6d647e615 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,14 +15,36 @@ members = [ "lp-glsl/crates/lp-jit-util", "lp-glsl/crates/lp-riscv-shared", "lp-glsl/crates/lp-riscv-tools", - "lp-glsl/apps/esp32-glsl-jit", "lp-glsl/apps/lp-builtin-gen", "lp-glsl/apps/lp-builtins-app", "lp-glsl/apps/lp-test", - "lp-glsl/apps/test-structreturn", + "lp-glsl/apps/esp32-glsl-jit", ] resolver = "2" +# Exclude lp-builtins-app and esp32-glsl-jit from default workspace builds (they're no_std, only build for RISC-V) +# They're still in workspace members so they can use workspace dependencies +# Build them explicitly with: just build-builtins or just esp32-build +default-members = [ + # lp-app workspace members + "lp-app/crates/lp-engine", + "lp-app/crates/lp-engine-client", + "lp-app/crates/lp-shared", + "lp-app/crates/lp-server", + "lp-app/apps/lp-cli", + "lp-app/crates/lp-model", + # lp-glsl workspace members (excluding lp-builtins-app) + "lp-glsl/crates/lp-builtins", + "lp-glsl/crates/lp-filetests-gen", + "lp-glsl/crates/lp-glsl-compiler", + "lp-glsl/crates/lp-glsl-filetests", + "lp-glsl/crates/lp-jit-util", + "lp-glsl/crates/lp-riscv-shared", + "lp-glsl/crates/lp-riscv-tools", + "lp-glsl/apps/lp-builtin-gen", + "lp-glsl/apps/lp-test", +] + [workspace.package] version = "40.0.0" authors = ["The Wasmtime Project Developers"] @@ -71,6 +93,7 @@ cranelift-interpreter = { git = "https://github.com/Yona-Appletree/lp-cranelift. target-lexicon = "0.13.0" hashbrown = { version = "0.15", default-features = false, features = ["alloc", "default-hasher"] } anyhow = { version = "1.0.100", default-features = false } +log = { version = "0.4", default-features = false } regex = "1.9.1" glob = "0.3.3" object = { version = "0.37.3", default-features = false, features = ['read_core', 'elf'] } @@ -83,11 +106,12 @@ serde_json = { version = "1.0.80", default-features = false, features = ["alloc" # This applies to all packages in the workspace lto = true +# ESP32 app release profile optimizations (embedded target) [profile.release.package.esp32-glsl-jit] -# Size optimizations for embedded target (ESP32-C3) # Strip debuginfo but keep symbols (needed for defmt/probe-rs) - saves ~1-1.5 MB strip = "debuginfo" # Optimize for size instead of speed - saves ~200-500 KB opt-level = "z" # Single codegen unit for better dead code elimination - saves ~50-100 KB codegen-units = 1 +# Note: lto is inherited from workspace [profile.release] diff --git a/README.md b/README.md index cc3027944..64528bb0d 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,34 @@ # LightPlayer Application -LightPlayer is a framework for building and deploying interactive visualization applications -using GLSL primarily for addressable LED control on embedded systems. +LightPlayer is a framework for building and deploying interactive visual effects on microcontrollers and other embedded systems. -It employs a client-server architecture, where the server runs the visualization on -a headless machine (e.g. an esp32-c6 or raspberry pi), and the client communicates with -the server via a network or serial connection. +Effects are written as GLSL shaders a custom native compiler is used to allow running them on hardware without OpenGL, including microcontrollers. -The server portion is portable, and is designed to be runnable from any OS for -development, a headless host for larger deployments, or as bare-metal firmware -on an embedded system. +## Development Setup -On machines without OpenGL support, such as embedded systems, the shaders are compiled to native -code using the bespoke LightPlayer GLSL compiler which is built on a fork of Cranelift. +To get started with development: -# Workspace +1. **Initialize the development environment:** -This workspace contains the main LightPlayer application, including the device specific firmware, -engine, server, and clients. + ```bash + scripts/dev-init.sh + ``` -It exists within cranelift temporarily during active development of the compiler, because managing -multiple repositories is too cumbersome. + This will: + - Check for required tools (Rust, Cargo, rustup, just) + - Verify Rust version meets minimum requirements (1.90.0+) + - Install the RISC-V target (`riscv32imac-unknown-none-elf`) if needed + - Set up git hooks (pre-commit hook runs `just check`) + +2. **Required tools:** + - Rust toolchain (1.90.0 or later) - [Install Rust](https://rustup.rs/) + - `just` - Task runner: `cargo install just` or via package manager + +3. **Common development commands:** + - `just build` - Build all packages (host and RISC-V targets) + - `just check` - Run formatting and linting checks + - `just test` - Run all tests + - `just fmt` - Format code + - `just fix` - Format code and auto-fix clippy issues + +See `just --list` for all available commands. diff --git a/justfile b/justfile new file mode 100644 index 000000000..6dab557c8 --- /dev/null +++ b/justfile @@ -0,0 +1,154 @@ +# LightPlayer justfile +# Common development tasks + +# Variables +rv32_target := "riscv32imac-unknown-none-elf" +rv32_packages := "esp32-glsl-jit lp-builtins-app" +lp_glsl_dir := "lp-glsl" + +# Default recipe - show available commands +default: + @just --list + +# ============================================================================ +# Target setup +# ============================================================================ + +# Ensure RISC-V target is installed +install-rv32-target: + @if ! rustup target list --installed | grep -q "^{{rv32_target}}$"; then \ + echo "Installing target {{rv32_target}}..."; \ + rustup target add {{rv32_target}}; \ + else \ + echo "Target {{rv32_target}} already installed"; \ + fi + +# Generate builtin boilerplate code +generate-builtins: + cargo run --bin lp-builtin-gen -p lp-builtin-gen + +# ============================================================================ +# Build commands +# ============================================================================ + +# Build host target packages (default workspace) +build-host: + cargo build + +# Build host target packages in release mode +build-host-release: + cargo build --release + +# Build RISC-V target packages +# Note: esp32-glsl-jit is too large for debug builds, so it's built in release mode +build-rv32: install-rv32-target + @echo "Building RISC-V packages ({{rv32_target}})..." + cargo build --target {{rv32_target}} -p lp-builtins-app + cargo build --target {{rv32_target}} -p esp32-glsl-jit --release + +# Build RISC-V target packages in release mode +build-rv32-release: install-rv32-target + @echo "Building RISC-V packages in release mode ({{rv32_target}})..." + cargo build --target {{rv32_target}} -p esp32-glsl-jit -p lp-builtins-app --release + +# Build all packages (host and RISC-V) +build: build-host build-rv32 + +# Build all packages in release mode +build-release: build-host-release build-rv32-release + +# Build lp-builtins-app with special flags for filetests +# This is required before running tests that use the emulator +filetests-setup: generate-builtins install-rv32-target + @echo "Building lp-builtins-app for filetests ({{rv32_target}})..." + RUSTFLAGS="-C opt-level=1 -C panic=abort -C overflow-checks=off -C debuginfo=0 -C link-dead-code=off -C codegen-units=1" \ + cargo build \ + --target {{rv32_target}} \ + --package lp-builtins-app \ + --release + @if command -v nm > /dev/null 2>&1; then \ + LP_SYMBOLS=`nm target/{{rv32_target}}/release/lp-builtins-app 2>/dev/null | grep "__lp_" | wc -l | xargs`; \ + echo "✓ lp-builtins-app built with $LP_SYMBOLS built-ins"; \ + fi + +# ============================================================================ +# Formatting +# ============================================================================ + +# Format code +fmt: + cargo fmt --all + +# Check formatting without modifying files +fmt-check: + cargo fmt --all -- --check + +# ============================================================================ +# Linting +# ============================================================================ + +# Run clippy on host target packages +clippy-host: + cargo clippy --workspace --exclude lp-builtins-app --exclude esp32-glsl-jit -- --no-deps -D warnings + +# Run clippy on RISC-V target packages (esp32-glsl-jit only, lp-builtins-app is mostly generated code) +# Note: esp32-glsl-jit must be built in release mode (esp-hal requirement) +clippy-rv32: install-rv32-target + @echo "Running clippy on RISC-V packages ({{rv32_target}})..." + cargo clippy --target {{rv32_target}} -p esp32-glsl-jit --release -- --no-deps -D warnings + +# Run clippy on all packages (host and RISC-V) +clippy: clippy-host clippy-rv32 + +# Run clippy and auto-fix issues +clippy-fix: + cargo clippy --fix --allow-dirty --allow-staged + +# Format code and auto-fix clippy issues +fix: fmt clippy-fix + +# ============================================================================ +# Testing +# ============================================================================ + +# Run all tests (requires filetests-setup) +test: filetests-setup + cargo test + +# Run GLSL filetests specifically +test-filetests: filetests-setup + cargo test -p lp-glsl-filetests --test filetests + +# ============================================================================ +# CI and validation +# ============================================================================ + +# Check code for linting, formatting, etc... +check: fmt-check clippy + +# Full CI check: build, format check, clippy, and test +ci: check build test + +# ============================================================================ +# Cleanup +# ============================================================================ + +# Clean build artifacts +clean: + cargo clean + +# Clean everything including target directories +clean-all: clean + rm -rf {{lp_glsl_dir}}/target + +# ============================================================================ +# Git workflows +# ============================================================================ + +# Push changes to origin and create/update PR +push: check + scripts/push.sh + +# Push changes, run ci, and merge PR if successful +merge: check + scripts/push.sh --merge diff --git a/lp-app/Cargo.toml b/lp-app/Cargo.toml deleted file mode 100644 index d68c594a9..000000000 --- a/lp-app/Cargo.toml +++ /dev/null @@ -1,70 +0,0 @@ -# This file is kept for reference but workspace is now defined in root Cargo.toml -# [workspace] -# resolver = '2' -# members = [ -# "crates/lp-engine", -# "crates/lp-engine-client", -# "crates/lp-shared", -# "crates/lp-server", -# "apps/lp-cli", -# "crates/lp-model", -# ] - -[package] -version = "40.0.0" -authors = ["The Wasmtime Project Developers"] -edition = "2024" -license = "Apache-2.0 WITH LLVM-exception" -rust-version = "1.90.0" - -# [workspace.lints.rust] -unused_extern_crates = 'warn' -trivial_numeric_casts = 'warn' -unstable_features = 'warn' -unused_import_braces = 'warn' -unused-lifetimes = 'warn' -unused-macro-rules = 'warn' - -# [workspace.lints.clippy] -all = { level = 'allow', priority = -1 } -clone_on_copy = 'warn' -map_clone = 'warn' -uninlined_format_args = 'warn' -unnecessary_to_owned = 'warn' -manual_strip = 'warn' -useless_conversion = 'warn' -unnecessary_mut_passed = 'warn' -unnecessary_fallible_conversions = 'warn' -unnecessary_cast = 'warn' -allow_attributes_without_reason = 'warn' -from_over_into = 'warn' -redundant_field_names = 'warn' -multiple_bound_locations = 'warn' -extra_unused_type_parameters = 'warn' - -# [workspace.dependencies] -# Cranelift crates - reference root workspace -cranelift-codegen = { workspace = true } -cranelift-frontend = { workspace = true } -cranelift-module = { workspace = true } -cranelift-jit = { workspace = true } -cranelift-native = { workspace = true } -cranelift-object = { workspace = true } -cranelift-reader = { workspace = true } -cranelift-control = { workspace = true } - -# Common dependencies - reference root workspace -target-lexicon = { workspace = true } -hashbrown = { workspace = true } -anyhow = { workspace = true } -regex = { workspace = true } -glob = { workspace = true } -object = { workspace = true } -critical-section = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } - -# [profile.release] -# # Link-time optimization for all release builds - saves ~100-300 KB -# # This applies to all packages in the workspace -# lto = true diff --git a/lp-app/apps/lp-cli/src/client/client.rs b/lp-app/apps/lp-cli/src/client/client.rs index cf22496e0..a9085955a 100644 --- a/lp-app/apps/lp-cli/src/client/client.rs +++ b/lp-app/apps/lp-cli/src/client/client.rs @@ -4,16 +4,16 @@ use anyhow::{Error, Result}; use lp_model::{ + ClientMessage, ClientRequest, LpPath, LpPathBuf, ServerMessage, project::{ + FrameId, api::{ApiNodeSpecifier, SerializableProjectResponse}, handle::ProjectHandle, - FrameId, - }, server::{AvailableProject, FsResponse, LoadedProject, ServerMsgBody}, ClientMessage, ClientRequest, - LpPath, LpPathBuf, - ServerMessage, + }, + server::{AvailableProject, FsResponse, LoadedProject, ServerMsgBody}, }; -use std::sync::atomic::{AtomicU64, Ordering}; use std::sync::Arc; +use std::sync::atomic::{AtomicU64, Ordering}; use crate::client::transport::ClientTransport; @@ -38,7 +38,7 @@ impl LpClient { /// # Returns /// /// * `Self` - The client - #[allow(dead_code)] // Will be used in tests and other contexts + #[allow(dead_code, reason = "Will be used in tests and other contexts")] pub fn new(transport: Box) -> Self { Self { transport: Arc::new(tokio::sync::Mutex::new(transport)), @@ -74,13 +74,13 @@ impl LpClient { transport .send(msg) .await - .map_err(|e| Error::msg(format!("Transport error: {}", e)))?; + .map_err(|e| Error::msg(format!("Transport error: {e}")))?; // Wait for response transport .receive() .await - .map_err(|e| Error::msg(format!("Transport error: {}", e))) + .map_err(|e| Error::msg(format!("Transport error: {e}"))) } /// Read a file from the server filesystem @@ -103,7 +103,7 @@ impl LpClient { match response.msg { ServerMsgBody::Filesystem(FsResponse::Read { data, error, .. }) => { if let Some(err) = error { - return Err(Error::msg(format!("Server error: {}", err))); + return Err(Error::msg(format!("Server error: {err}"))); } data.ok_or_else(|| Error::msg("No data in read response")) } @@ -136,7 +136,7 @@ impl LpClient { match response.msg { ServerMsgBody::Filesystem(FsResponse::Write { error, .. }) => { if let Some(err) = error { - return Err(Error::msg(format!("Server error: {}", err))); + return Err(Error::msg(format!("Server error: {err}"))); } Ok(()) } @@ -167,7 +167,7 @@ impl LpClient { match response.msg { ServerMsgBody::Filesystem(FsResponse::DeleteFile { error, .. }) => { if let Some(err) = error { - return Err(Error::msg(format!("Server error: {}", err))); + return Err(Error::msg(format!("Server error: {err}"))); } Ok(()) } @@ -200,7 +200,7 @@ impl LpClient { match response.msg { ServerMsgBody::Filesystem(FsResponse::ListDir { entries, error, .. }) => { if let Some(err) = error { - return Err(Error::msg(format!("Server error: {}", err))); + return Err(Error::msg(format!("Server error: {err}"))); } Ok(entries) } @@ -247,7 +247,7 @@ impl LpClient { /// /// * `Ok(())` if the project was unloaded successfully /// * `Err` if unloading failed or transport error occurred - #[allow(dead_code)] // Will be used in future commands + #[allow(dead_code, reason = "Will be used in future commands")] pub async fn project_unload(&self, handle: ProjectHandle) -> Result<()> { let request = ClientRequest::UnloadProject { handle }; @@ -308,7 +308,7 @@ impl LpClient { /// /// * `Ok(Vec)` - List of available projects /// * `Err` if listing failed or transport error occurred - #[allow(dead_code)] // Will be used in future commands + #[allow(dead_code, reason = "Will be used in future commands")] pub async fn project_list_available(&self) -> Result> { let request = ClientRequest::ListAvailableProjects; @@ -329,7 +329,7 @@ impl LpClient { /// /// * `Ok(Vec)` - List of loaded projects /// * `Err` if listing failed or transport error occurred - #[allow(dead_code)] // Will be used in future commands + #[allow(dead_code, reason = "Will be used in future commands")] pub async fn project_list_loaded(&self) -> Result> { let request = ClientRequest::ListLoadedProjects; diff --git a/lp-app/apps/lp-cli/src/client/client_connect.rs b/lp-app/apps/lp-cli/src/client/client_connect.rs index 5c93ba937..8da343176 100644 --- a/lp-app/apps/lp-cli/src/client/client_connect.rs +++ b/lp-app/apps/lp-cli/src/client/client_connect.rs @@ -52,10 +52,10 @@ pub fn client_connect(spec: HostSpecifier) -> Result> { // WebSocketClientTransport::new is async, but client_connect is sync // We need to use tokio runtime to connect let rt = tokio::runtime::Runtime::new() - .map_err(|e| anyhow::anyhow!("Failed to create tokio runtime: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to create tokio runtime: {e}"))?; let transport = rt .block_on(WebSocketClientTransport::new(&url)) - .map_err(|e| anyhow::anyhow!("Failed to connect to {}: {}", url, e))?; + .map_err(|e| anyhow::anyhow!("Failed to connect to {url}: {e}"))?; Ok(Box::new(transport)) } HostSpecifier::Serial { .. } => { @@ -96,7 +96,7 @@ mod tests { let result = client_connect(spec); assert!(result.is_err()); if let Err(e) = result { - assert!(format!("{}", e).contains("not yet implemented")); + assert!(format!("{e}").contains("not yet implemented")); } } } diff --git a/lp-app/apps/lp-cli/src/client/local.rs b/lp-app/apps/lp-cli/src/client/local.rs index 70319dc9d..faf114eba 100644 --- a/lp-app/apps/lp-cli/src/client/local.rs +++ b/lp-app/apps/lp-cli/src/client/local.rs @@ -110,7 +110,10 @@ impl AsyncLocalServerTransport { /// Receive a client message (async, blocking) /// /// Waits until a message is available or the connection is closed. - #[allow(dead_code)] // Will be used in future async server implementations + #[allow( + dead_code, + reason = "Will be used in future async server implementations" + )] pub async fn receive(&mut self) -> Result, TransportError> { if self.closed { return Err(TransportError::ConnectionLost); @@ -120,7 +123,10 @@ impl AsyncLocalServerTransport { } /// Send a server message - #[allow(dead_code)] // Will be used in future async server implementations + #[allow( + dead_code, + reason = "Will be used in future async server implementations" + )] pub fn send(&mut self, msg: ServerMessage) -> Result<(), TransportError> { if self.closed { return Err(TransportError::ConnectionLost); @@ -133,7 +139,10 @@ impl AsyncLocalServerTransport { } /// Close the transport - #[allow(dead_code)] // Will be used in future async server implementations + #[allow( + dead_code, + reason = "Will be used in future async server implementations" + )] pub fn close(&mut self) -> Result<(), TransportError> { if self.closed { return Ok(()); diff --git a/lp-app/apps/lp-cli/src/client/local_server.rs b/lp-app/apps/lp-cli/src/client/local_server.rs index 4eb10b052..a76cc71f5 100644 --- a/lp-app/apps/lp-cli/src/client/local_server.rs +++ b/lp-app/apps/lp-cli/src/client/local_server.rs @@ -52,7 +52,7 @@ impl LocalServerTransport { let runtime = match tokio::runtime::Runtime::new() { Ok(r) => r, Err(e) => { - eprintln!("Failed to create tokio runtime for server: {}", e); + eprintln!("Failed to create tokio runtime for server: {e}"); return; } }; @@ -61,7 +61,7 @@ impl LocalServerTransport { let (server, _base_fs) = match create_server(None, true, None) { Ok((s, fs)) => (s, fs), Err(e) => { - eprintln!("Failed to create server: {}", e); + eprintln!("Failed to create server: {e}"); return; } }; @@ -78,7 +78,7 @@ impl LocalServerTransport { // Mark as closed when server exits closed_clone.store(true, Ordering::Relaxed); }) - .map_err(|e| anyhow::anyhow!("Failed to spawn server thread: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to spawn server thread: {e}"))?; Ok(Self { server_handle: Some(server_handle), @@ -97,7 +97,10 @@ impl LocalServerTransport { /// /// * `Ok(())` if the server was stopped successfully (or already closed) /// * `Err` if waiting for the thread failed - #[allow(dead_code)] // Will be used in future cleanup/shutdown scenarios + #[allow( + dead_code, + reason = "Will be used in future cleanup/shutdown scenarios" + )] pub fn close(&mut self) -> Result<()> { // Check if already closed if self.closed.load(Ordering::Relaxed) { diff --git a/lp-app/apps/lp-cli/src/client/mod.rs b/lp-app/apps/lp-cli/src/client/mod.rs index abeece887..9c496f8c3 100644 --- a/lp-app/apps/lp-cli/src/client/mod.rs +++ b/lp-app/apps/lp-cli/src/client/mod.rs @@ -6,12 +6,12 @@ pub mod specifier; pub mod transport; pub mod transport_ws; -pub use client::{serializable_response_to_project_response, LpClient}; +pub use client::{LpClient, serializable_response_to_project_response}; pub use client_connect::client_connect; // Public API re-exports (may be used by external code) -#[allow(unused_imports)] +#[allow(unused_imports, reason = "Public API re-exports")] pub use local_server::LocalServerTransport; -#[allow(unused_imports)] +#[allow(unused_imports, reason = "Public API re-exports")] pub use specifier::HostSpecifier; -#[allow(unused_imports)] +#[allow(unused_imports, reason = "Public API re-exports")] pub use transport::ClientTransport; diff --git a/lp-app/apps/lp-cli/src/client/specifier.rs b/lp-app/apps/lp-cli/src/client/specifier.rs index 87d0f4af5..0fbf84015 100644 --- a/lp-app/apps/lp-cli/src/client/specifier.rs +++ b/lp-app/apps/lp-cli/src/client/specifier.rs @@ -65,25 +65,24 @@ impl HostSpecifier { } bail!( - "Invalid host specifier: '{}'. Supported formats: ws://host:port/, wss://host:port/, serial:auto, serial:/dev/ttyUSB1, local", - s + "Invalid host specifier: '{s}'. Supported formats: ws://host:port/, wss://host:port/, serial:auto, serial:/dev/ttyUSB1, local" ) } /// Check if this is a websocket specifier - #[allow(dead_code)] // Useful helper method for future use + #[allow(dead_code, reason = "Useful helper method for future use")] pub fn is_websocket(&self) -> bool { matches!(self, HostSpecifier::WebSocket { .. }) } /// Check if this is a serial specifier - #[allow(dead_code)] // Useful helper method for future use + #[allow(dead_code, reason = "Useful helper method for future use")] pub fn is_serial(&self) -> bool { matches!(self, HostSpecifier::Serial { .. }) } /// Check if this is a local specifier - #[allow(dead_code)] // Useful helper method for future use + #[allow(dead_code, reason = "Useful helper method for future use")] pub fn is_local(&self) -> bool { matches!(self, HostSpecifier::Local) } @@ -92,9 +91,9 @@ impl HostSpecifier { impl fmt::Display for HostSpecifier { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - HostSpecifier::WebSocket { url } => write!(f, "{}", url), + HostSpecifier::WebSocket { url } => write!(f, "{url}"), HostSpecifier::Serial { port: None } => write!(f, "serial:auto"), - HostSpecifier::Serial { port: Some(port) } => write!(f, "serial:{}", port), + HostSpecifier::Serial { port: Some(port) } => write!(f, "serial:{port}"), HostSpecifier::Local => write!(f, "local"), } } diff --git a/lp-app/apps/lp-cli/src/client/transport.rs b/lp-app/apps/lp-cli/src/client/transport.rs index 9d1bd1aaf..89f733169 100644 --- a/lp-app/apps/lp-cli/src/client/transport.rs +++ b/lp-app/apps/lp-cli/src/client/transport.rs @@ -80,6 +80,9 @@ pub trait ClientTransport: Send { /// /// * `Ok(())` if the transport was closed successfully (or already closed) /// * `Err(TransportError)` if closing failed - #[allow(dead_code)] // Required trait method, will be used in cleanup scenarios + #[allow( + dead_code, + reason = "Required trait method, will be used in cleanup scenarios" + )] async fn close(&mut self) -> Result<(), TransportError>; } diff --git a/lp-app/apps/lp-cli/src/client/transport_ws.rs b/lp-app/apps/lp-cli/src/client/transport_ws.rs index 0a9ad3ee7..abdf0a96f 100644 --- a/lp-app/apps/lp-cli/src/client/transport_ws.rs +++ b/lp-app/apps/lp-cli/src/client/transport_ws.rs @@ -33,8 +33,7 @@ impl WebSocketClientTransport { // Connect via tokio-tungstenite let (stream, _) = connect_async(url).await.map_err(|e| { TransportError::Other(format!( - "Failed to establish WebSocket connection to '{}': {}", - url, e + "Failed to establish WebSocket connection to '{url}': {e}" )) })?; @@ -59,14 +58,14 @@ impl ClientTransport for WebSocketClientTransport { // Serialize ClientMessage to JSON let json = serde_json::to_string(&msg).map_err(|e| { - TransportError::Serialization(format!("Failed to serialize ClientMessage: {}", e)) + TransportError::Serialization(format!("Failed to serialize ClientMessage: {e}")) })?; // Send as text message stream .send(tokio_tungstenite::tungstenite::Message::Text(json)) .await - .map_err(|e| TransportError::Other(format!("Failed to send message: {}", e)))?; + .map_err(|e| TransportError::Other(format!("Failed to send message: {e}")))?; Ok(()) } @@ -88,8 +87,7 @@ impl ClientTransport for WebSocketClientTransport { // Deserialize ServerMessage from JSON return serde_json::from_str(&text).map_err(|e| { TransportError::Deserialization(format!( - "Failed to deserialize ServerMessage: {}", - e + "Failed to deserialize ServerMessage: {e}" )) }); } @@ -97,8 +95,7 @@ impl ClientTransport for WebSocketClientTransport { // Deserialize ServerMessage from binary JSON return serde_json::from_slice(&data).map_err(|e| { TransportError::Deserialization(format!( - "Failed to deserialize ServerMessage: {}", - e + "Failed to deserialize ServerMessage: {e}" )) }); } @@ -121,7 +118,7 @@ impl ClientTransport for WebSocketClientTransport { Some(Err(e)) => { // WebSocket error self.stream = None; - return Err(TransportError::Other(format!("WebSocket error: {}", e))); + return Err(TransportError::Other(format!("WebSocket error: {e}"))); } None => { // Stream ended diff --git a/lp-app/apps/lp-cli/src/commands/create/project.rs b/lp-app/apps/lp-cli/src/commands/create/project.rs index d46a86489..0846bbe2a 100644 --- a/lp-app/apps/lp-cli/src/commands/create/project.rs +++ b/lp-app/apps/lp-cli/src/commands/create/project.rs @@ -85,10 +85,7 @@ pub fn generate_uid(name: &str) -> String { let second = (seconds_today % 60) as u32; // Format: YYYY.MM.DD-HH.MM.SS- - format!( - "{:04}.{:02}.{:02}-{:02}.{:02}.{:02}-{}", - year, month, day, hour, minute, second, name - ) + format!("{year:04}.{month:02}.{day:02}-{hour:02}.{minute:02}.{second:02}-{name}") } /// Create project directory structure @@ -129,7 +126,7 @@ pub fn create_project_structure(dir: &Path, name: Option<&str>, uid: Option<&str let project_json = serde_json::to_string_pretty(&config).context("Failed to serialize project.json")?; fs.write_file("/project.json".as_path(), project_json.as_bytes()) - .map_err(|e| anyhow::anyhow!("Failed to write project.json: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to write project.json: {e}"))?; // Create default template create_default_template(&fs)?; @@ -153,7 +150,7 @@ pub fn create_default_template(fs: &dyn LpFs) -> Result<()> { "/src/main.texture/node.json".as_path(), texture_json.as_bytes(), ) - .map_err(|e| anyhow::anyhow!("Failed to write texture node.json: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to write texture node.json: {e}"))?; // Create shader node let shader_config = ShaderConfig { @@ -167,7 +164,7 @@ pub fn create_default_template(fs: &dyn LpFs) -> Result<()> { "/src/rainbow.shader/node.json".as_path(), shader_json.as_bytes(), ) - .map_err(|e| anyhow::anyhow!("Failed to write shader node.json: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to write shader node.json: {e}"))?; // Create shader GLSL fs.write_file( @@ -232,7 +229,7 @@ vec4 main(vec2 fragCoord, vec2 outputSize, float time) { return vec4(max(vec3(0.0), min(vec3(1.0), rgb)), 1.0); }"#, ) - .map_err(|e| anyhow::anyhow!("Failed to write shader main.glsl: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to write shader main.glsl: {e}"))?; // Create output node let output_config = OutputConfig::GpioStrip { pin: 4 }; @@ -242,7 +239,7 @@ vec4 main(vec2 fragCoord, vec2 outputSize, float time) { "/src/strip.output/node.json".as_path(), output_json.as_bytes(), ) - .map_err(|e| anyhow::anyhow!("Failed to write output node.json: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to write output node.json: {e}"))?; // Create fixture node let fixture_config = FixtureConfig { @@ -264,7 +261,7 @@ vec4 main(vec2 fragCoord, vec2 outputSize, float time) { "/src/fixture.fixture/node.json".as_path(), fixture_json.as_bytes(), ) - .map_err(|e| anyhow::anyhow!("Failed to write fixture node.json: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to write fixture node.json: {e}"))?; Ok(()) } @@ -282,10 +279,10 @@ pub fn print_success_message(dir: &Path, name: &str) { }; let next_step_cmd = - messages::format_command(&format!("cd {} && lp-cli dev ws://localhost:2812/", name)); + messages::format_command(&format!("cd {name} && lp-cli dev ws://localhost:2812/")); messages::print_success( - &format!("Project created successfully: {} (uid: {})", name, uid), + &format!("Project created successfully: {name} (uid: {uid})"), &[&next_step_cmd], ); } @@ -429,7 +426,7 @@ mod tests { "/src/main.texture/node.json".as_path(), texture_json.as_bytes(), ) - .map_err(|e| anyhow::anyhow!("Failed to write texture node.json: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to write texture node.json: {e}"))?; // Create shader node let shader_config = ShaderConfig { @@ -443,7 +440,7 @@ mod tests { "/src/rainbow.shader/node.json".as_path(), shader_json.as_bytes(), ) - .map_err(|e| anyhow::anyhow!("Failed to write shader node.json: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to write shader node.json: {e}"))?; // Create shader GLSL fs.write_file_mut( @@ -508,7 +505,7 @@ vec4 main(vec2 fragCoord, vec2 outputSize, float time) { return vec4(max(vec3(0.0), min(vec3(1.0), rgb)), 1.0); }"#, ) - .map_err(|e| anyhow::anyhow!("Failed to write shader main.glsl: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to write shader main.glsl: {e}"))?; // Create output node let output_config = OutputConfig::GpioStrip { pin: 4 }; @@ -518,7 +515,7 @@ vec4 main(vec2 fragCoord, vec2 outputSize, float time) { "/src/strip.output/node.json".as_path(), output_json.as_bytes(), ) - .map_err(|e| anyhow::anyhow!("Failed to write output node.json: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to write output node.json: {e}"))?; // Create fixture node let fixture_config = FixtureConfig { @@ -540,7 +537,7 @@ vec4 main(vec2 fragCoord, vec2 outputSize, float time) { "/src/strip.fixture/node.json".as_path(), fixture_json.as_bytes(), ) - .map_err(|e| anyhow::anyhow!("Failed to write fixture node.json: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to write fixture node.json: {e}"))?; Ok(()) } diff --git a/lp-app/apps/lp-cli/src/commands/dev/async_client.rs b/lp-app/apps/lp-cli/src/commands/dev/async_client.rs index 0fc429b9d..b69d348d0 100644 --- a/lp-app/apps/lp-cli/src/commands/dev/async_client.rs +++ b/lp-app/apps/lp-cli/src/commands/dev/async_client.rs @@ -4,5 +4,5 @@ //! for backwards compatibility with existing code. // Re-exports for backwards compatibility (may be used by external code) -#[allow(unused_imports)] +#[allow(unused_imports, reason = "Backwards compatibility re-exports")] pub use crate::client::{LpClient, serializable_response_to_project_response}; diff --git a/lp-app/apps/lp-cli/src/commands/dev/handler.rs b/lp-app/apps/lp-cli/src/commands/dev/handler.rs index 2aa3ab5bf..f41e44884 100644 --- a/lp-app/apps/lp-cli/src/commands/dev/handler.rs +++ b/lp-app/apps/lp-cli/src/commands/dev/handler.rs @@ -3,8 +3,8 @@ //! Orchestrates the dev command: connects to server, syncs project, and runs file watching and UI. use anyhow::{Context, Result}; -use lp_model::project::ProjectConfig; use lp_model::AsLpPath; +use lp_model::project::ProjectConfig; use lp_shared::fs::{LpFs, LpFsStd}; use std::path::PathBuf; use std::sync::Arc; @@ -59,7 +59,12 @@ pub fn handle_dev(mut args: DevArgs) -> Result<()> { args.dir = std::env::current_dir()? .join(&args.dir) .canonicalize() - .with_context(|| format!("Failed to resolve project directory: {}", args.dir.display()))?; + .with_context(|| { + format!( + "Failed to resolve project directory: {}", + args.dir.display() + ) + })?; // Validate local project let (project_uid, _project_name) = validate_local_project(&args.dir)?; @@ -89,8 +94,8 @@ async fn handle_dev_async( host_spec: HostSpecifier, ) -> Result<()> { // Format host specifier for error messages before it's moved - let host_spec_str = format!("{:?}", host_spec); - + let host_spec_str = format!("{host_spec:?}"); + // Connect to server let transport = client_connect(host_spec).context("Failed to connect to server")?; @@ -107,15 +112,10 @@ async fn handle_dev_async( // This ensures the project exists on the server before we try to load it push_project_async(&client, &*local_fs, &project_uid) .await - .with_context(|| { - format!( - "Failed to push project to server (host: {})", - host_spec_str - ) - })?; + .with_context(|| format!("Failed to push project to server (host: {host_spec_str})"))?; // Load project on server - let project_path = format!("projects/{}", project_uid); + let project_path = format!("projects/{project_uid}"); let project_handle = client .project_load(&project_path) .await @@ -131,7 +131,7 @@ async fn handle_dev_async( Arc::new(LpFsStd::new(args.dir.clone())); tokio::spawn(async move { if let Err(e) = fs_loop(transport, project_dir, project_uid, local_fs_for_loop).await { - eprintln!("fs_loop error: {}", e); + eprintln!("fs_loop error: {e}"); } }) }; @@ -165,7 +165,7 @@ async fn handle_dev_async( options, Box::new(|_cc| Box::new(ui_state)), ) { - eprintln!("UI error: {}", e); + eprintln!("UI error: {e}"); } } diff --git a/lp-app/apps/lp-cli/src/commands/dev/mod.rs b/lp-app/apps/lp-cli/src/commands/dev/mod.rs index b3ae84b99..3fa05a6a6 100644 --- a/lp-app/apps/lp-cli/src/commands/dev/mod.rs +++ b/lp-app/apps/lp-cli/src/commands/dev/mod.rs @@ -11,9 +11,9 @@ pub mod watcher; pub use args::DevArgs; pub use fs_loop::fs_loop; pub use handler::handle_dev; -#[allow(unused_imports)] // May be used in future pull functionality +#[allow(unused_imports, reason = "May be used in future pull functionality")] pub use pull_project::pull_project_async; pub use push_project::push_project_async; // Re-export for public API (used internally via direct path) -#[allow(unused_imports)] +#[allow(unused_imports, reason = "Public API re-export")] pub use sync::sync_file_change; diff --git a/lp-app/apps/lp-cli/src/commands/dev/pull_project.rs b/lp-app/apps/lp-cli/src/commands/dev/pull_project.rs index e1eb2c6e0..8bf264e80 100644 --- a/lp-app/apps/lp-cli/src/commands/dev/pull_project.rs +++ b/lp-app/apps/lp-cli/src/commands/dev/pull_project.rs @@ -23,20 +23,20 @@ use crate::client::LpClient; /// /// * `Ok(())` if all files were pulled successfully /// * `Err` if any file operation failed -#[allow(dead_code)] +#[allow(dead_code, reason = "Reserved for future pull functionality")] pub async fn pull_project_async( client: &LpClient, local_fs: &dyn LpFs, project_uid: &str, ) -> Result<()> { // Build server project path - let server_project_path = format!("/projects/{}", project_uid); + let server_project_path = format!("/projects/{project_uid}"); // List all files recursively in the project on server let files = client .fs_list_dir(server_project_path.as_path(), true) .await - .with_context(|| format!("Failed to list files in project: {}", server_project_path))?; + .with_context(|| format!("Failed to list files in project: {server_project_path}"))?; // Pull each file from the server for file_path in files { @@ -48,15 +48,15 @@ pub async fn pull_project_async( // Extract local path by removing the "/projects/{project_uid}/" prefix let file_path_str = file_path.as_str(); - let prefix_with_slash = format!("/projects/{}/", project_uid); - let prefix_without_slash = format!("projects/{}/", project_uid); + let prefix_with_slash = format!("/projects/{project_uid}/"); + let prefix_without_slash = format!("projects/{project_uid}/"); let local_path = if file_path_str.starts_with(&prefix_with_slash) { // Remove prefix and ensure it starts with '/' let relative = &file_path_str[prefix_with_slash.len()..]; if relative.starts_with('/') { relative.to_string() } else { - format!("/{}", relative) + format!("/{relative}") } } else if file_path_str.starts_with(&prefix_without_slash) { // Handle paths without leading slash (backward compatibility) @@ -64,16 +64,16 @@ pub async fn pull_project_async( if relative.starts_with('/') { relative.to_string() } else { - format!("/{}", relative) + format!("/{relative}") } - } else if file_path_str == format!("/projects/{}", project_uid) - || file_path_str == format!("projects/{}", project_uid) + } else if file_path_str == format!("/projects/{project_uid}") + || file_path_str == format!("projects/{project_uid}") { // This is the project directory itself, skip continue; } else { // Unexpected path format, skip with warning - eprintln!("Warning: Unexpected file path format: {}", file_path_str); + eprintln!("Warning: Unexpected file path format: {file_path_str}"); continue; }; @@ -81,11 +81,7 @@ pub async fn pull_project_async( local_fs .write_file(local_path.as_path(), &data) .map_err(|e| { - anyhow::anyhow!( - "Failed to write file to local filesystem {}: {}", - local_path, - e - ) + anyhow::anyhow!("Failed to write file to local filesystem {local_path}: {e}") })?; } diff --git a/lp-app/apps/lp-cli/src/commands/dev/push_project.rs b/lp-app/apps/lp-cli/src/commands/dev/push_project.rs index 2b7eebbd3..ae7488beb 100644 --- a/lp-app/apps/lp-cli/src/commands/dev/push_project.rs +++ b/lp-app/apps/lp-cli/src/commands/dev/push_project.rs @@ -31,7 +31,7 @@ pub async fn push_project_async( // List all files recursively in the project directory let entries = local_fs .list_dir("/".as_path(), true) - .map_err(|e| anyhow::anyhow!("Failed to list project files: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to list project files: {e}"))?; // Push each file to the server (skip directories) for entry_path in entries { @@ -55,27 +55,29 @@ pub async fn push_project_async( Ok(data) => data, Err(e) => { // If read fails and it's because it's a directory, skip it - if entry_str.ends_with('/') || local_fs.is_dir(entry_path.as_path()).unwrap_or(false) { + if entry_str.ends_with('/') + || local_fs.is_dir(entry_path.as_path()).unwrap_or(false) + { continue; } - return Err(anyhow::anyhow!("Failed to read file {}: {}", entry_str, e)); + return Err(anyhow::anyhow!("Failed to read file {entry_str}: {e}")); } }; // Build server path: /projects/{project_uid}/{entry_path} // Remove leading '/' from entry_path for server path, then prepend /projects/{project_uid}/ - let relative_path = if entry_str.starts_with('/') { - &entry_str[1..] + let relative_path = if let Some(stripped) = entry_str.strip_prefix('/') { + stripped } else { entry_str }; - let server_path = format!("/projects/{}/{}", project_uid, relative_path); + let server_path = format!("/projects/{project_uid}/{relative_path}"); // Write file to server client .fs_write(server_path.as_path(), data) .await - .with_context(|| format!("Failed to write file to server: {}", server_path))?; + .with_context(|| format!("Failed to write file to server: {server_path}"))?; } Ok(()) diff --git a/lp-app/apps/lp-cli/src/commands/dev/sync.rs b/lp-app/apps/lp-cli/src/commands/dev/sync.rs index c46c03f40..6a2e19e9d 100644 --- a/lp-app/apps/lp-cli/src/commands/dev/sync.rs +++ b/lp-app/apps/lp-cli/src/commands/dev/sync.rs @@ -34,12 +34,12 @@ pub async fn sync_file_change( // Build server path: /projects/{project_uid}/{file_path} // Remove leading '/' from change.path for server path, then prepend /projects/{project_uid}/ let path_str = change.path.as_str(); - let relative_path = if path_str.starts_with('/') { - &path_str[1..] + let relative_path = if let Some(stripped) = path_str.strip_prefix('/') { + stripped } else { path_str }; - let server_path = format!("/projects/{}/{}", project_uid, relative_path); + let server_path = format!("/projects/{project_uid}/{relative_path}"); match change.change_type { ChangeType::Create | ChangeType::Modify => { @@ -50,22 +50,22 @@ pub async fn sync_file_change( } // Read file from local filesystem - let data = local_fs - .read_file(change.path.as_path()) - .map_err(|e| anyhow::anyhow!("Failed to read file {}: {}", change.path.as_str(), e))?; + let data = local_fs.read_file(change.path.as_path()).map_err(|e| { + anyhow::anyhow!("Failed to read file {}: {}", change.path.as_str(), e) + })?; // Write file to server client .fs_write(server_path.as_path(), data) .await - .with_context(|| format!("Failed to write file to server: {}", server_path))?; + .with_context(|| format!("Failed to write file to server: {server_path}"))?; } ChangeType::Delete => { // Delete file from server client .fs_delete_file(server_path.as_path()) .await - .with_context(|| format!("Failed to delete file on server: {}", server_path))?; + .with_context(|| format!("Failed to delete file on server: {server_path}"))?; } } diff --git a/lp-app/apps/lp-cli/src/commands/dev/watcher.rs b/lp-app/apps/lp-cli/src/commands/dev/watcher.rs index fbf99b568..d38b9cf71 100644 --- a/lp-app/apps/lp-cli/src/commands/dev/watcher.rs +++ b/lp-app/apps/lp-cli/src/commands/dev/watcher.rs @@ -14,7 +14,7 @@ pub struct FileWatcher { /// Receiver for file change events event_receiver: mpsc::UnboundedReceiver, /// Root path of the project (for path normalization) - #[allow(dead_code)] + #[allow(dead_code, reason = "Stored for path normalization")] root_path: PathBuf, /// Watcher handle (kept alive to continue watching) _watcher: notify::RecommendedWatcher, @@ -33,9 +33,9 @@ impl FileWatcher { /// * `Err` if watcher creation failed pub fn new(root_path: PathBuf) -> Result { // Canonicalize root path to handle symlinks (e.g., /private/var on macOS) - let canonical_root = root_path - .canonicalize() - .with_context(|| format!("Failed to canonicalize root path: {}", root_path.display()))?; + let canonical_root = root_path.canonicalize().with_context(|| { + format!("Failed to canonicalize root path: {}", root_path.display()) + })?; // Create channel for events (use unbounded to avoid blocking in sync callback) let (event_tx, event_rx) = mpsc::unbounded_channel(); @@ -52,7 +52,7 @@ impl FileWatcher { // Process event synchronously (we're in a blocking thread) let root = root_path_clone.clone(); let tx = event_tx.clone(); - + // Process each path in the event for path in event.paths { // Skip if it's a directory (we only sync files) @@ -69,7 +69,7 @@ impl FileWatcher { let normalized_path = match Self::normalize_path_sync(&path, &root) { Ok(p) => p, Err(e) => { - eprintln!("Error normalizing path {:?}: {}", path, e); + eprintln!("Error normalizing path {path:?}: {e}"); continue; } }; @@ -104,7 +104,7 @@ impl FileWatcher { } } Err(e) => { - eprintln!("File watcher error: {}", e); + eprintln!("File watcher error: {e}"); } } }, @@ -179,10 +179,7 @@ impl FileWatcher { let relative = canonical_absolute .strip_prefix(&canonical_root) .with_context(|| { - format!( - "Path {:?} is not within root {:?}", - canonical_absolute, canonical_root - ) + format!("Path {canonical_absolute:?} is not within root {canonical_root:?}") })?; // Convert to string with leading / @@ -190,7 +187,7 @@ impl FileWatcher { if path_str.starts_with('/') { Ok(path_str) } else { - Ok(format!("/{}", path_str)) + Ok(format!("/{path_str}")) } } @@ -210,8 +207,8 @@ impl FileWatcher { #[cfg(test)] mod tests { use super::*; - use tempfile::TempDir; use std::time::Duration; + use tempfile::TempDir; #[tokio::test] async fn test_filewatcher_creation() { @@ -247,13 +244,13 @@ mod tests { async fn test_filewatcher_detects_modify() { let temp_dir = TempDir::new().unwrap(); let test_file = temp_dir.path().join("test.txt"); - + // Create file first std::fs::write(&test_file, b"initial").unwrap(); - + // Create watcher after file exists let mut watcher = FileWatcher::new(temp_dir.path().to_path_buf()).unwrap(); - + // Give watcher time to start tokio::time::sleep(Duration::from_millis(100)).await; @@ -280,13 +277,13 @@ mod tests { async fn test_filewatcher_detects_delete() { let temp_dir = TempDir::new().unwrap(); let test_file = temp_dir.path().join("test.txt"); - + // Create file first std::fs::write(&test_file, b"content").unwrap(); - + // Create watcher after file exists let mut watcher = FileWatcher::new(temp_dir.path().to_path_buf()).unwrap(); - + // Give watcher time to start tokio::time::sleep(Duration::from_millis(100)).await; @@ -297,7 +294,9 @@ mod tests { // Note: Some OSes may report delete events differently, so we may need to wait for multiple events let mut found_delete = false; for _ in 0..5 { - if let Ok(Some(change)) = tokio::time::timeout(Duration::from_secs(1), watcher.next_change()).await { + if let Ok(Some(change)) = + tokio::time::timeout(Duration::from_secs(1), watcher.next_change()).await + { if change.path.as_str() == "/test.txt" { if change.change_type == ChangeType::Delete { found_delete = true; diff --git a/lp-app/apps/lp-cli/src/commands/serve/handler.rs b/lp-app/apps/lp-cli/src/commands/serve/handler.rs index e0ef521a8..413d3108a 100644 --- a/lp-app/apps/lp-cli/src/commands/serve/handler.rs +++ b/lp-app/apps/lp-cli/src/commands/serve/handler.rs @@ -23,7 +23,7 @@ pub fn handle_serve(args: ServeArgs) -> Result<()> { // Create websocket server transport on port 2812 let transport = WebSocketServerTransport::new(2812) - .map_err(|e| anyhow::anyhow!("Failed to start websocket server: {}", e))?; + .map_err(|e| anyhow::anyhow!("Failed to start websocket server: {e}"))?; println!("Server started on ws://localhost:2812/"); println!("Press Ctrl+C to stop"); diff --git a/lp-app/apps/lp-cli/src/commands/serve/init.rs b/lp-app/apps/lp-cli/src/commands/serve/init.rs index c88d7c386..44cd75df4 100644 --- a/lp-app/apps/lp-cli/src/commands/serve/init.rs +++ b/lp-app/apps/lp-cli/src/commands/serve/init.rs @@ -27,7 +27,7 @@ pub fn initialize_server(dir: &Path, init: bool) -> Result { let cmd = format_command(&format!("lp-cli serve {} --init", dir.display())); return Err(print_error_and_return( &format!("No server.json found in {}", dir.display()), - &[&format!("To initialize a new server, run: {}", cmd)], + &[&format!("To initialize a new server, run: {cmd}")], )); } } else { diff --git a/lp-app/apps/lp-cli/src/commands/serve/server_loop.rs b/lp-app/apps/lp-cli/src/commands/serve/server_loop.rs index 059d404f1..13b7def2e 100644 --- a/lp-app/apps/lp-cli/src/commands/serve/server_loop.rs +++ b/lp-app/apps/lp-cli/src/commands/serve/server_loop.rs @@ -39,7 +39,7 @@ pub fn run_server_loop(mut server: LpServer, mut transport: return Ok(()); } // Other transport errors - log and continue - eprintln!("Transport error: {}", e); + eprintln!("Transport error: {e}"); break; } } @@ -53,13 +53,13 @@ pub fn run_server_loop(mut server: LpServer, mut transport: for response in responses { if let Message::Server(server_msg) = response { if let Err(e) = transport.send(server_msg) { - eprintln!("Failed to send response: {}", e); + eprintln!("Failed to send response: {e}"); } } } } Err(e) => { - eprintln!("Server error: {}", e); + eprintln!("Server error: {e}"); // Continue running despite errors } } diff --git a/lp-app/apps/lp-cli/src/debug_ui/nodes/fixture.rs b/lp-app/apps/lp-cli/src/debug_ui/nodes/fixture.rs index 848a78092..6996a632b 100644 --- a/lp-app/apps/lp-cli/src/debug_ui/nodes/fixture.rs +++ b/lp-app/apps/lp-cli/src/debug_ui/nodes/fixture.rs @@ -9,7 +9,7 @@ use lp_model::{NodeHandle, NodeKind}; pub fn fixture_color(handle: &NodeHandle) -> Color32 { // Generate distinct colors for different fixtures // Hash the handle to get a consistent number - let hash: u32 = format!("{:?}", handle).chars().map(|c| c as u32).sum(); + let hash: u32 = format!("{handle:?}").chars().map(|c| c as u32).sum(); let hue = (hash as f32 * 137.508) % 360.0; // Golden angle for distribution let (r, g, b) = hsv_to_rgb(hue / 360.0, 0.8, 0.9); Color32::from_rgb(r, g, b) diff --git a/lp-app/apps/lp-cli/src/debug_ui/nodes/output.rs b/lp-app/apps/lp-cli/src/debug_ui/nodes/output.rs index d33156a29..628d0ec2e 100644 --- a/lp-app/apps/lp-cli/src/debug_ui/nodes/output.rs +++ b/lp-app/apps/lp-cli/src/debug_ui/nodes/output.rs @@ -25,10 +25,10 @@ pub fn render_output_panel(ui: &mut egui::Ui, entry: &ClientNodeEntry, state: &O for chunk in state.channel_data.chunks(16) { let hex: String = chunk .iter() - .map(|b| format!("{:02x}", b)) + .map(|b| format!("{b:02x}")) .collect::>() .join(" "); - ui.label(format!(" {}", hex)); + ui.label(format!(" {hex}")); } } } diff --git a/lp-app/apps/lp-cli/src/debug_ui/nodes/shader.rs b/lp-app/apps/lp-cli/src/debug_ui/nodes/shader.rs index 81e815c0b..08396ff13 100644 --- a/lp-app/apps/lp-cli/src/debug_ui/nodes/shader.rs +++ b/lp-app/apps/lp-cli/src/debug_ui/nodes/shader.rs @@ -11,7 +11,7 @@ pub fn render_shader_panel(ui: &mut egui::Ui, entry: &ClientNodeEntry, state: &S ui.label(format!("Path: {:?}", entry.path)); ui.label(format!("Status: {:?}", entry.status)); if let Some(error) = &state.error { - ui.colored_label(egui::Color32::RED, format!("Error: {}", error)); + ui.colored_label(egui::Color32::RED, format!("Error: {error}")); } }); diff --git a/lp-app/apps/lp-cli/src/debug_ui/panels.rs b/lp-app/apps/lp-cli/src/debug_ui/panels.rs index 9576e6a5e..7f2f2b07a 100644 --- a/lp-app/apps/lp-cli/src/debug_ui/panels.rs +++ b/lp-app/apps/lp-cli/src/debug_ui/panels.rs @@ -17,7 +17,7 @@ pub fn render_status_panel( ui.horizontal(|ui| { ui.label(format!("Frame: {}", frame_id.as_i64())); ui.separator(); - ui.label(format!("Server FPS: {:.0}", avg_server_fps)); + ui.label(format!("Server FPS: {avg_server_fps:.0}")); ui.separator(); if sync_in_progress { ui.label(egui::RichText::new("Syncing...").color(egui::Color32::YELLOW)); diff --git a/lp-app/apps/lp-cli/src/debug_ui/ui.rs b/lp-app/apps/lp-cli/src/debug_ui/ui.rs index 738af4095..14a131f5a 100644 --- a/lp-app/apps/lp-cli/src/debug_ui/ui.rs +++ b/lp-app/apps/lp-cli/src/debug_ui/ui.rs @@ -97,20 +97,20 @@ impl DebugUiState { } } Err(e) => { - eprintln!("Failed to apply changes: {}", e); + eprintln!("Failed to apply changes: {e}"); self.sync_in_progress = false; } } } Err(e) => { - eprintln!("Failed to convert response: {}", e); + eprintln!("Failed to convert response: {e}"); self.sync_in_progress = false; } } } Ok(Err(e)) => { // Sync failed - eprintln!("Sync error: {}", e); + eprintln!("Sync error: {e}"); self.sync_in_progress = false; } Err(tokio::sync::oneshot::error::TryRecvError::Empty) => { diff --git a/lp-app/apps/lp-cli/src/messages.rs b/lp-app/apps/lp-cli/src/messages.rs index 7956d7738..6c6f67da8 100644 --- a/lp-app/apps/lp-cli/src/messages.rs +++ b/lp-app/apps/lp-cli/src/messages.rs @@ -5,22 +5,22 @@ /// Print success message with next steps pub fn print_success(message: &str, next_steps: &[&str]) { - println!("✓ {}", message); + println!("✓ {message}"); if !next_steps.is_empty() { println!("\nNext steps:"); for step in next_steps { - println!(" {}", step); + println!(" {step}"); } } } /// Print error message with suggestions pub fn print_error(message: &str, suggestions: &[&str]) { - eprintln!("✗ {}", message); + eprintln!("✗ {message}"); if !suggestions.is_empty() { eprintln!(); for suggestion in suggestions { - eprintln!(" {}", suggestion); + eprintln!(" {suggestion}"); } } } @@ -31,7 +31,7 @@ pub fn print_error(message: &str, suggestions: &[&str]) { /// an error for propagation. pub fn print_error_and_return(message: &str, suggestions: &[&str]) -> anyhow::Error { print_error(message, suggestions); - anyhow::anyhow!("{}", message) + anyhow::anyhow!("{message}") } /// Format command for copy-paste (with proper quoting if needed) diff --git a/lp-app/apps/lp-cli/src/server/create_server.rs b/lp-app/apps/lp-cli/src/server/create_server.rs index 913e4401c..caa580823 100644 --- a/lp-app/apps/lp-cli/src/server/create_server.rs +++ b/lp-app/apps/lp-cli/src/server/create_server.rs @@ -107,7 +107,7 @@ mod tests { let result = create_server::create_server(Some(dir), false, Some(false)); assert!(result.is_err()); if let Err(e) = result { - let err_msg = format!("{}", e); + let err_msg = format!("{e}"); assert!(err_msg.contains("No server.json found")); } } diff --git a/lp-app/apps/lp-cli/src/server/run_server_loop_async.rs b/lp-app/apps/lp-cli/src/server/run_server_loop_async.rs index ab6eab9c2..1bf840ec6 100644 --- a/lp-app/apps/lp-cli/src/server/run_server_loop_async.rs +++ b/lp-app/apps/lp-cli/src/server/run_server_loop_async.rs @@ -43,7 +43,7 @@ pub async fn run_server_loop_async( return Ok(()); } // Other transport errors - log and continue - eprintln!("Transport error: {}", e); + eprintln!("Transport error: {e}"); break; } } @@ -57,13 +57,13 @@ pub async fn run_server_loop_async( for response in responses { if let Message::Server(server_msg) = response { if let Err(e) = transport.send(server_msg) { - eprintln!("Failed to send response: {}", e); + eprintln!("Failed to send response: {e}"); } } } } Err(e) => { - eprintln!("Server error: {}", e); + eprintln!("Server error: {e}"); // Continue running despite errors } } diff --git a/lp-app/apps/lp-cli/src/server/transport_ws.rs b/lp-app/apps/lp-cli/src/server/transport_ws.rs index 45e567a3d..84cfb190c 100644 --- a/lp-app/apps/lp-cli/src/server/transport_ws.rs +++ b/lp-app/apps/lp-cli/src/server/transport_ws.rs @@ -24,7 +24,7 @@ struct Connection { /// Channel receiver for receiving messages from this connection /// Note: Currently unused as messages are received via the async task, /// but kept for potential future use or debugging - #[allow(dead_code)] + #[allow(dead_code, reason = "Reserved for future use or debugging")] receiver: mpsc::UnboundedReceiver, } @@ -45,13 +45,13 @@ struct SharedState { pub struct WebSocketServerTransport { /// TCP listener for accepting new connections /// Note: Kept to maintain ownership of the listener, even though not directly accessed - #[allow(dead_code)] + #[allow(dead_code, reason = "Maintains ownership of listener")] listener: TcpListener, /// Shared state for connection management shared_state: Arc>, /// Tokio runtime for async operations /// Note: Kept to ensure runtime stays alive for async tasks - #[allow(dead_code)] + #[allow(dead_code, reason = "Ensures runtime stays alive for async tasks")] runtime: Arc, } @@ -69,17 +69,17 @@ impl WebSocketServerTransport { pub fn new(port: u16) -> Result { // Create tokio runtime let runtime = Runtime::new() - .map_err(|e| TransportError::Other(format!("Failed to create tokio runtime: {}", e)))?; + .map_err(|e| TransportError::Other(format!("Failed to create tokio runtime: {e}")))?; // Bind TCP listener - let addr = format!("0.0.0.0:{}", port); + let addr = format!("0.0.0.0:{port}"); let listener = TcpListener::bind(&addr) - .map_err(|e| TransportError::Other(format!("Failed to bind to {}: {}", addr, e)))?; + .map_err(|e| TransportError::Other(format!("Failed to bind to {addr}: {e}")))?; // Set non-blocking mode for the listener listener .set_nonblocking(true) - .map_err(|e| TransportError::Other(format!("Failed to set non-blocking: {}", e)))?; + .map_err(|e| TransportError::Other(format!("Failed to set non-blocking: {e}")))?; // Create shared state let shared_state = Arc::new(Mutex::new(SharedState { @@ -96,7 +96,7 @@ impl WebSocketServerTransport { runtime_clone.spawn(Self::accept_connections_task( listener .try_clone() - .map_err(|e| TransportError::Other(format!("Failed to clone listener: {}", e)))?, + .map_err(|e| TransportError::Other(format!("Failed to clone listener: {e}")))?, shared_state_clone, )); @@ -122,7 +122,7 @@ impl WebSocketServerTransport { let listener = match TokioTcpListener::from_std(listener) { Ok(l) => l, Err(e) => { - eprintln!("Failed to convert listener to tokio: {}", e); + eprintln!("Failed to convert listener to tokio: {e}"); return; } }; @@ -147,7 +147,7 @@ impl WebSocketServerTransport { )); } Err(e) => { - eprintln!("Failed to accept websocket connection: {}", e); + eprintln!("Failed to accept websocket connection: {e}"); } } } @@ -191,14 +191,14 @@ impl WebSocketServerTransport { let json = match serde_json::to_string(&msg) { Ok(j) => j, Err(e) => { - eprintln!("Failed to serialize ServerMessage: {}", e); + eprintln!("Failed to serialize ServerMessage: {e}"); continue; } }; // Send via websocket if let Err(e) = ws_sender.send(Message::Text(json)).await { - eprintln!("Failed to send message: {}", e); + eprintln!("Failed to send message: {e}"); break; } } @@ -218,7 +218,7 @@ impl WebSocketServerTransport { .push_back((connection_id, client_msg)); } Err(e) => { - eprintln!("Failed to deserialize ClientMessage: {}", e); + eprintln!("Failed to deserialize ClientMessage: {e}"); } } } @@ -232,7 +232,7 @@ impl WebSocketServerTransport { .push_back((connection_id, client_msg)); } Err(e) => { - eprintln!("Failed to deserialize ClientMessage: {}", e); + eprintln!("Failed to deserialize ClientMessage: {e}"); } } } @@ -255,7 +255,7 @@ impl WebSocketServerTransport { // Ignore raw frames } Err(e) => { - eprintln!("WebSocket error: {}", e); + eprintln!("WebSocket error: {e}"); let mut state = shared_state.lock().unwrap(); state.connections.remove(&connection_id); break; @@ -279,7 +279,7 @@ impl ServerTransport for WebSocketServerTransport { // (we can't clone ServerMessage, so we serialize/deserialize for each connection) let json = serde_json::to_string(&msg).map_err(|e| { - TransportError::Serialization(format!("Failed to serialize ServerMessage: {}", e)) + TransportError::Serialization(format!("Failed to serialize ServerMessage: {e}")) })?; let state = self.shared_state.lock().unwrap(); @@ -299,8 +299,7 @@ impl ServerTransport for WebSocketServerTransport { Ok(m) => m, Err(e) => { return Err(TransportError::Deserialization(format!( - "Failed to deserialize ServerMessage: {}", - e + "Failed to deserialize ServerMessage: {e}" ))); } }; diff --git a/lp-app/apps/lp-cli/tests/file_watch_sync.rs b/lp-app/apps/lp-cli/tests/file_watch_sync.rs index d153cce86..5d184c2d9 100644 --- a/lp-app/apps/lp-cli/tests/file_watch_sync.rs +++ b/lp-app/apps/lp-cli/tests/file_watch_sync.rs @@ -149,7 +149,7 @@ fn test_sync_file_change_path_formatting() { // Test path without leading slash let file_path_no_slash = "src/test.glsl"; - let expected_server_path2 = format!("projects/{}/{}", project_uid, file_path_no_slash); + let expected_server_path2 = format!("projects/{project_uid}/{file_path_no_slash}"); assert_eq!(expected_server_path2, "projects/test-project/src/test.glsl"); // Test nested path diff --git a/lp-app/apps/lp-cli/tests/integration.rs b/lp-app/apps/lp-cli/tests/integration.rs index 9e607aeab..460049de0 100644 --- a/lp-app/apps/lp-cli/tests/integration.rs +++ b/lp-app/apps/lp-cli/tests/integration.rs @@ -11,8 +11,8 @@ use core::cell::RefCell; // NOTE: These integration tests use the old synchronous lp-client API which no longer exists. // They need to be rewritten to use the new async LpClient API. Marked as #[ignore] for now. -use lp_model::Message; use lp_model::AsLpPath; +use lp_model::Message; use lp_server::LpServer; use lp_shared::fs::{LpFs, LpFsMemory}; use lp_shared::output::MemoryOutputProvider; @@ -27,7 +27,9 @@ type ClientError = (); /// Returns `(server, client, client_transport, server_transport)` for /// synchronous message processing in tests. #[allow(dead_code, unused_variables)] -fn setup_server_and_client(_fs: LpFsMemory) -> (LpServer, LpClient, LocalTransport, LocalTransport) { +fn setup_server_and_client( + _fs: LpFsMemory, +) -> (LpServer, LpClient, LocalTransport, LocalTransport) { todo!("Rewrite for async LpClient") } @@ -59,10 +61,9 @@ fn create_test_project(fs: &mut LpFsMemory, name: &str, uid: &str) -> Result<(), // Create project.json let project_json = format!( r#"{{ - "uid": "{}", - "name": "{}" -}}"#, - uid, name + "uid": "{uid}", + "name": "{name}" +}}"# ); fs.write_file_mut("/project.json".as_path(), project_json.as_bytes()) .map_err(|_| todo!())?; @@ -110,10 +111,9 @@ fn test_create_command_structure() { // Create project.json let project_json = format!( r#"{{ - "uid": "{}", - "name": "{}" -}}"#, - project_uid, project_name + "uid": "{project_uid}", + "name": "{project_name}" +}}"# ); fs.write_file_mut("/project.json".as_path(), project_json.as_bytes()) .unwrap(); diff --git a/lp-app/crates/lp-engine-client/src/project/view.rs b/lp-app/crates/lp-engine-client/src/project/view.rs index d4d2e237d..6871a7a59 100644 --- a/lp-app/crates/lp-engine-client/src/project/view.rs +++ b/lp-app/crates/lp-engine-client/src/project/view.rs @@ -4,8 +4,8 @@ use alloc::format; use alloc::string::String; use alloc::vec::Vec; use lp_model::{ - project::api::{ApiNodeSpecifier, NodeChange, NodeState, NodeStatus}, FrameId, LpPathBuf, NodeConfig, NodeHandle, - NodeKind, + FrameId, LpPathBuf, NodeConfig, NodeHandle, NodeKind, + project::api::{ApiNodeSpecifier, NodeChange, NodeState, NodeStatus}, }; /// Client view of project diff --git a/lp-app/crates/lp-engine-client/src/test_util.rs b/lp-app/crates/lp-engine-client/src/test_util.rs index 11d23c262..2da1b145e 100644 --- a/lp-app/crates/lp-engine-client/src/test_util.rs +++ b/lp-app/crates/lp-engine-client/src/test_util.rs @@ -23,9 +23,8 @@ pub fn assert_first_output_red( assert_eq!( r, expected_r, - "Output channel 0 R: expected {}, got {}", - expected_r, r + "Output channel 0 R: expected {expected_r}, got {r}" ); - assert_eq!(g, 0, "Output channel 0 G: expected 0, got {}", g); - assert_eq!(b, 0, "Output channel 0 B: expected 0, got {}", b); + assert_eq!(g, 0, "Output channel 0 G: expected 0, got {g}"); + assert_eq!(b, 0, "Output channel 0 B: expected 0, got {b}"); } diff --git a/lp-app/crates/lp-engine-client/tests/client_view.rs b/lp-app/crates/lp-engine-client/tests/client_view.rs index dce977331..f11fe3722 100644 --- a/lp-app/crates/lp-engine-client/tests/client_view.rs +++ b/lp-app/crates/lp-engine-client/tests/client_view.rs @@ -2,7 +2,7 @@ extern crate alloc; use alloc::collections::BTreeMap; use lp_engine_client::ClientProjectView; -use lp_model::{project::api::ProjectResponse, FrameId, NodeHandle}; +use lp_model::{FrameId, NodeHandle, project::api::ProjectResponse}; #[test] fn test_client_view_creation() { diff --git a/lp-app/crates/lp-engine/src/error.rs b/lp-app/crates/lp-engine/src/error.rs index f37dbf5b4..609a40c2b 100644 --- a/lp-app/crates/lp-engine/src/error.rs +++ b/lp-app/crates/lp-engine/src/error.rs @@ -51,16 +51,16 @@ impl core::fmt::Display for Error { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { Error::Io { path, details } => { - write!(f, "I/O error: {} ({})", details, path) + write!(f, "I/O error: {details} ({path})") } Error::Parse { file, error } => { - write!(f, "Parse error in {}: {}", file, error) + write!(f, "Parse error in {file}: {error}") } Error::NotFound { path } => { - write!(f, "Not found: {}", path) + write!(f, "Not found: {path}") } Error::InvalidConfig { node_path, reason } => { - write!(f, "Invalid config for {}: {}", node_path, reason) + write!(f, "Invalid config for {node_path}: {reason}") } Error::WrongNodeKind { specifier, @@ -69,12 +69,11 @@ impl core::fmt::Display for Error { } => { write!( f, - "Wrong node kind for {}: expected {:?}, got {:?}", - specifier, expected, actual + "Wrong node kind for {specifier}: expected {expected:?}, got {actual:?}" ) } Error::Other { message } => { - write!(f, "Error: {}", message) + write!(f, "Error: {message}") } } } @@ -90,10 +89,10 @@ impl From for Error { fn from(err: lp_shared::OutputError) -> Self { match err { lp_shared::OutputError::PinAlreadyOpen { pin } => Error::Other { - message: format!("Pin {} is already open", pin), + message: format!("Pin {pin} is already open"), }, lp_shared::OutputError::InvalidHandle { handle } => Error::Other { - message: format!("Invalid handle: {}", handle), + message: format!("Invalid handle: {handle}"), }, lp_shared::OutputError::InvalidConfig { reason } => Error::InvalidConfig { node_path: String::from("output"), @@ -103,8 +102,7 @@ impl From for Error { Error::InvalidConfig { node_path: String::from("output"), reason: format!( - "Data length {} doesn't match expected byte_count {}", - actual, expected + "Data length {actual} doesn't match expected byte_count {expected}" ), } } diff --git a/lp-app/crates/lp-engine/src/nodes/shader/runtime.rs b/lp-app/crates/lp-engine/src/nodes/shader/runtime.rs index 566ae685b..74e48ee01 100644 --- a/lp-app/crates/lp-engine/src/nodes/shader/runtime.rs +++ b/lp-app/crates/lp-engine/src/nodes/shader/runtime.rs @@ -122,7 +122,7 @@ impl NodeRuntime for ShaderRuntime { 4, ) .map_err(|e| Error::Other { - message: format!("Shader execution failed: {}", e), + message: format!("Shader execution failed: {e}"), })?; // Extract RGBA from vec4 result @@ -189,7 +189,7 @@ impl NodeRuntime for ShaderRuntime { let texture_handle = ctx .resolve_texture(&shader_config.texture_spec) .map_err(|e| { - self.compilation_error = Some(format!("Failed to resolve texture: {}", e)); + self.compilation_error = Some(format!("Failed to resolve texture: {e}")); e })?; self.texture_handle = Some(texture_handle); @@ -254,7 +254,7 @@ impl ShaderRuntime { ctx: &dyn NodeInitContext, ) -> Result<(), Error> { let texture_handle = ctx.resolve_texture(&config.texture_spec).map_err(|e| { - self.compilation_error = Some(format!("Failed to resolve texture: {}", e)); + self.compilation_error = Some(format!("Failed to resolve texture: {e}")); e })?; self.texture_handle = Some(texture_handle); @@ -275,15 +275,17 @@ impl ShaderRuntime { } else { LpPathBuf::from(format!("/{}", glsl_path.as_str())) }; - let source_bytes = fs.read_file(glsl_path_abs.as_path()).map_err(|e| Error::Io { - path: glsl_path.as_str().to_string(), - details: format!("Failed to read GLSL file: {:?}", e), - })?; + let source_bytes = fs + .read_file(glsl_path_abs.as_path()) + .map_err(|e| Error::Io { + path: glsl_path.as_str().to_string(), + details: format!("Failed to read GLSL file: {e:?}"), + })?; let glsl_source = alloc::string::String::from_utf8(source_bytes).map_err(|e| Error::Parse { file: glsl_path.as_str().to_string(), - error: format!("Invalid UTF-8 in GLSL file: {}", e), + error: format!("Invalid UTF-8 in GLSL file: {e}"), })?; // Store source for state extraction @@ -310,11 +312,11 @@ impl ShaderRuntime { Ok(()) } Err(e) => { - self.compilation_error = Some(format!("{}", e)); + self.compilation_error = Some(format!("{e}")); self.executable = None; Err(Error::InvalidConfig { node_path: format!("shader-{}", self.node_handle.as_i32()), - reason: format!("GLSL compilation failed: {}", e), + reason: format!("GLSL compilation failed: {e}"), }) } } diff --git a/lp-app/crates/lp-engine/src/nodes/texture/runtime.rs b/lp-app/crates/lp-engine/src/nodes/texture/runtime.rs index e8856ef0a..496feb517 100644 --- a/lp-app/crates/lp-engine/src/nodes/texture/runtime.rs +++ b/lp-app/crates/lp-engine/src/nodes/texture/runtime.rs @@ -83,7 +83,7 @@ impl NodeRuntime for TextureRuntime { let texture = Texture::new(config.width, config.height, format).map_err(|e| { Error::InvalidConfig { node_path: format!("texture-{}", self.node_handle.as_i32()), - reason: format!("Failed to create texture: {}", e), + reason: format!("Failed to create texture: {e}"), } })?; @@ -131,7 +131,7 @@ impl NodeRuntime for TextureRuntime { let texture = Texture::new(texture_config.width, texture_config.height, format) .map_err(|e| Error::InvalidConfig { node_path: format!("texture-{}", self.node_handle.as_i32()), - reason: format!("Failed to resize texture: {}", e), + reason: format!("Failed to resize texture: {e}"), })?; self.texture = Some(texture); } diff --git a/lp-app/crates/lp-engine/src/project/loader.rs b/lp-app/crates/lp-engine/src/project/loader.rs index fbbaf2395..dc5f03919 100644 --- a/lp-app/crates/lp-engine/src/project/loader.rs +++ b/lp-app/crates/lp-engine/src/project/loader.rs @@ -34,7 +34,7 @@ pub(crate) fn node_kind_from_path(path: &LpPathBuf) -> Result { "fixture" => Ok(NodeKind::Fixture), _ => Err(Error::InvalidConfig { node_path: path_str.to_string(), - reason: format!("Unknown node kind: {}", suffix), + reason: format!("Unknown node kind: {suffix}"), }), } } @@ -53,12 +53,12 @@ pub fn load_from_filesystem(fs: &dyn LpFs) -> Result { let path = "/project.json"; let data = fs.read_file(path.as_path()).map_err(|e| Error::Io { path: path.to_string(), - details: format!("Failed to read: {:?}", e), + details: format!("Failed to read: {e:?}"), })?; let config: ProjectConfig = serde_json::from_slice(&data).map_err(|e| Error::Parse { file: path.to_string(), - error: format!("{}", e), + error: format!("{e}"), })?; Ok(config) @@ -69,7 +69,7 @@ pub fn discover_nodes(fs: &dyn LpFs) -> Result, Error> { let path = "/src"; let entries = fs.list_dir(path.as_path(), false).map_err(|e| Error::Io { path: path.to_string(), - details: format!("Failed to list directory: {:?}", e), + details: format!("Failed to list directory: {e:?}"), })?; let mut nodes = Vec::new(); @@ -83,16 +83,15 @@ pub fn discover_nodes(fs: &dyn LpFs) -> Result, Error> { } /// Load a node's config from filesystem -pub fn load_node( - fs: &dyn LpFs, - path: &LpPath, -) -> Result<(LpPathBuf, Box), Error> { +pub fn load_node(fs: &dyn LpFs, path: &LpPath) -> Result<(LpPathBuf, Box), Error> { let node_json_path = path.to_path_buf().join("node.json"); - let data = fs.read_file(node_json_path.as_path()).map_err(|e| Error::Io { - path: node_json_path.as_str().to_string(), - details: format!("Failed to read: {:?}", e), - })?; + let data = fs + .read_file(node_json_path.as_path()) + .map_err(|e| Error::Io { + path: node_json_path.as_str().to_string(), + details: format!("Failed to read: {e:?}"), + })?; // Determine node kind from path suffix let kind = node_kind_from_path(&path.to_path_buf())?; @@ -103,7 +102,7 @@ pub fn load_node( let cfg: lp_model::nodes::texture::TextureConfig = serde_json::from_slice(&data) .map_err(|e| Error::Parse { file: node_json_path.as_str().to_string(), - error: format!("Failed to parse texture config: {}", e), + error: format!("Failed to parse texture config: {e}"), })?; Box::new(cfg) } @@ -111,7 +110,7 @@ pub fn load_node( let cfg: lp_model::nodes::shader::ShaderConfig = serde_json::from_slice(&data) .map_err(|e| Error::Parse { file: node_json_path.as_str().to_string(), - error: format!("Failed to parse shader config: {}", e), + error: format!("Failed to parse shader config: {e}"), })?; Box::new(cfg) } @@ -119,7 +118,7 @@ pub fn load_node( let cfg: lp_model::nodes::output::OutputConfig = serde_json::from_slice(&data) .map_err(|e| Error::Parse { file: node_json_path.as_str().to_string(), - error: format!("Failed to parse output config: {}", e), + error: format!("Failed to parse output config: {e}"), })?; Box::new(cfg) } @@ -127,7 +126,7 @@ pub fn load_node( let cfg: lp_model::nodes::fixture::FixtureConfig = serde_json::from_slice(&data) .map_err(|e| Error::Parse { file: node_json_path.as_str().to_string(), - error: format!("Failed to parse fixture config: {}", e), + error: format!("Failed to parse fixture config: {e}"), })?; Box::new(cfg) } diff --git a/lp-app/crates/lp-engine/src/project/runtime.rs b/lp-app/crates/lp-engine/src/project/runtime.rs index 1ec7f41e1..65e1f3c7f 100644 --- a/lp-app/crates/lp-engine/src/project/runtime.rs +++ b/lp-app/crates/lp-engine/src/project/runtime.rs @@ -170,7 +170,7 @@ impl ProjectRuntime { kind, config, config_ver: self.frame_id, - status: NodeStatus::InitError(format!("Failed to load: {}", e)), + status: NodeStatus::InitError(format!("Failed to load: {e}")), runtime: None, state_ver: FrameId::default(), }; @@ -224,13 +224,13 @@ impl ProjectRuntime { .read_file(node_json_path.as_path()) .map_err(|e| Error::Io { path: node_json_path.as_str().to_string(), - details: format!("Failed to read: {:?}", e), + details: format!("Failed to read: {e:?}"), })?; Some( serde_json::from_slice::(&data) .map_err(|e| Error::Parse { file: node_json_path.as_str().to_string(), - error: format!("Failed to parse texture config: {}", e), + error: format!("Failed to parse texture config: {e}"), })?, ) } else { @@ -249,13 +249,13 @@ impl ProjectRuntime { .read_file(node_json_path.as_path()) .map_err(|e| Error::Io { path: node_json_path.as_str().to_string(), - details: format!("Failed to read: {:?}", e), + details: format!("Failed to read: {e:?}"), })?; Some( serde_json::from_slice::(&data) .map_err(|e| Error::Parse { file: node_json_path.as_str().to_string(), - error: format!("Failed to parse fixture config: {}", e), + error: format!("Failed to parse fixture config: {e}"), })?, ) } else { @@ -274,13 +274,13 @@ impl ProjectRuntime { .read_file(node_json_path.as_path()) .map_err(|e| Error::Io { path: node_json_path.as_str().to_string(), - details: format!("Failed to read: {:?}", e), + details: format!("Failed to read: {e:?}"), })?; Some( serde_json::from_slice::(&data) .map_err(|e| Error::Parse { file: node_json_path.as_str().to_string(), - error: format!("Failed to parse shader config: {}", e), + error: format!("Failed to parse shader config: {e}"), })?, ) } else { @@ -299,13 +299,13 @@ impl ProjectRuntime { .read_file(node_json_path.as_path()) .map_err(|e| Error::Io { path: node_json_path.as_str().to_string(), - details: format!("Failed to read: {:?}", e), + details: format!("Failed to read: {e:?}"), })?; Some( serde_json::from_slice::(&data) .map_err(|e| Error::Parse { file: node_json_path.as_str().to_string(), - error: format!("Failed to parse output config: {}", e), + error: format!("Failed to parse output config: {e}"), })?, ) } else { @@ -358,7 +358,7 @@ impl ProjectRuntime { entry.runtime = Some(runtime); } Err(e) => { - entry.status = NodeStatus::InitError(format!("{}", e)); + entry.status = NodeStatus::InitError(format!("{e}")); entry.runtime = None; } } @@ -486,7 +486,7 @@ impl ProjectRuntime { // Update status based on render result if let Some(entry) = self.nodes.get_mut(&handle) { if let Err(e) = render_result { - entry.status = NodeStatus::Error(format!("{}", e)); + entry.status = NodeStatus::Error(format!("{e}")); } } } @@ -527,7 +527,7 @@ impl ProjectRuntime { if let Err(e) = render_result { if let Some(entry) = self.nodes.get_mut(&handle) { - entry.status = NodeStatus::Error(format!("{}", e)); + entry.status = NodeStatus::Error(format!("{e}")); } } } @@ -683,8 +683,8 @@ impl ProjectRuntime { let stripped_str = stripped.as_str(); if stripped_str == "/" { "" - } else if stripped_str.starts_with('/') { - &stripped_str[1..] + } else if let Some(stripped) = stripped_str.strip_prefix('/') { + stripped } else { stripped_str } @@ -1111,7 +1111,10 @@ impl ProjectRuntime { /// Init context implementation struct InitContext<'a> { runtime: &'a ProjectRuntime, - #[allow(dead_code)] // Used for chroot filesystem creation, may be needed for future features + #[allow( + dead_code, + reason = "Used for chroot filesystem creation, may be needed for future features" + )] node_path: &'a LpPathBuf, node_fs: alloc::rc::Rc>, } @@ -1125,7 +1128,7 @@ impl<'a> InitContext<'a> { .chroot(node_dir.as_path()) .map_err(|e| Error::Io { path: node_dir.to_string(), - details: format!("Failed to chroot: {:?}", e), + details: format!("Failed to chroot: {e:?}"), })?; Ok(Self { diff --git a/lp-app/crates/lp-engine/tests/scene_render.rs b/lp-app/crates/lp-engine/tests/scene_render.rs index ea063a648..fb004985d 100644 --- a/lp-app/crates/lp-engine/tests/scene_render.rs +++ b/lp-app/crates/lp-engine/tests/scene_render.rs @@ -95,11 +95,10 @@ fn assert_memory_output_red( assert_eq!( r, expected_r, - "Output channel 0 R: expected {}, got {}", - expected_r, r + "Output channel 0 R: expected {expected_r}, got {r}" ); - assert_eq!(g, 0, "Output channel 0 G: expected 0, got {}", g); - assert_eq!(b, 0, "Output channel 0 B: expected 0, got {}", b); + assert_eq!(g, 0, "Output channel 0 G: expected 0, got {g}"); + assert_eq!(b, 0, "Output channel 0 B: expected 0, got {b}"); } /// Sync the client view with the runtime diff --git a/lp-app/crates/lp-engine/tests/scene_update.rs b/lp-app/crates/lp-engine/tests/scene_update.rs index 70eb5e06e..8b516a557 100644 --- a/lp-app/crates/lp-engine/tests/scene_update.rs +++ b/lp-app/crates/lp-engine/tests/scene_update.rs @@ -32,7 +32,9 @@ fn test_node_json_modification() { runtime.ensure_all_nodes_initialized().unwrap(); // Get shader handle - let shader_handle = runtime.handle_for_path("/src/shader-1.shader".as_path()).unwrap(); + let shader_handle = runtime + .handle_for_path("/src/shader-1.shader".as_path()) + .unwrap(); // Render a frame to get baseline runtime.tick(4).unwrap(); @@ -186,11 +188,15 @@ fn test_node_deletion() { runtime.ensure_all_nodes_initialized().unwrap(); // Get shader handle - let _shader_handle = runtime.handle_for_path("/src/shader-1.shader".as_path()).unwrap(); + let _shader_handle = runtime + .handle_for_path("/src/shader-1.shader".as_path()) + .unwrap(); // Delete node.json let shader_config_path = "/src/shader-1.shader/node.json"; - fs.borrow_mut().delete_file_mut(shader_config_path.as_path()).unwrap(); + fs.borrow_mut() + .delete_file_mut(shader_config_path.as_path()) + .unwrap(); // Get filesystem changes let changes = fs.borrow().get_changes(); @@ -199,7 +205,9 @@ fn test_node_deletion() { // Verify the node was removed assert!( - runtime.handle_for_path("/src/shader-1.shader".as_path()).is_err(), + runtime + .handle_for_path("/src/shader-1.shader".as_path()) + .is_err(), "Node should be removed after node.json deletion" ); } diff --git a/lp-app/crates/lp-model/src/path.rs b/lp-app/crates/lp-model/src/path.rs index e99e75991..1bdea325b 100644 --- a/lp-app/crates/lp-model/src/path.rs +++ b/lp-app/crates/lp-model/src/path.rs @@ -344,7 +344,7 @@ impl LpPathBuf { } else { // Relative path, append to base if self.0 == "/" { - LpPathBuf::from(format!("/{}", path_str)) + LpPathBuf::from(format!("/{path_str}")) } else { LpPathBuf::from(format!("{}/{}", self.0, path_str)) } @@ -368,7 +368,7 @@ impl LpPathBuf { .components() .collect::>() .iter() - .map(|s| *s) + .copied() .collect(); // Add relative path components diff --git a/lp-app/crates/lp-model/src/server/api.rs b/lp-app/crates/lp-model/src/server/api.rs index c52aa2a77..ff826a79e 100644 --- a/lp-app/crates/lp-model/src/server/api.rs +++ b/lp-app/crates/lp-model/src/server/api.rs @@ -1,6 +1,6 @@ -use crate::project::{api::SerializableProjectResponse, ProjectHandle, ProjectRequest}; -use crate::server::fs_api::{FsRequest, FsResponse}; use crate::LpPathBuf; +use crate::project::{ProjectHandle, ProjectRequest, api::SerializableProjectResponse}; +use crate::server::fs_api::{FsRequest, FsResponse}; use alloc::string::String; use alloc::vec::Vec; use serde::{Deserialize, Serialize}; diff --git a/lp-app/crates/lp-model/src/server/fs_api.rs b/lp-app/crates/lp-model/src/server/fs_api.rs index 011af3d3a..cff6bed55 100644 --- a/lp-app/crates/lp-model/src/server/fs_api.rs +++ b/lp-app/crates/lp-model/src/server/fs_api.rs @@ -2,9 +2,9 @@ //! //! Defines request and response types for filesystem operations. +use crate::LpPathBuf; use alloc::{string::String, vec::Vec}; use serde::{Deserialize, Serialize}; -use crate::LpPathBuf; /// Filesystem operation request #[derive(Debug, Clone, Serialize, Deserialize)] @@ -36,11 +36,20 @@ pub enum FsResponse { error: Option, }, /// Response to Write request - Write { path: LpPathBuf, error: Option }, + Write { + path: LpPathBuf, + error: Option, + }, /// Response to DeleteFile request - DeleteFile { path: LpPathBuf, error: Option }, + DeleteFile { + path: LpPathBuf, + error: Option, + }, /// Response to DeleteDir request - DeleteDir { path: LpPathBuf, error: Option }, + DeleteDir { + path: LpPathBuf, + error: Option, + }, /// Response to ListDir request ListDir { path: LpPathBuf, diff --git a/lp-app/crates/lp-model/src/transport_error.rs b/lp-app/crates/lp-model/src/transport_error.rs index a3e35fd71..bd706d5c4 100644 --- a/lp-app/crates/lp-model/src/transport_error.rs +++ b/lp-app/crates/lp-model/src/transport_error.rs @@ -22,12 +22,12 @@ pub enum TransportError { impl fmt::Display for TransportError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - TransportError::Serialization(msg) => write!(f, "Serialization error: {}", msg), + TransportError::Serialization(msg) => write!(f, "Serialization error: {msg}"), TransportError::Deserialization(msg) => { - write!(f, "Deserialization error: {}", msg) + write!(f, "Deserialization error: {msg}") } TransportError::ConnectionLost => write!(f, "Connection lost"), - TransportError::Other(msg) => write!(f, "Transport error: {}", msg), + TransportError::Other(msg) => write!(f, "Transport error: {msg}"), } } } diff --git a/lp-app/crates/lp-server/src/error.rs b/lp-app/crates/lp-server/src/error.rs index 7c5ccbecc..57a0a6200 100644 --- a/lp-app/crates/lp-server/src/error.rs +++ b/lp-app/crates/lp-server/src/error.rs @@ -24,14 +24,14 @@ impl fmt::Display for ServerError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { ServerError::ProjectNotFound(name) => { - write!(f, "Project not found: {}", name) + write!(f, "Project not found: {name}") } ServerError::ProjectExists(name) => { - write!(f, "Project already exists: {}", name) + write!(f, "Project already exists: {name}") } - ServerError::Filesystem(msg) => write!(f, "Filesystem error: {}", msg), - ServerError::Core(msg) => write!(f, "Core error: {}", msg), - ServerError::Serialization(msg) => write!(f, "Serialization error: {}", msg), + ServerError::Filesystem(msg) => write!(f, "Filesystem error: {msg}"), + ServerError::Core(msg) => write!(f, "Core error: {msg}"), + ServerError::Serialization(msg) => write!(f, "Serialization error: {msg}"), } } } diff --git a/lp-app/crates/lp-server/src/handlers.rs b/lp-app/crates/lp-server/src/handlers.rs index 39406b4cd..a7491d866 100644 --- a/lp-app/crates/lp-server/src/handlers.rs +++ b/lp-app/crates/lp-server/src/handlers.rs @@ -7,8 +7,8 @@ use crate::project_manager::ProjectManager; use alloc::{format, rc::Rc, vec::Vec}; use core::cell::RefCell; use lp_model::{ - server::{AvailableProject, FsRequest, FsResponse, ServerMsgBody as ServerMessagePayload}, AsLpPath, ClientMessage, LpPath, LpPathBuf, - ServerMessage, + AsLpPath, ClientMessage, LpPath, LpPathBuf, ServerMessage, + server::{AvailableProject, FsRequest, FsResponse, ServerMsgBody as ServerMessagePayload}, }; use lp_shared::fs::LpFs; use lp_shared::output::OutputProvider; @@ -58,42 +58,40 @@ fn handle_fs_request(fs: &mut dyn LpFs, request: FsRequest) -> Result Ok(FsResponse::Read { path, data: None, - error: Some(format!("{}", e)), + error: Some(format!("{e}")), }), }, FsRequest::Write { path, data } => match fs.write_file(path.as_path(), &data) { Ok(()) => Ok(FsResponse::Write { path, error: None }), Err(e) => Ok(FsResponse::Write { path, - error: Some(format!("{}", e)), + error: Some(format!("{e}")), }), }, FsRequest::DeleteFile { path } => match fs.delete_file(path.as_path()) { Ok(()) => Ok(FsResponse::DeleteFile { path, error: None }), Err(e) => Ok(FsResponse::DeleteFile { path, - error: Some(format!("{}", e)), + error: Some(format!("{e}")), }), }, FsRequest::DeleteDir { path } => match fs.delete_dir(path.as_path()) { Ok(()) => Ok(FsResponse::DeleteDir { path, error: None }), Err(e) => Ok(FsResponse::DeleteDir { path, - error: Some(format!("{}", e)), + error: Some(format!("{e}")), }), }, FsRequest::ListDir { path, recursive } => match fs.list_dir(path.as_path(), recursive) { - Ok(entries) => { - Ok(FsResponse::ListDir { - path, - entries, - error: None, - }) - } + Ok(entries) => Ok(FsResponse::ListDir { + path, + entries, + error: None, + }), Err(e) => Ok(FsResponse::ListDir { path, entries: Vec::new(), - error: Some(format!("{}", e)), + error: Some(format!("{e}")), }), }, } @@ -137,11 +135,11 @@ fn handle_project_request( let response = project .runtime_mut() .get_changes(since_frame, &detail_specifier) - .map_err(|e| ServerError::Core(format!("Failed to get changes: {}", e)))?; + .map_err(|e| ServerError::Core(format!("Failed to get changes: {e}")))?; let serializable_response = response .to_serializable() - .map_err(|e| ServerError::Core(format!("Failed to serialize response: {}", e)))?; + .map_err(|e| ServerError::Core(format!("Failed to serialize response: {e}")))?; Ok(ServerMessagePayload::ProjectRequest { response: serializable_response, diff --git a/lp-app/crates/lp-server/src/project.rs b/lp-app/crates/lp-server/src/project.rs index 7c7e75e06..24d359e56 100644 --- a/lp-app/crates/lp-server/src/project.rs +++ b/lp-app/crates/lp-server/src/project.rs @@ -34,7 +34,7 @@ impl Project { output_provider: Rc>, ) -> Result { let runtime = ProjectRuntime::new(fs, output_provider) - .map_err(|e| ServerError::Core(format!("{}", e)))?; + .map_err(|e| ServerError::Core(format!("{e}")))?; Ok(Self { name, diff --git a/lp-app/crates/lp-server/src/project_manager.rs b/lp-app/crates/lp-server/src/project_manager.rs index ed1478e14..67ffbf252 100644 --- a/lp-app/crates/lp-server/src/project_manager.rs +++ b/lp-app/crates/lp-server/src/project_manager.rs @@ -90,7 +90,7 @@ impl ProjectManager { // Create project-scoped filesystem using chroot let project_fs = base_fs .chroot(project_path.as_path()) - .map_err(|e| ServerError::Filesystem(format!("Failed to chroot to project: {}", e)))?; + .map_err(|e| ServerError::Filesystem(format!("Failed to chroot to project: {e}")))?; // Create a new project instance let mut project = Project::new( @@ -102,12 +102,11 @@ impl ProjectManager { // Auto-initialize the project runtime project.runtime_mut().load_nodes().map_err(|e| { - ServerError::Core(format!("Failed to load nodes for project {}: {}", name, e)) + ServerError::Core(format!("Failed to load nodes for project {name}: {e}")) })?; project.runtime_mut().init_nodes().map_err(|e| { ServerError::Core(format!( - "Failed to initialize nodes for project {}: {}", - name, e + "Failed to initialize nodes for project {name}: {e}" )) })?; project @@ -115,8 +114,7 @@ impl ProjectManager { .ensure_all_nodes_initialized() .map_err(|e| { ServerError::Core(format!( - "Failed to ensure all nodes initialized for project {}: {}", - name, e + "Failed to ensure all nodes initialized for project {name}: {e}" )) })?; @@ -144,8 +142,7 @@ impl ProjectManager { let project_path = lp_model::LpPathBuf::from(normalized_path); let name = project_path.file_name().ok_or_else(|| { ServerError::Core(format!( - "Invalid project path: cannot extract name from '{}'", - path + "Invalid project path: cannot extract name from '{path}'" )) })?; @@ -211,7 +208,7 @@ impl ProjectManager { let entries = fs .list_dir(self.projects_base_dir.as_path(), false) .map_err(|e| { - ServerError::Filesystem(format!("Failed to read projects directory: {}", e)) + ServerError::Filesystem(format!("Failed to read projects directory: {e}")) })?; let mut projects = Vec::new(); diff --git a/lp-app/crates/lp-server/src/template.rs b/lp-app/crates/lp-server/src/template.rs index 249d014a2..7f8e9a6c6 100644 --- a/lp-app/crates/lp-server/src/template.rs +++ b/lp-app/crates/lp-server/src/template.rs @@ -19,14 +19,14 @@ pub fn create_default_project_template(fs: &dyn LpFs) -> Result<(), ServerError> "/src/texture.texture/node.json".as_path(), br#"{"$type":"Memory","size":[64,64],"format":"RGB8"}"#, ) - .map_err(|e| ServerError::Filesystem(format!("Failed to write texture node.json: {}", e)))?; + .map_err(|e| ServerError::Filesystem(format!("Failed to write texture node.json: {e}")))?; // Create shader node fs.write_file( "/src/shader.shader/node.json".as_path(), br#"{"$type":"Single","texture_id":"/src/texture.texture"}"#, ) - .map_err(|e| ServerError::Filesystem(format!("Failed to write shader node.json: {}", e)))?; + .map_err(|e| ServerError::Filesystem(format!("Failed to write shader node.json: {e}")))?; fs.write_file( "/src/shader.shader/main.glsl".as_path(), @@ -90,21 +90,21 @@ vec4 main(vec2 fragCoord, vec2 outputSize, float time) { return vec4(max(vec3(0.0), min(vec3(1.0), rgb)), 1.0); }"#, ) - .map_err(|e| ServerError::Filesystem(format!("Failed to write shader main.glsl: {}", e)))?; + .map_err(|e| ServerError::Filesystem(format!("Failed to write shader main.glsl: {e}")))?; // Create output node fs.write_file( "/src/output.output/node.json".as_path(), br#"{"$type":"gpio_strip","chip":"ws2812","gpio_pin":4,"count":128}"#, ) - .map_err(|e| ServerError::Filesystem(format!("Failed to write output node.json: {}", e)))?; + .map_err(|e| ServerError::Filesystem(format!("Failed to write output node.json: {e}")))?; // Create fixture node fs.write_file( "/src/fixture.fixture/node.json".as_path(), br#"{"$type":"circle-list","output_id":"/src/output.output","texture_id":"/src/texture.texture","channel_order":"rgb","mapping":[{"channel":0,"center":[0.03125,0.0625],"radius":0.05},{"channel":1,"center":[0.09375,0.0625],"radius":0.05},{"channel":2,"center":[0.15625,0.0625],"radius":0.05},{"channel":3,"center":[0.21875,0.0625],"radius":0.05},{"channel":4,"center":[0.28125,0.0625],"radius":0.05},{"channel":5,"center":[0.34375,0.0625],"radius":0.05},{"channel":6,"center":[0.40625,0.0625],"radius":0.05},{"channel":7,"center":[0.46875,0.0625],"radius":0.05},{"channel":8,"center":[0.53125,0.0625],"radius":0.05},{"channel":9,"center":[0.59375,0.0625],"radius":0.05},{"channel":10,"center":[0.65625,0.0625],"radius":0.05},{"channel":11,"center":[0.71875,0.0625],"radius":0.05}]}"#, ) - .map_err(|e| ServerError::Filesystem(format!("Failed to write fixture node.json: {}", e)))?; + .map_err(|e| ServerError::Filesystem(format!("Failed to write fixture node.json: {e}")))?; Ok(()) } diff --git a/lp-app/crates/lp-shared/src/error.rs b/lp-app/crates/lp-shared/src/error.rs index f26eb146d..0ec694b94 100644 --- a/lp-app/crates/lp-shared/src/error.rs +++ b/lp-app/crates/lp-shared/src/error.rs @@ -17,9 +17,9 @@ pub enum FsError { impl fmt::Display for FsError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FsError::Filesystem(msg) => write!(f, "Filesystem error: {}", msg), - FsError::NotFound(msg) => write!(f, "File not found: {}", msg), - FsError::InvalidPath(msg) => write!(f, "Invalid path: {}", msg), + FsError::Filesystem(msg) => write!(f, "Filesystem error: {msg}"), + FsError::NotFound(msg) => write!(f, "File not found: {msg}"), + FsError::InvalidPath(msg) => write!(f, "Invalid path: {msg}"), } } } @@ -37,10 +37,10 @@ impl fmt::Display for TextureError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { TextureError::InvalidFormat(format) => { - write!(f, "Invalid texture format: {}", format) + write!(f, "Invalid texture format: {format}") } TextureError::DimensionsTooLarge { width, height } => { - write!(f, "Texture dimensions too large: {}x{}", width, height) + write!(f, "Texture dimensions too large: {width}x{height}") } } } @@ -65,23 +65,22 @@ impl fmt::Display for OutputError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { OutputError::PinAlreadyOpen { pin } => { - write!(f, "Pin {} is already open", pin) + write!(f, "Pin {pin} is already open") } OutputError::InvalidHandle { handle } => { - write!(f, "Invalid handle: {}", handle) + write!(f, "Invalid handle: {handle}") } OutputError::InvalidConfig { reason } => { - write!(f, "Invalid config: {}", reason) + write!(f, "Invalid config: {reason}") } OutputError::DataLengthMismatch { expected, actual } => { write!( f, - "Data length {} doesn't match expected byte_count {}", - actual, expected + "Data length {actual} doesn't match expected byte_count {expected}" ) } OutputError::Other { message } => { - write!(f, "Error: {}", message) + write!(f, "Error: {message}") } } } diff --git a/lp-app/crates/lp-shared/src/fs/lp_fs_mem.rs b/lp-app/crates/lp-shared/src/fs/lp_fs_mem.rs index a600ee44e..644d1b081 100644 --- a/lp-app/crates/lp-shared/src/fs/lp_fs_mem.rs +++ b/lp-app/crates/lp-shared/src/fs/lp_fs_mem.rs @@ -79,8 +79,7 @@ impl LpFsMemory { for file_path in files.keys() { if file_path.as_str().starts_with(&dir_prefix_str) { return Err(FsError::Filesystem(format!( - "Path {:?} is a directory, use delete_dir_mut() instead", - path + "Path {path:?} is a directory, use delete_dir_mut() instead" ))); } } @@ -280,7 +279,7 @@ impl LpFs for LpFsMemory { if let Some(slash_pos) = remainder.find('/') { // It's a subdirectory - add the directory path let dir_name = &remainder[..slash_pos]; - let full_dir_path = format!("{}{}", prefix_str, dir_name); + let full_dir_path = format!("{prefix_str}{dir_name}"); let full_dir_path_buf = LpPathBuf::from(full_dir_path.as_str()); if !entries.iter().any(|e| *e == full_dir_path_buf) { entries.push(full_dir_path_buf); @@ -304,13 +303,12 @@ impl LpFs for LpFsMemory { self.validate_path(normalized.as_path())?; // Check if it's a directory (by checking if any file starts with normalized + "/") - let dir_prefix_str = format!("{}/", normalized_str); + let dir_prefix_str = format!("{normalized_str}/"); let mut files = self.files.borrow_mut(); for file_path in files.keys() { if file_path.as_str().starts_with(&dir_prefix_str) { return Err(FsError::Filesystem(format!( - "Path {:?} is a directory, use delete_dir() instead", - normalized_str + "Path {normalized_str:?} is a directory, use delete_dir() instead" ))); } } @@ -337,7 +335,7 @@ impl LpFs for LpFsMemory { let prefix = if normalized_str.ends_with('/') { normalized_str.to_string() } else { - format!("{}/", normalized_str) + format!("{normalized_str}/") }; let mut files = self.files.borrow_mut(); diff --git a/lp-app/crates/lp-shared/src/fs/lp_fs_std.rs b/lp-app/crates/lp-shared/src/fs/lp_fs_std.rs index 2c422c1cb..96062dadb 100644 --- a/lp-app/crates/lp-shared/src/fs/lp_fs_std.rs +++ b/lp-app/crates/lp-shared/src/fs/lp_fs_std.rs @@ -2,16 +2,11 @@ use crate::error::FsError; use crate::fs::{ + LpFs, fs_event::{ChangeType, FsChange, FsVersion}, lp_fs_view::LpFsView, - LpFs, -}; -use alloc::{ - format, - rc::Rc, - string::ToString, - vec::Vec, }; +use alloc::{format, rc::Rc, string::ToString, vec::Vec}; use core::cell::RefCell; use hashbrown::HashMap; use lp_model::path::{LpPath, LpPathBuf}; @@ -42,7 +37,7 @@ impl LpFsStd { pub fn new(root_path: PathBuf) -> Self { // Ensure the root directory exists if let Err(e) = fs::create_dir_all(&root_path) { - log::warn!("Failed to create root directory {:?}: {}", root_path, e); + log::warn!("Failed to create root directory {root_path:?}: {e}"); } Self { root_path, @@ -94,7 +89,7 @@ impl LpFsStd { let canonical_root = self .root_path .canonicalize() - .map_err(|e| FsError::Filesystem(format!("Failed to canonicalize root path: {}", e)))?; + .map_err(|e| FsError::Filesystem(format!("Failed to canonicalize root path: {e}")))?; if !canonical_path.starts_with(&canonical_root) { return Err(FsError::InvalidPath(format!( @@ -159,8 +154,7 @@ impl LpFsStd { if component == ".." { if depth == 0 { return Err(FsError::InvalidPath(format!( - "Path {:?} would escape root directory", - path + "Path {path:?} would escape root directory" ))); } depth -= 1; @@ -179,29 +173,26 @@ impl LpFsStd { results: &mut Vec, ) -> Result<(), FsError> { let entries = fs::read_dir(dir_path).map_err(|e| { - FsError::Filesystem(format!("Failed to read directory {:?}: {}", dir_path, e)) + FsError::Filesystem(format!("Failed to read directory {dir_path:?}: {e}")) })?; for entry in entries { - let entry = entry.map_err(|e| { - FsError::Filesystem(format!("Failed to read directory entry: {}", e)) - })?; + let entry = entry + .map_err(|e| FsError::Filesystem(format!("Failed to read directory entry: {e}")))?; let entry_path = entry.path(); // Canonicalize the entry path let canonical_entry = entry_path.canonicalize().map_err(|e| { FsError::Filesystem(format!( - "Failed to canonicalize entry path {:?}: {}", - entry_path, e + "Failed to canonicalize entry path {entry_path:?}: {e}" )) })?; // Build the relative path from canonical root let relative_path = canonical_entry.strip_prefix(canonical_root).map_err(|_| { FsError::Filesystem(format!( - "Failed to compute relative path from root: entry={:?}, root={:?}", - canonical_entry, canonical_root + "Failed to compute relative path from root: entry={canonical_entry:?}, root={canonical_root:?}" )) })?; @@ -224,7 +215,7 @@ impl LpFs for LpFsStd { fn read_file(&self, path: &LpPath) -> Result, FsError> { let full_path = self.resolve_and_validate(path)?; fs::read(&full_path) - .map_err(|e| FsError::Filesystem(format!("Failed to read file {:?}: {}", full_path, e))) + .map_err(|e| FsError::Filesystem(format!("Failed to read file {full_path:?}: {e}"))) } fn write_file(&self, path: &LpPath, data: &[u8]) -> Result<(), FsError> { @@ -233,14 +224,12 @@ impl LpFs for LpFsStd { if let Some(parent) = full_path.parent() { if let Err(e) = fs::create_dir_all(parent) { return Err(FsError::Filesystem(format!( - "Failed to create directory {:?}: {}", - parent, e + "Failed to create directory {parent:?}: {e}" ))); } } - fs::write(&full_path, data).map_err(|e| { - FsError::Filesystem(format!("Failed to write file {:?}: {}", full_path, e)) - }) + fs::write(&full_path, data) + .map_err(|e| FsError::Filesystem(format!("Failed to write file {full_path:?}: {e}"))) } fn file_exists(&self, path: &LpPath) -> Result { @@ -272,8 +261,7 @@ impl LpFs for LpFsStd { // Check if it's actually a directory if !full_path.is_dir() { return Err(FsError::Filesystem(format!( - "Path {:?} is not a directory", - normalized_str + "Path {normalized_str:?} is not a directory" ))); } @@ -281,7 +269,7 @@ impl LpFs for LpFsStd { let canonical_root = self .root_path .canonicalize() - .map_err(|e| FsError::Filesystem(format!("Failed to canonicalize root path: {}", e)))?; + .map_err(|e| FsError::Filesystem(format!("Failed to canonicalize root path: {e}")))?; let mut results = Vec::new(); @@ -291,12 +279,12 @@ impl LpFs for LpFsStd { } else { // Non-recursive: only immediate children let entries = fs::read_dir(&full_path).map_err(|e| { - FsError::Filesystem(format!("Failed to read directory {:?}: {}", full_path, e)) + FsError::Filesystem(format!("Failed to read directory {full_path:?}: {e}")) })?; for entry in entries { let entry = entry.map_err(|e| { - FsError::Filesystem(format!("Failed to read directory entry: {}", e)) + FsError::Filesystem(format!("Failed to read directory entry: {e}")) })?; let entry_path = entry.path(); @@ -304,8 +292,7 @@ impl LpFs for LpFsStd { // Canonicalize the entry path let canonical_entry = entry_path.canonicalize().map_err(|e| { FsError::Filesystem(format!( - "Failed to canonicalize entry path {:?}: {}", - entry_path, e + "Failed to canonicalize entry path {entry_path:?}: {e}" )) })?; @@ -313,8 +300,7 @@ impl LpFs for LpFsStd { let relative_path = canonical_entry.strip_prefix(&canonical_root).map_err(|_| { FsError::Filesystem(format!( - "Failed to compute relative path from root: entry={:?}, root={:?}", - canonical_entry, canonical_root + "Failed to compute relative path from root: entry={canonical_entry:?}, root={canonical_root:?}" )) })?; @@ -342,14 +328,12 @@ impl LpFs for LpFsStd { // Check if it's a file (not a directory) if full_path.is_dir() { return Err(FsError::Filesystem(format!( - "Path {:?} is a directory, use delete_dir() instead", - normalized_str + "Path {normalized_str:?} is a directory, use delete_dir() instead" ))); } - fs::remove_file(&full_path).map_err(|e| { - FsError::Filesystem(format!("Failed to delete file {:?}: {}", full_path, e)) - }) + fs::remove_file(&full_path) + .map_err(|e| FsError::Filesystem(format!("Failed to delete file {full_path:?}: {e}"))) } fn delete_dir(&self, path: &LpPath) -> Result<(), FsError> { @@ -366,14 +350,13 @@ impl LpFs for LpFsStd { // Check if it's a directory if !full_path.is_dir() { return Err(FsError::Filesystem(format!( - "Path {:?} is not a directory, use delete_file() instead", - normalized_str + "Path {normalized_str:?} is not a directory, use delete_file() instead" ))); } // Delete recursively fs::remove_dir_all(&full_path).map_err(|e| { - FsError::Filesystem(format!("Failed to delete directory {:?}: {}", full_path, e)) + FsError::Filesystem(format!("Failed to delete directory {full_path:?}: {e}")) }) } @@ -407,7 +390,7 @@ impl LpFs for LpFsStd { let canonical_current_root = self .root_path .canonicalize() - .map_err(|e| FsError::Filesystem(format!("Failed to canonicalize root path: {}", e)))?; + .map_err(|e| FsError::Filesystem(format!("Failed to canonicalize root path: {e}")))?; if !canonical_new_root.starts_with(&canonical_current_root) { return Err(FsError::InvalidPath(format!( @@ -433,7 +416,10 @@ impl LpFs for LpFsStd { changes: Mutex::new(self.changes.lock().unwrap().clone()), })); - Ok(Rc::new(RefCell::new(LpFsView::new(parent_rc, prefix.as_path())))) + Ok(Rc::new(RefCell::new(LpFsView::new( + parent_rc, + prefix.as_path(), + )))) } fn current_version(&self) -> FsVersion { diff --git a/lp-app/crates/lp-shared/src/fs/lp_fs_view.rs b/lp-app/crates/lp-shared/src/fs/lp_fs_view.rs index 9bedd459b..1e9703641 100644 --- a/lp-app/crates/lp-shared/src/fs/lp_fs_view.rs +++ b/lp-app/crates/lp-shared/src/fs/lp_fs_view.rs @@ -76,7 +76,7 @@ impl LpFsView { } else if relative_str.starts_with('/') { relative_str } else { - return Some(LpPathBuf::from(format!("/{}", relative_str))); + return Some(LpPathBuf::from(format!("/{relative_str}"))); }; Some(LpPathBuf::from(normalized)) } else { @@ -214,7 +214,7 @@ impl LpFs for LpFsView { let relative_prefix = if normalized_subdir.ends_with('/') { normalized_subdir.to_string() } else { - format!("{}/", normalized_subdir) + format!("{normalized_subdir}/") }; // Construct full prefix in parent filesystem diff --git a/lp-app/crates/lp-shared/src/output/memory.rs b/lp-app/crates/lp-shared/src/output/memory.rs index 2c7da4008..e4231592c 100644 --- a/lp-app/crates/lp-shared/src/output/memory.rs +++ b/lp-app/crates/lp-shared/src/output/memory.rs @@ -10,7 +10,7 @@ use core::cell::RefCell; struct ChannelState { pin: u32, byte_count: u32, - #[allow(dead_code)] // Stored for future protocol-specific handling + #[allow(dead_code, reason = "Stored for future protocol-specific handling")] format: OutputFormat, data: Vec, } @@ -95,7 +95,7 @@ impl OutputProvider for MemoryOutputProvider { // Validate byte_count if byte_count == 0 { return Err(OutputError::InvalidConfig { - reason: format!("byte_count must be > 0, got {}", byte_count), + reason: format!("byte_count must be > 0, got {byte_count}"), }); } diff --git a/lp-app/crates/lp-shared/src/project/builder.rs b/lp-app/crates/lp-shared/src/project/builder.rs index 78969a964..00de77f12 100644 --- a/lp-app/crates/lp-shared/src/project/builder.rs +++ b/lp-app/crates/lp-shared/src/project/builder.rs @@ -177,8 +177,8 @@ impl TextureBuilder { let id = builder.texture_id; builder.texture_id += 1; - let path_str = format!("/src/texture-{}.texture", id); - let node_path = format!("{}/node.json", path_str); + let path_str = format!("/src/texture-{id}.texture"); + let node_path = format!("{path_str}/node.json"); let config = TextureConfig { width: self.width, @@ -213,9 +213,9 @@ impl ShaderBuilder { let id = builder.shader_id; builder.shader_id += 1; - let path_str = format!("/src/shader-{}.shader", id); - let node_path = format!("{}/node.json", path_str); - let glsl_path = format!("{}/main.glsl", path_str); + let path_str = format!("/src/shader-{id}.shader"); + let node_path = format!("{path_str}/node.json"); + let glsl_path = format!("{path_str}/main.glsl"); let config = ShaderConfig { glsl_path: "main.glsl".as_path_buf(), @@ -249,8 +249,8 @@ impl OutputBuilder { let id = builder.output_id; builder.output_id += 1; - let path_str = format!("/src/output-{}.output", id); - let node_path = format!("{}/node.json", path_str); + let path_str = format!("/src/output-{id}.output"); + let node_path = format!("{path_str}/node.json"); let config = OutputConfig::GpioStrip { pin: self.pin }; @@ -294,8 +294,8 @@ impl FixtureBuilder { let id = builder.fixture_id; builder.fixture_id += 1; - let path_str = format!("/src/fixture-{}.fixture", id); - let node_path = format!("{}/node.json", path_str); + let path_str = format!("/src/fixture-{id}.fixture"); + let node_path = format!("{path_str}/node.json"); let config = FixtureConfig { output_spec: NodeSpecifier::from(self.output_path.as_str()), diff --git a/lp-glsl/Cargo.toml b/lp-glsl/Cargo.toml deleted file mode 100644 index db40afa8c..000000000 --- a/lp-glsl/Cargo.toml +++ /dev/null @@ -1,93 +0,0 @@ -# This file is kept for reference but workspace is now defined in root Cargo.toml -# [workspace] -# resolver = '2' -# members = [ -# "crates/lp-builtins", -# "../lp-app/crates/lp-engine", -# "crates/lp-filetests-gen", -# "crates/lp-glsl-compiler", -# "crates/lp-glsl-filetests", -# "crates/lp-jit-util", -# "crates/lp-riscv-shared", -# "crates/lp-riscv-tools", -# "apps/embive-program", -# "apps/esp32-glsl-jit", -# "../lp-app/apps/lp-cli", -# "apps/lp-builtin-gen", -# "apps/lp-builtins-app", -# "apps/lp-test", -# "apps/test-structreturn", -# ] - -[package] -version = "40.0.0" -authors = ["The Wasmtime Project Developers"] -edition = "2024" -license = "Apache-2.0 WITH LLVM-exception" -rust-version = "1.90.0" - -# [workspace.lints.rust] -unused_extern_crates = 'warn' -trivial_numeric_casts = 'warn' -unstable_features = 'warn' -unused_import_braces = 'warn' -unused-lifetimes = 'warn' -unused-macro-rules = 'warn' - -# [workspace.lints.clippy] -all = { level = 'allow', priority = -1 } -clone_on_copy = 'warn' -map_clone = 'warn' -uninlined_format_args = 'warn' -unnecessary_to_owned = 'warn' -manual_strip = 'warn' -useless_conversion = 'warn' -unnecessary_mut_passed = 'warn' -unnecessary_fallible_conversions = 'warn' -unnecessary_cast = 'warn' -allow_attributes_without_reason = 'warn' -from_over_into = 'warn' -redundant_field_names = 'warn' -multiple_bound_locations = 'warn' -extra_unused_type_parameters = 'warn' - -# [workspace.dependencies] -# Cranelift crates - reference root workspace -cranelift-codegen = { workspace = true } -cranelift-frontend = { workspace = true } -cranelift-module = { workspace = true } -cranelift-jit = { workspace = true } -cranelift-native = { workspace = true } -cranelift-object = { workspace = true } -cranelift-reader = { workspace = true } -cranelift-control = { workspace = true } - -# Common dependencies - reference root workspace -target-lexicon = { workspace = true } -hashbrown = { workspace = true } -anyhow = { workspace = true } -regex = { workspace = true } -glob = { workspace = true } -object = { workspace = true } -critical-section = { workspace = true } -serde = { workspace = true } -serde_json = { workspace = true } - -# Note: ESP32-C3 supports atomic CAS, so portable-atomic's unsafe-assume-single-core -# feature should not be enabled. If esp-hal enables it incorrectly, this may be -# a bug in esp-hal v1.0.0-rc.0 that needs to be reported upstream. - -# [profile.release] -# # Link-time optimization for all release builds - saves ~100-300 KB -# # This applies to all packages in the workspace -# lto = true - -# [profile.release.package.esp32-glsl-jit] -# # Size optimizations for embedded target (ESP32-C3) -# # Strip debuginfo but keep symbols (needed for defmt/probe-rs) - saves ~1-1.5 MB -# strip = "debuginfo" -# # Optimize for size instead of speed - saves ~200-500 KB -# opt-level = "z" -# # Single codegen unit for better dead code elimination - saves ~50-100 KB -# codegen-units = 1 - diff --git a/lp-glsl/Cargo.toml.reference b/lp-glsl/Cargo.toml.reference new file mode 100644 index 000000000..3d969abaa --- /dev/null +++ b/lp-glsl/Cargo.toml.reference @@ -0,0 +1,93 @@ +# This file is kept for reference but workspace is now defined in root Cargo.toml +# [workspace] +# resolver = '2' +# members = [ +# "crates/lp-builtins", +# "../lp-app/crates/lp-engine", +# "crates/lp-filetests-gen", +# "crates/lp-glsl-compiler", +# "crates/lp-glsl-filetests", +# "crates/lp-jit-util", +# "crates/lp-riscv-shared", +# "crates/lp-riscv-tools", +# "apps/embive-program", +# "apps/esp32-glsl-jit", +# "../lp-app/apps/lp-cli", +# "apps/lp-builtin-gen", +# "apps/lp-builtins-app", +# "apps/lp-test", +# "apps/test-structreturn", +# ] + +# Package metadata moved to root Cargo.toml [workspace.package] +# version = "40.0.0" +# authors = ["The Wasmtime Project Developers"] +# edition = "2024" +# license = "Apache-2.0 WITH LLVM-exception" +# rust-version = "1.90.0" + +# [workspace.lints.rust] +# unused_extern_crates = 'warn' +# trivial_numeric_casts = 'warn' +# unstable_features = 'warn' +# unused_import_braces = 'warn' +# unused-lifetimes = 'warn' +# unused-macro-rules = 'warn' + +# [workspace.lints.clippy] +# all = { level = 'allow', priority = -1 } +# clone_on_copy = 'warn' +# map_clone = 'warn' +# uninlined_format_args = 'warn' +# unnecessary_to_owned = 'warn' +# manual_strip = 'warn' +# useless_conversion = 'warn' +# unnecessary_mut_passed = 'warn' +# unnecessary_fallible_conversions = 'warn' +# unnecessary_cast = 'warn' +# allow_attributes_without_reason = 'warn' +# from_over_into = 'warn' +# redundant_field_names = 'warn' +# multiple_bound_locations = 'warn' +# extra_unused_type_parameters = 'warn' + +# [workspace.dependencies] +# Cranelift crates - reference root workspace +# cranelift-codegen = { workspace = true } +# cranelift-frontend = { workspace = true } +# cranelift-module = { workspace = true } +# cranelift-jit = { workspace = true } +# cranelift-native = { workspace = true } +# cranelift-object = { workspace = true } +# cranelift-reader = { workspace = true } +# cranelift-control = { workspace = true } + +# Common dependencies - reference root workspace +# target-lexicon = { workspace = true } +# hashbrown = { workspace = true } +# anyhow = { workspace = true } +# regex = { workspace = true } +# glob = { workspace = true } +# object = { workspace = true } +# critical-section = { workspace = true } +# serde = { workspace = true } +# serde_json = { workspace = true } + +# Note: ESP32-C3 supports atomic CAS, so portable-atomic's unsafe-assume-single-core +# feature should not be enabled. If esp-hal enables it incorrectly, this may be +# a bug in esp-hal v1.0.0-rc.0 that needs to be reported upstream. + +# [profile.release] +# # Link-time optimization for all release builds - saves ~100-300 KB +# # This applies to all packages in the workspace +# lto = true + +# [profile.release.package.esp32-glsl-jit] +# # Size optimizations for embedded target (ESP32-C3) +# # Strip debuginfo but keep symbols (needed for defmt/probe-rs) - saves ~1-1.5 MB +# strip = "debuginfo" +# # Optimize for size instead of speed - saves ~200-500 KB +# opt-level = "z" +# # Single codegen unit for better dead code elimination - saves ~50-100 KB +# codegen-units = 1 + diff --git a/lp-glsl/apps/esp32-glsl-jit/Cargo.lock b/lp-glsl/apps/esp32-glsl-jit/Cargo.lock index 1394f3730..f9367c97b 100644 --- a/lp-glsl/apps/esp32-glsl-jit/Cargo.lock +++ b/lp-glsl/apps/esp32-glsl-jit/Cargo.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "allocator-api2" version = "0.3.1" @@ -61,6 +67,21 @@ version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +dependencies = [ + "allocator-api2 0.2.21", +] + +[[package]] +name = "bytecount" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" + [[package]] name = "bytemuck" version = "1.24.0" @@ -79,6 +100,118 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cranelift-bforest" +version = "0.127.0" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" +dependencies = [ + "cranelift-entity", +] + +[[package]] +name = "cranelift-bitset" +version = "0.127.0" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" + +[[package]] +name = "cranelift-codegen" +version = "0.127.0" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" +dependencies = [ + "bumpalo", + "cranelift-bforest", + "cranelift-bitset", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-control", + "cranelift-entity", + "cranelift-isle", + "hashbrown 0.15.5", + "log", + "regalloc2", + "rustc-hash", + "smallvec", + "target-lexicon", + "wasmtime-internal-math", +] + +[[package]] +name = "cranelift-codegen-meta" +version = "0.127.0" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" +dependencies = [ + "cranelift-codegen-shared", + "cranelift-srcgen", + "heck", +] + +[[package]] +name = "cranelift-codegen-shared" +version = "0.127.0" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" + +[[package]] +name = "cranelift-control" +version = "0.127.0" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" + +[[package]] +name = "cranelift-entity" +version = "0.127.0" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" +dependencies = [ + "cranelift-bitset", +] + +[[package]] +name = "cranelift-frontend" +version = "0.127.0" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" +dependencies = [ + "cranelift-codegen", + "hashbrown 0.15.5", + "log", + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cranelift-isle" +version = "0.127.0" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" + +[[package]] +name = "cranelift-jit" +version = "0.127.0" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", + "cranelift-entity", + "cranelift-module", + "hashbrown 0.15.5", + "log", + "target-lexicon", + "wasmtime-internal-jit-icache-coherence", +] + +[[package]] +name = "cranelift-module" +version = "0.127.0" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" +dependencies = [ + "anyhow", + "cranelift-codegen", + "cranelift-control", + "hashbrown 0.15.5", +] + +[[package]] +name = "cranelift-srcgen" +version = "0.127.0" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" + [[package]] name = "critical-section" version = "1.2.0" @@ -481,7 +614,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e95f1de57ce5a6600368f3d3c931b0dfe00501661e96f5ab83bc5cdee031784" dependencies = [ - "allocator-api2", + "allocator-api2 0.3.1", "cfg-if", "critical-section", "defmt 1.0.1", @@ -671,6 +804,31 @@ dependencies = [ "vcell", ] +[[package]] +name = "esp32-glsl-jit" +version = "0.1.0" +dependencies = [ + "cranelift-codegen", + "cranelift-control", + "cranelift-frontend", + "cranelift-module", + "defmt 1.0.1", + "embassy-executor", + "embassy-time", + "esp-alloc", + "esp-bootloader-esp-idf", + "esp-hal", + "esp-hal-embassy", + "glsl", + "hashbrown 0.15.5", + "log", + "lp-glsl-compiler", + "panic-rtt-target", + "portable-atomic", + "rtt-target", + "target-lexicon", +] + [[package]] name = "esp32c2" version = "0.27.0" @@ -693,24 +851,6 @@ dependencies = [ "vcell", ] -[[package]] -name = "esp32c3-jit-test" -version = "0.1.0" -dependencies = [ - "critical-section", - "defmt 1.0.1", - "embassy-executor", - "embassy-time", - "esp-alloc", - "esp-bootloader-esp-idf", - "esp-hal", - "esp-hal-embassy", - "panic-rtt-target", - "portable-atomic", - "riscv-shared", - "rtt-target", -] - [[package]] name = "esp32c6" version = "0.21.0" @@ -767,6 +907,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "fugit" version = "0.3.9" @@ -823,6 +969,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "glsl" +version = "7.0.0" +source = "git+https://github.com/Yona-Appletree/glsl-parser.git?branch=feature%2Fspans#0c5505aadd2ceee3dc62f290a03991f8d6466b63" +dependencies = [ + "nom", + "nom_locate", +] + [[package]] name = "hash32" version = "0.3.1" @@ -832,6 +987,16 @@ dependencies = [ "byteorder", ] +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", + "rustc-std-workspace-alloc", +] + [[package]] name = "hashbrown" version = "0.16.1" @@ -867,7 +1032,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.16.1", "serde", "serde_core", ] @@ -924,6 +1089,18 @@ dependencies = [ "syn", ] +[[package]] +name = "libc" +version = "0.2.180" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" + +[[package]] +name = "libm" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" + [[package]] name = "linked_list_allocator" version = "0.10.5" @@ -951,6 +1128,34 @@ version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" +[[package]] +name = "lp-builtins" +version = "40.0.0" + +[[package]] +name = "lp-glsl-compiler" +version = "40.0.0" +dependencies = [ + "cranelift-codegen", + "cranelift-control", + "cranelift-frontend", + "cranelift-jit", + "cranelift-module", + "glsl", + "hashbrown 0.15.5", + "lp-builtins", + "lp-jit-util", + "target-lexicon", +] + +[[package]] +name = "lp-jit-util" +version = "40.0.0" +dependencies = [ + "cranelift-codegen", + "target-lexicon", +] + [[package]] name = "memchr" version = "2.7.6" @@ -988,6 +1193,17 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "nom_locate" +version = "4.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e3c83c053b0713da60c5b8de47fe8e494fe3ece5267b2f23090a07a053ba8f3" +dependencies = [ + "bytecount", + "memchr", + "nom", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1106,29 +1322,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd7a31eed1591dcbc95d92ad7161908e72f4677f8fabf2a32ca49b4237cbf211" -[[package]] -name = "r5-builder" -version = "0.1.0" -dependencies = [ - "lpc-lpir", -] - -[[package]] -name = "lpc-lpir" -version = "0.1.0" -dependencies = [ - "nom", -] - -[[package]] -name = "r5-target-riscv32" -version = "0.1.0" -dependencies = [ - "lpc-lpir", - "riscv32-emulator", - "lpc-codegen", -] - [[package]] name = "rand_core" version = "0.6.4" @@ -1141,6 +1334,20 @@ version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +[[package]] +name = "regalloc2" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08effbc1fa53aaebff69521a5c05640523fab037b34a4a2c109506bc938246fa" +dependencies = [ + "allocator-api2 0.2.21", + "bumpalo", + "hashbrown 0.15.5", + "log", + "rustc-hash", + "smallvec", +] + [[package]] name = "riscv" version = "0.12.1" @@ -1182,29 +1389,6 @@ dependencies = [ "syn", ] -[[package]] -name = "riscv-shared" -version = "0.1.0" -dependencies = [ - "r5-builder", - "lpc-lpir", - "r5-target-riscv32", -] - -[[package]] -name = "riscv32-emulator" -version = "0.1.0" -dependencies = [ - "lpc-codegen", -] - -[[package]] -name = "lpc-codegen" -version = "0.1.0" -dependencies = [ - "nom", -] - [[package]] name = "rtt-target" version = "0.6.2" @@ -1217,6 +1401,18 @@ dependencies = [ "ufmt-write", ] +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustc-std-workspace-alloc" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d441c3b2ebf55cebf796bfdc265d67fa09db17b7bb6bd4be75c509e1e8fec3" + [[package]] name = "rustversion" version = "1.0.22" @@ -1272,6 +1468,12 @@ dependencies = [ "unsafe-libyaml", ] +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -1325,6 +1527,12 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "target-lexicon" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1dd07eb858a2067e2f3c7155d54e929265c264e6f37efe3ee7a8d1b5a1dd0ba" + [[package]] name = "termcolor" version = "1.4.1" @@ -1426,6 +1634,25 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" +[[package]] +name = "wasmtime-internal-jit-icache-coherence" +version = "40.0.0" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" +dependencies = [ + "anyhow", + "cfg-if", + "libc", + "windows-sys", +] + +[[package]] +name = "wasmtime-internal-math" +version = "40.0.0" +source = "git+https://github.com/Yona-Appletree/lp-cranelift.git?branch=feature%2Flp2025#bfc79b3ffb6c8f653d496f1c2974403235dde407" +dependencies = [ + "libm", +] + [[package]] name = "winapi-util" version = "0.1.11" diff --git a/lp-glsl/apps/esp32-glsl-jit/Cargo.toml b/lp-glsl/apps/esp32-glsl-jit/Cargo.toml index a50f145af..9f72ff8f9 100644 --- a/lp-glsl/apps/esp32-glsl-jit/Cargo.toml +++ b/lp-glsl/apps/esp32-glsl-jit/Cargo.toml @@ -39,6 +39,6 @@ esp-bootloader-esp-idf = { version = "0.2.0", features = ["esp32c6"] } version = "1.11" default-features = false -# Note: Release profile optimizations must be configured in lp-glsl/Cargo.toml -# (workspace root) since package-level profiles are ignored +# Note: Release profile optimizations are configured in workspace root Cargo.toml +# under [profile.release.package.esp32-glsl-jit] to work when building from workspace root diff --git a/lp-glsl/apps/esp32-glsl-jit/run.sh b/lp-glsl/apps/esp32-glsl-jit/run.sh new file mode 100755 index 000000000..2ae535db0 --- /dev/null +++ b/lp-glsl/apps/esp32-glsl-jit/run.sh @@ -0,0 +1,6 @@ +#!/bin/bash + +set -e + +cargo build --target riscv32imac-unknown-none-elf -p esp32-glsl-jit --release +probe-rs run --chip esp32c6 ../../target/riscv32imac-unknown-none-elf/release/esp32-glsl-jit \ No newline at end of file diff --git a/lp-glsl/apps/esp32-glsl-jit/src/main.rs b/lp-glsl/apps/esp32-glsl-jit/src/main.rs index 402c7d3dd..d319b23e7 100644 --- a/lp-glsl/apps/esp32-glsl-jit/src/main.rs +++ b/lp-glsl/apps/esp32-glsl-jit/src/main.rs @@ -147,45 +147,76 @@ int main(int x, int y) { } }; - // Compile GLSL to machine code + // Compile GLSL using normal JIT path info!("Step 2: Compiling GLSL to RISC-V machine code..."); let mut compiler = Compiler::new(); - let machine_code = match compiler.compile_to_code(source, &*isa) { - Ok(code) => { - info!(" ✓ Compilation successful: {} bytes", code.len()); - code + + // Create a Target from the ISA for JIT compilation + use lp_glsl_compiler::backend::target::Target; + let flags = isa.flags().clone(); + let target = Target::HostJit { + arch: None, // Auto-detect from host (ESP32 = RISC-V32) + flags, + isa: None, // Will be created from target + }; + + // Compile to JIT module + let gl_module = match compiler.compile_to_gl_module_jit(source, target) { + Ok(module) => { + defmt::info!(" ✓ GLSL compilation successful"); + module } Err(e) => { - // Log error details before panicking - // Display error code and message (defmt supports {} for strings) - info!("GLSL compilation failed!"); - info!("Error: {}", e.message.as_str()); + // Build error message string + use alloc::format; + let mut error_msg = + format!("GLSL compilation failed!\nError: {}\n", e.message.as_str()); - // Display location if available if let Some(ref loc) = e.location { - info!("At line {}, column {}", loc.line, loc.column); + error_msg.push_str(&format!("At line {}, column {}\n", loc.line, loc.column)); } - // Display span text if available (source code snippet) if let Some(ref span) = e.span_text { - info!("Source code: {}", span.as_str()); + error_msg.push_str(&format!("Source code: {}\n", span.as_str())); } - // Display notes (these often contain detailed error information) - // Break into smaller chunks to avoid defmt string length limits if !e.notes.is_empty() { - info!("Error details:"); + error_msg.push_str("Error details:\n"); for note in &e.notes { - // Split note by newlines and display each line for line in note.lines() { if !line.trim().is_empty() { - info!(" {}", line); + error_msg.push_str(&format!(" {}\n", line)); } } } } - defmt::panic!("GLSL compilation failed - see details above"); + defmt::panic!("{}", error_msg.as_str()); + } + }; + + // Build JIT executable directly to get the concrete type with function pointers + use lp_glsl_compiler::backend::codegen::jit::build_jit_executable; + let jit_module = match build_jit_executable(gl_module) { + Ok(module) => { + info!(" ✓ JIT executable built"); + module + } + Err(e) => { + info!("Failed to build executable: {}", e.message.as_str()); + defmt::panic!("JIT executable build failed"); + } + }; + + // Get the function pointer for "main" + let func_ptr = match jit_module.get_function_ptr("main") { + Ok(ptr) => { + info!(" ✓ Function pointer obtained"); + ptr + } + Err(e) => { + info!("Failed to get function pointer: {}", e.message.as_str()); + defmt::panic!("Function pointer not found"); } }; @@ -198,7 +229,7 @@ int main(int x, int y) { // Cast to function pointer - shader takes x, y coordinates and returns pixel value type ShaderFn = extern "C" fn(i32, i32) -> i32; - let shader_fn: ShaderFn = unsafe { core::mem::transmute(machine_code.as_ptr()) }; + let shader_fn: ShaderFn = unsafe { core::mem::transmute(func_ptr) }; // Image dimensions: 64x64 pixels = 4096 pixels per frame const IMAGE_WIDTH: i32 = 64; diff --git a/lp-glsl/apps/lp-builtin-gen/src/main.rs b/lp-glsl/apps/lp-builtin-gen/src/main.rs index d048cab41..8628c45b5 100644 --- a/lp-glsl/apps/lp-builtin-gen/src/main.rs +++ b/lp-glsl/apps/lp-builtin-gen/src/main.rs @@ -24,8 +24,7 @@ fn main() { generate_registry(®istry_path, &builtins); // Generate builtin_refs.rs - let builtin_refs_path = - workspace_root.join("lp-glsl/apps/lp-builtins-app/src/builtin_refs.rs"); + let builtin_refs_path = workspace_root.join("lp-glsl/apps/lp-builtins-app/src/builtin_refs.rs"); generate_builtin_refs(&builtin_refs_path, &builtins); // Generate mod.rs @@ -37,14 +36,30 @@ fn main() { .join("lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/math.rs"); generate_testcase_mapping(&math_rs_path, &builtins); + // Format generated files using cargo fmt + format_generated_files( + &workspace_root, + &[ + ®istry_path, + &builtin_refs_path, + &mod_rs_path, + &math_rs_path, + ], + ); + println!("Generated all builtin boilerplate files"); } fn find_workspace_root() -> Result> { let mut current = std::env::current_dir()?; loop { - if current.join("lp-glsl/Cargo.toml").exists() { - return Ok(current); + let cargo_toml = current.join("Cargo.toml"); + if cargo_toml.exists() { + // Check if this is the workspace root by looking for [workspace] section + let content = std::fs::read_to_string(&cargo_toml)?; + if content.contains("[workspace]") && current.join("lp-glsl").exists() { + return Ok(current); + } } if !current.pop() { return Err("Could not find workspace root".into()); @@ -77,10 +92,10 @@ fn discover_builtins(dir: &Path) -> Result, Box Option { let mut chars = s.chars(); match chars.next() { None => String::new(), - Some(c) => c.to_uppercase().collect::() + &chars.as_str(), + Some(c) => c.to_uppercase().collect::() + chars.as_str(), } }) .collect::(); @@ -159,12 +174,14 @@ fn generate_registry(path: &Path, builtins: &[BuiltinInfo]) { output .push_str("//! Provides enum-based registry for builtin functions with support for both\n"); output.push_str("//! JIT (function pointer) and emulator (ELF symbol) linking.\n"); - output.push_str("\n"); + output.push('\n'); output.push_str("use crate::error::{ErrorCode, GlslError};\n"); output.push_str("use cranelift_codegen::ir::{AbiParam, Signature, types};\n"); output.push_str("use cranelift_codegen::isa::CallConv;\n"); output.push_str("use cranelift_module::{Linkage, Module};\n\n"); + output.push_str("#[cfg(not(feature = \"std\"))]\n"); + output.push_str("use alloc::format;\n\n"); // Generate enum output.push_str("/// Enum identifying builtin functions.\n"); @@ -216,51 +233,51 @@ fn generate_registry(path: &Path, builtins: &[BuiltinInfo]) { let unary_ops: Vec<_> = builtins.iter().filter(|b| b.param_count == 1).collect(); if !ternary_ops.is_empty() { - output.push_str(" "); - for (i, builtin) in ternary_ops.iter().enumerate() { - if i > 0 { - output.push_str(" | "); + output.push_str(" "); + for (i, builtin) in ternary_ops.iter().enumerate() { + if i > 0 { + output.push_str(" | "); + } + output.push_str(&format!("BuiltinId::{}", builtin.enum_variant)); } - output.push_str(&format!("BuiltinId::{}", builtin.enum_variant)); + output.push_str(" => {\n"); + output.push_str(" // (i32, i32, i32) -> i32\n"); + output.push_str(" sig.params.push(AbiParam::new(types::I32));\n"); + output.push_str(" sig.params.push(AbiParam::new(types::I32));\n"); + output.push_str(" sig.params.push(AbiParam::new(types::I32));\n"); + output.push_str(" sig.returns.push(AbiParam::new(types::I32));\n"); + output.push_str(" }\n"); } - output.push_str(" => {\n"); - output.push_str(" // (i32, i32, i32) -> i32\n"); - output.push_str(" sig.params.push(AbiParam::new(types::I32));\n"); - output.push_str(" sig.params.push(AbiParam::new(types::I32));\n"); - output.push_str(" sig.params.push(AbiParam::new(types::I32));\n"); - output.push_str(" sig.returns.push(AbiParam::new(types::I32));\n"); - output.push_str(" }\n"); - } - if !binary_ops.is_empty() { - output.push_str(" "); - for (i, builtin) in binary_ops.iter().enumerate() { - if i > 0 { - output.push_str(" | "); + if !binary_ops.is_empty() { + output.push_str(" "); + for (i, builtin) in binary_ops.iter().enumerate() { + if i > 0 { + output.push_str(" | "); + } + output.push_str(&format!("BuiltinId::{}", builtin.enum_variant)); } - output.push_str(&format!("BuiltinId::{}", builtin.enum_variant)); + output.push_str(" => {\n"); + output.push_str(" // (i32, i32) -> i32\n"); + output.push_str(" sig.params.push(AbiParam::new(types::I32));\n"); + output.push_str(" sig.params.push(AbiParam::new(types::I32));\n"); + output.push_str(" sig.returns.push(AbiParam::new(types::I32));\n"); + output.push_str(" }\n"); } - output.push_str(" => {\n"); - output.push_str(" // (i32, i32) -> i32\n"); - output.push_str(" sig.params.push(AbiParam::new(types::I32));\n"); - output.push_str(" sig.params.push(AbiParam::new(types::I32));\n"); - output.push_str(" sig.returns.push(AbiParam::new(types::I32));\n"); - output.push_str(" }\n"); - } - if !unary_ops.is_empty() { - output.push_str(" "); - for (i, builtin) in unary_ops.iter().enumerate() { - if i > 0 { - output.push_str(" | "); + if !unary_ops.is_empty() { + output.push_str(" "); + for (i, builtin) in unary_ops.iter().enumerate() { + if i > 0 { + output.push_str(" | "); + } + output.push_str(&format!("BuiltinId::{}", builtin.enum_variant)); } - output.push_str(&format!("BuiltinId::{}", builtin.enum_variant)); - } - output.push_str(" => {\n"); - output.push_str(" // (i32) -> i32\n"); - output.push_str(" sig.params.push(AbiParam::new(types::I32));\n"); - output.push_str(" sig.returns.push(AbiParam::new(types::I32));\n"); - output.push_str(" }\n"); + output.push_str(" => {\n"); + output.push_str(" // (i32) -> i32\n"); + output.push_str(" sig.params.push(AbiParam::new(types::I32));\n"); + output.push_str(" sig.returns.push(AbiParam::new(types::I32));\n"); + output.push_str(" }\n"); } } @@ -332,9 +349,7 @@ fn generate_registry(path: &Path, builtins: &[BuiltinInfo]) { output.push_str(" .map_err(|e| {\n"); output.push_str(" GlslError::new(\n"); output.push_str(" ErrorCode::E0400,\n"); - output.push_str( - " format!(\"Failed to declare builtin '{}': {}\", name, e),\n", - ); + output.push_str(" format!(\"Failed to declare builtin '{name}': {e}\"),\n"); output.push_str(" )\n"); output.push_str(" })?;\n"); output.push_str(" }\n\n"); @@ -419,7 +434,7 @@ fn generate_builtin_refs(path: &Path, builtins: &[BuiltinInfo]) { )); } - output.push_str("\n"); + output.push('\n'); output.push_str(" // Force these to be included by using them in a way that can't be optimized away\n"); output.push_str(" // We'll use volatile reads to prevent optimization\n"); @@ -457,13 +472,13 @@ fn generate_mod_rs(path: &Path, builtins: &[BuiltinInfo]) { output.push_str("//!\n"); output.push_str("//! Functions operate on i32 values representing fixed-point numbers\n"); output.push_str("//! with 16 bits of fractional precision.\n"); - output.push_str("\n"); + output.push('\n'); // Generate mod declarations for builtin in builtins { output.push_str(&format!("mod {};\n", builtin.file_name)); } - output.push_str("\n"); + output.push('\n'); output.push_str("#[cfg(test)]\n"); output.push_str("mod test_helpers;\n\n"); @@ -523,26 +538,26 @@ fn generate_testcase_mapping(path: &Path, builtins: &[BuiltinInfo]) { // No builtins, so no mappings } else { for builtin in builtins { - let base_name = builtin.symbol_name.strip_prefix("__lp_fixed32_").unwrap(); + let base_name = builtin.symbol_name.strip_prefix("__lp_fixed32_").unwrap(); - // Generate C math function name (e.g., sinf) - let c_name = format!("{}f", base_name); + // Generate C math function name (e.g., sinf) + let c_name = format!("{}f", base_name); - // Generate intrinsic name (e.g., __lp_sin) - let intrinsic_name = format!("__lp_{}", base_name); + // Generate intrinsic name (e.g., __lp_sin) + let intrinsic_name = format!("__lp_{}", base_name); - // Special case: GLSL's mod() compiles to fmodf, not modf - let additional_names = if base_name == "mod" { - " | \"fmodf\"" - } else { - "" - }; + // Special case: GLSL's mod() compiles to fmodf, not modf + let additional_names = if base_name == "mod" { + " | \"fmodf\"" + } else { + "" + }; - new_function.push_str(&format!( - " \"{}\" | \"{}\"{additional_names} => Some((BuiltinId::{}, {})),\n", - c_name, intrinsic_name, builtin.enum_variant, builtin.param_count - )); - } + new_function.push_str(&format!( + " \"{}\" | \"{}\"{additional_names} => Some((BuiltinId::{}, {})),\n", + c_name, intrinsic_name, builtin.enum_variant, builtin.param_count + )); + } } new_function.push_str(" _ => None,\n"); @@ -552,3 +567,30 @@ fn generate_testcase_mapping(path: &Path, builtins: &[BuiltinInfo]) { let new_content = format!("{}{}{}", before, new_function, after); fs::write(path, new_content).expect("Failed to write math.rs"); } + +fn format_generated_files(workspace_root: &Path, files: &[&Path]) { + use std::process::Command; + + // Run cargo fmt on the generated files + let mut cmd = Command::new("cargo"); + cmd.arg("fmt"); + cmd.arg("--"); + + for file in files { + // Get relative path from workspace root + if let Ok(relative_path) = file.strip_prefix(workspace_root) { + cmd.arg(relative_path); + } + } + + // Run from workspace root + let output = cmd + .current_dir(workspace_root) + .output() + .expect("Failed to run cargo fmt"); + + if !output.status.success() { + eprintln!("Warning: cargo fmt failed on generated files:"); + eprintln!("{}", String::from_utf8_lossy(&output.stderr)); + } +} diff --git a/lp-glsl/apps/lp-builtins-app/src/builtin_refs.rs b/lp-glsl/apps/lp-builtins-app/src/builtin_refs.rs index d104210ef..f0704f216 100644 --- a/lp-glsl/apps/lp-builtins-app/src/builtin_refs.rs +++ b/lp-glsl/apps/lp-builtins-app/src/builtin_refs.rs @@ -7,33 +7,13 @@ //! scripts/build-builtins.sh use lp_builtins::builtins::fixed32::{ - __lp_fixed32_acos, - __lp_fixed32_acosh, - __lp_fixed32_asin, - __lp_fixed32_asinh, - __lp_fixed32_atan, - __lp_fixed32_atan2, - __lp_fixed32_atanh, - __lp_fixed32_cos, - __lp_fixed32_cosh, - __lp_fixed32_div, - __lp_fixed32_exp, - __lp_fixed32_exp2, - __lp_fixed32_fma, - __lp_fixed32_inversesqrt, - __lp_fixed32_ldexp, - __lp_fixed32_log, - __lp_fixed32_log2, - __lp_fixed32_mod, - __lp_fixed32_mul, - __lp_fixed32_pow, - __lp_fixed32_round, - __lp_fixed32_roundeven, - __lp_fixed32_sin, - __lp_fixed32_sinh, - __lp_fixed32_sqrt, - __lp_fixed32_tan, - __lp_fixed32_tanh, + __lp_fixed32_acos, __lp_fixed32_acosh, __lp_fixed32_asin, __lp_fixed32_asinh, + __lp_fixed32_atan, __lp_fixed32_atan2, __lp_fixed32_atanh, __lp_fixed32_cos, __lp_fixed32_cosh, + __lp_fixed32_div, __lp_fixed32_exp, __lp_fixed32_exp2, __lp_fixed32_fma, + __lp_fixed32_inversesqrt, __lp_fixed32_ldexp, __lp_fixed32_log, __lp_fixed32_log2, + __lp_fixed32_mod, __lp_fixed32_mul, __lp_fixed32_pow, __lp_fixed32_round, + __lp_fixed32_roundeven, __lp_fixed32_sin, __lp_fixed32_sinh, __lp_fixed32_sqrt, + __lp_fixed32_tan, __lp_fixed32_tanh, }; /// Reference all builtin functions to prevent dead code elimination. diff --git a/lp-glsl/apps/lp-builtins-app/src/main.rs b/lp-glsl/apps/lp-builtins-app/src/main.rs index dfb5b778b..87fca15ee 100644 --- a/lp-glsl/apps/lp-builtins-app/src/main.rs +++ b/lp-glsl/apps/lp-builtins-app/src/main.rs @@ -74,9 +74,7 @@ fn panic_syscall( /// Exit the interpreter #[inline(always)] fn ebreak() -> ! { - unsafe { - asm!("ebreak", options(nostack, noreturn)) - } + unsafe { asm!("ebreak", options(nostack, noreturn)) } } /// Panic handler @@ -200,11 +198,11 @@ unsafe extern "C" fn _code_entry() -> ! { // Call placeholder main function unsafe extern "C" { - fn main(); + fn _lp_main(); } unsafe { - main(); + _lp_main(); } unsafe { @@ -228,7 +226,7 @@ static mut __USER_MAIN_PTR: u32 = 0xDEADBEEF; /// 2. Reads __USER_MAIN_PTR from .data section /// 3. Jumps to user _init if set, otherwise halts gracefully #[unsafe(no_mangle)] -pub extern "C" fn main() -> () { +pub extern "C" fn _lp_main() -> () { // Reference all builtin functions to prevent dead code elimination // This is done via the generated builtin_refs module builtin_refs::ensure_builtins_referenced(); diff --git a/lp-glsl/apps/test-structreturn/Cargo.toml b/lp-glsl/apps/test-structreturn/Cargo.toml deleted file mode 100644 index 99e284f0b..000000000 --- a/lp-glsl/apps/test-structreturn/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "test-structreturn" -version = "0.1.0" -edition = "2021" - -[dependencies] -cranelift-codegen = { workspace = true, features = ["riscv32", "arm64"] } -cranelift-frontend = { workspace = true } -cranelift-module = { workspace = true } -cranelift-jit = { workspace = true } -target-lexicon = { workspace = true } -lp-jit-util = { path = "../../crates/lp-jit-util" } - -[features] -default = [] -asm = [] - diff --git a/lp-glsl/apps/test-structreturn/src/asm_test.rs b/lp-glsl/apps/test-structreturn/src/asm_test.rs deleted file mode 100644 index aa10f557f..000000000 --- a/lp-glsl/apps/test-structreturn/src/asm_test.rs +++ /dev/null @@ -1,21 +0,0 @@ -//! Separate file to test assembly generation -//! Compile with: rustc --emit asm asm_test.rs - -#[no_mangle] -pub extern "C" fn test_structreturn_apple_aarch64(buffer: *mut f32) { - unsafe { - *buffer.add(0) = 1.0; - *buffer.add(1) = 2.0; - *buffer.add(2) = 3.0; - } -} - -#[no_mangle] -pub extern "C" fn test_structreturn_systemv(buffer: *mut f32) { - unsafe { - *buffer.add(0) = 1.0; - *buffer.add(1) = 2.0; - *buffer.add(2) = 3.0; - } -} - diff --git a/lp-glsl/apps/test-structreturn/src/main.rs b/lp-glsl/apps/test-structreturn/src/main.rs deleted file mode 100644 index dc3307ef0..000000000 --- a/lp-glsl/apps/test-structreturn/src/main.rs +++ /dev/null @@ -1,218 +0,0 @@ -//! Direct CLIF test for StructReturn JIT execution -//! Compare assembly of native Rust vs JIT-compiled StructReturn functions - -use cranelift_codegen::ir::{AbiParam, ArgumentPurpose, InstBuilder, MemFlags}; -use cranelift_codegen::isa::{lookup as isa_lookup, CallConv}; -use cranelift_codegen::settings::{self, Configurable}; -use cranelift_frontend::{FunctionBuilder, FunctionBuilderContext}; -use cranelift_jit::{JITBuilder, JITModule}; -use cranelift_module::{Linkage, Module}; -use lp_jit_util::call_structreturn; -use std::fs; -use target_lexicon::Triple; - -/// Native Rust function that mimics StructReturn calling convention -/// This is what we expect the JIT function to match -#[no_mangle] -pub extern "C" fn native_structreturn_vec3(buffer: *mut f32) { - unsafe { - *buffer.add(0) = 1.0; - *buffer.add(1) = 2.0; - *buffer.add(2) = 3.0; - } -} - -/// Test StructReturn for a specific ISA by directly building CLIF -fn test_structreturn_clif( - triple: Triple, - isa_name: &str, - expected_values: &[f32], -) -> Result<(), String> { - println!("\n=== Testing StructReturn CLIF on {} ===", isa_name); - - // Create ISA - let mut flag_builder = settings::builder(); - flag_builder.set("use_colocated_libcalls", "false").unwrap(); - flag_builder.set("opt_level", "none").unwrap(); - - let isa_builder = isa_lookup(triple.clone()) - .map_err(|e| format!("Failed to lookup ISA for {}: {:?}", isa_name, e))?; - - let isa = isa_builder - .finish(settings::Flags::new(flag_builder)) - .map_err(|e| format!("Failed to create ISA for {}: {:?}", isa_name, e))?; - - let jit_builder = JITBuilder::with_isa(isa.clone(), cranelift_module::default_libcall_names()); - let mut module = JITModule::new(jit_builder); - - // Get calling convention for this triple - let call_conv = CallConv::triple_default(&triple); - let pointer_type = module.isa().pointer_type(); - - println!("Calling convention: {:?}", call_conv); - println!("Pointer type: {:?}", pointer_type); - - // Create function signature with StructReturn - let mut sig = cranelift_codegen::ir::Signature::new(call_conv); - - // Add StructReturn parameter FIRST (critical for ABI) - sig.params.push(AbiParam::special( - pointer_type, - ArgumentPurpose::StructReturn, - )); - // Returns void - sig.returns.clear(); - - println!("Signature: {:?}", sig); - - let func_id = module - .declare_function("test_vec", Linkage::Export, &sig) - .map_err(|e| format!("Failed to declare function: {:?}", e))?; - - // Build the function - let mut ctx = module.make_context(); - ctx.func.signature = sig; - - // Get StructReturn parameter index - let struct_ret_index = ctx - .func - .signature - .special_param_index(ArgumentPurpose::StructReturn) - .ok_or_else(|| "StructReturn parameter not found".to_string())?; - - println!("StructReturn parameter index: {}", struct_ret_index); - - let mut builder_ctx = FunctionBuilderContext::new(); - let mut builder = FunctionBuilder::new(&mut ctx.func, &mut builder_ctx); - - let entry = builder.create_block(); - builder.append_block_params_for_function_params(entry); - builder.switch_to_block(entry); - builder.seal_block(entry); - - // Get StructReturn pointer from block params - let ret_ptr = builder.block_params(entry)[struct_ret_index]; - - // Write values to the return buffer - for (i, &val) in expected_values.iter().enumerate() { - let const_val = builder.ins().f32const(val); - let offset = (i * 4) as i32; // 4 bytes per f32 - builder - .ins() - .store(MemFlags::trusted(), const_val, ret_ptr, offset); - } - - builder.ins().return_(&[]); - builder.finalize(); - - println!("Function CLIF:\n{}", &ctx.func); - - // Compile and save the code buffer before finalizing - println!("Compiling function..."); - module - .define_function(func_id, &mut ctx) - .map_err(|e| format!("Failed to define function: {:?}", e))?; - - // Get the compiled code buffer for disassembly - let mut ctx2 = module.make_context(); - ctx2.func = ctx.func.clone(); - let compiled = ctx2 - .compile(module.isa(), &mut Default::default()) - .map_err(|e| format!("Failed to compile for disassembly: {:?}", e))?; - let code_buffer = compiled.code_buffer(); - - // Save JIT code to file for disassembly - fs::write("/tmp/jit_code.bin", code_buffer) - .map_err(|e| format!("Failed to write JIT code: {}", e))?; - println!( - "Saved JIT code ({} bytes) to /tmp/jit_code.bin", - code_buffer.len() - ); - - println!("Finalizing definitions..."); - module - .finalize_definitions() - .map_err(|e| format!("Failed to finalize definitions: {:?}", e))?; - - // Get function pointer - println!("Getting function pointer..."); - let code_ptr = module.get_finalized_function(func_id); - println!("Function pointer: {:p}", code_ptr); - - // Test native function first - println!("\n--- Testing native Rust function ---"); - let mut buffer_native = vec![0.0f32; expected_values.len()]; - native_structreturn_vec3(buffer_native.as_mut_ptr()); - println!("Native result: {:?}", buffer_native); - if buffer_native == expected_values { - println!("✅ Native function works!"); - } else { - return Err(format!( - "Native function failed: expected {:?}, got {:?}", - expected_values, buffer_native - )); - } - - // Call the JIT function using the correct calling convention - println!("\n--- Testing JIT function ---"); - let buffer_size = expected_values.len(); - let mut buffer = vec![0.0f32; buffer_size]; - let buffer_ptr = buffer.as_mut_ptr(); - - println!("Buffer ptr: {:p}, size: {}", buffer_ptr, buffer_size); - println!("Buffer alignment: {}", std::mem::align_of::()); - - // Use the utility function to handle platform-specific calling conventions - println!("Using calling convention: {:?}", call_conv); - unsafe { - call_structreturn(code_ptr, buffer_ptr, buffer_size, call_conv, pointer_type) - .map_err(|e| format!("StructReturn call failed: {}", e))?; - } - - println!("Function returned"); - println!("Result: {:?}", buffer); - println!("Expected: {:?}", expected_values); - - // Verify - if buffer == expected_values { - println!("✅ SUCCESS: StructReturn works correctly on {}!", isa_name); - Ok(()) - } else { - Err(format!( - "❌ FAILED on {}: Expected {:?}, got {:?}", - isa_name, expected_values, buffer - )) - } -} - -/// Test on native ISA -fn test_native() -> Result<(), String> { - let triple = Triple::host(); - let isa_name = format!("native ({})", triple); - test_structreturn_clif(triple, &isa_name, &[1.0, 2.0, 3.0]) -} - -fn main() { - println!("Testing StructReturn with direct CLIF JIT execution..."); - println!("Comparing native Rust vs JIT-compiled assembly\n"); - - match test_native() { - Ok(()) => { - println!("\n🎉 StructReturn JIT test passed!"); - println!("\nTo examine assembly:"); - println!( - " otool -tv target/release/test-structreturn | grep -A 20 native_structreturn" - ); - println!(" otool -tv /tmp/jit_code.bin"); - } - Err(e) => { - println!("\n❌ StructReturn JIT test failed: {}", e); - println!("\nTo examine assembly:"); - println!( - " otool -tv target/release/test-structreturn | grep -A 20 native_structreturn" - ); - println!(" otool -tv /tmp/jit_code.bin"); - std::process::exit(1); - } - } -} diff --git a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/asin.rs b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/asin.rs index 5dc809f85..e2d5cef3f 100644 --- a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/asin.rs +++ b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/asin.rs @@ -16,7 +16,7 @@ const FIX16_ONE: i32 = 0x00010000; // 65536 #[unsafe(no_mangle)] pub extern "C" fn __lp_fixed32_asin(x: i32) -> i32 { // Domain check: |x| > 1 returns 0 (libfixmath behavior) - if x > FIX16_ONE || x < -FIX16_ONE { + if !(-FIX16_ONE..=FIX16_ONE).contains(&x) { return 0; } diff --git a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/exp.rs b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/exp.rs index e36e6b57f..abf6a10d0 100644 --- a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/exp.rs +++ b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/exp.rs @@ -43,7 +43,7 @@ pub extern "C" fn __lp_fixed32_exp(x: i32) -> i32 { // Compute power series: term_n+1 = term_n * x / n for i in 2..30 { // Convert i to fixed point for division - let i_fixed = (i as i32) << 16; + let i_fixed = i << 16; term = __lp_fixed32_mul(term, __lp_fixed32_div(in_value, i_fixed)); result += term; diff --git a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/log.rs b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/log.rs index d6b451207..7f3967b74 100644 --- a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/log.rs +++ b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/log.rs @@ -81,7 +81,7 @@ pub extern "C" fn __lp_fixed32_log(x: i32) -> i32 { count += 1; // Stop if delta is small enough (within 1) or we've done enough iterations // libfixmath checks: (delta > 1) || (delta < -1) - if count >= 10 || (delta_clamped <= 1 && delta_clamped >= -1) { + if count >= 10 || (-1..=1).contains(&delta_clamped) { break; } } diff --git a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/mod_builtin.rs b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/mod_builtin.rs index 7f947b061..031f7a392 100644 --- a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/mod_builtin.rs +++ b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/mod_builtin.rs @@ -16,7 +16,7 @@ pub extern "C" fn __lp_fixed32_mod(x: i32, y: i32) -> i32 { // floor(x / y): In fixed-point Q16.16, floor is just shifting right by 16 then left by 16 // This truncates the fractional part (rounds toward negative infinity for negative numbers) // Use arithmetic shift to preserve sign - let floored = (div_result as i32 >> 16) << 16; + let floored = (div_result >> 16) << 16; // y * floor(x / y) using mul builtin let y_times_floor = __lp_fixed32_mul(y, floored); diff --git a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/mul.rs b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/mul.rs index 72fe8b56f..b7c1f4ebd 100644 --- a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/mul.rs +++ b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/mul.rs @@ -26,15 +26,14 @@ pub extern "C" fn __lp_fixed32_mul(a: i32, b: i32) -> i32 { // Saturate to fixed-point range BEFORE truncation // Clamp to [MIN_FIXED, MAX_FIXED] - let clamped = if shifted_wide > MAX_FIXED as i64 { + + if shifted_wide > MAX_FIXED as i64 { MAX_FIXED } else if shifted_wide < MIN_FIXED as i64 { MIN_FIXED } else { shifted_wide as i32 - }; - - clamped + } } #[cfg(test)] diff --git a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/pow.rs b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/pow.rs index 2755d4077..653135c7c 100644 --- a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/pow.rs +++ b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/pow.rs @@ -84,7 +84,6 @@ mod tests { #[cfg(test)] extern crate std; use super::*; - use crate::builtins::fixed32::test_helpers::test_fixed32_function_relative; /// Test pow with 2-arg function fn test_pow_helper(inputs: &[(f32, f32, f32)], tolerance: f32, min_tolerance: f32) { diff --git a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/round.rs b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/round.rs index 27a9ca13d..2c2e08369 100644 --- a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/round.rs +++ b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/round.rs @@ -5,8 +5,9 @@ /// Compute round(x) - round to nearest integer, halfway cases away from zero /// /// In fixed-point Q16.16: -/// - Add 0.5 (32768) for positive numbers, subtract 0.5 for negative +/// - Add 0.5 (32768) for all numbers /// - Then truncate by shifting right 16 bits and left 16 bits +/// - For negative numbers, adding 0.5 then truncating rounds away from zero correctly #[unsafe(no_mangle)] pub extern "C" fn __lp_fixed32_round(x: i32) -> i32 { if x == 0 { @@ -21,9 +22,20 @@ pub extern "C" fn __lp_fixed32_round(x: i32) -> i32 { let added = x.wrapping_add(half); (added >> 16) << 16 } else { - // For negative: subtract 0.5, then truncate - let subtracted = x.wrapping_sub(half); - (subtracted >> 16) << 16 + // For negative: add 0.5, then truncate + let added = x.wrapping_add(half); + let result = (added >> 16) << 16; + + // Special case: if we're at exactly halfway (-1.5, -2.5, etc.), + // adding 0.5 gives us exactly an integer, but we need to round away from zero + // (more negative). Check if original was at halfway point. + let fractional_part = x & 0xFFFF; + if fractional_part == half { + // At halfway: round away from zero (subtract 1 more) + result.wrapping_sub(0x10000) // Subtract 1.0 in fixed point + } else { + result + } } } diff --git a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/roundeven.rs b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/roundeven.rs index 19b2bebcf..d5e09bb4f 100644 --- a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/roundeven.rs +++ b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/roundeven.rs @@ -16,7 +16,9 @@ pub extern "C" fn __lp_fixed32_roundeven(x: i32) -> i32 { } // Extract integer and fractional parts - let integer_part = x >> 16; + // For negative numbers, right shift rounds toward negative infinity, not toward zero + // So we need to adjust: add 0xFFFF before shifting to truncate toward zero + let integer_part = if x >= 0 { x >> 16 } else { (x + 0xFFFF) >> 16 }; let fractional_part = x & 0xFFFF; // Check if we're at halfway point (fractional part == 0x8000 = 0.5) @@ -38,15 +40,10 @@ pub extern "C" fn __lp_fixed32_roundeven(x: i32) -> i32 { } } } else { - // Normal rounding: add/subtract 0.5 and truncate + // Normal rounding: add 0.5 and truncate (works for both positive and negative) let half = 0x8000i32; - if x > 0 { - let added = x.wrapping_add(half); - (added >> 16) << 16 - } else { - let subtracted = x.wrapping_sub(half); - (subtracted >> 16) << 16 - } + let added = x.wrapping_add(half); + (added >> 16) << 16 } } diff --git a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/sqrt.rs b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/sqrt.rs index c8bd5f6bf..db4378e96 100644 --- a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/sqrt.rs +++ b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/sqrt.rs @@ -16,9 +16,8 @@ pub extern "C" fn __lp_fixed32_sqrt(x: i32) -> i32 { let x_scaled = (x as u64) << 16; let sqrt_scaled = x_scaled.isqrt(); - let result = sqrt_scaled as i32; - result + sqrt_scaled as i32 } #[cfg(test)] diff --git a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/test_helpers.rs b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/test_helpers.rs index 74b3e46c2..6bb7db176 100644 --- a/lp-glsl/crates/lp-builtins/src/builtins/fixed32/test_helpers.rs +++ b/lp-glsl/crates/lp-builtins/src/builtins/fixed32/test_helpers.rs @@ -29,6 +29,7 @@ pub fn fixed_to_float(fixed: i32) -> f32 { /// * `func` - The function to test (takes i32, returns i32) /// * `test_cases` - Array of (input_float, expected_output_float) pairs /// * `tolerance` - Maximum allowed absolute error between expected and actual +#[allow(dead_code, reason = "Test helper function")] pub fn test_fixed32_function(func: F, test_cases: &[(f32, f32)], tolerance: f32) where F: Fn(i32) -> i32 + Copy, diff --git a/lp-glsl/crates/lp-builtins/src/builtins/mod.rs b/lp-glsl/crates/lp-builtins/src/builtins/mod.rs index faeae5ed0..f1d227752 100644 --- a/lp-glsl/crates/lp-builtins/src/builtins/mod.rs +++ b/lp-glsl/crates/lp-builtins/src/builtins/mod.rs @@ -1 +1 @@ -pub mod fixed32; \ No newline at end of file +pub mod fixed32; diff --git a/lp-glsl/crates/lp-builtins/src/fixed32/mod.rs b/lp-glsl/crates/lp-builtins/src/fixed32/mod.rs index 5f44b1d6e..263ab8807 100644 --- a/lp-glsl/crates/lp-builtins/src/fixed32/mod.rs +++ b/lp-glsl/crates/lp-builtins/src/fixed32/mod.rs @@ -11,4 +11,4 @@ //! Functions operate on i32 values representing fixed-point numbers //! with 16 bits of fractional precision. -pub mod q32; \ No newline at end of file +pub mod q32; diff --git a/lp-glsl/crates/lp-builtins/src/lib.rs b/lp-glsl/crates/lp-builtins/src/lib.rs index f7185219e..7f38c3f04 100644 --- a/lp-glsl/crates/lp-builtins/src/lib.rs +++ b/lp-glsl/crates/lp-builtins/src/lib.rs @@ -6,9 +6,9 @@ //! Functions are exported with `#[no_mangle] pub extern "C"` for linking. // mem module provides memcpy/memset/memcmp for no_std environments -pub mod host; -pub mod mem; pub mod builtins; pub mod fixed32; +pub mod host; +pub mod mem; // Panic handler must be provided by the executable that uses this library // This crate is only used as a dependency, never built standalone diff --git a/lp-glsl/crates/lp-filetests-gen/src/expand.rs b/lp-glsl/crates/lp-filetests-gen/src/expand.rs index 6a331f3af..89639c463 100644 --- a/lp-glsl/crates/lp-filetests-gen/src/expand.rs +++ b/lp-glsl/crates/lp-filetests-gen/src/expand.rs @@ -23,7 +23,7 @@ pub fn expand_specifiers(specifiers: &[String]) -> Result> { } // Deduplicate specs - all_specs.sort_by(|a, b| format!("{:?}", a).cmp(&format!("{:?}", b))); + all_specs.sort_by(|a, b| format!("{a:?}").cmp(&format!("{b:?}"))); all_specs.dedup_by(|a, b| { a.category == b.category && a.vec_type == b.vec_type && a.dimension == b.dimension }); @@ -72,8 +72,7 @@ fn expand_single_specifier(specifier: &str, filetests_dir: &Path) -> Result Result "vec/vec4/fn-equal" @@ -170,8 +169,7 @@ fn parse_specifier(specifier: &str) -> Result> { generate_all_vec_specs() } else { bail!( - "Invalid specifier: {}. Expected format: vec/vec4/fn-equal or vec/vec3", - specifier + "Invalid specifier: {specifier}. Expected format: vec/vec4/fn-equal or vec/vec3" ); } } @@ -188,15 +186,11 @@ fn parse_specifier(specifier: &str) -> Result> { generate_all_specs_for_type_and_dimension(vec_type, dimension) } else { bail!( - "Invalid dimension or type: {}. Expected vec2, vec3, vec4, ivec2, etc.", - second + "Invalid dimension or type: {second}. Expected vec2, vec3, vec4, ivec2, etc." ); } } else { - bail!( - "Invalid specifier: {}. Expected format: vec/vec4/fn-equal", - specifier - ); + bail!("Invalid specifier: {specifier}. Expected format: vec/vec4/fn-equal"); } } 3 => { @@ -212,7 +206,7 @@ fn parse_specifier(specifier: &str) -> Result> { }]) } _ => { - bail!("Invalid specifier: {}. Too many path components", specifier); + bail!("Invalid specifier: {specifier}. Too many path components"); } } } @@ -241,11 +235,11 @@ fn parse_type_and_dimension(s: &str) -> Result<(VecType, Dimension)> { } else if s.starts_with("vec") { VecType::Vec } else { - bail!("Invalid vector type prefix in: {}", s); + bail!("Invalid vector type prefix in: {s}"); }; - let dimension = parse_dimension(s) - .ok_or_else(|| anyhow::anyhow!("Could not parse dimension from: {}", s))?; + let dimension = + parse_dimension(s).ok_or_else(|| anyhow::anyhow!("Could not parse dimension from: {s}"))?; Ok((vec_type, dimension)) } @@ -404,8 +398,8 @@ mod tests { fn test_expand_specifier_vec4() { // Test expanding "vec/vec4" - should generate all categories for vec4 only let specs = parse_specifier("vec/vec4").unwrap(); - // Should have 5 categories × 1 vector type (vec4) = 5 specs - assert_eq!(specs.len(), 5); + // Should have 10 categories × 1 vector type (vec4) = 10 specs + assert_eq!(specs.len(), 10); assert!(specs.iter().all(|s| s.dimension == Dimension::D4)); assert!(specs.iter().all(|s| s.vec_type == VecType::Vec)); } @@ -424,15 +418,15 @@ mod tests { fn test_expand_specifier_all_vec() { // Test expanding "vec" - should generate all tests let specs = parse_specifier("vec").unwrap(); - // Should have 5 categories × 3 vector types × 3 dimensions = 45 specs - assert_eq!(specs.len(), 45); + // Should have 10 categories × 3 vector types × 3 dimensions = 90 specs + assert_eq!(specs.len(), 90); } #[test] fn test_expand_specifier_ivec3() { // Test expanding "vec/ivec3" - should generate all categories for ivec3 let specs = parse_specifier("vec/ivec3").unwrap(); - assert_eq!(specs.len(), 5); // 5 categories + assert_eq!(specs.len(), 10); // 10 categories assert!(specs.iter().all(|s| s.vec_type == VecType::IVec)); assert!(specs.iter().all(|s| s.dimension == Dimension::D3)); } diff --git a/lp-glsl/crates/lp-filetests-gen/src/generator.rs b/lp-glsl/crates/lp-filetests-gen/src/generator.rs index f1b566fff..8050a96f1 100644 --- a/lp-glsl/crates/lp-filetests-gen/src/generator.rs +++ b/lp-glsl/crates/lp-filetests-gen/src/generator.rs @@ -39,7 +39,7 @@ pub fn generate(args: &Args) -> Result<()> { if !args.write { println!("\nTo write these files, run:"); let specifiers_str = args.specifiers.join(" "); - println!(" lp-filetests-gen {} --write", specifiers_str); + println!(" lp-filetests-gen {specifiers_str} --write"); } Ok(()) @@ -85,7 +85,7 @@ fn generate_test_file(spec: &TestSpec, write: bool) -> Result<()> { } else { // Dry-run: print content println!("=== {} ===", output_path.display()); - print!("{}", content); + print!("{content}"); println!(); } diff --git a/lp-glsl/crates/lp-filetests-gen/src/util.rs b/lp-glsl/crates/lp-filetests-gen/src/util.rs index 65d9d3d4f..a5a954ad5 100644 --- a/lp-glsl/crates/lp-filetests-gen/src/util.rs +++ b/lp-glsl/crates/lp-filetests-gen/src/util.rs @@ -5,8 +5,7 @@ pub fn generate_header(specifier: &str) -> String { format!( "// This file is GENERATED. Do not edit manually.\n\ // To regenerate, run:\n\ - // lp-filetests-gen {} --write\n\ - //\n", - specifier + // lp-filetests-gen {specifier} --write\n\ + //\n" ) } diff --git a/lp-glsl/crates/lp-filetests-gen/src/vec/fn_equal.rs b/lp-glsl/crates/lp-filetests-gen/src/vec/fn_equal.rs index 5a9043fc3..cc425c566 100644 --- a/lp-glsl/crates/lp-filetests-gen/src/vec/fn_equal.rs +++ b/lp-glsl/crates/lp-filetests-gen/src/vec/fn_equal.rs @@ -13,7 +13,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { let bvec_type_name = format_bvec_type_name(dimension); // Generate header with regeneration command - let specifier = format!("vec/{}/fn-equal", type_name); + let specifier = format!("vec/{type_name}/fn-equal"); let mut content = generate_header(&specifier); // Add test run and target directives @@ -26,8 +26,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { "// ============================================================================\n" )); content.push_str(&format!( - "// Equal: equal({}, {}) -> {} (component-wise)\n", - type_name, type_name, bvec_type_name + "// Equal: equal({type_name}, {type_name}) -> {bvec_type_name} (component-wise)\n" )); content.push_str(&format!( "// ============================================================================\n" @@ -395,13 +394,15 @@ fn generate_test_max_values(vec_type: VecType, dimension: Dimension) -> String { match dimension { Dimension::D2 => "uvec2(4294967295u, 4294967294u)".to_string(), Dimension::D3 => "uvec3(4294967295u, 4294967294u, 4294967293u)".to_string(), - Dimension::D4 => "uvec4(4294967295u, 4294967294u, 4294967293u, 4294967292u)".to_string(), + Dimension::D4 => + "uvec4(4294967295u, 4294967294u, 4294967293u, 4294967292u)".to_string(), }, type_name, match dimension { Dimension::D2 => "uvec2(4294967295u, 4294967294u)".to_string(), Dimension::D3 => "uvec3(4294967295u, 4294967294u, 4294967293u)".to_string(), - Dimension::D4 => "uvec4(4294967295u, 4294967294u, 4294967293u, 4294967292u)".to_string(), + Dimension::D4 => + "uvec4(4294967295u, 4294967294u, 4294967293u, 4294967292u)".to_string(), }, type_name, match dimension { @@ -418,20 +419,15 @@ fn generate_test_in_expression(vec_type: VecType, dimension: Dimension) -> Strin // Special case for D2 uvec: return bool instead of bvec2 (following manual file) if matches!(vec_type, VecType::UVec) && matches!(dimension, Dimension::D2) { return format!( - "bool test_{}_equal_function_in_expression() {{\n\ - {} a = uvec2(1u, 3u);\n\ - {} b = uvec2(1u, 4u);\n\ - {} c = uvec2(2u, 3u);\n\ + "bool test_{type_name}_equal_function_in_expression() {{\n\ + {type_name} a = uvec2(1u, 3u);\n\ + {type_name} b = uvec2(1u, 4u);\n\ + {type_name} c = uvec2(2u, 3u);\n\ return equal(a, b) == equal(b, c);\n\ // (true,false) == (false,false) = false\n\ }}\n\ \n\ -// run: test_{}_equal_function_in_expression() == false\n", - type_name, - type_name, - type_name, - type_name, - type_name +// run: test_{type_name}_equal_function_in_expression() == false\n" ); } diff --git a/lp-glsl/crates/lp-filetests-gen/src/vec/fn_greater_equal.rs b/lp-glsl/crates/lp-filetests-gen/src/vec/fn_greater_equal.rs index 9d7b3a7f8..300fdc802 100644 --- a/lp-glsl/crates/lp-filetests-gen/src/vec/fn_greater_equal.rs +++ b/lp-glsl/crates/lp-filetests-gen/src/vec/fn_greater_equal.rs @@ -13,7 +13,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { let bvec_type_name = format_bvec_type_name(dimension); // Generate header with regeneration command - let specifier = format!("vec/{}/fn-greater-equal", type_name); + let specifier = format!("vec/{type_name}/fn-greater-equal"); let mut content = generate_header(&specifier); // Add test run and target directives @@ -26,8 +26,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { "// ============================================================================\n" )); content.push_str(&format!( - "// Greater Than Equal: greaterThanEqual({}, {}) -> {} (component-wise)\n", - type_name, type_name, bvec_type_name + "// Greater Than Equal: greaterThanEqual({type_name}, {type_name}) -> {bvec_type_name} (component-wise)\n" )); content.push_str(&format!( "// ============================================================================\n" @@ -340,7 +339,11 @@ fn generate_test_zero(vec_type: VecType, dimension: Dimension) -> String { match dimension { Dimension::D2 => (vec![1, 0], vec![0, 1], vec![true, false]), Dimension::D3 => (vec![1, 0, 3], vec![0, 1, 2], vec![true, false, true]), - Dimension::D4 => (vec![1, 0, 3, 2], vec![0, 1, 2, 4], vec![true, false, true, false]), + Dimension::D4 => ( + vec![1, 0, 3, 2], + vec![0, 1, 2, 4], + vec![true, false, true, false], + ), } } else { // Values: a = [1, 0, 3, -1...], b = [0, 1, 2, 0...] @@ -348,7 +351,11 @@ fn generate_test_zero(vec_type: VecType, dimension: Dimension) -> String { match dimension { Dimension::D2 => (vec![1, 0], vec![0, 1], vec![true, false]), Dimension::D3 => (vec![1, 0, 3], vec![0, 1, 2], vec![true, false, true]), - Dimension::D4 => (vec![1, 0, 3, -1], vec![0, 1, 2, 0], vec![true, false, true, false]), + Dimension::D4 => ( + vec![1, 0, 3, -1], + vec![0, 1, 2, 0], + vec![true, false, true, false], + ), } }; diff --git a/lp-glsl/crates/lp-filetests-gen/src/vec/fn_greater_than.rs b/lp-glsl/crates/lp-filetests-gen/src/vec/fn_greater_than.rs index 5d8a7aa81..ac565b0a0 100644 --- a/lp-glsl/crates/lp-filetests-gen/src/vec/fn_greater_than.rs +++ b/lp-glsl/crates/lp-filetests-gen/src/vec/fn_greater_than.rs @@ -13,7 +13,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { let bvec_type_name = format_bvec_type_name(dimension); // Generate header with regeneration command - let specifier = format!("vec/{}/fn-greater-than", type_name); + let specifier = format!("vec/{type_name}/fn-greater-than"); let mut content = generate_header(&specifier); // Add test run and target directives @@ -26,8 +26,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { "// ============================================================================\n" )); content.push_str(&format!( - "// Greater Than: greaterThan({}, {}) -> {} (component-wise)\n", - type_name, type_name, bvec_type_name + "// Greater Than: greaterThan({type_name}, {type_name}) -> {bvec_type_name} (component-wise)\n" )); content.push_str(&format!( "// ============================================================================\n" @@ -300,7 +299,11 @@ fn generate_test_zero(vec_type: VecType, dimension: Dimension) -> String { match dimension { Dimension::D2 => (vec![1, 0], vec![0, 1], vec![true, false]), Dimension::D3 => (vec![1, 0, 3], vec![0, 1, 2], vec![true, false, true]), - Dimension::D4 => (vec![1, 0, 3, 2], vec![0, 1, 2, 4], vec![true, false, true, false]), + Dimension::D4 => ( + vec![1, 0, 3, 2], + vec![0, 1, 2, 4], + vec![true, false, true, false], + ), } } else { // a = [1, 0, 3, -1...], b = [0, 1, 2, 0...] @@ -308,7 +311,11 @@ fn generate_test_zero(vec_type: VecType, dimension: Dimension) -> String { match dimension { Dimension::D2 => (vec![1, 0], vec![0, 1], vec![true, false]), Dimension::D3 => (vec![1, 0, 3], vec![0, 1, 2], vec![true, false, true]), - Dimension::D4 => (vec![1, 0, 3, -1], vec![0, 1, 2, 0], vec![true, false, true, false]), + Dimension::D4 => ( + vec![1, 0, 3, -1], + vec![0, 1, 2, 0], + vec![true, false, true, false], + ), } }; diff --git a/lp-glsl/crates/lp-filetests-gen/src/vec/fn_less_equal.rs b/lp-glsl/crates/lp-filetests-gen/src/vec/fn_less_equal.rs index b1bd50a67..4e93fbfc8 100644 --- a/lp-glsl/crates/lp-filetests-gen/src/vec/fn_less_equal.rs +++ b/lp-glsl/crates/lp-filetests-gen/src/vec/fn_less_equal.rs @@ -13,7 +13,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { let bvec_type_name = format_bvec_type_name(dimension); // Generate header with regeneration command - let specifier = format!("vec/{}/fn-greater-equal", type_name); + let specifier = format!("vec/{type_name}/fn-greater-equal"); let mut content = generate_header(&specifier); // Add test run and target directives @@ -26,8 +26,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { "// ============================================================================\n" )); content.push_str(&format!( - "// Less Than Equal: lessThanEqual({}, {}) -> {} (component-wise)\n", - type_name, type_name, bvec_type_name + "// Less Than Equal: lessThanEqual({type_name}, {type_name}) -> {bvec_type_name} (component-wise)\n" )); content.push_str(&format!( "// ============================================================================\n" @@ -340,7 +339,11 @@ fn generate_test_zero(vec_type: VecType, dimension: Dimension) -> String { match dimension { Dimension::D2 => (vec![0, 1], vec![1, 0], vec![true, false]), Dimension::D3 => (vec![0, 1, 2], vec![1, 0, 3], vec![true, false, true]), - Dimension::D4 => (vec![0, 1, 2, 0], vec![1, 0, 3, 2], vec![true, false, true, true]), + Dimension::D4 => ( + vec![0, 1, 2, 0], + vec![1, 0, 3, 2], + vec![true, false, true, true], + ), } } else { // a = [0, 1, 2, 0...], b = [1, 0, 3, -1...] (swapped from greaterThanEqual) @@ -348,7 +351,11 @@ fn generate_test_zero(vec_type: VecType, dimension: Dimension) -> String { match dimension { Dimension::D2 => (vec![0, 1], vec![1, 0], vec![true, false]), Dimension::D3 => (vec![0, 1, 2], vec![1, 0, 3], vec![true, false, true]), - Dimension::D4 => (vec![0, 1, 2, 0], vec![1, 0, 3, -1], vec![true, false, true, false]), + Dimension::D4 => ( + vec![0, 1, 2, 0], + vec![1, 0, 3, -1], + vec![true, false, true, false], + ), } }; @@ -382,7 +389,11 @@ fn generate_test_variables(vec_type: VecType, dimension: Dimension) -> String { let (a_values, b_values, expected) = match dimension { Dimension::D2 => (vec![10, 15], vec![12, 10], vec![true, false]), Dimension::D3 => (vec![10, 15, 8], vec![12, 10, 12], vec![true, false, true]), - Dimension::D4 => (vec![10, 15, 8, 12], vec![12, 10, 12, 8], vec![true, false, true, false]), + Dimension::D4 => ( + vec![10, 15, 8, 12], + vec![12, 10, 12, 8], + vec![true, false, true, false], + ), }; let a_constructor = format_vector_constructor(vec_type, dimension, &a_values); @@ -415,7 +426,11 @@ fn generate_test_expressions(vec_type: VecType, dimension: Dimension) -> String let (a_values, b_values, expected) = match dimension { Dimension::D2 => (vec![3, 7], vec![5, 5], vec![true, false]), Dimension::D3 => (vec![3, 7, 2], vec![5, 5, 4], vec![true, false, true]), - Dimension::D4 => (vec![3, 7, 2, 9], vec![5, 5, 4, 8], vec![true, false, true, false]), + Dimension::D4 => ( + vec![3, 7, 2, 9], + vec![5, 5, 4, 8], + vec![true, false, true, false], + ), }; let a_constructor = format_vector_constructor(vec_type, dimension, &a_values); @@ -442,20 +457,15 @@ fn generate_test_in_expression(vec_type: VecType, dimension: Dimension) -> Strin // Special case for D4 uvec: return bool instead of bvec4 (following manual file) if matches!(vec_type, VecType::UVec) && matches!(dimension, Dimension::D4) { return format!( - "bool test_{}_less_equal_in_expression() {{\n\ - {} a = uvec4(1u, 5u, 3u, 7u);\n\ - {} b = uvec4(2u, 3u, 4u, 5u);\n\ - {} c = uvec4(3u, 7u, 1u, 9u);\n\ + "bool test_{type_name}_less_equal_in_expression() {{\n\ + {type_name} a = uvec4(1u, 5u, 3u, 7u);\n\ + {type_name} b = uvec4(2u, 3u, 4u, 5u);\n\ + {type_name} c = uvec4(3u, 7u, 1u, 9u);\n\ return lessThanEqual(a, b) == lessThanEqual(b, c);\n\ // (true,false,true,false) == (true,true,false,true) = false\n\ }}\n\ \n\ -// run: test_{}_less_equal_in_expression() == false\n", - type_name, - type_name, - type_name, - type_name, - type_name +// run: test_{type_name}_less_equal_in_expression() == false\n" ); } diff --git a/lp-glsl/crates/lp-filetests-gen/src/vec/fn_less_than.rs b/lp-glsl/crates/lp-filetests-gen/src/vec/fn_less_than.rs index b8b6c7f4f..e5fc2ac37 100644 --- a/lp-glsl/crates/lp-filetests-gen/src/vec/fn_less_than.rs +++ b/lp-glsl/crates/lp-filetests-gen/src/vec/fn_less_than.rs @@ -13,7 +13,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { let bvec_type_name = format_bvec_type_name(dimension); // Generate header with regeneration command - let specifier = format!("vec/{}/fn-less-than", type_name); + let specifier = format!("vec/{type_name}/fn-less-than"); let mut content = generate_header(&specifier); // Add test run and target directives @@ -26,8 +26,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { "// ============================================================================\n" )); content.push_str(&format!( - "// Less Than: lessThan({}, {}) -> {} (component-wise)\n", - type_name, type_name, bvec_type_name + "// Less Than: lessThan({type_name}, {type_name}) -> {bvec_type_name} (component-wise)\n" )); content.push_str(&format!( "// ============================================================================\n" @@ -300,7 +299,11 @@ fn generate_test_zero(vec_type: VecType, dimension: Dimension) -> String { match dimension { Dimension::D2 => (vec![0, 1], vec![1, 0], vec![true, false]), Dimension::D3 => (vec![0, 1, 2], vec![1, 0, 3], vec![true, false, true]), - Dimension::D4 => (vec![0, 1, 2, 0], vec![1, 0, 3, 2], vec![true, false, true, true]), + Dimension::D4 => ( + vec![0, 1, 2, 0], + vec![1, 0, 3, 2], + vec![true, false, true, true], + ), } } else { // a = [0, 1, 2, 0...], b = [1, 0, 3, -1...] @@ -308,7 +311,11 @@ fn generate_test_zero(vec_type: VecType, dimension: Dimension) -> String { match dimension { Dimension::D2 => (vec![0, 1], vec![1, 0], vec![true, false]), Dimension::D3 => (vec![0, 1, 2], vec![1, 0, 3], vec![true, false, true]), - Dimension::D4 => (vec![0, 1, 2, 0], vec![1, 0, 3, -1], vec![true, false, true, false]), + Dimension::D4 => ( + vec![0, 1, 2, 0], + vec![1, 0, 3, -1], + vec![true, false, true, false], + ), } }; @@ -342,7 +349,11 @@ fn generate_test_variables(vec_type: VecType, dimension: Dimension) -> String { let (a_values, b_values, expected) = match dimension { Dimension::D2 => (vec![10, 15], vec![12, 10], vec![true, false]), Dimension::D3 => (vec![10, 15, 8], vec![12, 10, 12], vec![true, false, true]), - Dimension::D4 => (vec![10, 15, 8, 12], vec![12, 10, 12, 8], vec![true, false, true, false]), + Dimension::D4 => ( + vec![10, 15, 8, 12], + vec![12, 10, 12, 8], + vec![true, false, true, false], + ), }; let a_constructor = format_vector_constructor(vec_type, dimension, &a_values); @@ -375,7 +386,11 @@ fn generate_test_expressions(vec_type: VecType, dimension: Dimension) -> String let (a_values, b_values, expected) = match dimension { Dimension::D2 => (vec![3, 7], vec![5, 5], vec![true, false]), Dimension::D3 => (vec![3, 7, 2], vec![5, 5, 4], vec![true, false, true]), - Dimension::D4 => (vec![3, 7, 2, 9], vec![5, 5, 4, 8], vec![true, false, true, false]), + Dimension::D4 => ( + vec![3, 7, 2, 9], + vec![5, 5, 4, 8], + vec![true, false, true, false], + ), }; let a_constructor = format_vector_constructor(vec_type, dimension, &a_values); @@ -402,20 +417,15 @@ fn generate_test_in_expression(vec_type: VecType, dimension: Dimension) -> Strin // Special case for D4 uvec: return bool instead of bvec4 (following manual file) if matches!(vec_type, VecType::UVec) && matches!(dimension, Dimension::D4) { return format!( - "bool test_{}_less_than_in_expression() {{\n\ - {} a = uvec4(1u, 5u, 3u, 7u);\n\ - {} b = uvec4(2u, 3u, 4u, 5u);\n\ - {} c = uvec4(3u, 7u, 1u, 9u);\n\ + "bool test_{type_name}_less_than_in_expression() {{\n\ + {type_name} a = uvec4(1u, 5u, 3u, 7u);\n\ + {type_name} b = uvec4(2u, 3u, 4u, 5u);\n\ + {type_name} c = uvec4(3u, 7u, 1u, 9u);\n\ return lessThan(a, b) == lessThan(b, c);\n\ // (true,false,true,false) == (true,true,false,true) = false\n\ }}\n\ \n\ -// run: test_{}_less_than_in_expression() == false\n", - type_name, - type_name, - type_name, - type_name, - type_name +// run: test_{type_name}_less_than_in_expression() == false\n" ); } diff --git a/lp-glsl/crates/lp-filetests-gen/src/vec/fn_max.rs b/lp-glsl/crates/lp-filetests-gen/src/vec/fn_max.rs index 3efd4547b..5d6efe221 100644 --- a/lp-glsl/crates/lp-filetests-gen/src/vec/fn_max.rs +++ b/lp-glsl/crates/lp-filetests-gen/src/vec/fn_max.rs @@ -9,7 +9,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { let type_name = format_type_name(vec_type, dimension); // Generate header with regeneration command - let specifier = format!("vec/{}/fn-max", type_name); + let specifier = format!("vec/{type_name}/fn-max"); let mut content = generate_header(&specifier); // Add test run and target directives @@ -22,8 +22,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { "// ============================================================================\n" )); content.push_str(&format!( - "// Max: max({}, {}) -> {} (component-wise maximum)\n", - type_name, type_name, type_name + "// Max: max({type_name}, {type_name}) -> {type_name} (component-wise maximum)\n" )); content.push_str(&format!( "// ============================================================================\n" @@ -84,23 +83,14 @@ fn generate_test_first_larger(vec_type: VecType, dimension: Dimension) -> String let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_max_first_larger() {{\n\ - // Function max() returns {} (component-wise maximum)\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_max_first_larger() {{\n\ + // Function max() returns {type_name} (component-wise maximum)\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return max(a, b);\n\ }}\n\ \n\ -// run: test_{}_max_first_larger() == {}\n", - type_name, - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - expected_constructor +// run: test_{type_name}_max_first_larger() == {expected_constructor}\n" ) } @@ -130,21 +120,13 @@ fn generate_test_second_larger(vec_type: VecType, dimension: Dimension) -> Strin let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_max_second_larger() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_max_second_larger() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return max(a, b);\n\ }}\n\ \n\ -// run: test_{}_max_second_larger() == {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - expected_constructor +// run: test_{type_name}_max_second_larger() == {expected_constructor}\n" ) } @@ -174,21 +156,13 @@ fn generate_test_mixed(vec_type: VecType, dimension: Dimension) -> String { let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_max_mixed() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_max_mixed() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return max(a, b);\n\ }}\n\ \n\ -// run: test_{}_max_mixed() == {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - expected_constructor +// run: test_{type_name}_max_mixed() == {expected_constructor}\n" ) } @@ -206,21 +180,13 @@ fn generate_test_equal(vec_type: VecType, dimension: Dimension) -> String { let constructor = format_vector_constructor(vec_type, dimension, &values); format!( - "{} test_{}_max_equal() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_max_equal() {{\n\ + {type_name} a = {constructor};\n\ + {type_name} b = {constructor};\n\ return max(a, b);\n\ }}\n\ \n\ -// run: test_{}_max_equal() == {}\n", - type_name, - type_name, - type_name, - constructor, - type_name, - constructor, - type_name, - constructor +// run: test_{type_name}_max_equal() == {constructor}\n" ) } @@ -255,21 +221,13 @@ fn generate_test_negative(vec_type: VecType, dimension: Dimension) -> String { let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_max_negative() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_max_negative() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return max(a, b);\n\ }}\n\ \n\ -// run: test_{}_max_negative() == {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - expected_constructor +// run: test_{type_name}_max_negative() == {expected_constructor}\n" ) } @@ -300,21 +258,13 @@ fn generate_test_zero(vec_type: VecType, dimension: Dimension) -> String { let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_max_zero() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_max_zero() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return max(a, b);\n\ }}\n\ \n\ -// run: test_{}_max_zero() == {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - expected_constructor +// run: test_{type_name}_max_zero() == {expected_constructor}\n" ) } @@ -344,21 +294,13 @@ fn generate_test_variables(vec_type: VecType, dimension: Dimension) -> String { let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_max_variables() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_max_variables() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return max(a, b);\n\ }}\n\ \n\ -// run: test_{}_max_variables() == {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - expected_constructor +// run: test_{type_name}_max_variables() == {expected_constructor}\n" ) } @@ -388,12 +330,11 @@ fn generate_test_expressions(vec_type: VecType, dimension: Dimension) -> String let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_max_expressions() {{\n\ - return max({}, {});\n\ + "{type_name} test_{type_name}_max_expressions() {{\n\ + return max({a_constructor}, {b_constructor});\n\ }}\n\ \n\ -// run: test_{}_max_expressions() == {}\n", - type_name, type_name, a_constructor, b_constructor, type_name, expected_constructor +// run: test_{type_name}_max_expressions() == {expected_constructor}\n" ) } @@ -429,23 +370,13 @@ fn generate_test_in_expression(vec_type: VecType, dimension: Dimension) -> Strin let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_max_in_expression() {{\n\ - {} a = {};\n\ - {} b = {};\n\ - {} c = {};\n\ + "{type_name} test_{type_name}_max_in_expression() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ + {type_name} c = {c_constructor};\n\ return max(a, max(b, c));\n\ }}\n\ \n\ -// run: test_{}_max_in_expression() == {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - c_constructor, - type_name, - expected_constructor +// run: test_{type_name}_max_in_expression() == {expected_constructor}\n" ) } diff --git a/lp-glsl/crates/lp-filetests-gen/src/vec/fn_min.rs b/lp-glsl/crates/lp-filetests-gen/src/vec/fn_min.rs index d9a2f7d73..65c62c1c4 100644 --- a/lp-glsl/crates/lp-filetests-gen/src/vec/fn_min.rs +++ b/lp-glsl/crates/lp-filetests-gen/src/vec/fn_min.rs @@ -9,7 +9,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { let type_name = format_type_name(vec_type, dimension); // Generate header with regeneration command - let specifier = format!("vec/{}/fn-min", type_name); + let specifier = format!("vec/{type_name}/fn-min"); let mut content = generate_header(&specifier); // Add test run and target directives @@ -22,8 +22,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { "// ============================================================================\n" )); content.push_str(&format!( - "// Min: min({}, {}) -> {} (component-wise minimum)\n", - type_name, type_name, type_name + "// Min: min({type_name}, {type_name}) -> {type_name} (component-wise minimum)\n" )); content.push_str(&format!( "// ============================================================================\n" @@ -84,23 +83,14 @@ fn generate_test_first_smaller(vec_type: VecType, dimension: Dimension) -> Strin let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_min_first_smaller() {{\n\ - // Function min() returns {} (component-wise minimum)\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_min_first_smaller() {{\n\ + // Function min() returns {type_name} (component-wise minimum)\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return min(a, b);\n\ }}\n\ \n\ -// run: test_{}_min_first_smaller() == {}\n", - type_name, - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - expected_constructor +// run: test_{type_name}_min_first_smaller() == {expected_constructor}\n" ) } @@ -130,21 +120,13 @@ fn generate_test_second_smaller(vec_type: VecType, dimension: Dimension) -> Stri let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_min_second_smaller() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_min_second_smaller() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return min(a, b);\n\ }}\n\ \n\ -// run: test_{}_min_second_smaller() == {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - expected_constructor +// run: test_{type_name}_min_second_smaller() == {expected_constructor}\n" ) } @@ -174,21 +156,13 @@ fn generate_test_mixed(vec_type: VecType, dimension: Dimension) -> String { let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_min_mixed() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_min_mixed() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return min(a, b);\n\ }}\n\ \n\ -// run: test_{}_min_mixed() == {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - expected_constructor +// run: test_{type_name}_min_mixed() == {expected_constructor}\n" ) } @@ -206,21 +180,13 @@ fn generate_test_equal(vec_type: VecType, dimension: Dimension) -> String { let constructor = format_vector_constructor(vec_type, dimension, &values); format!( - "{} test_{}_min_equal() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_min_equal() {{\n\ + {type_name} a = {constructor};\n\ + {type_name} b = {constructor};\n\ return min(a, b);\n\ }}\n\ \n\ -// run: test_{}_min_equal() == {}\n", - type_name, - type_name, - type_name, - constructor, - type_name, - constructor, - type_name, - constructor +// run: test_{type_name}_min_equal() == {constructor}\n" ) } @@ -255,21 +221,13 @@ fn generate_test_negative(vec_type: VecType, dimension: Dimension) -> String { let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_min_negative() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_min_negative() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return min(a, b);\n\ }}\n\ \n\ -// run: test_{}_min_negative() == {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - expected_constructor +// run: test_{type_name}_min_negative() == {expected_constructor}\n" ) } @@ -300,21 +258,13 @@ fn generate_test_zero(vec_type: VecType, dimension: Dimension) -> String { let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_min_zero() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_min_zero() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return min(a, b);\n\ }}\n\ \n\ -// run: test_{}_min_zero() == {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - expected_constructor +// run: test_{type_name}_min_zero() == {expected_constructor}\n" ) } @@ -344,21 +294,13 @@ fn generate_test_variables(vec_type: VecType, dimension: Dimension) -> String { let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_min_variables() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_min_variables() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return min(a, b);\n\ }}\n\ \n\ -// run: test_{}_min_variables() == {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - expected_constructor +// run: test_{type_name}_min_variables() == {expected_constructor}\n" ) } @@ -388,12 +330,11 @@ fn generate_test_expressions(vec_type: VecType, dimension: Dimension) -> String let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_min_expressions() {{\n\ - return min({}, {});\n\ + "{type_name} test_{type_name}_min_expressions() {{\n\ + return min({a_constructor}, {b_constructor});\n\ }}\n\ \n\ -// run: test_{}_min_expressions() == {}\n", - type_name, type_name, a_constructor, b_constructor, type_name, expected_constructor +// run: test_{type_name}_min_expressions() == {expected_constructor}\n" ) } @@ -429,23 +370,13 @@ fn generate_test_in_expression(vec_type: VecType, dimension: Dimension) -> Strin let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_min_in_expression() {{\n\ - {} a = {};\n\ - {} b = {};\n\ - {} c = {};\n\ + "{type_name} test_{type_name}_min_in_expression() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ + {type_name} c = {c_constructor};\n\ return min(a, min(b, c));\n\ }}\n\ \n\ -// run: test_{}_min_in_expression() == {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - c_constructor, - type_name, - expected_constructor +// run: test_{type_name}_min_in_expression() == {expected_constructor}\n" ) } diff --git a/lp-glsl/crates/lp-filetests-gen/src/vec/op_add.rs b/lp-glsl/crates/lp-filetests-gen/src/vec/op_add.rs index 47865b3a0..13c98ab76 100644 --- a/lp-glsl/crates/lp-filetests-gen/src/vec/op_add.rs +++ b/lp-glsl/crates/lp-filetests-gen/src/vec/op_add.rs @@ -9,7 +9,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { let type_name = format_type_name(vec_type, dimension); // Generate header with regeneration command - let specifier = format!("vec/{}/op-add", type_name); + let specifier = format!("vec/{type_name}/op-add"); let mut content = generate_header(&specifier); // Add test run and target directives @@ -22,8 +22,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { "// ============================================================================\n" )); content.push_str(&format!( - "// Add: {} + {} -> {} (component-wise)\n", - type_name, type_name, type_name + "// Add: {type_name} + {type_name} -> {type_name} (component-wise)\n" )); content.push_str(&format!( "// ============================================================================\n" @@ -107,23 +106,14 @@ fn generate_test_positive_positive(vec_type: VecType, dimension: Dimension) -> S let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_add_positive_positive() {{\n\ + "{type_name} test_{type_name}_add_positive_positive() {{\n\ // Addition with positive vectors (component-wise)\n\ - {} a = {};\n\ - {} b = {};\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a + b;\n\ }}\n\ \n\ -// run: test_{}_add_positive_positive() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_add_positive_positive() {cmp_op} {expected_constructor}\n" ) } @@ -159,22 +149,13 @@ fn generate_test_positive_negative(vec_type: VecType, dimension: Dimension) -> S let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_add_positive_negative() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_add_positive_negative() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a + b;\n\ }}\n\ \n\ -// run: test_{}_add_positive_negative() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_add_positive_negative() {cmp_op} {expected_constructor}\n" ) } @@ -210,22 +191,13 @@ fn generate_test_negative_negative(vec_type: VecType, dimension: Dimension) -> S let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_add_negative_negative() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_add_negative_negative() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a + b;\n\ }}\n\ \n\ -// run: test_{}_add_negative_negative() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_add_negative_negative() {cmp_op} {expected_constructor}\n" ) } @@ -250,22 +222,13 @@ fn generate_test_zero(vec_type: VecType, dimension: Dimension) -> String { let b_constructor = format_vector_constructor(vec_type, dimension, &b_values); format!( - "{} test_{}_add_zero() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_add_zero() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a + b;\n\ }}\n\ \n\ -// run: test_{}_add_zero() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - a_constructor +// run: test_{type_name}_add_zero() {cmp_op} {a_constructor}\n" ) } @@ -296,22 +259,13 @@ fn generate_test_variables(vec_type: VecType, dimension: Dimension) -> String { let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_add_variables() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_add_variables() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a + b;\n\ }}\n\ \n\ -// run: test_{}_add_variables() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_add_variables() {cmp_op} {expected_constructor}\n" ) } @@ -342,12 +296,11 @@ fn generate_test_expressions(vec_type: VecType, dimension: Dimension) -> String let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_add_expressions() {{\n\ - return {} + {};\n\ + "{type_name} test_{type_name}_add_expressions() {{\n\ + return {a_constructor} + {b_constructor};\n\ }}\n\ \n\ -// run: test_{}_add_expressions() {} {}\n", - type_name, type_name, a_constructor, b_constructor, type_name, cmp_op, expected_constructor +// run: test_{type_name}_add_expressions() {cmp_op} {expected_constructor}\n" ) } @@ -378,21 +331,13 @@ fn generate_test_in_assignment(vec_type: VecType, dimension: Dimension) -> Strin let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_add_in_assignment() {{\n\ - {} result = {};\n\ - result = result + {};\n\ + "{type_name} test_{type_name}_add_in_assignment() {{\n\ + {type_name} result = {initial_constructor};\n\ + result = result + {add_constructor};\n\ return result;\n\ }}\n\ \n\ -// run: test_{}_add_in_assignment() {} {}\n", - type_name, - type_name, - type_name, - initial_constructor, - add_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_add_in_assignment() {cmp_op} {expected_constructor}\n" ) } @@ -438,24 +383,15 @@ fn generate_test_large_numbers(vec_type: VecType, dimension: Dimension) -> Strin let expected_constructor = format_vector_constructor(vec_type, dimension, &expected_values); format!( - "{} test_{}_add_large_numbers() {{\n\ + "{type_name} test_{type_name}_add_large_numbers() {{\n\ // Large numbers are clamped to fixed16x16 max (32767.99998)\n\ // Addition saturates to max for each component\n\ - {} a = {};\n\ - {} b = {};\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a + b;\n\ }}\n\ \n\ -// run: test_{}_add_large_numbers() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_add_large_numbers() {cmp_op} {expected_constructor}\n" ) } @@ -470,7 +406,11 @@ fn generate_test_mixed_components(vec_type: VecType, dimension: Dimension) -> St match dimension { Dimension::D2 => (vec![100, 50], vec![200, 75], vec![300, 125]), Dimension::D3 => (vec![100, 50, 75], vec![200, 75, 150], vec![300, 125, 225]), - Dimension::D4 => (vec![100, 50, 75, 25], vec![200, 75, 150, 50], vec![300, 125, 225, 75]), + Dimension::D4 => ( + vec![100, 50, 75, 25], + vec![200, 75, 150, 50], + vec![300, 125, 225, 75], + ), } } else { // Values: a = [1, -2, 3, -4...], b = [-3, 4, -1, 2...] @@ -487,22 +427,13 @@ fn generate_test_mixed_components(vec_type: VecType, dimension: Dimension) -> St let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_add_mixed_components() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_add_mixed_components() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a + b;\n\ }}\n\ \n\ -// run: test_{}_add_mixed_components() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_add_mixed_components() {cmp_op} {expected_constructor}\n" ) } @@ -529,7 +460,8 @@ fn generate_test_max_values(vec_type: VecType, dimension: Dimension) -> String { match dimension { Dimension::D2 => "uvec2(4294967295u, 4294967294u)".to_string(), Dimension::D3 => "uvec3(4294967295u, 4294967294u, 4294967293u)".to_string(), - Dimension::D4 => "uvec4(4294967295u, 4294967294u, 4294967293u, 4294967292u)".to_string(), + Dimension::D4 => + "uvec4(4294967295u, 4294967294u, 4294967293u, 4294967292u)".to_string(), }, type_name, match dimension { @@ -578,21 +510,12 @@ fn generate_test_fractions(vec_type: VecType, dimension: Dimension) -> String { }; format!( - "{} test_{}_add_fractions() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_add_fractions() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a + b;\n\ }}\n\ \n\ -// run: test_{}_add_fractions() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_add_fractions() {cmp_op} {expected_constructor}\n" ) } diff --git a/lp-glsl/crates/lp-filetests-gen/src/vec/op_equal.rs b/lp-glsl/crates/lp-filetests-gen/src/vec/op_equal.rs index 4d9ecba19..f098e3c6c 100644 --- a/lp-glsl/crates/lp-filetests-gen/src/vec/op_equal.rs +++ b/lp-glsl/crates/lp-filetests-gen/src/vec/op_equal.rs @@ -11,7 +11,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { let type_name = format_type_name(vec_type, dimension); // Generate header with regeneration command - let specifier = format!("vec/{}/op-equal", type_name); + let specifier = format!("vec/{type_name}/op-equal"); let mut content = generate_header(&specifier); // Add test run and target directives @@ -81,15 +81,14 @@ fn generate_test_operator_true(vec_type: VecType, dimension: Dimension) -> Strin let constructor = format_vector_constructor(vec_type, dimension, &values); format!( - "bool test_{}_equal_operator_true() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "bool test_{type_name}_equal_operator_true() {{\n\ + {type_name} a = {constructor};\n\ + {type_name} b = {constructor};\n\ // Operator == returns bool (aggregate comparison - all components must match)\n\ return a == b;\n\ }}\n\ \n\ -// run: test_{}_equal_operator_true() == true\n", - type_name, type_name, constructor, type_name, constructor, type_name +// run: test_{type_name}_equal_operator_true() == true\n" ) } @@ -113,14 +112,13 @@ fn generate_test_operator_false(vec_type: VecType, dimension: Dimension) -> Stri let b_constructor = format_vector_constructor(vec_type, dimension, &b_values); format!( - "bool test_{}_equal_operator_false() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "bool test_{type_name}_equal_operator_false() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a == b;\n\ }}\n\ \n\ -// run: test_{}_equal_operator_false() == false\n", - type_name, type_name, a_constructor, type_name, b_constructor, type_name +// run: test_{type_name}_equal_operator_false() == false\n" ) } @@ -144,14 +142,13 @@ fn generate_test_operator_partial_match(vec_type: VecType, dimension: Dimension) let b_constructor = format_vector_constructor(vec_type, dimension, &b_values); format!( - "bool test_{}_equal_operator_partial_match() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "bool test_{type_name}_equal_operator_partial_match() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a == b;\n\ }}\n\ \n\ -// run: test_{}_equal_operator_partial_match() == false\n", - type_name, type_name, a_constructor, type_name, b_constructor, type_name +// run: test_{type_name}_equal_operator_partial_match() == false\n" ) } @@ -169,14 +166,13 @@ fn generate_test_operator_all_zero(vec_type: VecType, dimension: Dimension) -> S let constructor = format_vector_constructor(vec_type, dimension, &values); format!( - "bool test_{}_equal_operator_all_zero() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "bool test_{type_name}_equal_operator_all_zero() {{\n\ + {type_name} a = {constructor};\n\ + {type_name} b = {constructor};\n\ return a == b;\n\ }}\n\ \n\ -// run: test_{}_equal_operator_all_zero() == true\n", - type_name, type_name, constructor, type_name, constructor, type_name +// run: test_{type_name}_equal_operator_all_zero() == true\n" ) } @@ -199,14 +195,13 @@ fn generate_test_operator_negative(vec_type: VecType, dimension: Dimension) -> S let constructor = format_vector_constructor(vec_type, dimension, &values); format!( - "bool test_{}_equal_operator_negative() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "bool test_{type_name}_equal_operator_negative() {{\n\ + {type_name} a = {constructor};\n\ + {type_name} b = {constructor};\n\ return a == b;\n\ }}\n\ \n\ -// run: test_{}_equal_operator_negative() == true\n", - type_name, type_name, constructor, type_name, constructor, type_name +// run: test_{type_name}_equal_operator_negative() == true\n" ) } @@ -230,15 +225,14 @@ fn generate_test_operator_after_assignment(vec_type: VecType, dimension: Dimensi let b_constructor = format_vector_constructor(vec_type, dimension, &b_values); format!( - "bool test_{}_equal_operator_after_assignment() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "bool test_{type_name}_equal_operator_after_assignment() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ b = a;\n\ return a == b;\n\ }}\n\ \n\ -// run: test_{}_equal_operator_after_assignment() == true\n", - type_name, type_name, a_constructor, type_name, b_constructor, type_name +// run: test_{type_name}_equal_operator_after_assignment() == true\n" ) } diff --git a/lp-glsl/crates/lp-filetests-gen/src/vec/op_multiply.rs b/lp-glsl/crates/lp-filetests-gen/src/vec/op_multiply.rs index add2b4a6a..3286f1a7d 100644 --- a/lp-glsl/crates/lp-filetests-gen/src/vec/op_multiply.rs +++ b/lp-glsl/crates/lp-filetests-gen/src/vec/op_multiply.rs @@ -9,7 +9,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { let type_name = format_type_name(vec_type, dimension); // Generate header with regeneration command - let specifier = format!("vec/{}/op-multiply", type_name); + let specifier = format!("vec/{type_name}/op-multiply"); let mut content = generate_header(&specifier); // Add test run and target directives @@ -22,8 +22,7 @@ pub fn generate(vec_type: VecType, dimension: Dimension) -> String { "// ============================================================================\n" )); content.push_str(&format!( - "// Multiply: {} * {} -> {} (component-wise)\n", - type_name, type_name, type_name + "// Multiply: {type_name} * {type_name} -> {type_name} (component-wise)\n" )); content.push_str(&format!( "// ============================================================================\n" @@ -109,23 +108,14 @@ fn generate_test_positive_positive(vec_type: VecType, dimension: Dimension) -> S let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_multiply_positive_positive() {{\n\ + "{type_name} test_{type_name}_multiply_positive_positive() {{\n\ // Multiplication with positive vectors (component-wise)\n\ - {} a = {};\n\ - {} b = {};\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a * b;\n\ }}\n\ \n\ -// run: test_{}_multiply_positive_positive() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_multiply_positive_positive() {cmp_op} {expected_constructor}\n" ) } @@ -161,22 +151,13 @@ fn generate_test_positive_negative(vec_type: VecType, dimension: Dimension) -> S let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_multiply_positive_negative() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_multiply_positive_negative() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a * b;\n\ }}\n\ \n\ -// run: test_{}_multiply_positive_negative() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_multiply_positive_negative() {cmp_op} {expected_constructor}\n" ) } @@ -212,22 +193,13 @@ fn generate_test_negative_negative(vec_type: VecType, dimension: Dimension) -> S let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_multiply_negative_negative() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_multiply_negative_negative() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a * b;\n\ }}\n\ \n\ -// run: test_{}_multiply_negative_negative() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_multiply_negative_negative() {cmp_op} {expected_constructor}\n" ) } @@ -258,22 +230,13 @@ fn generate_test_by_zero(vec_type: VecType, dimension: Dimension) -> String { let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_multiply_by_zero() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_multiply_by_zero() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a * b;\n\ }}\n\ \n\ -// run: test_{}_multiply_by_zero() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_multiply_by_zero() {cmp_op} {expected_constructor}\n" ) } @@ -298,22 +261,13 @@ fn generate_test_by_one(vec_type: VecType, dimension: Dimension) -> String { let b_constructor = format_vector_constructor(vec_type, dimension, &b_values); format!( - "{} test_{}_multiply_by_one() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_multiply_by_one() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a * b;\n\ }}\n\ \n\ -// run: test_{}_multiply_by_one() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - a_constructor +// run: test_{type_name}_multiply_by_one() {cmp_op} {a_constructor}\n" ) } @@ -344,22 +298,13 @@ fn generate_test_variables(vec_type: VecType, dimension: Dimension) -> String { let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_multiply_variables() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_multiply_variables() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a * b;\n\ }}\n\ \n\ -// run: test_{}_multiply_variables() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_multiply_variables() {cmp_op} {expected_constructor}\n" ) } @@ -390,12 +335,11 @@ fn generate_test_expressions(vec_type: VecType, dimension: Dimension) -> String let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_multiply_expressions() {{\n\ - return {} * {};\n\ + "{type_name} test_{type_name}_multiply_expressions() {{\n\ + return {a_constructor} * {b_constructor};\n\ }}\n\ \n\ -// run: test_{}_multiply_expressions() {} {}\n", - type_name, type_name, a_constructor, b_constructor, type_name, cmp_op, expected_constructor +// run: test_{type_name}_multiply_expressions() {cmp_op} {expected_constructor}\n" ) } @@ -426,21 +370,13 @@ fn generate_test_in_assignment(vec_type: VecType, dimension: Dimension) -> Strin let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_multiply_in_assignment() {{\n\ - {} result = {};\n\ - result = result * {};\n\ + "{type_name} test_{type_name}_multiply_in_assignment() {{\n\ + {type_name} result = {initial_constructor};\n\ + result = result * {multiply_constructor};\n\ return result;\n\ }}\n\ \n\ -// run: test_{}_multiply_in_assignment() {} {}\n", - type_name, - type_name, - type_name, - initial_constructor, - multiply_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_multiply_in_assignment() {cmp_op} {expected_constructor}\n" ) } @@ -485,22 +421,13 @@ fn generate_test_large_numbers(vec_type: VecType, dimension: Dimension) -> Strin let expected_constructor = format_vector_constructor(vec_type, dimension, &expected_values); format!( - "{} test_{}_multiply_large_numbers() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_multiply_large_numbers() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a * b;\n\ }}\n\ \n\ -// run: test_{}_multiply_large_numbers() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_multiply_large_numbers() {cmp_op} {expected_constructor}\n" ) } @@ -536,22 +463,13 @@ fn generate_test_mixed_components(vec_type: VecType, dimension: Dimension) -> St let expected_constructor = format_vector_constructor(vec_type, dimension, &expected); format!( - "{} test_{}_multiply_mixed_components() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_multiply_mixed_components() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a * b;\n\ }}\n\ \n\ -// run: test_{}_multiply_mixed_components() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_multiply_mixed_components() {cmp_op} {expected_constructor}\n" ) } @@ -585,22 +503,13 @@ fn generate_test_overflow(vec_type: VecType, dimension: Dimension) -> String { }; format!( - "{} test_{}_multiply_overflow() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_multiply_overflow() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a * b;\n\ }}\n\ \n\ -// run: test_{}_multiply_overflow() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_multiply_overflow() {cmp_op} {expected_constructor}\n" ) } @@ -650,21 +559,12 @@ fn generate_test_fractions(vec_type: VecType, dimension: Dimension) -> String { }; format!( - "{} test_{}_multiply_fractions() {{\n\ - {} a = {};\n\ - {} b = {};\n\ + "{type_name} test_{type_name}_multiply_fractions() {{\n\ + {type_name} a = {a_constructor};\n\ + {type_name} b = {b_constructor};\n\ return a * b;\n\ }}\n\ \n\ -// run: test_{}_multiply_fractions() {} {}\n", - type_name, - type_name, - type_name, - a_constructor, - type_name, - b_constructor, - type_name, - cmp_op, - expected_constructor +// run: test_{type_name}_multiply_fractions() {cmp_op} {expected_constructor}\n" ) } diff --git a/lp-glsl/crates/lp-filetests-gen/src/vec/util.rs b/lp-glsl/crates/lp-filetests-gen/src/vec/util.rs index e267e09c8..d6cfb472a 100644 --- a/lp-glsl/crates/lp-filetests-gen/src/vec/util.rs +++ b/lp-glsl/crates/lp-filetests-gen/src/vec/util.rs @@ -5,9 +5,9 @@ use crate::types::{Dimension, VecType}; /// Format a literal value based on vector type. pub fn format_literal(value: i32, vec_type: VecType) -> String { match vec_type { - VecType::Vec => format!("{}.0", value), - VecType::IVec => format!("{}", value), - VecType::UVec => format!("{}u", value), + VecType::Vec => format!("{value}.0"), + VecType::IVec => format!("{value}"), + VecType::UVec => format!("{value}u"), VecType::BVec => { if value != 0 { "true".to_string() @@ -33,7 +33,7 @@ pub fn format_type_name(vec_type: VecType, dimension: Dimension) -> String { VecType::BVec => "bvec", }; - format!("{}{}", prefix, dim_str) + format!("{prefix}{dim_str}") } /// Format the return type name for comparison functions (always bvec). @@ -43,7 +43,7 @@ pub fn format_bvec_type_name(dimension: Dimension) -> String { Dimension::D3 => "3", Dimension::D4 => "4", }; - format!("bvec{}", dim_str) + format!("bvec{dim_str}") } /// Generate a vector constructor call. diff --git a/lp-glsl/crates/lp-glsl-compiler/build.rs b/lp-glsl/crates/lp-glsl-compiler/build.rs index 55b32a8a1..c5f877f4c 100644 --- a/lp-glsl/crates/lp-glsl-compiler/build.rs +++ b/lp-glsl/crates/lp-glsl-compiler/build.rs @@ -69,8 +69,7 @@ fn main() { std::fs::write( &include_file, format!( - "pub const LP_BUILTINS_EXE_BYTES: &[u8] = include_bytes!(\"{}\");\n", - include_path + "pub const LP_BUILTINS_EXE_BYTES: &[u8] = include_bytes!(\"{include_path}\");\n" ), ) .expect("Failed to write builtins exe include file"); @@ -96,7 +95,7 @@ fn main() { } /// Find the workspace root by looking for Cargo.toml with [workspace] -#[allow(dead_code)] +#[allow(dead_code, reason = "Used for workspace detection in build script")] fn find_workspace_root(start: &str) -> Option { use std::path::Path; let mut current = Path::new(start); diff --git a/lp-glsl/crates/lp-glsl-compiler/examples/simple.rs b/lp-glsl/crates/lp-glsl-compiler/examples/simple.rs index 9a218b61e..86d5f4379 100644 --- a/lp-glsl/crates/lp-glsl-compiler/examples/simple.rs +++ b/lp-glsl/crates/lp-glsl-compiler/examples/simple.rs @@ -19,7 +19,7 @@ fn main() { let mut executable1 = glsl_jit(shader1, options.clone()).unwrap(); let result1 = executable1.call_i32("main", &[]).unwrap(); - println!("Example 1 - Integer arithmetic: {} (expected: 42)", result1); + println!("Example 1 - Integer arithmetic: {result1} (expected: 42)"); assert_eq!(result1, 42); // Example 2: Boolean comparison @@ -33,10 +33,7 @@ fn main() { let mut executable2 = glsl_jit(shader2, options.clone()).unwrap(); let result2 = executable2.call_bool("main", &[]).unwrap(); - println!( - "Example 2 - Boolean comparison: {} (expected: true)", - result2 - ); + println!("Example 2 - Boolean comparison: {result2} (expected: true)"); assert_eq!(result2, true); // Example 3: Complex expression @@ -51,7 +48,7 @@ fn main() { let mut executable3 = glsl_jit(shader3, options).unwrap(); let result3 = executable3.call_i32("main", &[]).unwrap(); - println!("Example 3 - Complex expression: {} (expected: 12)", result3); + println!("Example 3 - Complex expression: {result3} (expected: 12)"); assert_eq!(result3, 12); // (5 + 3) * 2 - 4 = 12 println!("\nAll examples passed!"); diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/builtins/registry.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/builtins/registry.rs index 0b21586d0..1f8ae4ece 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/builtins/registry.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/builtins/registry.rs @@ -16,6 +16,9 @@ use cranelift_codegen::ir::{AbiParam, Signature, types}; use cranelift_codegen::isa::CallConv; use cranelift_module::{Linkage, Module}; +#[cfg(not(feature = "std"))] +use alloc::format; + /// Enum identifying builtin functions. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum BuiltinId { @@ -93,13 +96,37 @@ impl BuiltinId { sig.params.push(AbiParam::new(types::I32)); sig.returns.push(AbiParam::new(types::I32)); } - BuiltinId::Fixed32Atan2 | BuiltinId::Fixed32Div | BuiltinId::Fixed32Ldexp | BuiltinId::Fixed32Mod | BuiltinId::Fixed32Mul | BuiltinId::Fixed32Pow => { + BuiltinId::Fixed32Atan2 + | BuiltinId::Fixed32Div + | BuiltinId::Fixed32Ldexp + | BuiltinId::Fixed32Mod + | BuiltinId::Fixed32Mul + | BuiltinId::Fixed32Pow => { // (i32, i32) -> i32 sig.params.push(AbiParam::new(types::I32)); sig.params.push(AbiParam::new(types::I32)); sig.returns.push(AbiParam::new(types::I32)); } - BuiltinId::Fixed32Acos | BuiltinId::Fixed32Acosh | BuiltinId::Fixed32Asin | BuiltinId::Fixed32Asinh | BuiltinId::Fixed32Atan | BuiltinId::Fixed32Atanh | BuiltinId::Fixed32Cos | BuiltinId::Fixed32Cosh | BuiltinId::Fixed32Exp | BuiltinId::Fixed32Exp2 | BuiltinId::Fixed32Inversesqrt | BuiltinId::Fixed32Log | BuiltinId::Fixed32Log2 | BuiltinId::Fixed32Round | BuiltinId::Fixed32Roundeven | BuiltinId::Fixed32Sin | BuiltinId::Fixed32Sinh | BuiltinId::Fixed32Sqrt | BuiltinId::Fixed32Tan | BuiltinId::Fixed32Tanh => { + BuiltinId::Fixed32Acos + | BuiltinId::Fixed32Acosh + | BuiltinId::Fixed32Asin + | BuiltinId::Fixed32Asinh + | BuiltinId::Fixed32Atan + | BuiltinId::Fixed32Atanh + | BuiltinId::Fixed32Cos + | BuiltinId::Fixed32Cosh + | BuiltinId::Fixed32Exp + | BuiltinId::Fixed32Exp2 + | BuiltinId::Fixed32Inversesqrt + | BuiltinId::Fixed32Log + | BuiltinId::Fixed32Log2 + | BuiltinId::Fixed32Round + | BuiltinId::Fixed32Roundeven + | BuiltinId::Fixed32Sin + | BuiltinId::Fixed32Sinh + | BuiltinId::Fixed32Sqrt + | BuiltinId::Fixed32Tan + | BuiltinId::Fixed32Tanh => { // (i32) -> i32 sig.params.push(AbiParam::new(types::I32)); sig.returns.push(AbiParam::new(types::I32)); @@ -194,7 +221,7 @@ pub fn declare_builtins(module: &mut M) -> Result<(), GlslError> { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("Failed to declare builtin '{}': {}", name, e), + format!("Failed to declare builtin '{name}': {e}"), ) })?; } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/codegen/builtins_linker.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/codegen/builtins_linker.rs index 24db7edd3..b6c08ad7a 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/codegen/builtins_linker.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/codegen/builtins_linker.rs @@ -45,9 +45,8 @@ pub fn link_and_verify_builtins( GlslError::new( ErrorCode::E0400, format!( - "Failed to load base executable: {}. \ - Ensure lp-builtins-app is correctly compiled.", - e + "Failed to load base executable: {e}. \ + Ensure lp-builtins-app is correctly compiled." ), ) })?; @@ -66,9 +65,8 @@ pub fn link_and_verify_builtins( GlslError::new( ErrorCode::E0400, format!( - "Failed to load object file: {}. \ - Ensure the object file is correctly compiled.", - e + "Failed to load object file: {e}. \ + Ensure the object file is correctly compiled." ), ) })?; @@ -113,10 +111,9 @@ pub fn link_and_verify_builtins( return Err(GlslError::new( ErrorCode::E0400, format!( - "Builtin symbols are undefined after loading: {:?}. \ + "Builtin symbols are undefined after loading: {undefined_symbols:?}. \ These symbols were declared but not resolved. \ - Ensure lp-builtins library is built and linked correctly.", - undefined_symbols + Ensure lp-builtins library is built and linked correctly." ), )); } @@ -125,9 +122,8 @@ pub fn link_and_verify_builtins( return Err(GlslError::new( ErrorCode::E0400, format!( - "Builtin symbols not found after loading: {:?}. \ - Ensure lp-builtins library is built and contains these symbols.", - missing_symbols + "Builtin symbols not found after loading: {missing_symbols:?}. \ + Ensure lp-builtins library is built and contains these symbols." ), )); } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/codegen/emu.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/codegen/emu.rs index 619309dd6..8371c9c87 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/codegen/emu.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/codegen/emu.rs @@ -30,7 +30,7 @@ pub struct EmulatorOptions { /// Maximum memory size in bytes (RAM) pub max_memory: usize, /// Stack size in bytes (stored for future use) - #[allow(unused)] + #[allow(unused, reason = "Reserved for future stack size configuration")] pub stack_size: usize, /// Maximum instruction count before timeout pub max_instructions: u64, @@ -57,7 +57,10 @@ pub fn build_emu_executable( // 1. Define all functions (compile them) // Collect function data first to avoid borrowing conflicts - let funcs: Vec<( + // IMPORTANT: Sort by name to ensure consistent FuncId-to-symbol mapping. + // Cranelift's ObjectModule maps FuncIds to symbol names based on definition order, + // so we must define functions in the same order they were declared (which should be sorted). + let mut funcs: Vec<( String, cranelift_codegen::ir::Function, cranelift_module::FuncId, @@ -66,6 +69,7 @@ pub fn build_emu_executable( .iter() .map(|(name, gl_func)| (name.clone(), gl_func.function.clone(), gl_func.func_id)) .collect(); + funcs.sort_by_key(|(name, _, _)| name.clone()); // Collect V-Code and disassembly for all functions #[cfg(feature = "std")] @@ -101,7 +105,7 @@ pub fn build_emu_executable( // TODO: This is a hacky way to get the verifier error and it should be improved // Check if this is a verifier error by checking the error message // If it is, verify the function again to get detailed error messages - let error_str = format!("{}", e); + let error_str = format!("{e}"); let error_msg = if error_str.contains("Verifier errors") { // It's a verifier error - verify the function again to get detailed errors use cranelift_codegen::verifier::verify_function; @@ -128,10 +132,10 @@ pub fn build_emu_executable( } } else { // Fallback if verification somehow succeeds - format!("Failed to define function '{}': {}", name, e) + format!("Failed to define function '{name}': {e}") } } else { - format!("Failed to define function '{}': {}", name, e) + format!("Failed to define function '{name}': {e}") }; let mut error = GlslError::new(ErrorCode::E0400, error_msg); @@ -141,23 +145,21 @@ pub fn build_emu_executable( match (&original_clif, &transformed_clif) { (Some(original), Some(transformed)) if original != transformed => { error = error.with_note(format!( - "=== CLIF IR (BEFORE transformation) ===\n{}", - original + "=== CLIF IR (BEFORE transformation) ===\n{original}" )); error = error.with_note(format!( - "=== CLIF IR (AFTER transformation) ===\n{}", - transformed + "=== CLIF IR (AFTER transformation) ===\n{transformed}" )); } (Some(ir), Some(_)) => { // They're the same, just show one - error = error.with_note(format!("=== CLIF IR ===\n{}", ir)); + error = error.with_note(format!("=== CLIF IR ===\n{ir}")); } (Some(ir), None) => { - error = error.with_note(format!("=== CLIF IR ===\n{}", ir)); + error = error.with_note(format!("=== CLIF IR ===\n{ir}")); } (None, Some(ir)) => { - error = error.with_note(format!("=== CLIF IR ===\n{}", ir)); + error = error.with_note(format!("=== CLIF IR ===\n{ir}")); } (None, None) => { // No CLIF IR available @@ -213,12 +215,12 @@ pub fn build_emu_executable( // Store actual disassembly (RISC-V assembly) if let Some(ref disasm_str) = disasm { - all_disasm_parts.push(format!("// function {}:\n{}", name, disasm_str)); + all_disasm_parts.push(format!("// function {name}:\n{disasm_str}")); } // Store VCode separately (intermediate representation) if let Some(ref vcode_str) = vcode { - all_vcode_parts.push(format!("// function {}:\n{}", name, vcode_str)); + all_vcode_parts.push(format!("// function {name}:\n{vcode_str}")); } } } @@ -274,7 +276,7 @@ pub fn build_emu_executable( let product = gl_module.into_module().finish(); let elf_bytes = product .emit() - .map_err(|e| GlslError::new(ErrorCode::E0400, format!("Failed to emit ELF: {}", e)))?; + .map_err(|e| GlslError::new(ErrorCode::E0400, format!("Failed to emit ELF: {e}")))?; // Debug: Check symbols BEFORE linking crate::debug!("=== Symbols BEFORE linking ==="); @@ -387,6 +389,11 @@ pub fn build_emu_executable( .with_log_level(LogLevel::Instructions); // 7. Run bootstrap init: initialize .bss/.data and optionally call user _init + // Temporarily increase instruction limit for bootstrap init (which can be longer) + let original_max_instructions = options.max_instructions; + let init_max_instructions = 10000u64.max(options.max_instructions * 10); + emulator.set_max_instructions(init_max_instructions); + // Set up stack pointer (sp = x2) to point to high RAM let sp_value = 0x80000000u32.wrapping_add((ram_size as u32).wrapping_sub(16)); emulator.set_register(Gpr::Sp, sp_value as i32); @@ -473,6 +480,9 @@ pub fn build_emu_executable( } } + // Restore original instruction limit for normal function execution + emulator.set_max_instructions(original_max_instructions); + crate::debug!("Bootstrap init completed successfully"); // 8. Create GlslEmulatorModule diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/codegen/jit.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/codegen/jit.rs index 170f8a0f4..b425d9659 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/codegen/jit.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/codegen/jit.rs @@ -3,8 +3,7 @@ use crate::backend::module::gl_module::GlModule; use crate::error::{ErrorCode, GlslError}; use crate::exec::jit::GlslJitModule; -use alloc::string::String; -use alloc::vec::Vec; +use alloc::{format, string::String, vec::Vec}; use cranelift_jit::JITModule; use cranelift_module::Module; use hashbrown::HashMap; @@ -42,7 +41,7 @@ pub fn build_jit_executable( .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("Failed to define function '{}': {}", name, e), + format!("Failed to define function '{name}': {e}"), ) })?; // Clear context using immutable borrow @@ -59,7 +58,7 @@ pub fn build_jit_executable( .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("Failed to finalize definitions: {}", e), + format!("Failed to finalize definitions: {e}"), ) })?; @@ -83,12 +82,9 @@ pub fn build_jit_executable( let call_conv = gl_module .target .default_call_conv() - .map_err(|e| GlslError::new(ErrorCode::E0400, format!("Failed to get call conv: {}", e)))?; + .map_err(|e| GlslError::new(ErrorCode::E0400, format!("Failed to get call conv: {e}")))?; let pointer_type = gl_module.target.pointer_type().map_err(|e| { - GlslError::new( - ErrorCode::E0400, - format!("Failed to get pointer type: {}", e), - ) + GlslError::new(ErrorCode::E0400, format!("Failed to get pointer type: {e}")) })?; // 5. Create GlslJitModule diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/host/impls.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/host/impls.rs index f5c12830d..fe2621e60 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/host/impls.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/host/impls.rs @@ -24,6 +24,6 @@ pub extern "C" fn __host_println(ptr: *const u8, len: usize) { let slice = core::slice::from_raw_parts(ptr, len); let msg = core::str::from_utf8_unchecked(slice); // Delegate to std::println! - std::println!("{}", msg); + std::println!("{msg}"); } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/host/registry.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/host/registry.rs index 3ca1f28dc..226cae78f 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/host/registry.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/host/registry.rs @@ -46,11 +46,12 @@ impl HostId { /// /// Returns the function pointer that can be registered with JITModule. #[cfg(feature = "std")] -pub fn get_host_function_pointer(host: HostId) -> *const u8 { +pub fn get_host_function_pointer(host: HostId) -> Option<*const u8> { use crate::backend::host::impls; + match host { - HostId::Debug => impls::__host_debug as *const u8, - HostId::Println => impls::__host_println as *const u8, + HostId::Debug => Some(impls::__host_debug as *const u8), + HostId::Println => Some(impls::__host_println as *const u8), } } @@ -58,9 +59,8 @@ pub fn get_host_function_pointer(host: HostId) -> *const u8 { /// /// Returns None since host functions require std. #[cfg(not(feature = "std"))] -pub fn get_host_function_pointer(_host: HostId) -> *const u8 { - // Return a null pointer - host functions don't work in no_std - core::ptr::null() +pub fn get_host_function_pointer(_host: HostId) -> Option<*const u8> { + None } /// Declare host functions as external symbols. @@ -76,7 +76,7 @@ pub fn declare_host_functions(module: &mut M) -> Result<(), GlslError .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("Failed to declare host function '{}': {}", name, e), + format!("Failed to declare host function '{name}': {e}"), ) })?; } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/module/gl_module.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/module/gl_module.rs index ade6fe9dc..ebc6f4ec2 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/module/gl_module.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/module/gl_module.rs @@ -6,8 +6,8 @@ use crate::error::{ErrorCode, GlslError}; use crate::frontend::semantic::functions::{FunctionRegistry, FunctionSignature}; use crate::frontend::src_loc::GlSourceMap; use crate::frontend::src_loc_manager::SourceLocManager; -#[cfg(feature = "std")] use alloc::boxed::Box; +use alloc::format; use alloc::string::String; use cranelift_jit::JITModule; use cranelift_module::Module; @@ -52,13 +52,10 @@ impl GlModule { return Some(get_function_pointer(*builtin)); } } - // Check host functions (only in std mode) #[cfg(feature = "std")] - { - for host in HostId::all() { - if host.name() == name { - return Some(get_host_function_pointer(*host)); - } + for host in HostId::all() { + if host.name() == name { + return get_host_function_pointer(*host); } } None @@ -206,8 +203,7 @@ impl GlModule { GlslError::new( crate::error::ErrorCode::E0400, format!( - "Builtin function '{}' not found in module declarations. Ensure declare_builtins() was called.", - name + "Builtin function '{name}' not found in module declarations. Ensure declare_builtins() was called." ), ) })?; @@ -251,7 +247,7 @@ impl GlModule { if func.signature != sig { return Err(GlslError::new( ErrorCode::E0400, - format!("Function signature mismatch for '{}'", name), + format!("Function signature mismatch for '{name}'"), )); } @@ -262,10 +258,16 @@ impl GlModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("Failed to declare function '{}': {}", name, e), + format!("Failed to declare function '{name}': {e}"), ) })?; + // IMPORTANT: Update the function's name to match the FuncId + // Cranelift uses the function name to match it with the FuncId during define_function + use cranelift_codegen::ir::UserFuncName; + let mut func_with_name = func; + func_with_name.name = UserFuncName::user(0, func_id.as_u32()); + // Store Function IR self.fns.insert( String::from(name), @@ -273,7 +275,7 @@ impl GlModule { name: String::from(name), clif_sig: sig, func_id, - function: func, + function: func_with_name, }, ); @@ -297,7 +299,7 @@ impl GlModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("Failed to declare function '{}': {}", name, e), + format!("Failed to declare function '{name}': {e}"), ) })?; @@ -364,9 +366,15 @@ impl GlModule { use cranelift_module::Linkage; // 1. Transform all function signatures and create FuncId mappings + // IMPORTANT: Iterate in sorted order to ensure consistent FuncId assignment. + // Cranelift's ObjectModule maps FuncIds to symbol names based on declaration order, + // so we must declare functions in a deterministic order. let mut func_id_map = hashbrown::HashMap::new(); let mut old_func_id_map = hashbrown::HashMap::new(); - for (name, gl_func) in &fns { + use alloc::vec::Vec; + let mut sorted_fns: Vec<_> = fns.iter().collect(); + sorted_fns.sort_by_key(|(name, _)| *name); + for (name, gl_func) in sorted_fns { let new_sig = transform.transform_signature(&gl_func.clif_sig); // Determine linkage - for now, use Local (can be enhanced later) let linkage = Linkage::Local; @@ -376,10 +384,7 @@ impl GlModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!( - "Failed to declare function '{}' in transformed module: {}", - name, e - ), + format!("Failed to declare function '{name}' in transformed module: {e}"), ) })?; func_id_map.insert(name.clone(), func_id); @@ -404,7 +409,10 @@ impl GlModule { } // 2. Transform function bodies - for (name, gl_func) in fns { + // IMPORTANT: Transform in the SAME sorted order as declaration to ensure FuncId consistency + let mut sorted_fns_for_transform: Vec<_> = fns.into_iter().collect(); + sorted_fns_for_transform.sort_by_key(|(name, _)| name.clone()); + for (name, gl_func) in sorted_fns_for_transform { let mut transform_ctx = TransformContext { module: &mut new_module, func_id_map: func_id_map.clone(), @@ -413,13 +421,38 @@ impl GlModule { let transformed_func = transform.transform_function(&gl_func.function, &mut transform_ctx)?; - // Use public API to add transformed function + // Get the FuncId that was assigned during declaration (step 1) + let func_id = *func_id_map.get(&name).ok_or_else(|| { + GlslError::new( + ErrorCode::E0400, + format!("Function '{name}' not found in func_id_map"), + ) + })?; + + // Update the function in place using the FuncId from declaration + // This ensures we use the same FuncId that was assigned during declaration let new_sig = transform.transform_signature(&gl_func.clif_sig); - // Determine linkage - for now, use Local (can be enhanced later) - let linkage = Linkage::Local; + + // IMPORTANT: Update the function's name to match the FuncId + // Cranelift uses the function name to match it with the FuncId during define_function + use cranelift_codegen::ir::UserFuncName; + let mut transformed_func_with_name = transformed_func; + transformed_func_with_name.name = UserFuncName::user(0, func_id.as_u32()); + // Remove the placeholder that was created during declaration new_module.fns.remove(&name); - new_module.add_function(&name, linkage, new_sig, transformed_func)?; + + // Store the transformed function with the correct FuncId + // Note: The function is already declared in the module, so we just update our internal state + new_module.fns.insert( + name.clone(), + GlFunc { + name: name.clone(), + clif_sig: new_sig, + func_id, + function: transformed_func_with_name, + }, + ); } Ok(new_module) @@ -430,7 +463,7 @@ impl GlModule { impl GlModule { /// Build executable from JIT module /// Returns a boxed GlslExecutable trait object for generic code - #[allow(unused)] + #[allow(unused, reason = "Method used via trait object")] pub fn build_executable( self, ) -> Result, GlslError> { @@ -476,7 +509,7 @@ impl GlModule { impl GlModule { /// Build executable from Object module (for emulator) /// Returns a boxed GlslExecutable trait object for generic code - #[allow(unused)] + #[allow(unused, reason = "Method used via trait object")] pub fn build_executable( self, options: &crate::backend::codegen::emu::EmulatorOptions, diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/module/test_helpers.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/module/test_helpers.rs index a989a0a83..7572f5142 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/module/test_helpers.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/module/test_helpers.rs @@ -33,7 +33,7 @@ pub mod test_helpers { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("Failed to declare function '{}': {}", name, e), + format!("Failed to declare function '{name}': {e}"), ) })?; @@ -96,10 +96,7 @@ pub mod test_helpers { .ok_or_else(|| { GlslError::new( ErrorCode::E0400, - format!( - "Function '{}' not found (must be declared first)", - callee_name - ), + format!("Function '{callee_name}' not found (must be declared first)"), ) })? .func_id; @@ -111,7 +108,7 @@ pub mod test_helpers { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("Failed to declare function '{}': {}", name, e), + format!("Failed to declare function '{name}': {e}"), ) })?; diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/target/builder.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/target/builder.rs index ff5ae4075..4d61701da 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/target/builder.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/target/builder.rs @@ -16,18 +16,19 @@ pub enum ModuleBuilder { impl Target { /// Create the appropriate Module builder for this target - /// Internal implementation details are hidden - caller doesn't care about ModuleKind + /// For Rv32Emu, this creates an ObjectBuilder (for emulator) + /// For HostJit, this creates a JITBuilder pub fn create_module_builder(&mut self) -> Result { let isa = self.create_isa()?.clone(); // Clone owned ISA for builder match self { #[cfg(feature = "emulator")] Target::Rv32Emu { .. } => { - // Internally knows: ObjectModule, riscv32 triple, etc. + // Rv32Emu creates ObjectModule for emulator execution ObjectBuilder::new(isa, b"module", default_libcall_names()) .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("ObjectBuilder creation failed: {}", e), + format!("ObjectBuilder creation failed: {e}"), ) }) .map(|b| ModuleBuilder::Object(b)) @@ -38,7 +39,7 @@ impl Target { "Emulator feature is not enabled", )), Target::HostJit { .. } => { - // Internally knows: JITModule, host triple, etc. + // HostJit creates JITModule Ok(ModuleBuilder::JIT(JITBuilder::with_isa( isa, default_libcall_names(), @@ -46,6 +47,13 @@ impl Target { } } } + + /// Create a JIT builder for this target + /// This allows Rv32Emu to create JITModule (for embedded JIT) instead of ObjectModule + pub fn create_jit_builder(&mut self) -> Result { + let isa = self.create_isa()?.clone(); + Ok(JITBuilder::with_isa(isa, default_libcall_names())) + } } #[cfg(test)] diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/target/target.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/target/target.rs index e9e974f04..7f0d62144 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/target/target.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/target/target.rs @@ -1,5 +1,7 @@ //! Semantic target enum - hides implementation details +use alloc::format; + use crate::error::{ErrorCode, GlslError}; use cranelift_codegen::ir::Type; use cranelift_codegen::isa::CallConv; @@ -19,7 +21,7 @@ pub enum Target { /// Host JIT target (runs on current machine) HostJit { /// Optional architecture override (if None, detect from host) - #[allow(unused)] + #[allow(unused, reason = "Architecture override for future use")] arch: Option, flags: Flags, /// Cached ISA (created lazily) @@ -47,7 +49,8 @@ impl Target { } /// Create host JIT with specific architecture - #[allow(unused)] + #[allow(unused, reason = "Method for architecture-specific JIT creation")] + #[cfg(feature = "std")] pub fn host_jit_with_arch(arch: Architecture) -> Result { Ok(Self::HostJit { arch: Some(arch), @@ -59,7 +62,10 @@ impl Target { /// Create or get cached ISA for this target pub fn create_isa(&mut self) -> Result<&OwnedTargetIsa, GlslError> { match self { - #[allow(unused_variables)] + #[allow( + unused_variables, + reason = "Flags may be used in future ISA configuration" + )] Target::Rv32Emu { flags, isa } => { if isa.is_none() { #[cfg(feature = "emulator")] @@ -67,7 +73,7 @@ impl Target { let triple = riscv32_triple(); use cranelift_codegen::isa::riscv32::isa_builder; *isa = Some(isa_builder(triple).finish(flags.clone()).map_err(|e| { - GlslError::new(ErrorCode::E0400, format!("ISA creation failed: {}", e)) + GlslError::new(ErrorCode::E0400, format!("ISA creation failed: {e}")) })?); } #[cfg(not(feature = "emulator"))] @@ -82,7 +88,7 @@ impl Target { } Target::HostJit { arch: _, - flags, + flags: _flags, isa, } => { if isa.is_none() { @@ -92,11 +98,11 @@ impl Target { let isa_builder = cranelift_native::builder().map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("host machine is not supported: {}", e), + format!("host machine is not supported: {e}"), ) })?; - *isa = Some(isa_builder.finish(flags.clone()).map_err(|e| { - GlslError::new(ErrorCode::E0400, format!("ISA creation failed: {}", e)) + *isa = Some(isa_builder.finish(_flags.clone()).map_err(|e| { + GlslError::new(ErrorCode::E0400, format!("ISA creation failed: {e}")) })?); } #[cfg(not(feature = "std"))] @@ -132,13 +138,13 @@ fn default_riscv32_flags() -> Result { // Enable PIC for emulator target to generate GOT-based relocations for external symbols // This matches how test_load_object_file_with_actual_builtins compiles code .set("is_pic", "true") - .map_err(|e| GlslError::new(ErrorCode::E0400, format!("failed to set is_pic: {}", e)))?; + .map_err(|e| GlslError::new(ErrorCode::E0400, format!("failed to set is_pic: {e}")))?; flag_builder .set("use_colocated_libcalls", "false") .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("failed to set use_colocated_libcalls: {}", e), + format!("failed to set use_colocated_libcalls: {e}"), ) })?; flag_builder @@ -146,7 +152,7 @@ fn default_riscv32_flags() -> Result { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("failed to set enable_multi_ret_implicit_sret: {}", e), + format!("failed to set enable_multi_ret_implicit_sret: {e}"), ) })?; @@ -160,13 +166,13 @@ fn default_host_flags() -> Result { flag_builder // Disable PIC for JIT target - cranelift-jit requires is_pic=false .set("is_pic", "false") - .map_err(|e| GlslError::new(ErrorCode::E0400, format!("failed to set is_pic: {}", e)))?; + .map_err(|e| GlslError::new(ErrorCode::E0400, format!("failed to set is_pic: {e}")))?; flag_builder .set("use_colocated_libcalls", "false") .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("failed to set use_colocated_libcalls: {}", e), + format!("failed to set use_colocated_libcalls: {e}"), ) })?; flag_builder @@ -174,7 +180,7 @@ fn default_host_flags() -> Result { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("failed to set enable_multi_ret_implicit_sret: {}", e), + format!("failed to set enable_multi_ret_implicit_sret: {e}"), ) })?; @@ -199,7 +205,7 @@ fn riscv32_triple() -> target_lexicon::Triple { } /// Helper: Convert Architecture to Triple -#[allow(unused)] +#[allow(unused, reason = "Utility function for architecture conversion")] fn triple_for_arch(arch: Architecture) -> target_lexicon::Triple { use target_lexicon::{BinaryFormat, Environment, OperatingSystem, Triple, Vendor}; @@ -214,7 +220,7 @@ fn triple_for_arch(arch: Architecture) -> target_lexicon::Triple { /// Helper: Detect host triple #[cfg(feature = "std")] -#[allow(unused)] +#[allow(unused, reason = "Utility function for host detection")] fn detect_host_triple() -> target_lexicon::Triple { target_lexicon::Triple::host() } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/arithmetic.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/arithmetic.rs index b7c4a9706..d7ee16921 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/arithmetic.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/arithmetic.rs @@ -10,6 +10,8 @@ use cranelift_codegen::ir::{Function, Inst, InstBuilder, condcodes::IntCC, types use cranelift_frontend::FunctionBuilder; use hashbrown::HashMap; +use alloc::format; + /// Convert Fadd to fixed-point addition with saturation pub(crate) fn convert_fadd( old_func: &Function, @@ -140,10 +142,7 @@ pub(crate) fn convert_fmul( let func_id = func_id_map.get(builtin_name).ok_or_else(|| { GlslError::new( crate::error::ErrorCode::E0400, - format!( - "Builtin function '{}' not found in func_id_map", - builtin_name - ), + format!("Builtin function '{builtin_name}' not found in func_id_map"), ) })?; diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/calls.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/calls.rs index 82dca75be..d3057caa4 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/calls.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/calls.rs @@ -5,7 +5,7 @@ use crate::backend::transform::fixed32::converters::{get_first_result, map_value use crate::backend::transform::fixed32::signature::convert_signature; use crate::backend::transform::fixed32::types::FixedPointFormat; use crate::error::{ErrorCode, GlslError}; -use alloc::{string::String, vec::Vec}; +use alloc::{format, string::String, vec::Vec}; use cranelift_codegen::ir::{ AbiParam, ExtFuncData, ExternalName, FuncRef, Function, Inst, InstBuilder, InstructionData, SigRef, Signature, UserExternalName, Value, types, @@ -63,8 +63,7 @@ pub(crate) fn map_external_function( GlslError::new( ErrorCode::E0400, alloc::format!( - "UserExternalNameRef {} not found in old function's user_named_funcs", - old_user_ref + "UserExternalNameRef {old_user_ref} not found in old function's user_named_funcs" ), ) })?; @@ -83,7 +82,7 @@ pub(crate) fn map_external_function( let new_func_id = func_id_map.get(func_name).ok_or_else(|| { GlslError::new( ErrorCode::E0400, - alloc::format!("Function '{}' not found in func_id_map", func_name), + alloc::format!("Function '{func_name}' not found in func_id_map"), ) })?; @@ -275,10 +274,7 @@ pub(crate) fn convert_call( let func_id = func_id_map.get(builtin_name).ok_or_else(|| { GlslError::new( ErrorCode::E0400, - format!( - "Builtin function '{}' not found in func_id_map", - builtin_name - ), + format!("Builtin function '{builtin_name}' not found in func_id_map"), ) })?; @@ -333,9 +329,32 @@ pub(crate) fn convert_call( // Handle TestCase vs User external names let new_name = match &old_ext_func.name { - ExternalName::TestCase(_) => { - // Clone TestCase name directly (like identity transform) - old_ext_func.name.clone() + ExternalName::TestCase(testcase_name) => { + // Convert TestCase name to User name for ObjectModule compatibility + // ObjectModule doesn't support TestCase names in relocations (unimplemented!) + // Extract function name from TestCase format (%name -> name) + let func_name_str = + core::str::from_utf8(testcase_name.raw()).map_err(|e| { + GlslError::new( + ErrorCode::E0400, + format!("Invalid TestCase name encoding: {e}"), + ) + })?; + // Look up the new FuncId for this function name + let new_func_id = func_id_map.get(func_name_str).ok_or_else(|| { + GlslError::new( + ErrorCode::E0400, + format!("Function '{func_name_str}' not found in func_id_map"), + ) + })?; + // Create UserExternalName with the new FuncId + let new_user_name = cranelift_codegen::ir::UserExternalName { + namespace: 0, + index: new_func_id.as_u32(), + }; + let new_user_ref = + builder.func.declare_imported_user_function(new_user_name); + ExternalName::User(new_user_ref) } ExternalName::User(old_user_ref) => { // Map User function reference to new FuncId @@ -347,8 +366,7 @@ pub(crate) fn convert_call( GlslError::new( ErrorCode::E0400, format!( - "UserExternalNameRef {} not found in old function's user_named_funcs", - old_user_ref + "UserExternalNameRef {old_user_ref} not found in old function's user_named_funcs" ), ) })?; @@ -365,7 +383,7 @@ pub(crate) fn convert_call( let new_func_id = func_id_map.get(func_name).ok_or_else(|| { GlslError::new( ErrorCode::E0400, - format!("Function '{}' not found in func_id_map", func_name), + format!("Function '{func_name}' not found in func_id_map"), ) })?; let new_user_name = UserExternalName { @@ -426,7 +444,7 @@ pub(crate) fn convert_call( } else { return Err(GlslError::new( ErrorCode::E0301, - alloc::format!("Call instruction has unexpected format: {:?}", inst_data), + alloc::format!("Call instruction has unexpected format: {inst_data:?}"), )); } @@ -491,10 +509,7 @@ pub(crate) fn convert_call_indirect( } else { return Err(GlslError::new( ErrorCode::E0301, - alloc::format!( - "CallIndirect instruction has unexpected format: {:?}", - inst_data - ), + alloc::format!("CallIndirect instruction has unexpected format: {inst_data:?}"), )); } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/comparison.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/comparison.rs index 72ab2faf9..2e3829234 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/comparison.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/comparison.rs @@ -76,7 +76,7 @@ pub(crate) fn convert_fcmp( } else { return Err(GlslError::new( crate::error::ErrorCode::E0301, - alloc::format!("Fcmp instruction has unexpected format: {:?}", inst_data), + alloc::format!("Fcmp instruction has unexpected format: {inst_data:?}"), )); } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/constants.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/constants.rs index f410f3516..c06d00f6c 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/constants.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/constants.rs @@ -24,9 +24,7 @@ pub(crate) fn convert_f32const( return Err(GlslError::new( ErrorCode::E0301, alloc::format!( - "F32const instruction {:?} has unexpected format: {:?}", - old_inst, - inst_data + "F32const instruction {old_inst:?} has unexpected format: {inst_data:?}" ), )); } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/math.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/math.rs index 2b79d7c80..7a99431e7 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/math.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/math.rs @@ -10,6 +10,8 @@ use cranelift_codegen::ir::{Function, Inst, InstBuilder, Value, types}; use cranelift_frontend::FunctionBuilder; use hashbrown::HashMap; +use alloc::format; + /// Map TestCase function name to BuiltinId and argument count. /// /// Returns None if the function name is not a math function that should be converted. @@ -56,29 +58,6 @@ pub fn map_testcase_to_builtin(testcase_name: &str) -> Option<(BuiltinId, usize) } } - - - - - - - - - - - - - - - - - - - - - - - /// Convert Ceil instruction. pub(crate) fn convert_ceil( old_func: &Function, @@ -193,10 +172,7 @@ pub(crate) fn convert_sqrt( let func_id = func_id_map.get(builtin_name).ok_or_else(|| { GlslError::new( crate::error::ErrorCode::E0400, - format!( - "Builtin function '{}' not found in func_id_map", - builtin_name - ), + format!("Builtin function '{builtin_name}' not found in func_id_map"), ) })?; diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/memory.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/memory.rs index cb14ae372..fdf917651 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/memory.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/converters/memory.rs @@ -46,7 +46,7 @@ pub(crate) fn convert_load( } else { return Err(GlslError::new( ErrorCode::E0301, - alloc::format!("Load instruction has unexpected format: {:?}", inst_data), + alloc::format!("Load instruction has unexpected format: {inst_data:?}"), )); } @@ -88,9 +88,7 @@ pub(crate) fn convert_store( return Err(GlslError::new( ErrorCode::E0301, alloc::format!( - "Store value type mismatch: expected {:?}, got {:?}", - target_type, - mapped_type + "Store value type mismatch: expected {target_type:?}, got {mapped_type:?}" ), )); } @@ -114,7 +112,7 @@ pub(crate) fn convert_store( } else { return Err(GlslError::new( ErrorCode::E0301, - alloc::format!("Store instruction has unexpected format: {:?}", inst_data), + alloc::format!("Store instruction has unexpected format: {inst_data:?}"), )); } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/div_play2_rust_based.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/div_play2_rust_based.rs index 616ffc887..20802ed1e 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/div_play2_rust_based.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/div_play2_rust_based.rs @@ -18,7 +18,7 @@ /// long division step with the dividend `duo`. /// /// Adapted from compiler-builtins/src/int/specialized_div_rem/norm_shift.rs -#[allow(dead_code)] +#[allow(dead_code, reason = "Division helper function")] fn u32_normalization_shift(duo: u32, div: u32, full_normalization: bool) -> usize { // Use leading_zeros since RISC-V has CLZ instruction (or we can use software fallback) let mut shl = (div.leading_zeros() - duo.leading_zeros()) as usize; @@ -35,7 +35,7 @@ fn u32_normalization_shift(duo: u32, div: u32, full_normalization: bool) -> usiz /// 32-bit by 32-bit division helper. /// /// This delegates to hardware division when available, or uses software division. -#[allow(dead_code)] +#[allow(dead_code, reason = "Division helper function")] fn u32_by_u32_div_rem(duo: u32, div: u32) -> (u32, u32) { // Use checked_div/checked_rem to avoid panic dependencies if let Some(quo) = duo.checked_div(div) { @@ -58,7 +58,7 @@ fn u32_by_u32_div_rem(duo: u32, div: u32) -> (u32, u32) { /// This implementation is adapted from Rust's compiler-builtins delegate algorithm /// for u64_div_rem on 32-bit targets. See: /// https://github.com/rust-lang/compiler-builtins/blob/main/compiler-builtins/src/int/specialized_div_rem/delegate.rs -#[allow(dead_code)] +#[allow(dead_code, reason = "Division helper function for 64-bit division on 32-bit targets")] pub fn divide64(dividend_hi: u32, dividend_lo: u32, divisor: u32) -> u32 { // Check that dividend_hi < divisor to avoid quotient overflow assert!(dividend_hi < divisor, "Quotient would overflow 32 bits"); diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/fixed32_test_util.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/fixed32_test_util.rs index 3c8185561..82d6613f6 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/fixed32_test_util.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/fixed32_test_util.rs @@ -1,13 +1,13 @@ use crate::backend::transform::fixed32::types::float_to_fixed16x16; /// Convert float to 16.16 fixed-point for comparison -#[allow(dead_code)] +#[allow(dead_code, reason = "Test utility function")] fn float_to_fixed32(f: f32) -> i32 { float_to_fixed16x16(f) } /// Convert fixed-point back to float -#[allow(dead_code)] +#[allow(dead_code, reason = "Test utility function")] fn fixed32_to_float(fixed: i32) -> f32 { fixed as f32 / 65536.0 } @@ -21,7 +21,7 @@ fn fixed32_to_float(fixed: i32) -> f32 { pub fn run_fixed32_test(clif_input: &str, expected_float: f32) { // Print input CLIF eprintln!("\n=== CLIF IR (INPUT) ==="); - eprintln!("{}", clif_input); + eprintln!("{clif_input}"); // Parse CLIF let test_file = @@ -44,7 +44,6 @@ pub fn run_fixed32_test(clif_input: &str, expected_float: f32) { .expect("Failed to add function to module"); } - use crate::GlslExecutable; use crate::backend::codegen::emu::EmulatorOptions; use crate::backend::module::gl_module::GlModule; use crate::backend::target::Target; @@ -57,10 +56,10 @@ pub fn run_fixed32_test(clif_input: &str, expected_float: f32) { use std::prelude::rust_2015::{String, Vec}; // Print parsed CLIF (before transformation) eprintln!("\n=== CLIF IR (BEFORE transformation) ==="); for (name, func) in &original_funcs { - eprintln!("function {}:", name); + eprintln!("function {name}:"); let mut buf = String::new(); write_function(&mut buf, func).unwrap(); - eprintln!("{}", buf); + eprintln!("{buf}"); } // Apply fixed32 transform @@ -73,10 +72,10 @@ pub fn run_fixed32_test(clif_input: &str, expected_float: f32) { eprintln!("\n=== CLIF IR (AFTER transformation) ==="); for (name, _) in &original_funcs { if let Some(gl_func) = transformed_module.get_func(name) { - eprintln!("function {}:", name); + eprintln!("function {name}:"); let mut buf = String::new(); write_function(&mut buf, &gl_func.function).unwrap(); - eprintln!("{}", buf); + eprintln!("{buf}"); } } @@ -94,18 +93,22 @@ pub fn run_fixed32_test(clif_input: &str, expected_float: f32) { // Call main function and get result eprintln!("\n=== Executing main function ==="); - let result_i32 = executable - .call_i32("main", &[]) - .expect("Failed to execute main function"); + let result_i32 = match executable.call_i32("main", &[]) { + Ok(result) => result, + Err(e) => { + // On error, output emulator state and execution log like filetests do + if let Some(ref emulator_state) = executable.format_emulator_state() { + eprintln!("{emulator_state}"); + } + panic!("Failed to execute main function: {e}"); + } + }; // Convert expected float to fixed-point let expected_fixed = float_to_fixed32(expected_float); eprintln!("\n=== Results ==="); - eprintln!( - "Expected: {} (fixed-point) = {} (float)", - expected_fixed, expected_float - ); + eprintln!("Expected: {expected_fixed} (fixed-point) = {expected_float} (float)"); eprintln!( "Got: {} (fixed-point) = {} (float)", result_i32, @@ -114,13 +117,18 @@ pub fn run_fixed32_test(clif_input: &str, expected_float: f32) { // Compare results (allow small tolerance for rounding) let tolerance = 1; // 1 fixed-point unit ≈ 0.000015 - assert!( - (result_i32 - expected_fixed).abs() <= tolerance, - "Expected fixed-point value {} (float {}), got {} (float {})\n\n\ - See debug output above for CLIF before/after transformation.", - expected_fixed, - expected_float, - result_i32, - fixed32_to_float(result_i32) - ); + if (result_i32 - expected_fixed).abs() > tolerance { + // On failure, output emulator state and execution log like filetests do + if let Some(ref emulator_state) = executable.format_emulator_state() { + eprintln!("{emulator_state}"); + } + panic!( + "Expected fixed-point value {} (float {}), got {} (float {})\n\n\ + See debug output above for CLIF before/after transformation.", + expected_fixed, + expected_float, + result_i32, + fixed32_to_float(result_i32) + ); + } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/transform.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/transform.rs index a0b24fc59..c77e3f843 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/transform.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/transform.rs @@ -161,7 +161,8 @@ block1: #[cfg(feature = "std")] #[cfg(feature = "emulator")] fn test_do_while() { - // Test do-while loop with continue - should return 1 (only first iteration adds to sum) + // Test do-while loop with continue - should return 10 + // Iterations: i=0 (sum=0), i=1 (sum=1), i=2 (sum=3, continue), i=3 (sum=6, continue), i=4 (sum=10, continue), i=5 (exit) use crate::backend::transform::shared::transform_test_util::run_int32_test; run_int32_test( r#" @@ -183,7 +184,7 @@ int main() { return test_continue_do_while_loop_after_first(); } "#, - 1, // Expected result: sum should be 1 (only first iteration adds 0+0=0, then i becomes 1, sum becomes 1, then continue skips rest) + 10, // Expected result: 0+1+2+3+4 = 10 ); } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/types.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/types.rs index d7f8f1ecf..9e11e0e91 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/types.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/fixed32/types.rs @@ -11,7 +11,7 @@ pub enum FixedPointFormat { Fixed16x16, /// 32.32 format: 32 integer bits, 32 fractional bits (uses I64) /// Note: Not yet fully implemented - #[allow(dead_code)] + #[allow(dead_code, reason = "Reserved for future fixed32x32 implementation")] Fixed32x32, } @@ -52,7 +52,10 @@ pub fn float_to_fixed16x16(f: f32) -> i32 { } /// Convert fixed16x16 back to float32 (for debugging/constants). -#[allow(dead_code)] +#[allow( + dead_code, + reason = "Utility function for debugging and constant conversion" +)] pub fn fixed16x16_to_float(fixed: i32) -> f32 { fixed as f32 / crate::frontend::codegen::constants::FIXED16X16_SCALE } @@ -62,7 +65,7 @@ pub fn fixed16x16_to_float(fixed: i32) -> f32 { /// Fixed32x32 format uses 32 integer bits and 32 fractional bits. /// Range: -2147483648.0 to +2147483647.9999999998 /// Precision: 1/4294967296 (approximately 0.00000000023) -#[allow(dead_code)] // Reserved for future use +#[allow(dead_code, reason = "Reserved for future use")] pub fn float_to_fixed32x32(f: f32) -> i64 { // Convert to f64 for more precision in intermediate calculations let f64_val = f as f64; @@ -79,7 +82,10 @@ pub fn float_to_fixed32x32(f: f32) -> i64 { } /// Convert fixed32x32 back to float32 (for debugging/constants). -#[allow(dead_code)] +#[allow( + dead_code, + reason = "Utility function for debugging and constant conversion" +)] pub fn fixed32x32_to_float(fixed: i64) -> f32 { (fixed as f64 / 4294967296.0) as f32 } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/identity/transform.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/identity/transform.rs index 41ceb9a90..4c3a56118 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/identity/transform.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/identity/transform.rs @@ -3,7 +3,8 @@ use crate::backend::transform::pipeline::{Transform, TransformContext}; use crate::backend::transform::shared::{copy_instruction, transform_function_body}; use crate::error::GlslError; -use cranelift_codegen::ir::{Function, Signature}; +use alloc::{format, vec::Vec}; +use cranelift_codegen::ir::{Function, InstBuilder, Signature}; /// Identity transform - copies functions exactly without modification pub struct IdentityTransform; @@ -16,26 +17,139 @@ impl Transform for IdentityTransform { fn transform_function( &self, old_func: &Function, - _ctx: &mut TransformContext<'_, M>, + ctx: &mut TransformContext<'_, M>, ) -> Result { // Get transformed signature let new_sig = self.transform_signature(&old_func.signature); + // Capture func_id_map and old_func_id_map for FuncId remapping + let func_id_map = ctx.func_id_map.clone(); + let old_func_id_map = ctx.old_func_id_map.clone(); + transform_function_body( old_func, new_sig, - // Instruction transformation: copy instructions exactly + // Instruction transformation: copy instructions exactly, but remap FuncIds move |old_func, old_inst, builder, value_map, stack_slot_map, block_map| { - copy_instruction( - old_func, - old_inst, - builder, - value_map, - stack_slot_map, - block_map, - None, // func_ref_map not used by copy_instruction - |t| t, // Identity type mapping - ) + // Handle Call instructions specially to remap FuncIds + use cranelift_codegen::ir::{ExtFuncData, InstructionData}; + let inst_data = &old_func.dfg.insts[old_inst]; + if let InstructionData::Call { func_ref, args, .. } = inst_data { + let old_args = args.as_slice(&old_func.dfg.value_lists); + let new_args: Vec<_> = old_args + .iter() + .map(|&v| { + value_map.get(&v).copied().ok_or_else(|| { + GlslError::new( + crate::error::ErrorCode::E0301, + format!("Value {v:?} not found in value_map"), + ) + }) + }) + .collect::, _>>()?; + + let old_ext_func = &old_func.dfg.ext_funcs[*func_ref]; + let old_sig_ref = old_ext_func.signature; + let old_sig = &old_func.dfg.signatures[old_sig_ref]; + let new_sig_ref = builder.func.import_signature(old_sig.clone()); + + // Remap FuncId for User external names, and convert TestCase to User + use cranelift_codegen::ir::ExternalName; + let new_name = match &old_ext_func.name { + ExternalName::TestCase(testcase_name) => { + // Convert TestCase name to User name for ObjectModule compatibility + // ObjectModule doesn't support TestCase names in relocations (unimplemented!) + let func_name_str = + core::str::from_utf8(testcase_name.raw()).map_err(|e| { + GlslError::new( + crate::error::ErrorCode::E0301, + format!("Invalid TestCase name encoding: {e}"), + ) + })?; + // Look up the new FuncId for this function name + let new_func_id = func_id_map.get(func_name_str).ok_or_else(|| { + GlslError::new( + crate::error::ErrorCode::E0301, + format!("Function '{func_name_str}' not found in func_id_map"), + ) + })?; + // Create UserExternalName with the new FuncId + let new_user_name = cranelift_codegen::ir::UserExternalName { + namespace: 0, + index: new_func_id.as_u32(), + }; + let new_user_ref = + builder.func.declare_imported_user_function(new_user_name); + ExternalName::User(new_user_ref) + } + ExternalName::User(old_user_ref) => { + let user_name = old_func + .params + .user_named_funcs() + .get(*old_user_ref) + .ok_or_else(|| { + GlslError::new( + crate::error::ErrorCode::E0301, + format!( + "UserExternalNameRef {old_user_ref} not found in function's user_named_funcs" + ), + ) + })?; + // Map old FuncId -> function name -> new FuncId + let old_func_id = cranelift_module::FuncId::from_u32(user_name.index); + if let Some(func_name) = old_func_id_map.get(&old_func_id) { + if let Some(new_func_id) = func_id_map.get(func_name) { + let new_user_name = cranelift_codegen::ir::UserExternalName { + namespace: user_name.namespace, + index: new_func_id.as_u32(), + }; + let new_user_ref = + builder.func.declare_imported_user_function(new_user_name); + ExternalName::User(new_user_ref) + } else { + // Fallback: use original if mapping not found + let new_user_ref = builder + .func + .declare_imported_user_function(user_name.clone()); + ExternalName::User(new_user_ref) + } + } else { + // Fallback: use original if mapping not found + let new_user_ref = builder + .func + .declare_imported_user_function(user_name.clone()); + ExternalName::User(new_user_ref) + } + } + _ => old_ext_func.name.clone(), + }; + + let new_ext_func = ExtFuncData { + name: new_name, + signature: new_sig_ref, + colocated: old_ext_func.colocated, + }; + let new_func_ref = builder.func.import_function(new_ext_func); + let call_inst = builder.ins().call(new_func_ref, &new_args); + let old_results: Vec<_> = old_func.dfg.inst_results(old_inst).to_vec(); + let new_results = builder.inst_results(call_inst); + for (old_result, new_result) in old_results.iter().zip(new_results.iter()) { + value_map.insert(*old_result, *new_result); + } + Ok(()) + } else { + // For non-call instructions, use copy_instruction + copy_instruction( + old_func, + old_inst, + builder, + value_map, + stack_slot_map, + block_map, + None, // func_ref_map not used + |t| t, // Identity type mapping + ) + } }, // Type mapping: identity (no conversion) ) diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/blocks.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/blocks.rs index 087b29f37..9dd40e907 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/blocks.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/blocks.rs @@ -124,8 +124,7 @@ pub fn map_entry_block_params( return Err(GlslError::new( ErrorCode::E0301, format!( - "Function parameter not mapped: old_param={:?}, expected new_param={:?}", - old_param, new_param + "Function parameter not mapped: old_param={old_param:?}, expected new_param={new_param:?}" ), )); } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/instruction_copy.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/instruction_copy.rs index 0e3aee0ed..216c31e70 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/instruction_copy.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/instruction_copy.rs @@ -62,8 +62,7 @@ pub fn copy_instruction( return Err(GlslError::new( crate::error::ErrorCode::E0301, alloc::format!( - "Fcmp instruction {:?} should be converted, not copied. This is an internal error.", - old_inst + "Fcmp instruction {old_inst:?} should be converted, not copied. This is an internal error." ), )); } @@ -259,8 +258,7 @@ pub fn copy_instruction( builder.ins().trapz(condition, *code); } else { panic!( - "CondTrap instruction with unexpected opcode {:?} in copy_instruction. This is an internal error - CondTrap should only be used with Trapnz or Trapz opcodes.", - opcode + "CondTrap instruction with unexpected opcode {opcode:?} in copy_instruction. This is an internal error - CondTrap should only be used with Trapnz or Trapz opcodes." ); } return Ok(()); @@ -321,12 +319,13 @@ pub fn copy_instruction( GlslError::new( ErrorCode::E0301, format!( - "UserExternalNameRef {} not found in function's user_named_funcs", - old_user_ref + "UserExternalNameRef {old_user_ref} not found in function's user_named_funcs" ), ) })?; // Declare the imported user function in the new function + // Note: FuncId remapping for transforms is handled at the transform level + // (e.g., in identity/transform.rs and fixed32/converters/calls.rs) let new_user_ref = builder .func .declare_imported_user_function(user_name.clone()); @@ -412,8 +411,7 @@ pub fn copy_instruction( let old_results: Vec = old_func.dfg.inst_results(old_inst).to_vec(); if old_results.is_empty() { panic!( - "Instruction {:?} with format {:?} has no results but was not handled in copy_instruction. This is an internal error - all side-effect-only instructions must be explicitly handled.", - opcode, inst_data + "Instruction {opcode:?} with format {inst_data:?} has no results but was not handled in copy_instruction. This is an internal error - all side-effect-only instructions must be explicitly handled." ); } @@ -571,8 +569,7 @@ pub fn copy_instruction( GlslError::new( ErrorCode::E0301, format!( - "Stack slot {:?} not found in stack_slot_map when copying instruction {:?}. This indicates a bug in function copying - all stack slots must be copied before copying instructions.", - stack_slot, opcode + "Stack slot {stack_slot:?} not found in stack_slot_map when copying instruction {opcode:?}. This indicates a bug in function copying - all stack slots must be copied before copying instructions." ), ) })? @@ -601,8 +598,7 @@ pub fn copy_instruction( GlslError::new( ErrorCode::E0301, format!( - "Stack slot {:?} not found in stack_slot_map when copying instruction {:?}. This indicates a bug in function copying - all stack slots must be copied before copying instructions.", - stack_slot, opcode + "Stack slot {stack_slot:?} not found in stack_slot_map when copying instruction {opcode:?}. This indicates a bug in function copying - all stack slots must be copied before copying instructions." ), ) })? @@ -632,15 +628,13 @@ pub fn copy_instruction( // to handle them case-by-case or use a different approach. // For now, panic with a helpful message - this should be extended as needed. panic!( - "MultiAry instruction {:?} not yet supported in copy_instruction (Return is handled separately). This is an internal error - please add support for this instruction format.", - opcode + "MultiAry instruction {opcode:?} not yet supported in copy_instruction (Return is handled separately). This is an internal error - please add support for this instruction format." ); } _ => { // For other instruction formats, panic (internal error - should never happen) panic!( - "Instruction {:?} with format {:?} not yet supported in copy_instruction. This is an internal error - all instruction formats should be handled. inst_data: {:?}", - opcode, inst_data, inst_data + "Instruction {opcode:?} with format {inst_data:?} not yet supported in copy_instruction. This is an internal error - all instruction formats should be handled. inst_data: {inst_data:?}" ); } }; diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/stack_slots.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/stack_slots.rs index 43bec6b37..eaeb2e904 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/stack_slots.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/stack_slots.rs @@ -31,9 +31,7 @@ pub fn copy_stack_slots( return Err(GlslError::new( ErrorCode::E0301, alloc::format!( - "Failed to create stack slot {:?} in new function (copied from {:?})", - new_slot_idx, - old_slot_idx + "Failed to create stack slot {new_slot_idx:?} in new function (copied from {old_slot_idx:?})" ), )); } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/transform_test_util.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/transform_test_util.rs index 918c7d188..727355074 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/transform_test_util.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/transform/shared/transform_test_util.rs @@ -1,6 +1,6 @@ use crate::backend::transform::identity::IdentityTransform; use crate::backend::transform::pipeline::Transform; -use cranelift_codegen::write_function; +use alloc::string::ToString; use cranelift_module::Linkage; use cranelift_reader::{ParseOptions, parse_test}; use std::prelude::rust_2015::{String, Vec}; @@ -25,13 +25,33 @@ fn normalize_clif(clif: &str) -> String { fn format_module( module: &crate::backend::module::gl_module::GlModule, ) -> String { + use crate::backend::util::clif_format::format_function; + use hashbrown::HashMap; + + // Build mapping from func_id string to function name for updating external references + let mut name_mapping: HashMap = HashMap::new(); + for (name, gl_func) in &module.fns { + name_mapping.insert(gl_func.func_id.as_u32().to_string(), name.clone()); + } + let mut result = String::new(); // Sort functions by name for deterministic output let mut funcs: Vec<_> = module.fns.iter().collect(); funcs.sort_by_key(|(name, _)| *name); - for (_name, gl_func) in funcs { - write_function(&mut result, &gl_func.function).unwrap(); - result.push('\n'); + for (name, gl_func) in funcs { + // Use format_function to convert User names back to TestCase names for comparison + match format_function(&gl_func.function, name, &name_mapping) { + Ok(func_text) => { + result.push_str(&func_text); + result.push('\n'); + } + Err(_) => { + // Fallback to write_function if format_function fails + use cranelift_codegen::write_function; + write_function(&mut result, &gl_func.function).unwrap(); + result.push('\n'); + } + } } result } @@ -83,10 +103,9 @@ pub fn assert_identity_transform(message: &str, clif_input: &str) { assert_eq!( normalized_parsed, normalized_transformed, - "{}\n\ - PARSED:\n{}\n\n\ - TRANSFORMED:\n{}", - message, parsed_buf, transformed_buf + "{message}\n\ + PARSED:\n{parsed_buf}\n\n\ + TRANSFORMED:\n{transformed_buf}" ); } @@ -103,10 +122,9 @@ pub fn assert_nop_fixed32_transform(message: &str, clif_input: &str) { assert_eq!( normalized_parsed, normalized_transformed, - "{}\n\ - PARSED:\n{}\n\n\ - TRANSFORMED:\n{}", - message, parsed_buf, transformed_buf + "{message}\n\ + PARSED:\n{parsed_buf}\n\n\ + TRANSFORMED:\n{transformed_buf}" ); } @@ -116,22 +134,18 @@ fn build_and_run_module( gl_module: crate::backend::module::gl_module::GlModule, transform_name: &str, ) -> i32 { - use crate::GlslExecutable; use crate::backend::codegen::emu::EmulatorOptions; use cranelift_codegen::write_function; // Print transformed CLIF - eprintln!( - "\n=== CLIF IR (AFTER {} transformation) ===", - transform_name - ); + eprintln!("\n=== CLIF IR (AFTER {transform_name} transformation) ==="); let mut funcs: Vec<_> = gl_module.fns.iter().collect(); funcs.sort_by_key(|(name, _)| *name); for (name, gl_func) in funcs { - eprintln!("function {}:", name); + eprintln!("function {name}:"); let mut buf = String::new(); write_function(&mut buf, &gl_func.function).unwrap(); - eprintln!("{}", buf); + eprintln!("{buf}"); } // Build executable @@ -141,13 +155,13 @@ fn build_and_run_module( max_instructions: 10000, }; - eprintln!("\n=== Building executable ({}) ===", transform_name); + eprintln!("\n=== Building executable ({transform_name}) ==="); let mut executable = gl_module .build_executable(&options, None, None) .expect("Failed to build executable"); // Call main function and get result - eprintln!("\n=== Executing main function ({}) ===", transform_name); + eprintln!("\n=== Executing main function ({transform_name}) ==="); executable .call_i32("main", &[]) .expect("Failed to execute main function") @@ -166,7 +180,7 @@ pub fn run_int32_test(glsl_source: &str, expected_int: i32) { // Print input GLSL eprintln!("\n=== GLSL Source (INPUT) ==="); - eprintln!("{}", glsl_source); + eprintln!("{glsl_source}"); let target = Target::riscv32_emulator().unwrap(); let mut compiler = GlslCompiler::new(); @@ -183,10 +197,10 @@ pub fn run_int32_test(glsl_source: &str, expected_int: i32) { let mut funcs: Vec<_> = raw_module.fns.iter().collect(); funcs.sort_by_key(|(name, _)| *name); for (name, gl_func) in funcs { - eprintln!("function {}:", name); + eprintln!("function {name}:"); let mut buf = String::new(); write_function(&mut buf, &gl_func.function).unwrap(); - eprintln!("{}", buf); + eprintln!("{buf}"); } // Run raw (no transform) @@ -194,7 +208,7 @@ pub fn run_int32_test(glsl_source: &str, expected_int: i32) { // Compile GLSL for identity transform eprintln!("\n=== Compiling GLSL (identity transform) ==="); - let mut identity_module = compiler + let identity_module = compiler .compile_to_gl_module_object(glsl_source, target.clone()) .expect("Failed to compile GLSL"); let identity_module = identity_module @@ -204,7 +218,7 @@ pub fn run_int32_test(glsl_source: &str, expected_int: i32) { // Compile GLSL for fixed32 transform eprintln!("\n=== Compiling GLSL (fixed32 transform) ==="); - let mut fixed32_module = compiler + let fixed32_module = compiler .compile_to_gl_module_object(glsl_source, target.clone()) .expect("Failed to compile GLSL"); let fixed32_transform = Fixed32Transform::new(FixedPointFormat::Fixed16x16); @@ -215,34 +229,29 @@ pub fn run_int32_test(glsl_source: &str, expected_int: i32) { // Verify all results match expected value eprintln!("\n=== Results ==="); - eprintln!("Expected: {}", expected_int); - eprintln!("Raw: {}", raw_result); - eprintln!("Identity: {}", identity_result); - eprintln!("Fixed32: {}", fixed32_result); + eprintln!("Expected: {expected_int}"); + eprintln!("Raw: {raw_result}"); + eprintln!("Identity: {identity_result}"); + eprintln!("Fixed32: {fixed32_result}"); assert_eq!( raw_result, expected_int, - "Raw execution failed: expected {}, got {}", - expected_int, raw_result + "Raw execution failed: expected {expected_int}, got {raw_result}" ); assert_eq!( identity_result, expected_int, - "Identity transform failed: expected {}, got {}", - expected_int, identity_result + "Identity transform failed: expected {expected_int}, got {identity_result}" ); assert_eq!( fixed32_result, expected_int, - "Fixed32 transform failed: expected {}, got {}", - expected_int, fixed32_result + "Fixed32 transform failed: expected {expected_int}, got {fixed32_result}" ); assert_eq!( raw_result, identity_result, - "Raw and identity results differ: raw={}, identity={}", - raw_result, identity_result + "Raw and identity results differ: raw={raw_result}, identity={identity_result}" ); assert_eq!( raw_result, fixed32_result, - "Raw and fixed32 results differ: raw={}, fixed32={}", - raw_result, fixed32_result + "Raw and fixed32 results differ: raw={raw_result}, fixed32={fixed32_result}" ); } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/backend/util/clif_format.rs b/lp-glsl/crates/lp-glsl-compiler/src/backend/util/clif_format.rs index 1cbdf01fe..93a693755 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/backend/util/clif_format.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/backend/util/clif_format.rs @@ -48,7 +48,7 @@ pub fn format_clif_module(module: &GlModule) -> Result, @@ -88,7 +88,7 @@ fn format_function( write_function(&mut buf, &func_clone).map_err(|e| { GlslError::new( crate::error::ErrorCode::E0400, - format!("failed to write function: {}", e), + format!("failed to write function: {e}"), ) })?; diff --git a/lp-glsl/crates/lp-glsl-compiler/src/error.rs b/lp-glsl/crates/lp-glsl-compiler/src/error.rs index 2d12a8b79..b0c674031 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/error.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/error.rs @@ -3,7 +3,10 @@ //! This module provides structured error types with source locations, //! error codes, and helpful diagnostics inspired by Rust's error reporting. -#![allow(dead_code)] // Allow during development +#![allow( + dead_code, + reason = "Allow during development - error types may not all be used yet" +)] use alloc::{format, string::String, vec::Vec}; @@ -215,7 +218,7 @@ impl fmt::Display for GlslError { // Add location if available if let Some(ref loc) = self.location { if !loc.is_unknown() { - write!(f, "\n --> {}", loc)?; + write!(f, "\n --> {loc}")?; } } @@ -223,11 +226,11 @@ impl fmt::Display for GlslError { if let Some(ref text) = self.span_text { // span_text already contains formatted lines with line numbers and carets // Add one blank line after the code snippet - write!(f, "\n{}\n", text)?; + write!(f, "\n{text}\n")?; } else if let Some(ref loc) = self.location { // If we have location but no span_text, show just the location if !loc.is_unknown() { - writeln!(f, "\n --> {}", loc)?; + writeln!(f, "\n --> {loc}")?; } } @@ -238,12 +241,12 @@ impl fmt::Display for GlslError { &self.notes[..] }; for note in notes_to_show { - write!(f, "\nnote: {}", note)?; + write!(f, "\nnote: {note}")?; } // Add spec reference if let Some(ref spec_ref) = self.spec_ref { - write!(f, "\n = spec: {}", spec_ref)?; + write!(f, "\n = spec: {spec_ref}")?; } Ok(()) @@ -259,23 +262,20 @@ impl GlslError { /// Create an undefined variable error. pub fn undefined_variable(name: impl Into) -> Self { let name = name.into(); - Self::new(ErrorCode::E0100, format!("undefined variable `{}`", name)) + Self::new(ErrorCode::E0100, format!("undefined variable `{name}`")) } /// Create an undefined function error. pub fn undefined_function(name: impl Into) -> Self { let name = name.into(); - Self::new(ErrorCode::E0101, format!("undefined function `{}`", name)) + Self::new(ErrorCode::E0101, format!("undefined function `{name}`")) } /// Create a type mismatch error. pub fn type_mismatch(expected: impl fmt::Debug, found: impl fmt::Debug) -> Self { Self::new( ErrorCode::E0102, - format!( - "type mismatch: expected `{:?}`, found `{:?}`", - expected, found - ), + format!("type mismatch: expected `{expected:?}`, found `{found:?}`"), ) } @@ -295,7 +295,7 @@ impl GlslError { let type_name = type_name.into(); Self::new( ErrorCode::E0109, - format!("type `{}` is not supported", type_name), + format!("type `{type_name}` is not supported"), ) } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/exec/emu.rs b/lp-glsl/crates/lp-glsl-compiler/src/exec/emu.rs index 999d66acd..a73ad86d3 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/exec/emu.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/exec/emu.rs @@ -45,7 +45,10 @@ pub struct GlslEmulatorModule { // Source map for managing file locations pub(crate) source_map: GlSourceMap, // Track next buffer allocation address (allocated from start of RAM, growing upward) - #[allow(dead_code)] // Reserved for future use when manual buffer allocation is needed + #[allow( + dead_code, + reason = "Reserved for future use when manual buffer allocation is needed" + )] pub(crate) next_buffer_addr: u32, } @@ -77,7 +80,7 @@ impl GlslEmulatorModule { self.function_addresses.get(name).copied().ok_or_else(|| { GlslError::new( ErrorCode::E0101, - format!("Function '{}' not found in object file", name), + format!("Function '{name}' not found in object file"), ) }) } @@ -91,7 +94,7 @@ impl GlslEmulatorModule { self.cranelift_signatures.get(name).ok_or_else(|| { GlslError::new( ErrorCode::E0101, - format!("Function signature for '{}' not found", name), + format!("Function signature for '{name}' not found"), ) }) } @@ -122,7 +125,7 @@ impl GlslEmulatorModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?}, got I32", param_ty), + format!("Type mismatch: expected {param_ty:?}, got I32"), )); } } @@ -144,7 +147,7 @@ impl GlslEmulatorModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?}, got F32", param_ty), + format!("Type mismatch: expected {param_ty:?}, got F32"), )); } } @@ -158,7 +161,7 @@ impl GlslEmulatorModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?}, got Bool", param_ty), + format!("Type mismatch: expected {param_ty:?}, got Bool"), )); } } @@ -178,14 +181,17 @@ impl GlslEmulatorModule { } types::I32 => { // Convert f32 to fixed-point i32 - let fixed = - (*component * crate::frontend::codegen::constants::FIXED16X16_SCALE) as i32; + let fixed = (*component + * crate::frontend::codegen::constants::FIXED16X16_SCALE) + as i32; args.push(DataValue::I32(fixed)); } _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for vec2 component, got F32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for vec2 component, got F32" + ), )); } } @@ -206,14 +212,17 @@ impl GlslEmulatorModule { } types::I32 => { // Convert f32 to fixed-point i32 - let fixed = - (*component * crate::frontend::codegen::constants::FIXED16X16_SCALE) as i32; + let fixed = (*component + * crate::frontend::codegen::constants::FIXED16X16_SCALE) + as i32; args.push(DataValue::I32(fixed)); } _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for vec3 component, got F32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for vec3 component, got F32" + ), )); } } @@ -234,14 +243,17 @@ impl GlslEmulatorModule { } types::I32 => { // Convert f32 to fixed-point i32 - let fixed = - (*component * crate::frontend::codegen::constants::FIXED16X16_SCALE) as i32; + let fixed = (*component + * crate::frontend::codegen::constants::FIXED16X16_SCALE) + as i32; args.push(DataValue::I32(fixed)); } _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for vec4 component, got F32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for vec4 component, got F32" + ), )); } } @@ -261,7 +273,9 @@ impl GlslEmulatorModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for ivec2 component, got I32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for ivec2 component, got I32" + ), )); } } @@ -281,7 +295,9 @@ impl GlslEmulatorModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for ivec3 component, got I32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for ivec3 component, got I32" + ), )); } } @@ -301,7 +317,9 @@ impl GlslEmulatorModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for ivec4 component, got I32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for ivec4 component, got I32" + ), )); } } @@ -321,7 +339,9 @@ impl GlslEmulatorModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for uvec2 component, got U32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for uvec2 component, got U32" + ), )); } } @@ -341,7 +361,9 @@ impl GlslEmulatorModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for uvec3 component, got U32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for uvec3 component, got U32" + ), )); } } @@ -361,7 +383,9 @@ impl GlslEmulatorModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for uvec4 component, got U32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for uvec4 component, got U32" + ), )); } } @@ -381,7 +405,9 @@ impl GlslEmulatorModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for bvec2 component, got Bool", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for bvec2 component, got Bool" + ), )); } } @@ -401,7 +427,9 @@ impl GlslEmulatorModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for bvec3 component, got Bool", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for bvec3 component, got Bool" + ), )); } } @@ -421,7 +449,9 @@ impl GlslEmulatorModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for bvec4 component, got Bool", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for bvec4 component, got Bool" + ), )); } } @@ -449,7 +479,10 @@ impl GlslEmulatorModule { /// Allocate a buffer in the emulator's RAM and return its address. /// Buffers are allocated from the start of RAM (growing upward), leaving space /// for the stack at the end (growing downward). - #[allow(dead_code)] // Reserved for future use when manual buffer allocation is needed + #[allow( + dead_code, + reason = "Reserved for future use when manual buffer allocation is needed" + )] fn allocate_buffer_in_ram(&mut self, size: usize) -> Result { // DEFAULT_RAM_START is 0x80000000 (from lp-riscv-tools/src/emu/memory.rs) const DEFAULT_RAM_START: u32 = 0x80000000; @@ -473,8 +506,7 @@ impl GlslEmulatorModule { return Err(GlslError::new( crate::error::ErrorCode::E0400, format!( - "Buffer allocation would exceed RAM size (need {} bytes at addr 0x{:x}, have {} bytes RAM with {} bytes reserved for stack)", - aligned_size, buffer_addr, current_len, STACK_RESERVE + "Buffer allocation would exceed RAM size (need {aligned_size} bytes at addr 0x{buffer_addr:x}, have {current_len} bytes RAM with {STACK_RESERVE} bytes reserved for stack)" ), )); } @@ -488,7 +520,7 @@ impl GlslEmulatorModule { .map_err(|e| { GlslError::new( crate::error::ErrorCode::E0400, - format!("Failed to initialize buffer at 0x{:x}: {:?}", addr, e), + format!("Failed to initialize buffer at 0x{addr:x}: {e:?}"), ) })?; } @@ -511,38 +543,36 @@ impl GlslEmulatorModule { let full_message = if function_name.is_empty() { base_message.to_string() } else { - format!("{} (function: {})", base_message, function_name) + format!("{base_message} (function: {function_name})") }; let mut error = GlslError::new(code, full_message); // Add CLIF IR if available (both before and after transformation) if let Some(ref original_clif) = self.original_clif { error = error.with_note(format!( - "=== CLIF IR (BEFORE transformation) ===\n{}", - original_clif + "=== CLIF IR (BEFORE transformation) ===\n{original_clif}" )); } if let Some(ref transformed_clif) = self.transformed_clif { error = error.with_note(format!( - "=== CLIF IR (AFTER transformation) ===\n{}", - transformed_clif + "=== CLIF IR (AFTER transformation) ===\n{transformed_clif}" )); } // Add VCode and disassembly if available (from compilation) if let Some(ref vcode) = self.vcode { - error = error.with_note(format!("=== VCode ===\n{}", vcode)); + error = error.with_note(format!("=== VCode ===\n{vcode}")); } if let Some(ref disassembly) = self.disassembly { - error = error.with_note(format!("=== Disassembled ===\n{}", disassembly)); + error = error.with_note(format!("=== Disassembled ===\n{disassembly}")); } // Fall back to runtime disassembly if stored disassembly not available if self.vcode.is_none() && self.disassembly.is_none() { if let Ok(disasm) = self.disassemble_binary() { - error = error.with_note(format!("=== Assembly Disassembly ===\n{}", disasm)); + error = error.with_note(format!("=== Assembly Disassembly ===\n{disasm}")); } } @@ -572,7 +602,7 @@ impl GlslEmulatorModule { for (idx, line) in source_lines.iter().enumerate() { let line_num = start_line + idx; if line_num == trap_line { - source_display.push_str(&format!("{:>3} | {}\n", line_num, line)); + source_display.push_str(&format!("{line_num:>3} | {line}\n")); // Bound check col_pos to prevent excessive string allocation let col_pos = trap_column.saturating_sub(1).min(line.len()).min(200); source_display.push_str(&format!( @@ -580,7 +610,7 @@ impl GlslEmulatorModule { " ".repeat(col_pos) )); } else { - source_display.push_str(&format!("{:>3} | {}\n", line_num, line)); + source_display.push_str(&format!("{line_num:>3} | {line}\n")); } } Some(String::from(source_display.trim_end())) @@ -619,10 +649,7 @@ impl GlslEmulatorModule { let trap_name = trap_code_to_string(trap_code); - let mut error = GlslError::new( - ErrorCode::E0400, - format!("execution trapped: {}", trap_name), - ); + let mut error = GlslError::new(ErrorCode::E0400, format!("execution trapped: {trap_name}")); // Try to find source location from SourceLoc // First, try the exact srcloc from trap_info @@ -680,46 +707,44 @@ impl GlslEmulatorModule { } // Add trap details as notes - error = error.with_note(format!("Trap occurred at PC 0x{:08x}", pc)); + error = error.with_note(format!("Trap occurred at PC 0x{pc:08x}")); if !func_name.is_empty() { - error = error.with_note(format!("Function: {}", func_name)); + error = error.with_note(format!("Function: {func_name}")); } // Add CLIF IR (before and after transformation) if available if let Some(ref original_clif) = self.original_clif { error = error.with_note(format!( - "=== CLIF IR (BEFORE transformation) ===\n{}", - original_clif + "=== CLIF IR (BEFORE transformation) ===\n{original_clif}" )); } if let Some(ref transformed_clif) = self.transformed_clif { error = error.with_note(format!( - "=== CLIF IR (AFTER transformation) ===\n{}", - transformed_clif + "=== CLIF IR (AFTER transformation) ===\n{transformed_clif}" )); } // Add VCode and disassembly if available if let Some(ref vcode) = self.vcode { - error = error.with_note(format!("VCode:\n{}", vcode)); + error = error.with_note(format!("VCode:\n{vcode}")); } if let Some(ref disassembly) = self.disassembly { - error = error.with_note(format!("Disassembled:\n{}", disassembly)); + error = error.with_note(format!("Disassembled:\n{disassembly}")); } error } /// Safely format a function, avoiding panics from Display - #[allow(dead_code)] // Reserved for future use in error reporting + #[allow(dead_code, reason = "Reserved for future use in error reporting")] fn format_function_safely(&self, func: &cranelift_codegen::ir::Function) -> String { #[cfg(feature = "std")] { use std::panic; // Try full formatting first - match panic::catch_unwind(panic::AssertUnwindSafe(|| format!("{}", func))) { + match panic::catch_unwind(panic::AssertUnwindSafe(|| format!("{func}"))) { Ok(s) => return s, Err(_) => { // Fall through to manual formatting @@ -750,7 +775,7 @@ impl GlslEmulatorModule { // Add blocks and instructions result.push_str("\n"); for block in func.layout.blocks() { - result.push_str(&format!("\n{}", block)); + result.push_str(&format!("\n{block}")); // Block parameters let params = func.dfg.block_params(block); @@ -798,7 +823,7 @@ impl GlslEmulatorModule { if i > 0 { result.push_str(", "); } - result.push_str(&format!("v{}", res)); + result.push_str(&format!("v{res}")); } } } @@ -839,8 +864,7 @@ impl GlslEmulatorModule { if zero_count > MAX_ZERO_RUN { // Summarize long zero runs disasm.push_str(&format!( - " {:08x}: ... ({} zero words skipped)\n", - zero_start, zero_count + " {zero_start:08x}: ... ({zero_count} zero words skipped)\n" )); } else { // Show short zero runs @@ -859,8 +883,7 @@ impl GlslEmulatorModule { // Use proper disassembly formatting let inst_str = lp_riscv_tools::format_instruction(instruction); disasm.push_str(&format!( - " {:08x}: {:08x} {}\n", - offset, instruction, inst_str + " {offset:08x}: {instruction:08x} {inst_str}\n" )); } @@ -872,8 +895,7 @@ impl GlslEmulatorModule { let zero_count = (offset - zero_start) / 4; if zero_count > MAX_ZERO_RUN { disasm.push_str(&format!( - " {:08x}: ... ({} zero words skipped)\n", - zero_start, zero_count + " {zero_start:08x}: ... ({zero_count} zero words skipped)\n" )); } else { for i in 0..zero_count { @@ -899,7 +921,7 @@ impl GlslEmulatorModule { } /// Find the source location (line number) of a function definition in the GLSL source - #[allow(dead_code)] // Reserved for future use in error reporting + #[allow(dead_code, reason = "Reserved for future use in error reporting")] fn find_function_source_location( &self, func_name: &str, @@ -911,9 +933,9 @@ impl GlslEmulatorModule { // Search for function definition: "type func_name(" or "func_name(" // This is a simple heuristic - we look for the function name followed by ( let pattern = if func_name == "main" { - format!("{}()", func_name) + format!("{func_name}()") } else { - format!("{}(", func_name) + format!("{func_name}(") }; // Find the first occurrence of the pattern @@ -932,7 +954,7 @@ impl GlslEmulatorModule { } /// Extract source lines around a given line number for display - #[allow(dead_code)] // Reserved for future use in error reporting + #[allow(dead_code, reason = "Reserved for future use in error reporting")] fn extract_source_lines( &self, file_id: crate::frontend::src_loc::GlFileId, @@ -1004,7 +1026,7 @@ impl GlslExecutable for GlslEmulatorModule { } other => self.build_enhanced_error( ErrorCode::E0400, - &format!("Emulator execution failed: {}", other), + &format!("Emulator execution failed: {other}"), name, ), })?; @@ -1048,13 +1070,26 @@ impl GlslExecutable for GlslEmulatorModule { .call_function(func_address, &data_args, &sig) .map_err(|e| match e { EmulatorError::Trap { code, pc, regs } => { + crate::debug!( + "Emulator trap calling '{}':\n{}", + name, + self.emulator.dump_state() + ); self.format_trap_error_from_emulator_error(code, pc, ®s, name) } - other => self.build_enhanced_error( - ErrorCode::E0400, - &format!("Emulator execution failed: {}", other), - name, - ), + other => { + crate::debug!( + "Emulator error calling '{}': {}\n{}", + name, + other, + self.emulator.dump_state() + ); + self.build_enhanced_error( + ErrorCode::E0400, + &format!("Emulator execution failed: {other}"), + name, + ) + } })?; // Extract i32 return value @@ -1062,7 +1097,7 @@ impl GlslExecutable for GlslEmulatorModule { Some(cranelift_codegen::data_value::DataValue::I32(v)) => Ok(*v), _ => Err(GlslError::new( ErrorCode::E0400, - "Expected i32 return value", + format!("Expected i32 return value, got: {results:?}"), )), } } @@ -1107,7 +1142,7 @@ impl GlslExecutable for GlslEmulatorModule { } other => self.build_enhanced_error( ErrorCode::E0400, - &format!("Emulator execution failed: {}", other), + &format!("Emulator execution failed: {other}"), name, ), })?; @@ -1163,7 +1198,7 @@ impl GlslExecutable for GlslEmulatorModule { .map_err(|e| { self.build_enhanced_error( ErrorCode::E0400, - &format!("Emulator execution failed: {}", e), + &format!("Emulator execution failed: {e}"), name, ) })?; @@ -1242,7 +1277,7 @@ impl GlslExecutable for GlslEmulatorModule { } other => self.build_enhanced_error( ErrorCode::E0400, - &format!("Emulator execution failed: {}", other), + &format!("Emulator execution failed: {other}"), name, ), })?; @@ -1275,7 +1310,7 @@ impl GlslExecutable for GlslEmulatorModule { .map_err(|e| { self.build_enhanced_error( ErrorCode::E0400, - &format!("Emulator execution failed: {}", e), + &format!("Emulator execution failed: {e}"), name, ) })?; @@ -1366,7 +1401,7 @@ impl GlslExecutable for GlslEmulatorModule { } other => self.build_enhanced_error( ErrorCode::E0400, - &format!("Emulator execution failed: {}", other), + &format!("Emulator execution failed: {other}"), name, ), })?; @@ -1395,7 +1430,7 @@ impl GlslExecutable for GlslEmulatorModule { .map_err(|e| { self.build_enhanced_error( ErrorCode::E0400, - &format!("Emulator execution failed: {}", e), + &format!("Emulator execution failed: {e}"), name, ) })?; @@ -1486,7 +1521,7 @@ impl GlslExecutable for GlslEmulatorModule { } other => self.build_enhanced_error( ErrorCode::E0400, - &format!("Emulator execution failed: {}", other), + &format!("Emulator execution failed: {other}"), name, ), })?; @@ -1515,7 +1550,7 @@ impl GlslExecutable for GlslEmulatorModule { .map_err(|e| { self.build_enhanced_error( ErrorCode::E0400, - &format!("Emulator execution failed: {}", e), + &format!("Emulator execution failed: {e}"), name, ) })?; @@ -1606,7 +1641,7 @@ impl GlslExecutable for GlslEmulatorModule { } other => self.build_enhanced_error( ErrorCode::E0400, - &format!("Emulator execution failed: {}", other), + &format!("Emulator execution failed: {other}"), name, ), })?; @@ -1637,7 +1672,7 @@ impl GlslExecutable for GlslEmulatorModule { .map_err(|e| { self.build_enhanced_error( ErrorCode::E0400, - &format!("Emulator execution failed: {}", e), + &format!("Emulator execution failed: {e}"), name, ) })?; @@ -1722,7 +1757,7 @@ impl GlslExecutable for GlslEmulatorModule { } other => self.build_enhanced_error( ErrorCode::E0400, - &format!("Emulator execution failed: {}", other), + &format!("Emulator execution failed: {other}"), name, ), })?; @@ -1753,7 +1788,7 @@ impl GlslExecutable for GlslEmulatorModule { .map_err(|e| { self.build_enhanced_error( ErrorCode::E0400, - &format!("Emulator execution failed: {}", e), + &format!("Emulator execution failed: {e}"), name, ) })?; @@ -1788,14 +1823,15 @@ impl GlslExecutable for GlslEmulatorModule { #[cfg(feature = "std")] fn format_emulator_state(&self) -> Option { let state_dump = self.emulator.dump_state(); - let debug_info = self.emulator.format_debug_info(None, 100); + let current_pc = self.emulator.get_pc(); + // Always show disassembly around current PC (like filetests do) + let debug_info = self.emulator.format_debug_info(Some(current_pc), 100); // Only include debug info section if there's actual content if debug_info.is_empty() { - Some(format!("\n=== Emulator State ===\n{}", state_dump)) + Some(format!("\n=== Emulator State ===\n{state_dump}")) } else { Some(format!( - "\n=== Emulator State ===\n{}\n\n=== Debug Info ===\n{}", - state_dump, debug_info + "\n=== Emulator State ===\n{state_dump}\n\n=== Debug Info ===\n{debug_info}" )) } } @@ -1889,9 +1925,7 @@ mod tests { // Allow some tolerance for fixed-point conversion assert!( (result_fixed - expected_fixed).abs() < 10, - "Expected fixed-point value around {}, got {}", - expected_fixed, - result_fixed + "Expected fixed-point value around {expected_fixed}, got {result_fixed}" ); // Verify it's approximately correct as float @@ -1917,9 +1951,7 @@ mod tests { let result_float = fixed32_to_float(float_to_fixed32(result)); assert!( (result_float - expected).abs() < 0.0001, - "Expected ~{}, got {}", - expected, - result_float + "Expected ~{expected}, got {result_float}" ); } @@ -1942,9 +1974,7 @@ mod tests { let result_float = fixed32_to_float(float_to_fixed32(result)); assert!( (result_float - expected).abs() < 0.001, - "Expected ~{}, got {}", - expected, - result_float + "Expected ~{expected}, got {result_float}" ); } @@ -1971,9 +2001,7 @@ mod tests { let result_float = fixed32_to_float(float_to_fixed32(result)); assert!( (result_float - expected).abs() < 0.001, - "Expected ~{}, got {}", - expected, - result_float + "Expected ~{expected}, got {result_float}" ); } @@ -1995,9 +2023,7 @@ mod tests { let result_float = fixed32_to_float(float_to_fixed32(result)); assert!( (result_float - expected).abs() < 0.01, - "Expected sqrt(4.0) ≈ {}, got {}", - expected, - result_float + "Expected sqrt(4.0) ≈ {expected}, got {result_float}" ); } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/exec/executable.rs b/lp-glsl/crates/lp-glsl-compiler/src/exec/executable.rs index 2aac1e105..58b50da2f 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/exec/executable.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/exec/executable.rs @@ -230,5 +230,5 @@ impl GlslOptions { // Re-export module types for convenience // Note: GlslEmulatorModule is conditionally compiled and may not be used in all builds #[cfg(feature = "emulator")] -#[allow(unused_imports)] +#[allow(unused_imports, reason = "Conditionally compiled re-export")] pub use crate::exec::emu::GlslEmulatorModule; diff --git a/lp-glsl/crates/lp-glsl-compiler/src/exec/execute_fn.rs b/lp-glsl/crates/lp-glsl-compiler/src/exec/execute_fn.rs index a204fd3d1..8c6cc2684 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/exec/execute_fn.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/exec/execute_fn.rs @@ -21,17 +21,17 @@ pub fn execute_function( // Try to get the signature to determine return type let sig = executable .get_function_signature(name) - .ok_or_else(|| anyhow::anyhow!("function '{}' not found", name))?; + .ok_or_else(|| anyhow::anyhow!("function '{name}' not found"))?; // Helper to add emulator state to error if available fn format_error(e: crate::error::GlslError, executable: &dyn GlslExecutable) -> anyhow::Error { // Use {:#} format to preserve location and span_text formatting // Notes are already included in the Display implementation - let error_msg = format!("{:#}", e); + let error_msg = format!("{e:#}"); if let Some(state) = executable.format_emulator_state() { - anyhow::anyhow!("{}{}", error_msg, state) + anyhow::anyhow!("{error_msg}{state}") } else { - anyhow::anyhow!("{}", error_msg) + anyhow::anyhow!("{error_msg}") } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/exec/glsl_value.rs b/lp-glsl/crates/lp-glsl-compiler/src/exec/glsl_value.rs index 9fdca91bd..5e28e6dd2 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/exec/glsl_value.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/exec/glsl_value.rs @@ -5,6 +5,13 @@ use glsl::syntax::{Expr, JumpStatement, SimpleStatement, Statement}; use alloc::{format, vec::Vec}; +/// Truncate f32 toward zero (no_std compatible) +/// Casts to i32 which truncates toward zero, then to i64 +#[inline] +fn trunc_f32(f: f32) -> i64 { + (f as i32) as i64 +} + /// GLSL value types for function arguments /// /// ## Matrix Storage Format @@ -55,25 +62,25 @@ impl GlslValue { // Wrap the literal in a minimal function to parse it // We'll try different return types to determine the literal type let wrappers = [ - format!("int main() {{ return {}; }}", literal_str), - format!("uint main() {{ return {}; }}", literal_str), - format!("float main() {{ return {}; }}", literal_str), - format!("bool main() {{ return {}; }}", literal_str), - format!("vec2 main() {{ return {}; }}", literal_str), - format!("vec3 main() {{ return {}; }}", literal_str), - format!("vec4 main() {{ return {}; }}", literal_str), - format!("ivec2 main() {{ return {}; }}", literal_str), - format!("ivec3 main() {{ return {}; }}", literal_str), - format!("ivec4 main() {{ return {}; }}", literal_str), - format!("uvec2 main() {{ return {}; }}", literal_str), - format!("uvec3 main() {{ return {}; }}", literal_str), - format!("uvec4 main() {{ return {}; }}", literal_str), - format!("bvec2 main() {{ return {}; }}", literal_str), - format!("bvec3 main() {{ return {}; }}", literal_str), - format!("bvec4 main() {{ return {}; }}", literal_str), - format!("mat2 main() {{ return {}; }}", literal_str), - format!("mat3 main() {{ return {}; }}", literal_str), - format!("mat4 main() {{ return {}; }}", literal_str), + format!("int main() {{ return {literal_str}; }}"), + format!("uint main() {{ return {literal_str}; }}"), + format!("float main() {{ return {literal_str}; }}"), + format!("bool main() {{ return {literal_str}; }}"), + format!("vec2 main() {{ return {literal_str}; }}"), + format!("vec3 main() {{ return {literal_str}; }}"), + format!("vec4 main() {{ return {literal_str}; }}"), + format!("ivec2 main() {{ return {literal_str}; }}"), + format!("ivec3 main() {{ return {literal_str}; }}"), + format!("ivec4 main() {{ return {literal_str}; }}"), + format!("uvec2 main() {{ return {literal_str}; }}"), + format!("uvec3 main() {{ return {literal_str}; }}"), + format!("uvec4 main() {{ return {literal_str}; }}"), + format!("bvec2 main() {{ return {literal_str}; }}"), + format!("bvec3 main() {{ return {literal_str}; }}"), + format!("bvec4 main() {{ return {literal_str}; }}"), + format!("mat2 main() {{ return {literal_str}; }}"), + format!("mat3 main() {{ return {literal_str}; }}"), + format!("mat4 main() {{ return {literal_str}; }}"), ]; for wrapper in &wrappers { @@ -227,8 +234,7 @@ impl GlslValue { Err(GlslError::new( ErrorCode::E0400, format!( - "invalid literal: `{}` (must be an integer, float, boolean, vector, or matrix literal)", - literal_str + "invalid literal: `{literal_str}` (must be an integer, float, boolean, vector, or matrix literal)" ), )) } @@ -515,7 +521,7 @@ fn parse_int_vector_constructor(args: &[Expr], dim: usize) -> Result, G } Expr::FloatConst(f, _) => { // Convert float to int: truncate towards zero, clamp to i32 range - let truncated = f.trunc() as i64; + let truncated = trunc_f32(*f); let clamped = if truncated < i32::MIN as i64 { i32::MIN } else if truncated > i32::MAX as i64 { @@ -570,7 +576,7 @@ fn parse_int_vector_constructor(args: &[Expr], dim: usize) -> Result, G let nested = parse_vector_constructor(args, nested_dim)?; // Convert float components to int for val in nested { - let truncated = val.trunc() as i64; + let truncated = trunc_f32(val); let clamped = if truncated < i32::MIN as i64 { i32::MIN } else if truncated > i32::MAX as i64 { @@ -607,7 +613,7 @@ fn parse_int_vector_constructor(args: &[Expr], dim: usize) -> Result, G } Expr::FloatConst(f, _) => { // Convert negative float to int - let truncated = (-f).trunc() as i64; + let truncated = trunc_f32(-f); let clamped = if truncated < i32::MIN as i64 { i32::MIN } else if truncated > i32::MAX as i64 { @@ -669,7 +675,7 @@ fn parse_uint_vector_constructor(args: &[Expr], dim: usize) -> Result, Expr::FloatConst(f, _) => { // Convert float to uint: truncate towards zero, then cast to unsigned // Negative values wrap around (e.g., -2.7 -> -2 -> 4294967294u) - let truncated = f.trunc() as i32; + let truncated = trunc_f32(*f) as i32; let as_uint = truncated as u32; components.push(as_uint); } @@ -701,7 +707,7 @@ fn parse_uint_vector_constructor(args: &[Expr], dim: usize) -> Result, let nested = parse_vector_constructor(args, nested_dim)?; // Convert float/int components to uint for val in nested { - let truncated = val.trunc() as i64; + let truncated = trunc_f32(val); let clamped = if truncated < 0 { 0 } else if truncated > u32::MAX as i64 { @@ -741,7 +747,7 @@ fn parse_uint_vector_constructor(args: &[Expr], dim: usize) -> Result, } Expr::FloatConst(f, _) => { // Convert negative float to uint: -5.7 → 4294967291 (wrapping) - let truncated = (-f).trunc() as i64; + let truncated = trunc_f32(-f); let wrapped = if truncated >= 0 { (truncated as u32).wrapping_neg() } else { @@ -826,8 +832,7 @@ fn parse_matrix_constructor(args: &[Expr], dim: usize) -> Result<[[f32; 4]; 4], return Err(GlslError::new( ErrorCode::E0400, format!( - "matrix column vector dimension mismatch: expected vec{}, got {}", - dim, vec_dim + "matrix column vector dimension mismatch: expected vec{dim}, got {vec_dim}" ), )); } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/exec/jit.rs b/lp-glsl/crates/lp-glsl-compiler/src/exec/jit.rs index 8623eb7de..56f0667a3 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/exec/jit.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/exec/jit.rs @@ -10,12 +10,12 @@ use cranelift_codegen::ir::types; use hashbrown::HashMap; use lp_jit_util::{call_structreturn, call_structreturn_with_args}; -use alloc::{format, string::String, vec::Vec}; +use alloc::{format, string::String, vec, vec::Vec}; /// JIT-compiled GLSL module (executes on host or embedded) /// Works in both std and no_std (JITModule supports no_std) pub struct GlslJitModule { - #[allow(dead_code)] + #[allow(dead_code, reason = "JIT module stored for future use")] pub(crate) jit_module: cranelift_jit::JITModule, pub(crate) function_ptrs: HashMap, pub(crate) signatures: HashMap, @@ -26,19 +26,27 @@ pub struct GlslJitModule { } impl GlslJitModule { + /// Get function pointer by name + pub fn get_function_ptr(&self, name: &str) -> Result<*const u8, GlslError> { + use crate::error::ErrorCode; + self.function_ptrs + .get(name) + .copied() + .ok_or_else(|| GlslError::new(ErrorCode::E0101, format!("Function '{name}' not found"))) + } + /// Validate that only "main" function is being called fn validate_main_only(&self, name: &str) -> Result<(), GlslError> { use crate::error::ErrorCode; if name != "main" { return Err(GlslError::new( ErrorCode::E0400, - format!("Only 'main' function is supported, got '{}'", name), + format!("Only 'main' function is supported, got '{name}'"), )); } Ok(()) } - // Helper to convert GlslValue to calling convention arguments for JIT // Vectors are expanded into multiple scalar arguments matching the calling convention fn glsl_value_to_jit_args( @@ -71,7 +79,7 @@ impl GlslJitModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?}, got I32", param_ty), + format!("Type mismatch: expected {param_ty:?}, got I32"), )); } } @@ -93,13 +101,14 @@ impl GlslJitModule { types::F32 => args.push(v.to_bits() as u64), types::I32 => { // Convert f32 to fixed-point i32 (Q16.16 format) - let fixed = (*v * crate::frontend::codegen::constants::FIXED16X16_SCALE) as i32; + let fixed = + (*v * crate::frontend::codegen::constants::FIXED16X16_SCALE) as i32; args.push(fixed as u64); } _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?}, got F32", param_ty), + format!("Type mismatch: expected {param_ty:?}, got F32"), )); } } @@ -123,7 +132,7 @@ impl GlslJitModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?}, got Bool", param_ty), + format!("Type mismatch: expected {param_ty:?}, got Bool"), )); } } @@ -147,13 +156,17 @@ impl GlslJitModule { types::F32 => args.push(component.to_bits() as u64), types::I32 => { // Convert f32 to fixed-point i32 (Q16.16 format) - let fixed = (*component * crate::frontend::codegen::constants::FIXED16X16_SCALE) as i32; + let fixed = (*component + * crate::frontend::codegen::constants::FIXED16X16_SCALE) + as i32; args.push(fixed as u64); } _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for vec2 component, got F32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for vec2 component, got F32" + ), )); } } @@ -178,13 +191,17 @@ impl GlslJitModule { types::F32 => args.push(component.to_bits() as u64), types::I32 => { // Convert f32 to fixed-point i32 (Q16.16 format) - let fixed = (*component * crate::frontend::codegen::constants::FIXED16X16_SCALE) as i32; + let fixed = (*component + * crate::frontend::codegen::constants::FIXED16X16_SCALE) + as i32; args.push(fixed as u64); } _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for vec3 component, got F32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for vec3 component, got F32" + ), )); } } @@ -209,13 +226,17 @@ impl GlslJitModule { types::F32 => args.push(component.to_bits() as u64), types::I32 => { // Convert f32 to fixed-point i32 (Q16.16 format) - let fixed = (*component * crate::frontend::codegen::constants::FIXED16X16_SCALE) as i32; + let fixed = (*component + * crate::frontend::codegen::constants::FIXED16X16_SCALE) + as i32; args.push(fixed as u64); } _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for vec4 component, got F32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for vec4 component, got F32" + ), )); } } @@ -242,7 +263,9 @@ impl GlslJitModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for ivec2 component, got I32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for ivec2 component, got I32" + ), )); } } @@ -269,7 +292,9 @@ impl GlslJitModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for ivec3 component, got I32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for ivec3 component, got I32" + ), )); } } @@ -296,7 +321,9 @@ impl GlslJitModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for ivec4 component, got I32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for ivec4 component, got I32" + ), )); } } @@ -323,7 +350,9 @@ impl GlslJitModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for uvec2 component, got U32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for uvec2 component, got U32" + ), )); } } @@ -350,7 +379,9 @@ impl GlslJitModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for uvec3 component, got U32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for uvec3 component, got U32" + ), )); } } @@ -377,7 +408,9 @@ impl GlslJitModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for uvec4 component, got U32", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for uvec4 component, got U32" + ), )); } } @@ -404,7 +437,9 @@ impl GlslJitModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for bvec2 component, got Bool", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for bvec2 component, got Bool" + ), )); } } @@ -431,7 +466,9 @@ impl GlslJitModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for bvec3 component, got Bool", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for bvec3 component, got Bool" + ), )); } } @@ -458,7 +495,9 @@ impl GlslJitModule { _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("Type mismatch: expected {:?} for bvec4 component, got Bool", param_ty), + format!( + "Type mismatch: expected {param_ty:?} for bvec4 component, got Bool" + ), )); } } @@ -491,14 +530,14 @@ impl GlslExecutable for GlslJitModule { self.validate_main_only(name)?; let func_ptr = self.function_ptrs.get(name).ok_or_else(|| { - GlslError::new(ErrorCode::E0101, format!("Function '{}' not found", name)) + GlslError::new(ErrorCode::E0101, format!("Function '{name}' not found")) })?; // Get the actual Cranelift signature for this function let sig = self.cranelift_signatures.get(name).ok_or_else(|| { GlslError::new( ErrorCode::E0101, - format!("Function signature for '{}' not found", name), + format!("Function signature for '{name}' not found"), ) })?; @@ -560,14 +599,14 @@ impl GlslExecutable for GlslJitModule { self.validate_main_only(name)?; let func_ptr = self.function_ptrs.get(name).ok_or_else(|| { - GlslError::new(ErrorCode::E0101, format!("Function '{}' not found", name)) + GlslError::new(ErrorCode::E0101, format!("Function '{name}' not found")) })?; // Get the actual Cranelift signature for this function let sig = self.cranelift_signatures.get(name).ok_or_else(|| { GlslError::new( ErrorCode::E0101, - format!("Function signature for '{}' not found", name), + format!("Function signature for '{name}' not found"), ) })?; @@ -624,14 +663,14 @@ impl GlslExecutable for GlslJitModule { self.validate_main_only(name)?; let func_ptr = self.function_ptrs.get(name).ok_or_else(|| { - GlslError::new(ErrorCode::E0101, format!("Function '{}' not found", name)) + GlslError::new(ErrorCode::E0101, format!("Function '{name}' not found")) })?; // Get the actual Cranelift signature for this function let sig = self.cranelift_signatures.get(name).ok_or_else(|| { GlslError::new( ErrorCode::E0101, - format!("Function signature for '{}' not found", name), + format!("Function signature for '{name}' not found"), ) })?; @@ -730,14 +769,14 @@ impl GlslExecutable for GlslJitModule { self.validate_main_only(name)?; let func_ptr = self.function_ptrs.get(name).ok_or_else(|| { - GlslError::new(ErrorCode::E0101, format!("Function '{}' not found", name)) + GlslError::new(ErrorCode::E0101, format!("Function '{name}' not found")) })?; // Get the actual Cranelift signature for this function let sig = self.cranelift_signatures.get(name).ok_or_else(|| { GlslError::new( ErrorCode::E0101, - format!("Function signature for '{}' not found", name), + format!("Function signature for '{name}' not found"), ) })?; @@ -800,14 +839,14 @@ impl GlslExecutable for GlslJitModule { self.validate_main_only(name)?; let func_ptr = self.function_ptrs.get(name).ok_or_else(|| { - GlslError::new(ErrorCode::E0101, format!("Function '{}' not found", name)) + GlslError::new(ErrorCode::E0101, format!("Function '{name}' not found")) })?; // Get the actual Cranelift signature for this function let sig = self.cranelift_signatures.get(name).ok_or_else(|| { GlslError::new( ErrorCode::E0101, - format!("Function signature for '{}' not found", name), + format!("Function signature for '{name}' not found"), ) })?; @@ -848,12 +887,12 @@ impl GlslExecutable for GlslJitModule { // Boolean values are stored as i8 but with 4-byte alignment (matching return statement codegen) let buffer_size = dim * 4; let mut buffer = vec![0u8; buffer_size]; - + if jit_args.is_empty() { unsafe { call_structreturn( *func_ptr, - buffer.as_mut_ptr() as *mut u8, + buffer.as_mut_ptr(), buffer_size, self.call_conv, self.pointer_type, @@ -861,7 +900,7 @@ impl GlslExecutable for GlslJitModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("StructReturn call failed for bvec{}: {}", dim, e), + format!("StructReturn call failed for bvec{dim}: {e}"), ) })?; } @@ -869,7 +908,7 @@ impl GlslExecutable for GlslJitModule { unsafe { call_structreturn_with_args( *func_ptr, - buffer.as_mut_ptr() as *mut u8, + buffer.as_mut_ptr(), buffer_size, &jit_args, self.call_conv, @@ -878,7 +917,7 @@ impl GlslExecutable for GlslJitModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("StructReturn call with args failed for bvec{}: {}", dim, e), + format!("StructReturn call with args failed for bvec{dim}: {e}"), ) })?; } @@ -905,14 +944,14 @@ impl GlslExecutable for GlslJitModule { self.validate_main_only(name)?; let func_ptr = self.function_ptrs.get(name).ok_or_else(|| { - GlslError::new(ErrorCode::E0101, format!("Function '{}' not found", name)) + GlslError::new(ErrorCode::E0101, format!("Function '{name}' not found")) })?; // Get the actual Cranelift signature for this function let sig = self.cranelift_signatures.get(name).ok_or_else(|| { GlslError::new( ErrorCode::E0101, - format!("Function signature for '{}' not found", name), + format!("Function signature for '{name}' not found"), ) })?; @@ -952,12 +991,12 @@ impl GlslExecutable for GlslJitModule { // Use struct return for integer vectors (multiple i32s returned via pointer) let buffer_size = dim * 4; // Each i32 is 4 bytes let mut buffer = vec![0u8; buffer_size]; - + if jit_args.is_empty() { unsafe { call_structreturn( *func_ptr, - buffer.as_mut_ptr() as *mut u8, + buffer.as_mut_ptr(), buffer_size, self.call_conv, self.pointer_type, @@ -965,7 +1004,7 @@ impl GlslExecutable for GlslJitModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("StructReturn call failed for ivec{}: {}", dim, e), + format!("StructReturn call failed for ivec{dim}: {e}"), ) })?; } @@ -973,7 +1012,7 @@ impl GlslExecutable for GlslJitModule { unsafe { call_structreturn_with_args( *func_ptr, - buffer.as_mut_ptr() as *mut u8, + buffer.as_mut_ptr(), buffer_size, &jit_args, self.call_conv, @@ -982,7 +1021,7 @@ impl GlslExecutable for GlslJitModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("StructReturn call with args failed for ivec{}: {}", dim, e), + format!("StructReturn call with args failed for ivec{dim}: {e}"), ) })?; } @@ -1015,14 +1054,14 @@ impl GlslExecutable for GlslJitModule { self.validate_main_only(name)?; let func_ptr = self.function_ptrs.get(name).ok_or_else(|| { - GlslError::new(ErrorCode::E0101, format!("Function '{}' not found", name)) + GlslError::new(ErrorCode::E0101, format!("Function '{name}' not found")) })?; // Get the actual Cranelift signature for this function let sig = self.cranelift_signatures.get(name).ok_or_else(|| { GlslError::new( ErrorCode::E0101, - format!("Function signature for '{}' not found", name), + format!("Function signature for '{name}' not found"), ) })?; @@ -1062,12 +1101,12 @@ impl GlslExecutable for GlslJitModule { // Use struct return for unsigned integer vectors (multiple i32s returned via pointer, interpreted as u32) let buffer_size = dim * 4; // Each i32/u32 is 4 bytes let mut buffer = vec![0u8; buffer_size]; - + if jit_args.is_empty() { unsafe { call_structreturn( *func_ptr, - buffer.as_mut_ptr() as *mut u8, + buffer.as_mut_ptr(), buffer_size, self.call_conv, self.pointer_type, @@ -1075,7 +1114,7 @@ impl GlslExecutable for GlslJitModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("StructReturn call failed for uvec{}: {}", dim, e), + format!("StructReturn call failed for uvec{dim}: {e}"), ) })?; } @@ -1083,7 +1122,7 @@ impl GlslExecutable for GlslJitModule { unsafe { call_structreturn_with_args( *func_ptr, - buffer.as_mut_ptr() as *mut u8, + buffer.as_mut_ptr(), buffer_size, &jit_args, self.call_conv, @@ -1092,7 +1131,7 @@ impl GlslExecutable for GlslJitModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("StructReturn call with args failed for uvec{}: {}", dim, e), + format!("StructReturn call with args failed for uvec{dim}: {e}"), ) })?; } @@ -1125,14 +1164,14 @@ impl GlslExecutable for GlslJitModule { self.validate_main_only(name)?; let func_ptr = self.function_ptrs.get(name).ok_or_else(|| { - GlslError::new(ErrorCode::E0101, format!("Function '{}' not found", name)) + GlslError::new(ErrorCode::E0101, format!("Function '{name}' not found")) })?; // Get the actual Cranelift signature for this function let sig = self.cranelift_signatures.get(name).ok_or_else(|| { GlslError::new( ErrorCode::E0101, - format!("Function signature for '{}' not found", name), + format!("Function signature for '{name}' not found"), ) })?; @@ -1177,9 +1216,10 @@ impl GlslExecutable for GlslJitModule { // Otherwise, check return registers if available let return_type = if uses_struct_return { // Check if any non-StructReturn parameter is I32 (indicates Fixed32 format) - let has_i32_params = sig.params.iter().skip(1).any(|p| { - p.purpose != ArgumentPurpose::StructReturn && p.value_type == types::I32 - }); + let has_i32_params = + sig.params.iter().skip(1).any(|p| { + p.purpose != ArgumentPurpose::StructReturn && p.value_type == types::I32 + }); if has_i32_params { types::I32 // Fixed-point } else { @@ -1207,7 +1247,7 @@ impl GlslExecutable for GlslJitModule { if return_type == types::I32 { // Fixed-point: use i32 buffer, then convert to f32 let mut i32_buffer = vec![0i32; dim]; - + if jit_args.is_empty() { // No arguments case - use simpler call unsafe { @@ -1221,7 +1261,7 @@ impl GlslExecutable for GlslJitModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("StructReturn call failed for vec{}: {}", dim, e), + format!("StructReturn call failed for vec{dim}: {e}"), ) })?; } @@ -1239,28 +1279,30 @@ impl GlslExecutable for GlslJitModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("StructReturn call with args failed for vec{}: {}", dim, e), + format!("StructReturn call with args failed for vec{dim}: {e}"), ) })?; } } - + // Convert fixed-point i32 values to f32 let mut f32_buffer = Vec::with_capacity(dim); for fixed_value in i32_buffer { - f32_buffer.push(fixed_value as f32 / crate::frontend::codegen::constants::FIXED16X16_SCALE); + f32_buffer.push( + fixed_value as f32 / crate::frontend::codegen::constants::FIXED16X16_SCALE, + ); } Ok(f32_buffer) } else { // Native float: use f32 buffer directly let mut buffer = vec![0.0f32; dim]; - + if jit_args.is_empty() { // No arguments case - use simpler call unsafe { call_structreturn( *func_ptr, - buffer.as_mut_ptr() as *mut u8, + buffer.as_mut_ptr(), buffer_size, self.call_conv, self.pointer_type, @@ -1268,7 +1310,7 @@ impl GlslExecutable for GlslJitModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("StructReturn call failed for vec{}: {}", dim, e), + format!("StructReturn call failed for vec{dim}: {e}"), ) })?; } @@ -1277,7 +1319,7 @@ impl GlslExecutable for GlslJitModule { unsafe { call_structreturn_with_args( *func_ptr, - buffer.as_mut_ptr() as *mut u8, + buffer.as_mut_ptr(), buffer_size, &jit_args, self.call_conv, @@ -1286,7 +1328,7 @@ impl GlslExecutable for GlslJitModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("StructReturn call with args failed for vec{}: {}", dim, e), + format!("StructReturn call with args failed for vec{dim}: {e}"), ) })?; } @@ -1308,14 +1350,14 @@ impl GlslExecutable for GlslJitModule { self.validate_main_only(name)?; let func_ptr = self.function_ptrs.get(name).ok_or_else(|| { - GlslError::new(ErrorCode::E0101, format!("Function '{}' not found", name)) + GlslError::new(ErrorCode::E0101, format!("Function '{name}' not found")) })?; // Get the actual Cranelift signature for this function let sig = self.cranelift_signatures.get(name).ok_or_else(|| { GlslError::new( ErrorCode::E0101, - format!("Function signature for '{}' not found", name), + format!("Function signature for '{name}' not found"), ) })?; @@ -1356,7 +1398,7 @@ impl GlslExecutable for GlslJitModule { let count = rows * cols; let buffer_size = count * core::mem::size_of::(); let mut buffer = vec![0.0f32; count]; - + if jit_args.is_empty() { unsafe { call_structreturn( @@ -1369,7 +1411,7 @@ impl GlslExecutable for GlslJitModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("StructReturn call failed for mat{}x{}: {}", rows, cols, e), + format!("StructReturn call failed for mat{rows}x{cols}: {e}"), ) })?; } @@ -1386,7 +1428,7 @@ impl GlslExecutable for GlslJitModule { .map_err(|e| { GlslError::new( ErrorCode::E0400, - format!("StructReturn call with args failed for mat{}x{}: {}", rows, cols, e), + format!("StructReturn call with args failed for mat{rows}x{cols}: {e}"), ) })?; } @@ -1418,7 +1460,7 @@ mod tests { let options = GlslOptions { run_mode: RunMode::HostJit, - decimal_format: DecimalFormat::Float, + decimal_format: DecimalFormat::Fixed32, }; let mut executable = glsl_jit(source, options).expect("Compilation failed"); @@ -1438,7 +1480,7 @@ mod tests { let options = GlslOptions { run_mode: RunMode::HostJit, - decimal_format: DecimalFormat::Float, + decimal_format: DecimalFormat::Fixed32, }; let mut executable = glsl_jit(source, options).expect("Compilation failed"); @@ -1456,11 +1498,12 @@ mod tests { let options = GlslOptions { run_mode: RunMode::HostJit, - decimal_format: DecimalFormat::Float, + decimal_format: DecimalFormat::Fixed32, }; let mut executable = glsl_jit(source, options).expect("Compilation failed"); let result = executable.call_f32("main", &[]).expect("Execution failed"); + // Fixed32 format: allow tolerance for fixed-point conversion precision assert!((result - 3.14).abs() < 0.01); } @@ -1474,7 +1517,7 @@ mod tests { let options = GlslOptions { run_mode: RunMode::HostJit, - decimal_format: DecimalFormat::Float, + decimal_format: DecimalFormat::Fixed32, }; let mut executable = glsl_jit(source, options).expect("Compilation failed"); diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/common.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/common.rs index 356a6973d..c550cd7d0 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/common.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/common.rs @@ -5,7 +5,7 @@ use crate::frontend::codegen::context::CodegenContext; use crate::semantic::types::Type; use cranelift_codegen::ir::{InstBuilder, Value, condcodes::IntCC, types}; -use alloc::vec::Vec; +use alloc::{format, vec, vec::Vec}; impl<'a, M: cranelift_module::Module> CodegenContext<'a, M> { /// min(x, y) - component-wise for vectors @@ -554,7 +554,7 @@ impl<'a, M: cranelift_module::Module> CodegenContext<'a, M> { _ => { return Err(GlslError::new( ErrorCode::E0105, - format!("isinf() not supported for vector dimension {}", dim), + format!("isinf() not supported for vector dimension {dim}"), )); } } @@ -605,7 +605,7 @@ impl<'a, M: cranelift_module::Module> CodegenContext<'a, M> { _ => { return Err(GlslError::new( ErrorCode::E0105, - format!("isnan() not supported for vector dimension {}", dim), + format!("isnan() not supported for vector dimension {dim}"), )); } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/geometric.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/geometric.rs index 3ee5101d4..f739af934 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/geometric.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/geometric.rs @@ -5,6 +5,7 @@ use crate::frontend::codegen::context::CodegenContext; use crate::semantic::types::Type; use cranelift_codegen::ir::{InstBuilder, Value}; +use alloc::vec; use alloc::vec::Vec; impl<'a, M: cranelift_module::Module> CodegenContext<'a, M> { diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/matrix.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/matrix.rs index 3483ef820..f6159ec81 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/matrix.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/matrix.rs @@ -5,13 +5,11 @@ use crate::frontend::codegen::context::CodegenContext; use crate::semantic::types::Type; use cranelift_codegen::ir::{InstBuilder, Value}; -use alloc::vec::Vec; - -#[cfg(not(feature = "std"))] -use alloc::format; -#[cfg(feature = "std")] -use std::format; -#[allow(non_snake_case)] +use alloc::{format, vec, vec::Vec}; +#[allow( + non_snake_case, + reason = "Matrix function names follow GLSL naming convention" +)] impl<'a, M: cranelift_module::Module> CodegenContext<'a, M> { /// Component-wise matrix multiply: result[i][j] = x[i][j] * y[i][j] pub fn builtin_matrixCompMult( @@ -64,8 +62,7 @@ impl<'a, M: cranelift_module::Module> CodegenContext<'a, M> { return Err(GlslError::new( ErrorCode::E0104, format!( - "outerProduct() requires matching vector sizes (got {} and {})", - vec1_size, vec2_size + "outerProduct() requires matching vector sizes (got {vec1_size} and {vec2_size})" ), )); } @@ -306,10 +303,7 @@ impl<'a, M: cranelift_module::Module> CodegenContext<'a, M> { _ => { return Err(GlslError::new( ErrorCode::E0104, - format!( - "determinant() not supported for {}-dimensional matrices", - rows - ), + format!("determinant() not supported for {rows}-dimensional matrices"), )); } }; @@ -588,7 +582,7 @@ impl<'a, M: cranelift_module::Module> CodegenContext<'a, M> { _ => { return Err(GlslError::new( ErrorCode::E0104, - format!("inverse() not supported for {}-dimensional matrices", rows), + format!("inverse() not supported for {rows}-dimensional matrices"), )); } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/mod.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/mod.rs index 60e58e36f..2a53a52c8 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/mod.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/mod.rs @@ -13,12 +13,7 @@ use crate::frontend::codegen::context::CodegenContext; use crate::semantic::types::Type; use cranelift_codegen::ir::Value; -use alloc::vec::Vec; - -#[cfg(not(feature = "std"))] -use alloc::format; -#[cfg(feature = "std")] -use std::format; +use alloc::{format, vec::Vec}; impl<'a, M: cranelift_module::Module> CodegenContext<'a, M> { pub fn emit_builtin_call( &mut self, @@ -96,7 +91,7 @@ impl<'a, M: cranelift_module::Module> CodegenContext<'a, M> { _ => Err(GlslError::new( ErrorCode::E0400, - format!("built-in function not implemented: {}", name), + format!("built-in function not implemented: {name}"), )), } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/relational.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/relational.rs index 1c09f8a0f..cee45f6a8 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/relational.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/builtins/relational.rs @@ -5,6 +5,7 @@ use crate::frontend::codegen::context::CodegenContext; use crate::semantic::types::Type; use cranelift_codegen::ir::{InstBuilder, Value, condcodes::IntCC, types}; +use alloc::vec; use alloc::vec::Vec; impl<'a, M: cranelift_module::Module> CodegenContext<'a, M> { diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/context.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/context.rs index 3f197e283..2e0723033 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/context.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/context.rs @@ -12,7 +12,7 @@ use crate::semantic::types::Type as GlslType; use crate::semantic::types::Type; use alloc::string::String; -use alloc::{format, vec::Vec}; +use alloc::{format, vec, vec::Vec}; pub struct VarInfo { pub cranelift_vars: Vec, // Changed from single Variable to support vectors diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/assignment.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/assignment.rs index e76b76d4a..ff459ae4b 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/assignment.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/assignment.rs @@ -8,7 +8,7 @@ use crate::semantic::types::Type as GlslType; use cranelift_codegen::ir::Value; use glsl::syntax::Expr; -use alloc::{format, vec::Vec}; +use alloc::{format, vec, vec::Vec}; /// Emit assignment expression as RValue /// @@ -111,7 +111,7 @@ pub fn emit_assignment_typed( } else { return Err(GlslError::new( ErrorCode::E0400, - format!("unsupported array element type: {:?}", element_ty), + format!("unsupported array element type: {element_ty:?}"), )); } } @@ -234,7 +234,7 @@ fn emit_compound_assignment_typed( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("unsupported compound assignment operator: {:?}", op), + format!("unsupported compound assignment operator: {op:?}"), )); } }; diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/binary.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/binary.rs index 7cf1cd619..41134e2c8 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/binary.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/binary.rs @@ -14,7 +14,7 @@ use super::coercion; use super::matrix; use super::vector; -use alloc::{format, vec::Vec}; +use alloc::{format, vec, vec::Vec}; /// Emit code to compute a binary expression as an RValue pub fn emit_binary_rvalue( @@ -127,10 +127,7 @@ fn emit_scalar_binary( // This should have been caught by type checking, but handle gracefully return Err(GlslError::new( ErrorCode::E0400, - format!( - "comparison between {:?} and {:?} not supported", - lhs_ty, rhs_ty - ), + format!("comparison between {lhs_ty:?} and {rhs_ty:?} not supported"), )); } else { promote_numeric(lhs_ty, rhs_ty) @@ -189,7 +186,7 @@ fn emit_scalar_binary_op( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("add not supported for {:?}", operand_ty), + format!("add not supported for {operand_ty:?}"), )); } }, @@ -199,7 +196,7 @@ fn emit_scalar_binary_op( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("sub not supported for {:?}", operand_ty), + format!("sub not supported for {operand_ty:?}"), )); } }, @@ -209,7 +206,7 @@ fn emit_scalar_binary_op( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("mult not supported for {:?}", operand_ty), + format!("mult not supported for {operand_ty:?}"), )); } }, @@ -224,7 +221,7 @@ fn emit_scalar_binary_op( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("div not supported for {:?}", operand_ty), + format!("div not supported for {operand_ty:?}"), )); } } @@ -239,10 +236,7 @@ fn emit_scalar_binary_op( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!( - "modulo not supported for {:?} (only integer types)", - operand_ty - ), + format!("modulo not supported for {operand_ty:?} (only integer types)"), )); } } @@ -261,7 +255,7 @@ fn emit_scalar_binary_op( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("equal not supported for {:?}", operand_ty), + format!("equal not supported for {operand_ty:?}"), )); } }; @@ -281,7 +275,7 @@ fn emit_scalar_binary_op( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("nonEqual not supported for {:?}", operand_ty), + format!("nonEqual not supported for {operand_ty:?}"), )); } }; @@ -297,7 +291,7 @@ fn emit_scalar_binary_op( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("LT not supported for {:?}", operand_ty), + format!("LT not supported for {operand_ty:?}"), )); } }; @@ -313,7 +307,7 @@ fn emit_scalar_binary_op( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("GT not supported for {:?}", operand_ty), + format!("GT not supported for {operand_ty:?}"), )); } }; @@ -335,7 +329,7 @@ fn emit_scalar_binary_op( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("LTE not supported for {:?}", operand_ty), + format!("LTE not supported for {operand_ty:?}"), )); } }; @@ -361,7 +355,7 @@ fn emit_scalar_binary_op( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("GTE not supported for {:?}", operand_ty), + format!("GTE not supported for {operand_ty:?}"), )); } }; @@ -409,7 +403,7 @@ fn emit_scalar_binary_op( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("binary operator not supported yet: {:?}", op), + format!("binary operator not supported yet: {op:?}"), )); } }; diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/coercion.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/coercion.rs index 110bfc6f6..473a38ea1 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/coercion.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/coercion.rs @@ -7,6 +7,8 @@ use cranelift_codegen::ir::{ types, }; +use alloc::format; + pub fn coerce_to_type( ctx: &mut CodegenContext<'_, M>, val: Value, @@ -104,7 +106,7 @@ pub fn coerce_to_type_with_location( Ok(ctx.builder.ins().fcvt_from_uint(types::F32, val)) } _ => { - let error_msg = format!("cannot implicitly convert {:?} to {:?}", from_ty, to_ty); + let error_msg = format!("cannot implicitly convert {from_ty:?} to {to_ty:?}"); let mut error = GlslError::new(ErrorCode::E0103, error_msg); if let Some(s) = span { error = error.with_location(source_span_to_location(&s)); diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/component.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/component.rs index 55faaa6a6..937185552 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/component.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/component.rs @@ -5,7 +5,7 @@ use crate::semantic::types::Type as GlslType; use cranelift_codegen::ir::{InstBuilder, TrapCode, Value, condcodes::IntCC, types}; use glsl::syntax::{Expr, SourceSpan}; -use alloc::vec::Vec; +use alloc::{format, vec, vec::Vec}; use hashbrown::HashSet; /// Component naming sets for vector swizzles @@ -33,7 +33,7 @@ pub fn emit_component_access( let span = extract_span_from_expr(base_expr); let error = GlslError::new( ErrorCode::E0112, - format!("component access on non-vector type: {:?}", ty), + format!("component access on non-vector type: {ty:?}"), ) .with_location(source_span_to_location(&span)); return Err(ctx.add_span_to_error(error, &span)); @@ -90,7 +90,7 @@ pub fn emit_indexing( let var_info = ctx.lookup_var_info(array_name).ok_or_else(|| { GlslError::new( ErrorCode::E0400, - format!("array variable '{}' not found", array_name), + format!("array variable '{array_name}' not found"), ) .with_location(source_span_to_location(span)) })?; @@ -98,7 +98,7 @@ pub fn emit_indexing( let array_ptr = var_info.array_ptr.ok_or_else(|| { GlslError::new( ErrorCode::E0400, - format!("variable '{}' is not an array", array_name), + format!("variable '{array_name}' is not an array"), ) .with_location(source_span_to_location(span)) })?; @@ -393,8 +393,7 @@ pub fn emit_indexing( return Err(GlslError::new( ErrorCode::E0400, format!( - "cannot index into {:?} (only matrices and vectors can be indexed)", - current_ty + "cannot index into {current_ty:?} (only matrices and vectors can be indexed)" ), ) .with_location(source_span_to_location(span))); @@ -429,10 +428,7 @@ pub fn parse_vector_swizzle( } let component_count = vec_ty.component_count().ok_or_else(|| { - GlslError::new( - ErrorCode::E0112, - format!("{:?} is not a vector type", vec_ty), - ) + GlslError::new(ErrorCode::E0112, format!("{vec_ty:?} is not a vector type")) })?; // Determine which naming set is used and validate consistency @@ -448,8 +444,7 @@ pub fn parse_vector_swizzle( let mut error = GlslError::new( ErrorCode::E0111, format!( - "component '{}' not valid for {:?} (has only {} components)", - ch, vec_ty, component_count + "component '{ch}' not valid for {vec_ty:?} (has only {component_count} components)" ), ); if let Some(s) = span { @@ -478,7 +473,7 @@ fn determine_naming_set(swizzle: &str) -> Result { _ => { return Err(GlslError::new( ErrorCode::E0113, - format!("invalid swizzle character: '{}'", ch), + format!("invalid swizzle character: '{ch}'"), )); } } @@ -489,10 +484,7 @@ fn determine_naming_set(swizzle: &str) -> Result { if sets_used > 1 { return Err(GlslError::new( ErrorCode::E0113, - format!( - "swizzle '{}' mixes component naming sets (xyzw/rgba/stpq)", - swizzle - ), + format!("swizzle '{swizzle}' mixes component naming sets (xyzw/rgba/stpq)"), )); } @@ -515,7 +507,7 @@ fn parse_single_component(ch: char, naming_set: NamingSet) -> Result Ok(3), _ => Err(GlslError::new( ErrorCode::E0113, - format!("invalid component '{}' for xyzw naming set", ch), + format!("invalid component '{ch}' for xyzw naming set"), )), }, NamingSet::RGBA => match ch { @@ -525,7 +517,7 @@ fn parse_single_component(ch: char, naming_set: NamingSet) -> Result Ok(3), _ => Err(GlslError::new( ErrorCode::E0113, - format!("invalid component '{}' for rgba naming set", ch), + format!("invalid component '{ch}' for rgba naming set"), )), }, NamingSet::STPQ => match ch { @@ -535,7 +527,7 @@ fn parse_single_component(ch: char, naming_set: NamingSet) -> Result Ok(3), _ => Err(GlslError::new( ErrorCode::E0113, - format!("invalid component '{}' for stpq naming set", ch), + format!("invalid component '{ch}' for stpq naming set"), )), }, } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/constructor.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/constructor.rs index 7dee21da8..1157a828b 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/constructor.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/constructor.rs @@ -7,7 +7,7 @@ use glsl::syntax::Expr; use super::coercion; -use alloc::{format, vec::Vec}; +use alloc::{format, vec, vec::Vec}; pub fn emit_vector_constructor( ctx: &mut CodegenContext<'_, M>, @@ -230,7 +230,7 @@ pub fn emit_scalar_constructor( if args.len() != 1 { return Err(GlslError::new( ErrorCode::E0115, - format!("`{}` constructor requires exactly one argument", type_name), + format!("`{type_name}` constructor requires exactly one argument"), ) .with_location(source_span_to_location(&span))); } @@ -250,10 +250,7 @@ pub fn emit_scalar_constructor( } else { return Err(GlslError::new( ErrorCode::E0115, - format!( - "`{}` constructor requires at least one component", - type_name - ), + format!("`{type_name}` constructor requires at least one component"), ) .with_location(source_span_to_location(&span))); }; @@ -274,7 +271,7 @@ pub fn emit_scalar_constructor( _ => { return Err(GlslError::new( ErrorCode::E0112, - format!("`{}` is not a scalar type", type_name), + format!("`{type_name}` is not a scalar type"), ) .with_location(source_span_to_location(&span))); } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/function.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/function.rs index c749e14f2..57e713786 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/function.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/function.rs @@ -9,7 +9,7 @@ use glsl::syntax::Expr; use super::coercion; use super::constructor; -use alloc::{format, vec::Vec}; +use alloc::{format, vec, vec::Vec}; /// Emit code to compute a function call as an RValue pub fn emit_function_call_rvalue( @@ -210,8 +210,7 @@ fn validate_function_call( let error = GlslError::new( ErrorCode::E0400, format!( - "function parameter mismatch: expected {} block parameters, got 0", - expected_count + "function parameter mismatch: expected {expected_count} block parameters, got 0" ), ) .with_location(crate::error::source_span_to_location(call_span)) @@ -314,10 +313,7 @@ fn prepare_call_arguments( if arg_val_idx >= arg_vals_flat.len() { return Err(GlslError::new( ErrorCode::E0400, - format!( - "Not enough argument values for parameter {}", - glsl_param_idx - ), + format!("Not enough argument values for parameter {glsl_param_idx}"), )); } @@ -471,8 +467,8 @@ fn emit_user_function_call( return_vals.len(), func_sig.return_type ); - for (i, val) in return_vals.iter().enumerate() { - crate::debug!(" return_vals[{}] = {:?}", i, val); + for (_i, _val) in return_vals.iter().enumerate() { + crate::debug!(" return_vals[{}] = {:?}", _i, _val); } // Step 7: Package return values @@ -482,8 +478,8 @@ fn emit_user_function_call( packaged_vals.len(), packaged_ty ); - for (i, val) in packaged_vals.iter().enumerate() { - crate::debug!(" packaged_vals[{}] = {:?}", i, val); + for (_i, _val) in packaged_vals.iter().enumerate() { + crate::debug!(" packaged_vals[{}] = {:?}", _i, _val); } Ok((packaged_vals, packaged_ty)) } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/incdec.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/incdec.rs index 0096e2917..1355bfb57 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/incdec.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/incdec.rs @@ -114,7 +114,7 @@ fn emit_incdec( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("increment/decrement not supported for type {:?}", base_ty), + format!("increment/decrement not supported for type {base_ty:?}"), ) .with_location(source_span_to_location(&span))); } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/matrix.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/matrix.rs index 8fbea31cd..348645542 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/matrix.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/matrix.rs @@ -6,7 +6,7 @@ use cranelift_codegen::ir::{InstBuilder, Value}; use super::coercion; -use alloc::{format, vec::Vec}; +use alloc::{format, vec, vec::Vec}; pub fn emit_matrix_binary( ctx: &mut CodegenContext<'_, M>, @@ -87,7 +87,7 @@ pub fn emit_matrix_binary( if cols != vec_size { return Err(GlslError::new(ErrorCode::E0106, - format!("matrix × vector dimension mismatch: {}×{} matrix requires {}-component vector", rows, cols, cols)) + format!("matrix × vector dimension mismatch: {rows}×{cols} matrix requires {cols}-component vector")) .with_location(source_span_to_location(&span))); } @@ -118,7 +118,7 @@ pub fn emit_matrix_binary( if vec_size != rows { return Err(GlslError::new(ErrorCode::E0106, - format!("vector × matrix dimension mismatch: {}-component vector requires {}×{} matrix", vec_size, rows, cols)) + format!("vector × matrix dimension mismatch: {vec_size}-component vector requires {rows}×{cols} matrix")) .with_location(source_span_to_location(&span))); } @@ -151,8 +151,7 @@ pub fn emit_matrix_binary( return Err(GlslError::new( ErrorCode::E0106, format!( - "matrix × matrix dimension mismatch: {}×{} × {}×{} requires {} == {}", - lhs_rows, lhs_cols, rhs_rows, rhs_cols, lhs_cols, rhs_rows + "matrix × matrix dimension mismatch: {lhs_rows}×{lhs_cols} × {rhs_rows}×{rhs_cols} requires {lhs_cols} == {rhs_rows}" ), ) .with_location(source_span_to_location(&span))); @@ -275,7 +274,7 @@ pub fn emit_matrix_binary( _ => Err(GlslError::new( ErrorCode::E0106, - format!("operator {:?} not supported for matrices", op), + format!("operator {op:?} not supported for matrices"), ) .with_location(source_span_to_location(&span))), } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/mod.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/mod.rs index 2ee84bc0a..d42262628 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/mod.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/mod.rs @@ -70,7 +70,7 @@ impl<'a, M: cranelift_module::Module> CodegenContext<'a, M> { _ => Err(GlslError::new( ErrorCode::E0400, - format!("expression not supported yet: {:?}", expr), + format!("expression not supported yet: {expr:?}"), )), } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/ternary.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/ternary.rs index 5b367184e..e00ef6db1 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/ternary.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/ternary.rs @@ -8,7 +8,7 @@ use glsl::syntax::Expr; use super::coercion; -use alloc::{format, vec::Vec}; +use alloc::{format, vec, vec::Vec}; /// Emit code to compute a ternary expression as an RValue pub fn emit_ternary_rvalue( @@ -35,10 +35,7 @@ pub fn emit_ternary_rvalue( "ternary condition must be scalar bool type", ) .with_location(source_span_to_location(&cond_span)) - .with_note(format!( - "condition has type `{:?}`, expected `Bool`", - cond_ty - ))); + .with_note(format!("condition has type `{cond_ty:?}`, expected `Bool`"))); } // Condition must be scalar, so take the first (and only) value @@ -75,8 +72,7 @@ pub fn emit_ternary_rvalue( ) .with_location(source_span_to_location(span)) .with_note(format!( - "true branch has type `{:?}`, false branch has type `{:?}`", - true_ty, false_ty + "true branch has type `{true_ty:?}`, false branch has type `{false_ty:?}`" )) .with_note("branches must have matching types or allow implicit conversion")); }; diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/unary.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/unary.rs index 91c363603..390a1227c 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/unary.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/unary.rs @@ -95,7 +95,7 @@ fn emit_unary_op( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("unary minus not supported for {:?}", operand_ty), + format!("unary minus not supported for {operand_ty:?}"), )); } } @@ -104,7 +104,7 @@ fn emit_unary_op( if operand_ty != &GlslType::Bool { return Err(GlslError::new( ErrorCode::E0107, - format!("logical NOT requires bool, got {:?}", operand_ty), + format!("logical NOT requires bool, got {operand_ty:?}"), )); } let zero = ctx.builder.ins().iconst(types::I8, 0); @@ -127,7 +127,7 @@ fn emit_unary_op( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("unary operator not supported yet: {:?}", op), + format!("unary operator not supported yet: {op:?}"), )); } }; diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/vector.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/vector.rs index b2f72512b..036561e51 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/vector.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/expr/vector.rs @@ -6,7 +6,7 @@ use cranelift_codegen::ir::{InstBuilder, Value}; use super::binary; use super::coercion; -use alloc::{format, vec::Vec}; +use alloc::{format, vec, vec::Vec}; pub fn emit_vector_binary( ctx: &mut CodegenContext<'_, M>, @@ -26,7 +26,7 @@ pub fn emit_vector_binary( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!("operator {:?} not supported on vectors yet", op), + format!("operator {op:?} not supported on vectors yet"), )); } } @@ -43,10 +43,7 @@ pub fn emit_vector_binary( if lhs_ty != rhs_ty { let mut error = GlslError::new( ErrorCode::E0106, - format!( - "vector operation requires matching types, got {:?} and {:?}", - lhs_ty, rhs_ty - ), + format!("vector operation requires matching types, got {lhs_ty:?} and {rhs_ty:?}"), ); if let Some(s) = span { error = error.with_location(source_span_to_location(&s)); @@ -61,7 +58,7 @@ pub fn emit_vector_binary( if !rhs_ty.is_numeric() || !vec_base.is_numeric() { return Err(GlslError::new( ErrorCode::E0106, - format!("cannot use {:?} with {:?}", rhs_ty, lhs_ty), + format!("cannot use {rhs_ty:?} with {lhs_ty:?}"), )); } (lhs_ty.clone(), VectorOpMode::VectorScalar) @@ -71,7 +68,7 @@ pub fn emit_vector_binary( if !lhs_ty.is_numeric() || !vec_base.is_numeric() { return Err(GlslError::new( ErrorCode::E0400, - format!("Cannot use {:?} with {:?}", lhs_ty, rhs_ty), + format!("Cannot use {lhs_ty:?} with {rhs_ty:?}"), )); } (rhs_ty.clone(), VectorOpMode::ScalarVector) diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/helpers.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/helpers.rs index efa31fe5b..43df823e9 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/helpers.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/helpers.rs @@ -38,10 +38,7 @@ fn generate_default_scalar_return( _ => { return Err(GlslError::new( ErrorCode::E0400, - format!( - "unsupported return type for default return: {:?}", - return_type - ), + format!("unsupported return type for default return: {return_type:?}"), )); } }; @@ -92,7 +89,7 @@ fn create_zero_value( Type::Bool => Ok(ctx.builder.ins().iconst(types::I8, 0)), _ => Err(GlslError::new( ErrorCode::E0400, - format!("unsupported base type for zero value: {:?}", base_ty), + format!("unsupported base type for zero value: {base_ty:?}"), )), } } @@ -110,7 +107,7 @@ fn generate_default_vector_return( let base_ty = return_type.vector_base_type().ok_or_else(|| { GlslError::new( ErrorCode::E0400, - format!("expected vector type, got: {:?}", return_type), + format!("expected vector type, got: {return_type:?}"), ) })?; let count = return_type.component_count().unwrap(); @@ -164,7 +161,7 @@ fn generate_default_matrix_return( let element_count = return_type.matrix_element_count().ok_or_else(|| { GlslError::new( ErrorCode::E0400, - format!("expected matrix type, got: {:?}", return_type), + format!("expected matrix type, got: {return_type:?}"), ) })?; diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/read.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/read.rs index 56941205f..321ec0b82 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/read.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/read.rs @@ -3,7 +3,7 @@ use crate::error::{ErrorCode, GlslError}; use crate::frontend::codegen::context::CodegenContext; use crate::semantic::types::Type as GlslType; -use alloc::vec::Vec; +use alloc::{format, vec, vec::Vec}; use cranelift_codegen::ir::{InstBuilder, Value}; use super::super::expr::component; @@ -243,7 +243,7 @@ pub fn read_lvalue( } else { Err(GlslError::new( ErrorCode::E0400, - format!("unsupported array element type: {:?}", element_ty), + format!("unsupported array element type: {element_ty:?}"), )) } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/component/mod.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/component/mod.rs index 5d398cf68..28150a86d 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/component/mod.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/component/mod.rs @@ -8,6 +8,8 @@ use crate::frontend::codegen::context::CodegenContext; use crate::semantic::types::Type as GlslType; use glsl::syntax::Expr; +use alloc::format; + use super::super::super::expr::component; use super::super::types::LValue; @@ -51,7 +53,7 @@ pub fn resolve_component_lvalue( let span = extract_span_from_expr(base_expr); return Err(GlslError::new( ErrorCode::E0112, - format!("component access on non-vector type: {:?}", base_ty), + format!("component access on non-vector type: {base_ty:?}"), ) .with_location(source_span_to_location(&span))); } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/array.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/array.rs index 177a41ed9..81c3c9b58 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/array.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/array.rs @@ -5,6 +5,8 @@ use crate::frontend::codegen::context::CodegenContext; use crate::semantic::types::Type as GlslType; use glsl::syntax::{ArraySpecifier, ArraySpecifierDimension, SourceSpan}; +use alloc::format; + use super::super::super::super::expr::component; use super::super::super::types::LValue; use super::nested::resolve_nested_array_indexing; diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/helpers.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/helpers.rs index b3fa84f67..81ce04831 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/helpers.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/helpers.rs @@ -3,7 +3,7 @@ use crate::error::{ErrorCode, GlslError, extract_span_from_expr, source_span_to_location}; use crate::frontend::codegen::context::CodegenContext; use crate::semantic::types::Type as GlslType; -use alloc::vec::Vec; +use alloc::{format, vec::Vec}; use cranelift_frontend::Variable; use glsl::syntax::{Expr, SourceSpan}; diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/matrix_vector.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/matrix_vector.rs index 9b775842d..1c15d4eff 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/matrix_vector.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/matrix_vector.rs @@ -5,6 +5,8 @@ use crate::frontend::codegen::context::CodegenContext; use crate::semantic::types::Type as GlslType; use glsl::syntax::{ArraySpecifier, ArraySpecifierDimension, SourceSpan}; +use alloc::format; + use super::super::super::types::LValue; use super::helpers::{ extract_base_vars_and_ty, process_matrix_dimension, process_vector_dimension, validate_index, @@ -85,8 +87,7 @@ pub fn resolve_matrix_vector_indexing( return Err(GlslError::new( ErrorCode::E0400, format!( - "cannot index into {:?} (only arrays, matrices and vectors can be indexed)", - current_ty + "cannot index into {current_ty:?} (only arrays, matrices and vectors can be indexed)" ), ) .with_location(source_span_to_location(span))); diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/nested.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/nested.rs index c64a4c94e..e6061454e 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/nested.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/resolve/indexing/nested.rs @@ -3,7 +3,7 @@ use crate::error::{ErrorCode, GlslError, source_span_to_location}; use crate::frontend::codegen::context::CodegenContext; use crate::semantic::types::Type as GlslType; -use alloc::vec::Vec; +use alloc::{format, vec::Vec}; use cranelift_codegen::ir::Value; use glsl::syntax::{ArraySpecifier, ArraySpecifierDimension, SourceSpan}; @@ -71,7 +71,6 @@ pub fn resolve_nested_array_indexing( // Process dimensions starting from index 1 (skip the first array dimension) let mut current_ty = base_ty.clone(); let current_vars = base_vars; - let mut row: Option = None; let mut col: Option = None; crate::debug!( @@ -80,13 +79,12 @@ pub fn resolve_nested_array_indexing( array_spec.dimensions.0.len() - 1 ); - for (dim_idx, dimension) in array_spec.dimensions.0.iter().skip(1).enumerate() { + for (_dim_idx, dimension) in array_spec.dimensions.0.iter().skip(1).enumerate() { crate::debug!( - " Processing dimension {}: current_ty={:?}, col={:?}, row={:?}", - dim_idx + 1, + " Processing dimension {}: current_ty={:?}, col={:?}", + _dim_idx + 1, current_ty, - col, - row + col ); let index_expr = match dimension { ArraySpecifierDimension::ExplicitlySized(expr) => expr, @@ -117,18 +115,18 @@ pub fn resolve_nested_array_indexing( // Vector indexing: vec[index] returns scalar component // If we already have a column, this is a matrix element access if col.is_some() { + let row = index; crate::debug!( " Matrix element access: col={}, row={}, base_ty={:?}", col.unwrap(), - index, + row, base_ty ); - row = Some(index); let _ = process_vector_dimension(¤t_ty, index, span)?; return Ok(LValue::MatrixElement { base_vars: current_vars, base_ty: base_ty.clone(), - row: row.unwrap(), + row, col: col.unwrap(), }); } else { @@ -144,8 +142,7 @@ pub fn resolve_nested_array_indexing( return Err(GlslError::new( ErrorCode::E0400, format!( - "cannot index into {:?} (only matrices and vectors can be indexed after array)", - current_ty + "cannot index into {current_ty:?} (only matrices and vectors can be indexed after array)" ), ) .with_location(source_span_to_location(span))); diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/write.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/write.rs index 5ca6d39fe..a32644770 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/write.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/lvalue/write.rs @@ -4,6 +4,8 @@ use crate::error::{ErrorCode, GlslError}; use crate::frontend::codegen::context::CodegenContext; use cranelift_codegen::ir::{InstBuilder, Value}; +use alloc::format; + use super::super::expr::component; use super::types::LValue; @@ -285,7 +287,7 @@ pub fn write_lvalue( } else { Err(GlslError::new( ErrorCode::E0400, - format!("unsupported array element type: {:?}", element_ty), + format!("unsupported array element type: {element_ty:?}"), )) } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/rvalue.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/rvalue.rs index 2d8139a76..5ef4d1dee 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/rvalue.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/rvalue.rs @@ -7,7 +7,7 @@ use crate::semantic::types::Type as GlslType; use cranelift_codegen::ir::Value; -use alloc::vec::Vec; +use alloc::{vec, vec::Vec}; /// Represents an RValue (right-hand value) - a computed value /// diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/declaration.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/declaration.rs index 1782bf73f..a3ee07cb8 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/declaration.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/declaration.rs @@ -87,8 +87,7 @@ pub fn emit_declaration( GlslError::new( ErrorCode::E0400, format!( - "array element type mismatch: expected {:?}, got {:?}", - element_ty, init_element_ty + "array element type mismatch: expected {element_ty:?}, got {init_element_ty:?}" ), ) })?; @@ -142,8 +141,7 @@ pub fn emit_declaration( return Err(GlslError::new( ErrorCode::E0400, format!( - "unsupported array element type for zero initialization: {:?}", - element_ty + "unsupported array element type for zero initialization: {element_ty:?}" ), )); } @@ -284,8 +282,7 @@ pub fn emit_declaration( GlslError::new( ErrorCode::E0400, format!( - "array element type mismatch: expected {:?}, got {:?}", - element_ty, init_element_ty + "array element type mismatch: expected {element_ty:?}, got {init_element_ty:?}" ), ) })?; @@ -347,8 +344,7 @@ pub fn emit_declaration( return Err(GlslError::new( ErrorCode::E0400, format!( - "unsupported array element type for zero initialization: {:?}", - element_ty + "unsupported array element type for zero initialization: {element_ty:?}" ), )); } @@ -467,8 +463,7 @@ pub fn emit_initializer( GlslError::new( ErrorCode::E0400, format!( - "array initializer type mismatch: expected {:?}, got {:?}", - expected_ty, item_ty + "array initializer type mismatch: expected {expected_ty:?}, got {item_ty:?}" ), ) })?; diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/if.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/if.rs index d9325dece..8677e7350 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/if.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/if.rs @@ -3,6 +3,8 @@ use glsl::syntax::SelectionStatement; use crate::error::{ErrorCode, GlslError}; use crate::frontend::codegen::context::CodegenContext; +use alloc::format; + /// Emit if statement (selection statement) pub fn emit_if_stmt( ctx: &mut CodegenContext<'_, M>, @@ -18,10 +20,7 @@ pub fn emit_if_stmt( if cond_ty != crate::frontend::semantic::types::Type::Bool { let error = GlslError::new(ErrorCode::E0107, "condition must be bool type") .with_location(source_span_to_location(&cond_span)) - .with_note(format!( - "condition has type `{:?}`, expected `Bool`", - cond_ty - )); + .with_note(format!("condition has type `{cond_ty:?}`, expected `Bool`")); return Err(ctx.add_span_to_error(error, &cond_span)); } // Condition must be scalar, so we take the first (and only) value diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/jump.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/jump.rs index a6c94d51b..43a5f4f20 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/jump.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/jump.rs @@ -3,6 +3,8 @@ use glsl::syntax::JumpStatement; use crate::error::{ErrorCode, GlslError}; use crate::frontend::codegen::context::CodegenContext; +use alloc::format; + /// Emit jump statement (dispatch to break, continue, return) pub fn emit_jump_stmt( ctx: &mut CodegenContext<'_, M>, @@ -21,7 +23,7 @@ pub fn emit_jump_stmt( ), _ => Err(GlslError::new( ErrorCode::E0400, - format!("jump statement not supported: {:?}", jump), + format!("jump statement not supported: {jump:?}"), )), } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/loops.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/loops.rs index 37ba9aa33..b424d5287 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/loops.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/loops.rs @@ -3,6 +3,8 @@ use glsl::syntax::IterationStatement; use crate::error::{ErrorCode, GlslError}; use crate::frontend::codegen::context::CodegenContext; +use alloc::format; + /// Emit iteration statement (dispatch to specific loop types) pub fn emit_iteration_stmt( ctx: &mut CodegenContext<'_, M>, diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/mod.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/mod.rs index a7d741a0f..6e3ed367a 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/mod.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/codegen/stmt/mod.rs @@ -38,7 +38,7 @@ impl<'a, M: cranelift_module::Module> CodegenContext<'a, M> { SimpleStatement::Jump(jump) => self.emit_jump_stmt(jump), _ => Err(GlslError::new( ErrorCode::E0400, - format!("statement type not supported: {:?}", stmt), + format!("statement type not supported: {stmt:?}"), )), } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/glsl_compiler.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/glsl_compiler.rs index 7d151bed6..dcde4c2f1 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/glsl_compiler.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/glsl_compiler.rs @@ -14,12 +14,13 @@ use cranelift_object::ObjectModule; use hashbrown::HashMap; use alloc::string::String; +use alloc::vec; use alloc::vec::Vec; use alloc::format; /// GLSL compiler that compiles GLSL source to GlModule pub struct GlslCompiler { - #[allow(dead_code)] + #[allow(dead_code, reason = "Builder context stored for future use")] builder_context: FunctionBuilderContext, } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/mod.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/mod.rs index 5e9d358dc..758ae3c18 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/mod.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/mod.rs @@ -12,9 +12,9 @@ pub mod src_loc; pub mod src_loc_manager; // Re-exports used by crate root; suppress unused warnings within this module. -#[allow(unused_imports)] +#[allow(unused_imports, reason = "Re-exports for crate root")] pub use glsl_compiler::GlslCompiler; -#[allow(unused_imports)] +#[allow(unused_imports, reason = "Re-exports for crate root")] pub use pipeline::{ Backend, CompilationPipeline, CompiledShader, ParseResult, SemanticResult, TransformationPass, parse_program_with_registry, @@ -27,7 +27,9 @@ pub use pipeline::{ #[cfg(feature = "emulator")] use crate::backend::codegen::emu::EmulatorOptions; use crate::backend::module::gl_module::GlModule; +#[cfg(feature = "std")] use crate::backend::target::Target; +#[cfg(feature = "std")] use crate::backend::transform::fixed32::{Fixed32Transform, FixedPointFormat}; use crate::error::GlslError; use crate::exec::executable::{GlslExecutable, GlslOptions, RunMode}; @@ -45,27 +47,23 @@ pub fn compile_glsl_to_gl_module_jit( source: &str, options: &GlslOptions, ) -> Result, GlslError> { + #[allow( + unused_variables, + reason = "source is used conditionally in #[cfg(feature = \"std\")] block" + )] + let _source = source; + #[cfg(feature = "std")] use crate::exec::executable::DecimalFormat; options.validate()?; + #[cfg(feature = "std")] let mut compiler = GlslCompiler::new(); // Determine target based on run mode + #[cfg(feature = "std")] let target = match &options.run_mode { - RunMode::HostJit => { - #[cfg(feature = "std")] - { - Target::host_jit()? - } - #[cfg(not(feature = "std"))] - { - return Err(GlslError::new( - crate::error::ErrorCode::E0400, - "HostJit mode requires 'std' feature flag", - )); - } - } + RunMode::HostJit => Target::host_jit()?, RunMode::Emulator { .. } => { return Err(GlslError::new( crate::error::ErrorCode::E0400, @@ -74,31 +72,50 @@ pub fn compile_glsl_to_gl_module_jit( } }; - // Compile to GlModule - let mut module = compiler.compile_to_gl_module_jit(source, target)?; - - // Apply transformations - match options.decimal_format { - DecimalFormat::Fixed32 => { - let transform = Fixed32Transform::new(FixedPointFormat::Fixed16x16); - module = module.apply_transform(transform)?; - } - DecimalFormat::Fixed64 => { + #[cfg(not(feature = "std"))] + match &options.run_mode { + RunMode::HostJit => { return Err(GlslError::new( crate::error::ErrorCode::E0400, - "Fixed64 format is not yet supported. Only Fixed32 format is currently supported.", + "HostJit mode requires 'std' feature flag", )); } - DecimalFormat::Float => { + RunMode::Emulator { .. } => { return Err(GlslError::new( crate::error::ErrorCode::E0400, - "Float format is not yet supported. Only Fixed32 format is currently supported. \ - Float format will cause TestCase relocation errors. Use Fixed32 format instead.", + "Emulator mode not supported for JIT compilation", )); } } - Ok(module) + #[cfg(feature = "std")] + { + // Compile to GlModule + let mut module = compiler.compile_to_gl_module_jit(_source, target)?; + + // Apply transformations + match options.decimal_format { + DecimalFormat::Fixed32 => { + let transform = Fixed32Transform::new(FixedPointFormat::Fixed16x16); + module = module.apply_transform(transform)?; + } + DecimalFormat::Fixed64 => { + return Err(GlslError::new( + crate::error::ErrorCode::E0400, + "Fixed64 format is not yet supported. Only Fixed32 format is currently supported.", + )); + } + DecimalFormat::Float => { + return Err(GlslError::new( + crate::error::ErrorCode::E0400, + "Float format is not yet supported. Only Fixed32 format is currently supported. \ + Float format will cause TestCase relocation errors. Use Fixed32 format instead.", + )); + } + } + + Ok(module) + } } /// Compile GLSL to GlModule (internal, reusable) diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/builtins.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/builtins.rs index 100f3e7f9..09dbaf85f 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/builtins.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/builtins.rs @@ -2,11 +2,8 @@ use crate::frontend::semantic::types::Type; -use alloc::vec::Vec; - use alloc::string::{String, ToString}; - -use alloc::format; +use alloc::{format, vec, vec::Vec}; #[derive(Debug, Clone, PartialEq, Eq)] pub struct BuiltinSignature { @@ -633,7 +630,7 @@ pub fn lookup_builtin(name: &str) -> Option> { /// Type check a built-in function call and return the result type pub fn check_builtin_call(name: &str, arg_types: &[Type]) -> Result { let signatures = - lookup_builtin(name).ok_or_else(|| format!("Unknown built-in function: {}", name))?; + lookup_builtin(name).ok_or_else(|| format!("Unknown built-in function: {name}"))?; // Try each overload for sig in &signatures { @@ -642,10 +639,7 @@ pub fn check_builtin_call(name: &str, arg_types: &[Type]) -> Result Result Result Type::Mat4, _ => { return Err(format!( - "outerProduct: unsupported vector size {}", - vec1_size + "outerProduct: unsupported vector size {vec1_size}" )); } } } else { return Err(format!( - "outerProduct: vector sizes must match for square matrices (got {} and {})", - vec1_size, vec2_size + "outerProduct: vector sizes must match for square matrices (got {vec1_size} and {vec2_size})" )); } } else { @@ -762,8 +754,7 @@ fn validate_gentype_consistency( if let Some(ref expected) = expected_type { if arg != expected { return Err(format!( - "GenType parameter type mismatch: expected {:?}, got {:?}", - expected, arg + "GenType parameter type mismatch: expected {expected:?}, got {arg:?}" )); } } else { diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/functions.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/functions.rs index 5d64d1af9..daecbf52d 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/functions.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/functions.rs @@ -5,12 +5,7 @@ use crate::frontend::semantic::type_check::can_implicitly_convert; use crate::frontend::semantic::types::Type; use hashbrown::HashMap; -use alloc::{string::String, vec::Vec}; - -#[cfg(not(feature = "std"))] -use alloc::format; -#[cfg(feature = "std")] -use std::format; +use alloc::{format, string::String, vec::Vec}; #[derive(Clone)] pub struct FunctionRegistry { functions: HashMap>, @@ -58,8 +53,8 @@ impl FunctionRegistry { arg_types: &[Type], ) -> Result<&FunctionSignature, GlslError> { let overloads = self.functions.get(name).ok_or_else(|| { - GlslError::new(ErrorCode::E0101, format!("undefined function `{}`", name)) - .with_note(format!("function `{}` is not defined", name)) + GlslError::new(ErrorCode::E0101, format!("undefined function `{name}`")) + .with_note(format!("function `{name}` is not defined")) })?; // Try exact match first @@ -78,11 +73,10 @@ impl FunctionRegistry { Err(GlslError::new( ErrorCode::E0114, - format!("no matching overload for function `{}`", name), + format!("no matching overload for function `{name}`"), ) .with_note(format!( - "cannot find function `{}` that accepts arguments of type {:?}", - name, arg_types + "cannot find function `{name}` that accepts arguments of type {arg_types:?}" ))) } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/mod.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/mod.rs index c1cf643c1..3cb30b808 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/mod.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/mod.rs @@ -32,7 +32,7 @@ pub struct TypedFunction { /// Semantic analyzer that orchestrates semantic analysis passes pub struct SemanticAnalyzer { - #[allow(dead_code)] + #[allow(dead_code, reason = "Function registry stored for future use")] registry: functions::FunctionRegistry, } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/scope.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/scope.rs index 074c418ff..8cb12bded 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/scope.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/scope.rs @@ -2,8 +2,7 @@ use crate::error::{ErrorCode, GlslError}; use crate::frontend::semantic::types::Type; use hashbrown::HashMap; -use alloc::string::String; -use alloc::vec::Vec; +use alloc::{format, string::String, vec, vec::Vec}; pub struct SymbolTable { scopes: Vec, @@ -44,7 +43,7 @@ impl SymbolTable { if scope.variables.contains_key(&name) { return Err(GlslError::new( ErrorCode::E0400, - format!("variable `{}` already declared", name), + format!("variable `{name}` already declared"), )); } scope.variables.insert( diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/constructors.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/constructors.rs index ba7896cda..619522d80 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/constructors.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/constructors.rs @@ -7,6 +7,8 @@ use glsl::syntax::SourceSpan; use super::conversion::can_implicitly_convert; +use alloc::format; + /// Check vector constructor arguments and infer result type pub fn check_vector_constructor(type_name: &str, args: &[Type]) -> Result { check_vector_constructor_with_span(type_name, args, None) @@ -22,7 +24,7 @@ pub fn check_vector_constructor_with_span( let component_count = result_type.component_count().ok_or_else(|| { GlslError::new( ErrorCode::E0112, - format!("`{}` is not a vector type", type_name), + format!("`{type_name}` is not a vector type"), ) })?; let base_type = result_type.vector_base_type().unwrap(); @@ -64,8 +66,7 @@ pub fn check_vector_constructor_with_span( format!("cannot construct `{}` from `{:?}`", type_name, args[0]), ) .with_note(format!( - "expected at least {} components, found {}", - component_count, src_component_count + "expected at least {component_count} components, found {src_component_count}" )), )); } @@ -89,11 +90,10 @@ pub fn check_vector_constructor_with_span( return Err(add_location( GlslError::new( ErrorCode::E0115, - format!("`{}` constructor has wrong number of components", type_name), + format!("`{type_name}` constructor has wrong number of components"), ) .with_note(format!( - "expected {} components, found {}", - component_count, total_components + "expected {component_count} components, found {total_components}" )), )); } @@ -110,7 +110,7 @@ pub fn check_vector_constructor_with_span( return Err(add_location( GlslError::new( ErrorCode::E0103, - format!("cannot use `{:?}` in `{}` constructor", arg, type_name), + format!("cannot use `{arg:?}` in `{type_name}` constructor"), ) .with_note("component type cannot be implicitly converted"), )); @@ -148,7 +148,7 @@ fn count_total_components(args: &[Type]) -> Result { } else { return Err(GlslError::new( ErrorCode::E0112, - format!("invalid constructor argument: `{:?}`", arg), + format!("invalid constructor argument: `{arg:?}`"), )); } } @@ -199,7 +199,7 @@ pub fn check_scalar_constructor_with_span( if args.len() != 1 { let mut error = GlslError::new( ErrorCode::E0115, - format!("`{}` constructor requires exactly one argument", type_name), + format!("`{type_name}` constructor requires exactly one argument"), ); if let Some(ref s) = span { error = error.with_location(source_span_to_location(s)); @@ -219,7 +219,7 @@ pub fn check_scalar_constructor_with_span( _ => { let mut error = GlslError::new( ErrorCode::E0112, - format!("`{}` is not a scalar type", type_name), + format!("`{type_name}` is not a scalar type"), ); if let Some(ref s) = span { error = error.with_location(source_span_to_location(s)); @@ -248,7 +248,7 @@ pub fn check_matrix_constructor(type_name: &str, args: &[Type]) -> Result Result Result Result Result Result Type { @@ -105,8 +107,7 @@ pub fn check_assignment_with_span( if !can_implicitly_convert(rhs_ty, lhs_ty) { let mut error = GlslError::new(ErrorCode::E0102, "type mismatch in assignment") .with_note(format!( - "cannot assign value of type `{:?}` to variable of type `{:?}`", - rhs_ty, lhs_ty + "cannot assign value of type `{rhs_ty:?}` to variable of type `{lhs_ty:?}`" )) .with_note("help: consider using an explicit type conversion"); diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/inference.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/inference.rs index 779774296..91a6b4827 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/inference.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/inference.rs @@ -89,7 +89,7 @@ pub fn infer_expr_type_with_registry( let span = extract_span_from_expr(expr); return Err(GlslError::new( ErrorCode::E0112, - format!("component access on non-vector type: {:?}", base_ty), + format!("component access on non-vector type: {base_ty:?}"), ) .with_location(source_span_to_location(&span))); } @@ -115,7 +115,7 @@ pub fn infer_expr_type_with_registry( Type::vector_type(&base_scalar_ty, swizzle_len).ok_or_else(|| { let mut error = GlslError::new( ErrorCode::E0113, - format!("invalid swizzle length: {}", swizzle_len), + format!("invalid swizzle length: {swizzle_len}"), ); if error.location.is_none() { error = error.with_location(source_span_to_location(&dot_span)); @@ -195,8 +195,7 @@ pub fn infer_expr_type_with_registry( Err(GlslError::new( ErrorCode::E0112, format!( - "cannot infer type for function call `{}` without function registry", - func_name + "cannot infer type for function call `{func_name}` without function registry" ), ) .with_location(source_span_to_location(&span))) @@ -275,8 +274,7 @@ pub fn infer_expr_type_with_registry( return Err(GlslError::new( ErrorCode::E0400, format!( - "cannot index into {:?} (only matrices and vectors can be indexed after array)", - current_ty + "cannot index into {current_ty:?} (only matrices and vectors can be indexed after array)" ), ) .with_location(source_span_to_location(span))); @@ -330,8 +328,7 @@ pub fn infer_expr_type_with_registry( return Err(GlslError::new( ErrorCode::E0400, format!( - "cannot index into {:?} (only arrays, matrices and vectors can be indexed)", - current_ty + "cannot index into {current_ty:?} (only arrays, matrices and vectors can be indexed)" ), ) .with_location(source_span_to_location(span))); @@ -350,10 +347,7 @@ pub fn infer_expr_type_with_registry( "ternary condition must be scalar bool type", ) .with_location(source_span_to_location(span)) - .with_note(format!( - "condition has type `{:?}`, expected `Bool`", - cond_ty - ))); + .with_note(format!("condition has type `{cond_ty:?}`, expected `Bool`"))); } // Infer types of both branches @@ -378,8 +372,7 @@ pub fn infer_expr_type_with_registry( ) .with_location(source_span_to_location(span)) .with_note(format!( - "true branch has type `{:?}`, false branch has type `{:?}`", - true_ty, false_ty + "true branch has type `{true_ty:?}`, false branch has type `{false_ty:?}`" )) .with_note("branches must have matching types or allow implicit conversion")) } @@ -389,7 +382,7 @@ pub fn infer_expr_type_with_registry( let span = extract_span_from_expr(expr); Err(GlslError::new( ErrorCode::E0112, - format!("cannot infer type for expression: {:?}", expr), + format!("cannot infer type for expression: {expr:?}"), ) .with_location(source_span_to_location(&span))) } @@ -425,10 +418,10 @@ pub fn infer_expr_type_in_context( // Wrap the expression in a minimal function to parse it // We'll try different return types to see which one parses successfully let wrappers = [ - format!("int main() {{ return {}; }}", expr_str), - format!("uint main() {{ return {}; }}", expr_str), - format!("float main() {{ return {}; }}", expr_str), - format!("bool main() {{ return {}; }}", expr_str), + format!("int main() {{ return {expr_str}; }}"), + format!("uint main() {{ return {expr_str}; }}"), + format!("float main() {{ return {expr_str}; }}"), + format!("bool main() {{ return {expr_str}; }}"), ]; // Use empty symbol table since we're only parsing expressions (no variables) @@ -446,6 +439,6 @@ pub fn infer_expr_type_in_context( Err(GlslError::new( ErrorCode::E0001, - format!("failed to parse expression: `{}`", expr_str), + format!("failed to parse expression: `{expr_str}`"), )) } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/matrix.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/matrix.rs index 32c5ab792..8c2573ca7 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/matrix.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/matrix.rs @@ -5,6 +5,8 @@ use crate::error::{ErrorCode, GlslError, source_span_to_location}; use crate::frontend::semantic::types::Type; use glsl::syntax::{BinaryOp, SourceSpan}; +use alloc::format; + /// Infer result type of matrix binary operation /// Implements GLSL spec: operators.adoc:1019-1098 pub fn infer_matrix_binary_result_type( @@ -26,8 +28,7 @@ pub fn infer_matrix_binary_result_type( ) .with_location(source_span_to_location(&span)) .with_note(format!( - "left operand: `{:?}`, right operand: `{:?}`", - lhs_ty, rhs_ty + "left operand: `{lhs_ty:?}`, right operand: `{rhs_ty:?}`" ))); } return Ok(lhs_ty.clone()); @@ -49,8 +50,7 @@ pub fn infer_matrix_binary_result_type( ) .with_location(source_span_to_location(&span)) .with_note(format!( - "left operand: `{:?}`, right operand: `{:?}`", - lhs_ty, rhs_ty + "left operand: `{lhs_ty:?}`, right operand: `{rhs_ty:?}`" ))); } return Ok(lhs_ty.clone()); @@ -96,11 +96,10 @@ pub fn infer_matrix_binary_result_type( if cols != vec_size { return Err(GlslError::new( ErrorCode::E0106, - format!("matrix × vector dimension mismatch: {}×{} matrix requires {}-component vector", - rows, cols, cols) + format!("matrix × vector dimension mismatch: {rows}×{cols} matrix requires {cols}-component vector") ) .with_location(source_span_to_location(&span)) - .with_note(format!("got {}-component vector", vec_size))); + .with_note(format!("got {vec_size}-component vector"))); } // Result is a vector with same number of components as matrix rows return Ok(lhs_ty.matrix_column_type().unwrap()); @@ -114,11 +113,10 @@ pub fn infer_matrix_binary_result_type( if vec_size != rows { return Err(GlslError::new( ErrorCode::E0106, - format!("vector × matrix dimension mismatch: {}-component vector requires {}×{} matrix", - vec_size, rows, cols) + format!("vector × matrix dimension mismatch: {vec_size}-component vector requires {rows}×{cols} matrix") ) .with_location(source_span_to_location(&span)) - .with_note(format!("got {}×{} matrix", rows, cols))); + .with_note(format!("got {rows}×{cols} matrix"))); } // Result is a vector with same number of components as matrix columns // For vec3 × mat3, result is vec3 (but conceptually row vector) @@ -135,8 +133,7 @@ pub fn infer_matrix_binary_result_type( return Err(GlslError::new( ErrorCode::E0106, format!( - "matrix × matrix dimension mismatch: {}×{} × {}×{} requires {} == {}", - lhs_rows, lhs_cols, rhs_rows, rhs_cols, lhs_cols, rhs_rows + "matrix × matrix dimension mismatch: {lhs_rows}×{lhs_cols} × {rhs_rows}×{rhs_cols} requires {lhs_cols} == {rhs_rows}" ), ) .with_location(source_span_to_location(&span))); @@ -190,8 +187,7 @@ pub fn infer_matrix_binary_result_type( ) .with_location(source_span_to_location(&span)) .with_note(format!( - "left operand: `{:?}`, right operand: `{:?}`", - lhs_ty, rhs_ty + "left operand: `{lhs_ty:?}`, right operand: `{rhs_ty:?}`" ))); } return Ok(Type::Bool); @@ -205,7 +201,7 @@ pub fn infer_matrix_binary_result_type( _ => Err(GlslError::new( ErrorCode::E0106, - format!("operator {:?} not supported for matrices", op), + format!("operator {op:?} not supported for matrices"), ) .with_location(source_span_to_location(&span))), } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/operators.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/operators.rs index 4ff451ccc..53f8729b6 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/operators.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/operators.rs @@ -8,6 +8,8 @@ use glsl::syntax::{BinaryOp, SourceSpan, UnaryOp}; use super::conversion::promote_numeric; use super::matrix; +use alloc::format; + /// Infer result type of binary operation (with implicit conversion) /// Implements GLSL spec: operators.adoc:775-855, operators.adoc:1019-1098 (matrix ops) pub fn infer_binary_result_type( @@ -34,8 +36,7 @@ pub fn infer_binary_result_type( return Err(GlslError::new( ErrorCode::E0106, format!( - "vector operation requires matching types, got {:?} and {:?}", - lhs_ty, rhs_ty + "vector operation requires matching types, got {lhs_ty:?} and {rhs_ty:?}" ), ) .with_location(source_span_to_location(&span))); @@ -49,7 +50,7 @@ pub fn infer_binary_result_type( if !rhs_ty.is_numeric() || !vec_base.is_numeric() { return Err(GlslError::new( ErrorCode::E0106, - format!("cannot use {:?} with {:?}", rhs_ty, lhs_ty), + format!("cannot use {rhs_ty:?} with {lhs_ty:?}"), ) .with_location(source_span_to_location(&span))); } @@ -61,7 +62,7 @@ pub fn infer_binary_result_type( if !lhs_ty.is_numeric() || !vec_base.is_numeric() { return Err(GlslError::new( ErrorCode::E0106, - format!("cannot use {:?} with {:?}", lhs_ty, rhs_ty), + format!("cannot use {lhs_ty:?} with {rhs_ty:?}"), ) .with_location(source_span_to_location(&span))); } @@ -73,12 +74,11 @@ pub fn infer_binary_result_type( if !lhs_ty.is_numeric() || !rhs_ty.is_numeric() { return Err(GlslError::new( ErrorCode::E0106, - format!("arithmetic operator {:?} requires numeric operands", op), + format!("arithmetic operator {op:?} requires numeric operands"), ) .with_location(source_span_to_location(&span)) .with_note(format!( - "left operand has type `{:?}`, right operand has type `{:?}`", - lhs_ty, rhs_ty + "left operand has type `{lhs_ty:?}`, right operand has type `{rhs_ty:?}`" ))); } // Result type is the promoted type @@ -115,8 +115,7 @@ pub fn infer_binary_result_type( return Err(GlslError::new( ErrorCode::E0106, format!( - "modulo operator requires integer operands, got {:?} and {:?}", - lhs_ty, rhs_ty + "modulo operator requires integer operands, got {lhs_ty:?} and {rhs_ty:?}" ), ) .with_location(source_span_to_location(&span))); @@ -139,8 +138,7 @@ pub fn infer_binary_result_type( return Err(GlslError::new( ErrorCode::E0106, format!( - "vector modulo requires matching types, got {:?} and {:?}", - lhs_ty, rhs_ty + "vector modulo requires matching types, got {lhs_ty:?} and {rhs_ty:?}" ), ) .with_location(source_span_to_location(&span))); @@ -156,10 +154,7 @@ pub fn infer_binary_result_type( { return Err(GlslError::new( ErrorCode::E0106, - format!( - "modulo requires integer types, got {:?} and {:?}", - lhs_ty, rhs_ty - ), + format!("modulo requires integer types, got {lhs_ty:?} and {rhs_ty:?}"), ) .with_location(source_span_to_location(&span))); } @@ -173,10 +168,7 @@ pub fn infer_binary_result_type( { return Err(GlslError::new( ErrorCode::E0106, - format!( - "modulo requires integer types, got {:?} and {:?}", - lhs_ty, rhs_ty - ), + format!("modulo requires integer types, got {lhs_ty:?} and {rhs_ty:?}"), ) .with_location(source_span_to_location(&span))); } @@ -196,8 +188,7 @@ pub fn infer_binary_result_type( return Err(GlslError::new( ErrorCode::E0106, format!( - "modulo operator requires integer operands, got {:?} and {:?}", - lhs_ty, rhs_ty + "modulo operator requires integer operands, got {lhs_ty:?} and {rhs_ty:?}" ), ) .with_location(source_span_to_location(&span))); @@ -213,12 +204,11 @@ pub fn infer_binary_result_type( if lhs_ty != rhs_ty { return Err(GlslError::new( ErrorCode::E0106, - format!("equality operator {:?} requires matching types", op), + format!("equality operator {op:?} requires matching types"), ) .with_location(source_span_to_location(&span)) .with_note(format!( - "left operand has type `{:?}`, right operand has type `{:?}`", - lhs_ty, rhs_ty + "left operand has type `{lhs_ty:?}`, right operand has type `{rhs_ty:?}`" ))); } Ok(Type::Bool) @@ -228,12 +218,11 @@ pub fn infer_binary_result_type( if !lhs_ty.is_numeric() || !rhs_ty.is_numeric() { return Err(GlslError::new( ErrorCode::E0106, - format!("comparison operator {:?} requires numeric operands", op), + format!("comparison operator {op:?} requires numeric operands"), ) .with_location(source_span_to_location(&span)) .with_note(format!( - "left operand has type `{:?}`, right operand has type `{:?}`", - lhs_ty, rhs_ty + "left operand has type `{lhs_ty:?}`, right operand has type `{rhs_ty:?}`" ))); } Ok(Type::Bool) @@ -244,12 +233,11 @@ pub fn infer_binary_result_type( if lhs_ty != &Type::Bool || rhs_ty != &Type::Bool { return Err(GlslError::new( ErrorCode::E0106, - format!("logical operator {:?} requires bool operands", op), + format!("logical operator {op:?} requires bool operands"), ) .with_location(source_span_to_location(&span)) .with_note(format!( - "left operand has type `{:?}`, right operand has type `{:?}`", - lhs_ty, rhs_ty + "left operand has type `{lhs_ty:?}`, right operand has type `{rhs_ty:?}`" ))); } Ok(Type::Bool) @@ -257,7 +245,7 @@ pub fn infer_binary_result_type( _ => Err(GlslError::new( ErrorCode::E0112, - format!("unsupported binary operator: {:?}", op), + format!("unsupported binary operator: {op:?}"), ) .with_location(source_span_to_location(&span))), } @@ -279,7 +267,7 @@ pub fn infer_unary_result_type( "unary minus requires numeric operand", ) .with_location(source_span_to_location(&span)) - .with_note(format!("operand has type `{:?}`", operand_ty))); + .with_note(format!("operand has type `{operand_ty:?}`"))); } Ok(operand_ty.clone()) } @@ -289,7 +277,7 @@ pub fn infer_unary_result_type( return Err( GlslError::new(ErrorCode::E0112, "logical NOT requires bool operand") .with_location(source_span_to_location(&span)) - .with_note(format!("operand has type `{:?}`", operand_ty)), + .with_note(format!("operand has type `{operand_ty:?}`")), ); } Ok(Type::Bool) @@ -302,14 +290,14 @@ pub fn infer_unary_result_type( "increment/decrement requires numeric operand (scalar, vector, or matrix)", ) .with_location(source_span_to_location(&span)) - .with_note(format!("operand has type `{:?}`", operand_ty))); + .with_note(format!("operand has type `{operand_ty:?}`"))); } Ok(operand_ty.clone()) } _ => Err(GlslError::new( ErrorCode::E0112, - format!("unsupported unary operator: {:?}", op), + format!("unsupported unary operator: {op:?}"), ) .with_location(source_span_to_location(&span))), } @@ -319,10 +307,8 @@ pub fn infer_unary_result_type( pub fn check_condition(cond_ty: &Type) -> Result<(), GlslError> { if cond_ty != &Type::Bool { return Err( - GlslError::new(ErrorCode::E0107, "condition must be bool type").with_note(format!( - "condition has type `{:?}`, expected `Bool`", - cond_ty - )), + GlslError::new(ErrorCode::E0107, "condition must be bool type") + .with_note(format!("condition has type `{cond_ty:?}`, expected `Bool`")), ); } Ok(()) @@ -338,7 +324,7 @@ pub fn infer_postinc_result_type(operand_ty: &Type, span: SourceSpan) -> Result< "post-increment requires numeric operand (scalar, vector, or matrix)", ) .with_location(source_span_to_location(&span)) - .with_note(format!("operand has type `{:?}`", operand_ty))); + .with_note(format!("operand has type `{operand_ty:?}`"))); } // Return same type as operand @@ -355,7 +341,7 @@ pub fn infer_preinc_result_type(operand_ty: &Type, span: SourceSpan) -> Result Result Result< "post-decrement requires numeric operand (scalar, vector, or matrix)", ) .with_location(source_span_to_location(&span)) - .with_note(format!("operand has type `{:?}`", operand_ty))); + .with_note(format!("operand has type `{operand_ty:?}`"))); } // Return same type as operand diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/swizzle.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/swizzle.rs index ff35aa085..85ac40962 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/swizzle.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_check/swizzle.rs @@ -2,6 +2,8 @@ use crate::error::{ErrorCode, GlslError}; +use alloc::format; + /// Parse swizzle string and return the number of components /// Validates that the swizzle is valid for the given vector size pub fn parse_swizzle_length(swizzle: &str, max_components: usize) -> Result { @@ -32,7 +34,7 @@ pub fn parse_swizzle_length(swizzle: &str, max_components: usize) -> Result { return Err(GlslError::new( ErrorCode::E0113, - format!("invalid swizzle character: '{}'", ch), + format!("invalid swizzle character: '{ch}'"), )); } } @@ -42,10 +44,7 @@ pub fn parse_swizzle_length(swizzle: &str, max_components: usize) -> Result 1 { return Err(GlslError::new( ErrorCode::E0113, - format!( - "swizzle '{}' mixes component naming sets (xyzw/rgba/stpq)", - swizzle - ), + format!("swizzle '{swizzle}' mixes component naming sets (xyzw/rgba/stpq)"), )); } @@ -67,7 +66,7 @@ pub fn parse_swizzle_length(swizzle: &str, max_components: usize) -> Result { return Err(GlslError::new( ErrorCode::E0113, - format!("invalid component '{}'", ch), + format!("invalid component '{ch}'"), )); } }; @@ -75,10 +74,7 @@ pub fn parse_swizzle_length(swizzle: &str, max_components: usize) -> Result= max_components { return Err(GlslError::new( ErrorCode::E0111, - format!( - "component '{}' not valid for vector with {} components", - ch, max_components - ), + format!("component '{ch}' not valid for vector with {max_components} components"), )); } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_resolver.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_resolver.rs index 6ed8b93f7..38b399a64 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_resolver.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/type_resolver.rs @@ -2,8 +2,7 @@ use crate::error::{GlslError, source_span_to_location}; use crate::frontend::semantic::types; -use alloc::boxed::Box; -use alloc::vec::Vec; +use alloc::{boxed::Box, format, vec::Vec}; /// Parse array dimensions from an ArraySpecifier /// Returns a vector of dimension sizes (outermost-first) diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/types.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/types.rs index c8d07324f..11eb07dbc 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/types.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/types.rs @@ -1,5 +1,4 @@ -use alloc::boxed::Box; -use alloc::vec::Vec; +use alloc::{boxed::Box, format, vec::Vec}; /// GLSL type system /// Phase 1: Only Int and Bool are fully supported #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -218,7 +217,7 @@ impl Type { } _ => Err(crate::error::GlslError::new( crate::error::ErrorCode::E0109, - format!("Type not yet supported for codegen: {:?}", self), + format!("Type not yet supported for codegen: {self:?}"), )), } } diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/validator.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/validator.rs index 4b93f12b4..3602e6f5f 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/validator.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/semantic/validator.rs @@ -99,7 +99,7 @@ fn validate_simple_statement( } _ => Err(GlslError::new( ErrorCode::E0400, - format!("unsupported statement type in validation: {:?}", stmt), + format!("unsupported statement type in validation: {stmt:?}"), )), } } @@ -364,14 +364,12 @@ fn validate_jump( let error = GlslError::new( ErrorCode::E0116, format!( - "return type mismatch: expected `{:?}`, found `{:?}`", - return_type, expr_type + "return type mismatch: expected `{return_type:?}`, found `{expr_type:?}`" ), ) .with_location(source_span_to_location(&expr_span)) .with_note(format!( - "function returns `{:?}` but expression has type `{:?}`", - return_type, expr_type + "function returns `{return_type:?}` but expression has type `{expr_type:?}`" )); return Err(add_span_text_to_error(error, Some(source), &expr_span)); } @@ -382,10 +380,7 @@ fn validate_jump( if *return_type != Type::Void { return Err(GlslError::new( ErrorCode::E0116, - format!( - "return type mismatch: expected `{:?}`, found `Void`", - return_type - ), + format!("return type mismatch: expected `{return_type:?}`, found `Void`"), )); } Ok(()) diff --git a/lp-glsl/crates/lp-glsl-compiler/src/frontend/src_loc.rs b/lp-glsl/crates/lp-glsl-compiler/src/frontend/src_loc.rs index 86c4fc9ed..7aa070832 100644 --- a/lp-glsl/crates/lp-glsl-compiler/src/frontend/src_loc.rs +++ b/lp-glsl/crates/lp-glsl-compiler/src/frontend/src_loc.rs @@ -3,7 +3,7 @@ //! This module provides a comprehensive system for managing source locations //! that supports multiple files, intrinsics, and synthetic sources. -use alloc::{collections::BTreeMap, string::String, vec::Vec}; +use alloc::{collections::BTreeMap, format, string::String, vec::Vec}; #[cfg(feature = "std")] use std::path::PathBuf; @@ -273,12 +273,12 @@ impl GlSourceMap { for (idx, line) in source_lines.iter().enumerate() { let line_num = start_line + idx; if line_num == span.start_line { - source_display.push_str(&format!("{:>4} | {}\n", line_num, line)); + source_display.push_str(&format!("{line_num:>4} | {line}\n")); // Point to the column if it's valid let col_pos = span.start_column.saturating_sub(1).min(line.len()).min(200); source_display.push_str(&format!(" | {}^ here\n", " ".repeat(col_pos))); } else { - source_display.push_str(&format!("{:>4} | {}\n", line_num, line)); + source_display.push_str(&format!("{line_num:>4} | {line}\n")); } } Some(String::from(source_display.trim_end())) @@ -297,9 +297,9 @@ mod tests { // Mock SourceSpan for testing (since we can't import glsl-parser in tests easily) struct MockSourceSpan { - #[allow(dead_code)] + #[allow(dead_code, reason = "Mock struct fields for testing")] pub line: usize, - #[allow(dead_code)] + #[allow(dead_code, reason = "Mock struct fields for testing")] pub column: usize, } diff --git a/lp-glsl/crates/lp-glsl-compiler/tests/testcase_reloc_panic.rs b/lp-glsl/crates/lp-glsl-compiler/tests/testcase_reloc_panic.rs index fbdf319c9..6c9b9ac88 100644 --- a/lp-glsl/crates/lp-glsl-compiler/tests/testcase_reloc_panic.rs +++ b/lp-glsl/crates/lp-glsl-compiler/tests/testcase_reloc_panic.rs @@ -72,7 +72,7 @@ vec4 main(vec2 fragCoord, vec2 outputSize, float time) { } Ok(Err(e)) => { // Compilation error - this is unexpected but not a panic - panic!("GLSL compilation failed (unexpected): {}", e); + panic!("GLSL compilation failed (unexpected): {e}"); } Err(_) => { // Panic occurred - this is the bug we're trying to fix diff --git a/lp-glsl/crates/lp-glsl-filetests/src/colors.rs b/lp-glsl/crates/lp-glsl-filetests/src/colors.rs index 0f462da59..192cad8a5 100644 --- a/lp-glsl/crates/lp-glsl-filetests/src/colors.rs +++ b/lp-glsl/crates/lp-glsl-filetests/src/colors.rs @@ -24,7 +24,7 @@ pub fn should_color() -> bool { /// Format text with color if colors are enabled. pub fn colorize(text: &str, color: &str) -> String { if should_color() { - format!("{}{}{}", color, text, RESET) + format!("{color}{text}{RESET}") } else { text.to_string() } diff --git a/lp-glsl/crates/lp-glsl-filetests/src/lib.rs b/lp-glsl/crates/lp-glsl-filetests/src/lib.rs index 065384740..86b031886 100644 --- a/lp-glsl/crates/lp-glsl-filetests/src/lib.rs +++ b/lp-glsl/crates/lp-glsl-filetests/src/lib.rs @@ -80,10 +80,7 @@ pub fn run_filetest_with_line_filter( .iter() .any(|directive| directive.line_number == line_number); if !has_matching_directive { - anyhow::bail!( - "line {} does not contain a valid run directive", - line_number - ); + anyhow::bail!("line {line_number} does not contain a valid run directive"); } } @@ -203,7 +200,7 @@ pub fn run(files: &[String]) -> anyhow::Result<()> { let spec = &test_specs[0]; let relative_path_str = relative_path(&spec.path, &filetests_dir); let display_path = if let Some(line) = spec.line_number { - format!("{}:{}", relative_path_str, line) + format!("{relative_path_str}:{line}") } else { relative_path_str }; @@ -219,10 +216,10 @@ pub fn run(files: &[String]) -> anyhow::Result<()> { } else if let Some(msg) = e.downcast_ref::<&'static str>() { msg.to_string() } else { - format!("{:?}", e) + format!("{e:?}") }; ( - Err(anyhow::anyhow!("panicked: {}", panic_msg)), + Err(anyhow::anyhow!("panicked: {panic_msg}")), test_run::TestCaseStats::default(), ) } @@ -232,7 +229,7 @@ pub fn run(files: &[String]) -> anyhow::Result<()> { Ok(()) => { println!( "{}", - colors::colorize(&format!("✓ {}", display_path), colors::GREEN) + colors::colorize(&format!("✓ {display_path}"), colors::GREEN) ); let elapsed = start_time.elapsed(); println!( @@ -243,7 +240,7 @@ pub fn run(files: &[String]) -> anyhow::Result<()> { } Err(_e) => { // Extract test expression and line number from error message - let error_str = format!("{:#}", _e); + let error_str = format!("{_e:#}"); let (test_expr, line_num) = extract_test_info_from_error(&error_str, spec.line_number); let filename_only = Path::new(&display_path) @@ -253,18 +250,18 @@ pub fn run(files: &[String]) -> anyhow::Result<()> { .to_string(); let failure_line = if let Some(expr) = test_expr { if let Some(line) = line_num { - format!("{}:{} {}", filename_only, line, expr) + format!("{filename_only}:{line} {expr}") } else { - format!("{} {}", filename_only, expr) + format!("{filename_only} {expr}") } } else { filename_only }; println!( "{}", - colors::colorize(&format!("✗ {}", failure_line), colors::RED) + colors::colorize(&format!("✗ {failure_line}"), colors::RED) ); - println!("\n{:#}", _e); + println!("\n{_e:#}"); let elapsed = start_time.elapsed(); println!( "\n{}", @@ -350,7 +347,7 @@ pub fn run(files: &[String]) -> anyhow::Result<()> { let spec = &tests[reported_tests].spec; let relative_path_str = relative_path(&spec.path, &filetests_dir); let display_path = if let Some(line) = spec.line_number { - format!("{}:{}", relative_path_str, line) + format!("{relative_path_str}:{line}") } else { relative_path_str }; @@ -395,12 +392,12 @@ pub fn run(files: &[String]) -> anyhow::Result<()> { String::new() }; let path_colored = if colors::should_color() { - format!("{}{} ", status_marker, counts_str) + format!("{status_marker}{counts_str} ") + &format!("{}{}{}", colors::DIM, display_path, colors::RESET) } else { - format!("{}{} {}", status_marker, counts_str, display_path) + format!("{status_marker}{counts_str} {display_path}") }; - println!("{}", path_colored); + println!("{path_colored}"); // Flush stdout to ensure output appears immediately use std::io::Write; let _ = std::io::stdout().flush(); @@ -425,12 +422,12 @@ pub fn run(files: &[String]) -> anyhow::Result<()> { String::new() }; let path_colored = if colors::should_color() { - format!("{}{} ", status_marker, counts_str) + format!("{status_marker}{counts_str} ") + &format!("{}{}{}", colors::DIM, display_path, colors::RESET) } else { - format!("{}{} {}", status_marker, counts_str, display_path) + format!("{status_marker}{counts_str} {display_path}") }; - println!("{}", path_colored); + println!("{path_colored}"); // Flush stdout to ensure output appears immediately use std::io::Write; let _ = std::io::stdout().flush(); @@ -494,7 +491,7 @@ pub fn run(files: &[String]) -> anyhow::Result<()> { for failed_test in &failed_tests { let relative_path = relative_path(&failed_test.path, &filetests_dir); let test_path = if let Some(line) = failed_test.line_number { - format!("{}:{}", relative_path, line) + format!("{relative_path}:{line}") } else { relative_path }; @@ -506,7 +503,7 @@ pub fn run(files: &[String]) -> anyhow::Result<()> { colors::RESET ); } else { - println!("scripts/glsl-filetests.sh {}", test_path); + println!("scripts/glsl-filetests.sh {test_path}"); } } } @@ -524,7 +521,7 @@ pub fn run(files: &[String]) -> anyhow::Result<()> { ); if failed > 0 { - anyhow::bail!("{} test file(s) failed", failed); + anyhow::bail!("{failed} test file(s) failed"); } Ok(()) @@ -569,7 +566,7 @@ fn expand_glob_patterns(pattern: &str, filetests_dir: &Path) -> Result { // Log warning but continue - eprintln!("Warning: glob pattern error: {}", e); + eprintln!("Warning: glob pattern error: {e}"); } } } @@ -657,14 +654,14 @@ fn extract_test_info_from_error( .split_whitespace() .next() .unwrap_or(""); - Some(format!("{} == {}", expr, expected)) + Some(format!("{expr} == {expected}")) } else if let Some(op_pos) = run_line.rfind(" ~= ") { let expr = run_line[..op_pos].trim(); let expected = run_line[op_pos + 4..] .split_whitespace() .next() .unwrap_or(""); - Some(format!("{} ~= {}", expr, expected)) + Some(format!("{expr} ~= {expected}")) } else { Some(run_line.to_string()) } @@ -691,11 +688,11 @@ fn format_results_summary( let time_str = if seconds < 1.0 { format!("{:.0}ms", elapsed.as_millis()) } else if seconds < 60.0 { - format!("{:.2}s", seconds) + format!("{seconds:.2}s") } else { let mins = elapsed.as_secs() / 60; let remaining_secs = elapsed.as_secs_f64() - (mins * 60) as f64; - format!("{}m {:.2}s", mins, remaining_secs) + format!("{mins}m {remaining_secs:.2}s") }; if colors::should_color() { @@ -731,9 +728,9 @@ fn format_results_summary( ); if total_test_cases > 0 { - format!("{}, {} in {}", test_cases_part, files_part, time_str) + format!("{test_cases_part}, {files_part} in {time_str}") } else { - format!("{} in {}", files_part, time_str) + format!("{files_part} in {time_str}") } } else { if total_test_cases > 0 { diff --git a/lp-glsl/crates/lp-glsl-filetests/src/parse/parse_run.rs b/lp-glsl/crates/lp-glsl-filetests/src/parse/parse_run.rs index 1a97d918a..adb51615a 100644 --- a/lp-glsl/crates/lp-glsl-filetests/src/parse/parse_run.rs +++ b/lp-glsl/crates/lp-glsl-filetests/src/parse/parse_run.rs @@ -24,10 +24,7 @@ pub fn parse_run_directive(line: &str, line_number: usize) -> Result Result<(String, Vec)> { } let close_paren_pos = close_paren_pos - .ok_or_else(|| anyhow::anyhow!("unmatched parentheses in expression: {}", expression))?; + .ok_or_else(|| anyhow::anyhow!("unmatched parentheses in expression: {expression}"))?; // Extract arguments string (between parentheses) let args_str = &args_str[..close_paren_pos]; @@ -138,7 +137,7 @@ pub fn parse_function_arguments(arg_strings: &[String]) -> Result fn format_glsl_value(value: &GlslValue) -> String { // TODO: Move this to util::file_update in Phase 4 // For now, use a simple format - format!("{:?}", value) + format!("{value:?}") } /// Compare actual and expected values. diff --git a/lp-glsl/crates/lp-glsl-filetests/src/test_run/run_detail.rs b/lp-glsl/crates/lp-glsl-filetests/src/test_run/run_detail.rs index b6c1e7713..dec9eaf8b 100644 --- a/lp-glsl/crates/lp-glsl-filetests/src/test_run/run_detail.rs +++ b/lp-glsl/crates/lp-glsl-filetests/src/test_run/run_detail.rs @@ -73,8 +73,8 @@ pub fn run( Ok(result) => result, Err(e) => { stats.failed += 1; - let error_msg = format!("failed to generate test GLSL: {}", e); - eprintln!("{}", error_msg); + let error_msg = format!("failed to generate test GLSL: {e}"); + eprintln!("{error_msg}"); errors.push(e.context(error_msg)); continue; } @@ -98,8 +98,8 @@ pub fn run( &relative_path, output_mode, ); - eprintln!("{}", formatted_error); - errors.push(anyhow::anyhow!("{}", formatted_error)); + eprintln!("{formatted_error}"); + errors.push(anyhow::anyhow!("{formatted_error}")); continue; } }; @@ -120,7 +120,7 @@ pub fn run( "failed to parse function call: {}", directive.expression_str ); - eprintln!("{}", error_msg); + eprintln!("{error_msg}"); errors.push(e.context(error_msg)); continue; } @@ -131,8 +131,8 @@ pub fn run( Ok(result) => result, Err(e) => { stats.failed += 1; - let error_msg = format!("failed to parse function arguments: {:?}", arg_strings); - eprintln!("{}", error_msg); + let error_msg = format!("failed to parse function arguments: {arg_strings:?}"); + eprintln!("{error_msg}"); errors.push(e.context(error_msg)); continue; } @@ -151,9 +151,9 @@ pub fn run( &format!( "expected trap but execution succeeded\n\nExpected: trap{}\nActual: value {}", if let Some(code) = exp.trap_code { - format!(" (code {})", code) + format!(" (code {code})") } else if let Some(ref msg) = exp.trap_message { - format!(" (message containing '{}')", msg) + format!(" (message containing '{msg}')") } else { String::new() }, @@ -166,13 +166,13 @@ pub fn run( output_mode, Some(&directive.expression_str), ); - eprintln!("{}", error_msg); - errors.push(anyhow::anyhow!("{}", error_msg)); + eprintln!("{error_msg}"); + errors.push(anyhow::anyhow!("{error_msg}")); continue; } (Err(e), None) => { // Got an error but didn't expect one - check if it's a trap - let error_str = format!("{:#}", e); + let error_str = format!("{e:#}"); let is_trap = error_str.contains("Trap:") || error_str.contains("trap") || error_str.contains("execution trapped"); @@ -185,8 +185,7 @@ pub fn run( let formatted_error = format_error( ErrorType::UnexpectedTrap, &format!( - "unexpected trap\n\nExpected: value\nActual: trap\n\nError details:\n{}", - error_msg + "unexpected trap\n\nExpected: value\nActual: trap\n\nError details:\n{error_msg}" ), &relative_path, directive.line_number, @@ -195,8 +194,8 @@ pub fn run( output_mode, Some(&directive.expression_str), ); - eprintln!("{}", formatted_error); - errors.push(anyhow::anyhow!("{}", formatted_error)); + eprintln!("{formatted_error}"); + errors.push(anyhow::anyhow!("{formatted_error}")); continue; } else { // Other error - format through unified formatter @@ -213,25 +212,24 @@ pub fn run( output_mode, Some(&directive.expression_str), ); - eprintln!("{}", formatted_error); - errors.push(anyhow::anyhow!("{}", formatted_error)); + eprintln!("{formatted_error}"); + errors.push(anyhow::anyhow!("{formatted_error}")); continue; } } (Err(e), Some(exp)) => { // Expected a trap and got one - verify it matches - let error_str = format!("{:#}", e); + let error_str = format!("{e:#}"); let error_msg = extract_error_message(&error_str); // Check trap code if specified if let Some(expected_code) = exp.trap_code { - if !error_str.contains(&format!("user{}", expected_code)) { + if !error_str.contains(&format!("user{expected_code}")) { stats.failed += 1; let formatted_error = format_error( ErrorType::TrapMismatch, &format!( - "trap code mismatch\n\nExpected: trap code {}\nActual trap: {}", - expected_code, error_msg + "trap code mismatch\n\nExpected: trap code {expected_code}\nActual trap: {error_msg}" ), &relative_path, directive.line_number, @@ -240,8 +238,8 @@ pub fn run( output_mode, Some(&directive.expression_str), ); - eprintln!("{}", formatted_error); - errors.push(anyhow::anyhow!("{}", formatted_error)); + eprintln!("{formatted_error}"); + errors.push(anyhow::anyhow!("{formatted_error}")); continue; } } @@ -253,8 +251,7 @@ pub fn run( let formatted_error = format_error( ErrorType::TrapMismatch, &format!( - "trap message mismatch\n\nExpected: trap message containing '{}'\nActual trap: {}", - expected_msg, error_msg + "trap message mismatch\n\nExpected: trap message containing '{expected_msg}'\nActual trap: {error_msg}" ), &relative_path, directive.line_number, @@ -263,8 +260,8 @@ pub fn run( output_mode, Some(&directive.expression_str), ); - eprintln!("{}", formatted_error); - errors.push(anyhow::anyhow!("{}", formatted_error)); + eprintln!("{formatted_error}"); + errors.push(anyhow::anyhow!("{formatted_error}")); continue; } } @@ -282,7 +279,7 @@ pub fn run( stats.failed += 1; let error_msg = format!("failed to parse expected value: {}", directive.expected_str); - eprintln!("{}", error_msg); + eprintln!("{error_msg}"); errors.push(e.context(error_msg)); continue; } @@ -325,7 +322,7 @@ pub fn run( colors::RESET ); } else { - eprintln!("✓ {} {}", file_line, test_expr); + eprintln!("✓ {file_line} {test_expr}"); } } } @@ -342,7 +339,7 @@ pub fn run( crate::parse::test_type::ComparisonOp::Approx => "~=", }; let tolerance_str = if let Some(tol) = directive.tolerance { - format!(" (tolerance: {})", tol) + format!(" (tolerance: {tol})") } else { String::new() }; @@ -371,8 +368,7 @@ pub fn run( ) } else { format!( - "{}\n\nexpected: {}\n actual: {}", - run_line, expected_formatted, actual_formatted + "{run_line}\n\nexpected: {expected_formatted}\n actual: {actual_formatted}" ) }; @@ -389,8 +385,8 @@ pub fn run( directive.expression_str, op_str, directive.expected_str )), ); - eprintln!("{}", formatted_error); - errors.push(anyhow::anyhow!("{}", formatted_error)); + eprintln!("{formatted_error}"); + errors.push(anyhow::anyhow!("{formatted_error}")); // } } } @@ -409,7 +405,7 @@ pub fn run( } summary }; - Err(anyhow::anyhow!("{}", error_summary)) + Err(anyhow::anyhow!("{error_summary}")) } else { Ok(()) }; @@ -441,8 +437,7 @@ fn format_compilation_error( format_error( ErrorType::Compilation, &format!( - "Compilation failed for test case at line {}:\n\nTest case: {}\n\n{}", - directive_line, expression, error + "Compilation failed for test case at line {directive_line}:\n\nTest case: {expression}\n\n{error}" ), relative_path, directive_line, @@ -485,15 +480,14 @@ fn format_error( // V-code if let Some(ref vcode) = exec.format_vcode() { - parts.push(format!("=== VCode ===\n{}", vcode)); + parts.push(format!("=== VCode ===\n{vcode}")); } // Transformed CLIF let (_original_clif, transformed_clif) = exec.format_clif_ir(); if let Some(ref transformed) = transformed_clif { parts.push(format!( - "=== CLIF IR (AFTER transformation) ===\n{}", - transformed + "=== CLIF IR (AFTER transformation) ===\n{transformed}" )); } @@ -501,8 +495,7 @@ fn format_error( let (original_clif, _transformed_clif) = exec.format_clif_ir(); if let Some(ref original) = original_clif { parts.push(format!( - "=== CLIF IR (BEFORE transformation) ===\n{}", - original + "=== CLIF IR (BEFORE transformation) ===\n{original}" )); } } @@ -533,8 +526,7 @@ fn format_error( "Rerun with debugging:".to_string() }; let rerun_section = format!( - "{}\n scripts/glsl-filetests.sh {}:{}\n\n{}\n DEBUG=1 scripts/glsl-filetests.sh {}:{}", - rerun_title, filename, line_number, debug_title, filename, line_number + "{rerun_title}\n scripts/glsl-filetests.sh {filename}:{line_number}\n\n{debug_title}\n DEBUG=1 scripts/glsl-filetests.sh {filename}:{line_number}" ); parts.push(rerun_section); diff --git a/lp-glsl/crates/lp-glsl-filetests/src/test_run/run_summary.rs b/lp-glsl/crates/lp-glsl-filetests/src/test_run/run_summary.rs index 1004c5d76..d6ea9e828 100644 --- a/lp-glsl/crates/lp-glsl-filetests/src/test_run/run_summary.rs +++ b/lp-glsl/crates/lp-glsl-filetests/src/test_run/run_summary.rs @@ -59,9 +59,7 @@ pub fn run( stats.passed = 0; return Ok(( Err(anyhow::anyhow!( - "Compilation failed for test file {}:\n\n{}", - relative_path, - e + "Compilation failed for test file {relative_path}:\n\n{e}" )), stats, )); @@ -138,7 +136,7 @@ pub fn run( } (Err(e), None) => { // Got an error but didn't expect one - check if it's a trap - let error_str = format!("{:#}", e); + let error_str = format!("{e:#}"); let is_trap = error_str.contains("Trap:") || error_str.contains("trap") || error_str.contains("execution trapped"); @@ -162,11 +160,11 @@ pub fn run( } (Err(_e), Some(exp)) => { // Expected a trap and got one - verify it matches - let error_str = format!("{:#}", _e); + let error_str = format!("{_e:#}"); // Check trap code if specified if let Some(expected_code) = exp.trap_code { - if !error_str.contains(&format!("user{}", expected_code)) { + if !error_str.contains(&format!("user{expected_code}")) { stats.failed += 1; if first_error.is_none() { first_error = Some(anyhow::anyhow!( diff --git a/lp-glsl/crates/lp-glsl-filetests/src/test_run/target.rs b/lp-glsl/crates/lp-glsl-filetests/src/test_run/target.rs index b33f9d404..fa7e67e39 100644 --- a/lp-glsl/crates/lp-glsl-filetests/src/test_run/target.rs +++ b/lp-glsl/crates/lp-glsl-filetests/src/test_run/target.rs @@ -16,10 +16,7 @@ const DEFAULT_MAX_INSTRUCTIONS: u64 = 1_000_000; pub fn parse_target(target: &str) -> Result<(RunMode, DecimalFormat)> { let parts: Vec<&str> = target.split('.').collect(); if parts.len() != 2 { - anyhow::bail!( - "invalid target format: expected '.', got '{}'", - target - ); + anyhow::bail!("invalid target format: expected '.', got '{target}'"); } let arch = parts[0]; @@ -31,13 +28,13 @@ pub fn parse_target(target: &str) -> Result<(RunMode, DecimalFormat)> { stack_size: DEFAULT_STACK_SIZE, max_instructions: DEFAULT_MAX_INSTRUCTIONS, }, - _ => anyhow::bail!("unsupported architecture: {}", arch), + _ => anyhow::bail!("unsupported architecture: {arch}"), }; let decimal_format = match format { "fixed32" => DecimalFormat::Fixed32, "float" => DecimalFormat::Float, - _ => anyhow::bail!("unsupported format: {}", format), + _ => anyhow::bail!("unsupported format: {format}"), }; Ok((run_mode, decimal_format)) diff --git a/lp-glsl/crates/lp-glsl-filetests/src/test_run/test_glsl.rs b/lp-glsl/crates/lp-glsl-filetests/src/test_run/test_glsl.rs index db724794b..b9f3f9d15 100644 --- a/lp-glsl/crates/lp-glsl-filetests/src/test_run/test_glsl.rs +++ b/lp-glsl/crates/lp-glsl-filetests/src/test_run/test_glsl.rs @@ -108,7 +108,7 @@ fn extract_function_name(expression: &str) -> Result { /// Search for function definition in source and extract its return type. /// Returns an error if the return type is not in KNOWN_TYPES. fn find_return_type_in_source(source: &str, func_name: &str) -> Result> { - let pattern = format!("{}(", func_name); + let pattern = format!("{func_name}("); for line in source.lines() { let trimmed = line.trim(); if let Some(pos) = trimmed.find(&pattern) { @@ -122,12 +122,8 @@ fn find_return_type_in_source(source: &str, func_name: &str) -> Result(); new_test.push_str(&format!( - "{}// run: {} {} {}\n", - indent, expression, op_str, expected_str + "{indent}// run: {expression} {op_str} {expected_str}\n" )); } else { // Malformed run directive, keep original @@ -131,9 +130,9 @@ impl FileUpdate { /// Format a float value with .0 suffix for whole numbers (matching GLSL literal format) fn format_float(f: f32) -> String { if f.fract() == 0.0 { - format!("{:.1}", f) + format!("{f:.1}") } else { - format!("{}", f) + format!("{f}") } } @@ -142,13 +141,13 @@ fn format_float(f: f32) -> String { pub fn format_glsl_value(value: &GlslValue) -> String { match value { GlslValue::I32(i) => i.to_string(), - GlslValue::U32(u) => format!("{}u", u), + GlslValue::U32(u) => format!("{u}u"), GlslValue::F32(f) => { // Format float with enough precision but avoid unnecessary decimals if f.fract() == 0.0 { - format!("{:.1}", f) + format!("{f:.1}") } else { - format!("{}", f) + format!("{f}") } } GlslValue::Bool(b) => b.to_string(), diff --git a/lp-glsl/crates/lp-glsl-filetests/src/util/test_utils.rs b/lp-glsl/crates/lp-glsl-filetests/src/util/test_utils.rs index 55dcde924..978932ebe 100644 --- a/lp-glsl/crates/lp-glsl-filetests/src/util/test_utils.rs +++ b/lp-glsl/crates/lp-glsl-filetests/src/util/test_utils.rs @@ -22,13 +22,13 @@ pub fn create_riscv32_isa() -> Result { let mut flag_builder = settings::builder(); flag_builder .set("is_pic", "false") - .map_err(|e| anyhow::anyhow!("failed to set is_pic: {}", e))?; + .map_err(|e| anyhow::anyhow!("failed to set is_pic: {e}"))?; flag_builder .set("use_colocated_libcalls", "false") - .map_err(|e| anyhow::anyhow!("failed to set use_colocated_libcalls: {}", e))?; + .map_err(|e| anyhow::anyhow!("failed to set use_colocated_libcalls: {e}"))?; flag_builder .set("enable_multi_ret_implicit_sret", "true") - .map_err(|e| anyhow::anyhow!("failed to set enable_multi_ret_implicit_sret: {}", e))?; + .map_err(|e| anyhow::anyhow!("failed to set enable_multi_ret_implicit_sret: {e}"))?; let flags = settings::Flags::new(flag_builder); let triple = Triple { @@ -41,5 +41,5 @@ pub fn create_riscv32_isa() -> Result { isa_builder(triple) .finish(flags) - .map_err(|e| anyhow::anyhow!("failed to create riscv32 ISA: {}", e)) + .map_err(|e| anyhow::anyhow!("failed to create riscv32 ISA: {e}")) } diff --git a/lp-glsl/crates/lp-glsl-filetests/src/util/validation.rs b/lp-glsl/crates/lp-glsl-filetests/src/util/validation.rs index ac6dc4c2e..456dd9382 100644 --- a/lp-glsl/crates/lp-glsl-filetests/src/util/validation.rs +++ b/lp-glsl/crates/lp-glsl-filetests/src/util/validation.rs @@ -20,7 +20,7 @@ pub fn validate_clif_module(module: &GlModule, isa: &dyn TargetIsa pretty_verifier_error(&gl_func.function, None, errors) ) }) - .with_context(|| format!("failed to validate function '{}'", name))?; + .with_context(|| format!("failed to validate function '{name}'"))?; } } diff --git a/lp-glsl/crates/lp-glsl-filetests/tests/filetests.rs b/lp-glsl/crates/lp-glsl-filetests/tests/filetests.rs index f42d8571c..a3ce797fb 100644 --- a/lp-glsl/crates/lp-glsl-filetests/tests/filetests.rs +++ b/lp-glsl/crates/lp-glsl-filetests/tests/filetests.rs @@ -8,49 +8,9 @@ use lp_glsl_filetests::{parse, run_filetest}; use std::path::PathBuf; use walkdir::WalkDir; -/// ANSI color codes for terminal output (matching Rust's test output style) -mod colors { - pub const GREEN: &str = "\x1b[32m"; - pub const RED: &str = "\x1b[31m"; - pub const RESET: &str = "\x1b[0m"; -} - -/// Check if colors should be enabled -/// Respects NO_COLOR environment variable -/// Colors are enabled by default (cargo test will handle TTY detection) -fn should_color() -> bool { - // Respect NO_COLOR environment variable (https://no-color.org/) - std::env::var("NO_COLOR").is_err() -} - -/// Print colored text if TTY, otherwise plain text -fn print_colored(text: &str, color: &str) { - if should_color() { - print!("{}{}{}", color, text, colors::RESET); - } else { - print!("{}", text); - } -} - -/// Print colored text with newline if TTY, otherwise plain text -fn println_colored(text: &str, color: &str) { - if should_color() { - println!("{}{}{}", color, text, colors::RESET); - } else { - println!("{}", text); - } -} - -/// Check if the builtins executable is available, returning an error if not. -fn check_builtins_executable() -> Result<()> { - // The builtins executable check will happen at runtime when tests try to compile. - // We can't easily check it here without accessing private modules, so we skip the check. - // Tests will fail with a clear error message if the builtins executable is missing. - Ok(()) -} - -/// Generate individual test functions for each .glsl file at runtime. -/// This allows `cargo test` to show each file as a separate test. +// Ignored: we do not want filetests to run as part of `cargo test`. +// They should be run separately using the `scripts/glsl-filetests.sh` script. +#[ignore] #[test] fn filetests() -> Result<()> { // Check builtins executable availability early @@ -101,12 +61,12 @@ fn filetests() -> Result<()> { let test_file = match parse::parse_test_file(path) { Ok(tf) => tf, Err(e) => { - print!("test {} ... ", relative_path); + print!("test {relative_path} ... "); println_colored("FAILED (parse error)", colors::RED); if should_color() { println!(" {}Error:{} {:#}", colors::RED, colors::RESET, e); } else { - println!(" Error: {:#}", e); + println!(" Error: {e:#}"); } failed += 1; continue; @@ -135,7 +95,7 @@ fn filetests() -> Result<()> { relative_path.to_string() }; - print!("test {} ... ", test_label); + print!("test {test_label} ... "); std::io::Write::flush(&mut std::io::stdout()).unwrap(); // Catch panics so one test failure doesn't stop others @@ -150,7 +110,7 @@ fn filetests() -> Result<()> { println_colored("FAILED", colors::RED); // Error is already fully formatted by GlslError::Display and format_compilation_error // Just display it directly - no reformatting needed - println!("\n{:#}", e); + println!("\n{e:#}"); failed += 1; } Err(panic_payload) => { @@ -163,7 +123,7 @@ fn filetests() -> Result<()> { } else { "unknown panic".to_string() }; - println!(" Panic: {}", panic_msg); + println!(" Panic: {panic_msg}"); failed += 1; } } @@ -172,15 +132,56 @@ fn filetests() -> Result<()> { print!("\ntest result: "); if failed == 0 { print_colored("ok", colors::GREEN); - println!(". {} passed; {} failed; 0 ignored", passed, failed); + println!(". {passed} passed; {failed} failed; 0 ignored"); } else { print_colored("FAILED", colors::RED); - println!(". {} passed; {} failed; 0 ignored", passed, failed); + println!(". {passed} passed; {failed} failed; 0 ignored"); } if failed > 0 { - anyhow::bail!("{} test file(s) failed", failed); + anyhow::bail!("{failed} test file(s) failed"); } Ok(()) } + +/// ANSI color codes for terminal output (matching Rust's test output style) +mod colors { + pub const GREEN: &str = "\x1b[32m"; + pub const RED: &str = "\x1b[31m"; + pub const RESET: &str = "\x1b[0m"; +} + +/// Check if colors should be enabled +/// Respects NO_COLOR environment variable +/// Colors are enabled by default (cargo test will handle TTY detection) +fn should_color() -> bool { + // Respect NO_COLOR environment variable (https://no-color.org/) + std::env::var("NO_COLOR").is_err() +} + +/// Print colored text if TTY, otherwise plain text +fn print_colored(text: &str, color: &str) { + if should_color() { + print!("{}{}{}", color, text, colors::RESET); + } else { + print!("{text}"); + } +} + +/// Print colored text with newline if TTY, otherwise plain text +fn println_colored(text: &str, color: &str) { + if should_color() { + println!("{}{}{}", color, text, colors::RESET); + } else { + println!("{text}"); + } +} + +/// Check if the builtins executable is available, returning an error if not. +fn check_builtins_executable() -> Result<()> { + // The builtins executable check will happen at runtime when tests try to compile. + // We can't easily check it here without accessing private modules, so we skip the check. + // Tests will fail with a clear error message if the builtins executable is missing. + Ok(()) +} diff --git a/lp-glsl/crates/lp-jit-util/src/call.rs b/lp-glsl/crates/lp-jit-util/src/call.rs index c04f1e866..da1ba5bd8 100644 --- a/lp-glsl/crates/lp-jit-util/src/call.rs +++ b/lp-glsl/crates/lp-jit-util/src/call.rs @@ -38,7 +38,7 @@ where // Dispatch to platform-specific implementation #[cfg(target_arch = "aarch64")] { - return match (call_conv, pointer_type) { + match (call_conv, pointer_type) { (CallConv::AppleAarch64, types::I64) => unsafe { call_structreturn_arm64_apple(func_ptr, buffer as *mut u8, buffer_size) }, @@ -49,7 +49,7 @@ where call_conv, pointer_type, }), - }; + } } #[cfg(target_arch = "riscv32")] @@ -152,18 +152,28 @@ where // Dispatch to platform-specific implementation #[cfg(target_arch = "aarch64")] { - return match (call_conv, pointer_type) { + match (call_conv, pointer_type) { (CallConv::AppleAarch64, types::I64) => unsafe { - call_structreturn_arm64_apple_with_args(func_ptr, buffer as *mut u8, buffer_size, args) + call_structreturn_arm64_apple_with_args( + func_ptr, + buffer as *mut u8, + buffer_size, + args, + ) }, (CallConv::SystemV, types::I64) => unsafe { - call_structreturn_arm64_systemv_with_args(func_ptr, buffer as *mut u8, buffer_size, args) + call_structreturn_arm64_systemv_with_args( + func_ptr, + buffer as *mut u8, + buffer_size, + args, + ) }, _ => Err(JitCallError::UnsupportedCallingConvention { call_conv, pointer_type, }), - }; + } } #[cfg(target_arch = "riscv32")] @@ -201,7 +211,7 @@ unsafe fn call_structreturn_arm64_apple_with_args( // On AppleAarch64, StructReturn uses x8 register // Regular arguments go in x0-x7, then stack // We'll use inline assembly to set up the call - + // Limit to reasonable number of arguments (8 register + some stack) if args.len() > 16 { return Err(JitCallError::UnsupportedCallingConvention { @@ -214,10 +224,10 @@ unsafe fn call_structreturn_arm64_apple_with_args( // Prepare arguments: first 8 go in x0-x7, rest on stack // StructReturn pointer goes in x8 // Function pointer goes in x9 (temp) - + // For simplicity, we'll use a macro to generate the call based on argument count // This is a bit verbose but ensures correct calling convention - + match args.len() { 0 => { asm!( @@ -470,7 +480,7 @@ unsafe fn call_structreturn_riscv32_with_args( // RISC-V32 SystemV: StructReturn pointer is first argument (a0) // Regular arguments follow in a1-a7, then stack // We need to construct a function pointer with the right signature - + // Limit to reasonable number of arguments if args.len() > 8 { return Err(JitCallError::UnsupportedCallingConvention { @@ -500,29 +510,74 @@ unsafe fn call_structreturn_riscv32_with_args( func(buffer, args[0] as u32, args[1] as u32, args[2] as u32); } 4 => { - let func: extern "C" fn(*mut u8, u32, u32, u32, u32) = core::mem::transmute(func_ptr); - func(buffer, args[0] as u32, args[1] as u32, args[2] as u32, args[3] as u32); + let func: extern "C" fn(*mut u8, u32, u32, u32, u32) = + core::mem::transmute(func_ptr); + func( + buffer, + args[0] as u32, + args[1] as u32, + args[2] as u32, + args[3] as u32, + ); } 5 => { - let func: extern "C" fn(*mut u8, u32, u32, u32, u32, u32) = core::mem::transmute(func_ptr); - func(buffer, args[0] as u32, args[1] as u32, args[2] as u32, args[3] as u32, args[4] as u32); + let func: extern "C" fn(*mut u8, u32, u32, u32, u32, u32) = + core::mem::transmute(func_ptr); + func( + buffer, + args[0] as u32, + args[1] as u32, + args[2] as u32, + args[3] as u32, + args[4] as u32, + ); } 6 => { - let func: extern "C" fn(*mut u8, u32, u32, u32, u32, u32, u32) = core::mem::transmute(func_ptr); - func(buffer, args[0] as u32, args[1] as u32, args[2] as u32, args[3] as u32, args[4] as u32, args[5] as u32); + let func: extern "C" fn(*mut u8, u32, u32, u32, u32, u32, u32) = + core::mem::transmute(func_ptr); + func( + buffer, + args[0] as u32, + args[1] as u32, + args[2] as u32, + args[3] as u32, + args[4] as u32, + args[5] as u32, + ); } 7 => { - let func: extern "C" fn(*mut u8, u32, u32, u32, u32, u32, u32, u32) = core::mem::transmute(func_ptr); - func(buffer, args[0] as u32, args[1] as u32, args[2] as u32, args[3] as u32, args[4] as u32, args[5] as u32, args[6] as u32); + let func: extern "C" fn(*mut u8, u32, u32, u32, u32, u32, u32, u32) = + core::mem::transmute(func_ptr); + func( + buffer, + args[0] as u32, + args[1] as u32, + args[2] as u32, + args[3] as u32, + args[4] as u32, + args[5] as u32, + args[6] as u32, + ); } _ => { // 8 arguments: first 7 in a1-a7, 8th on stack (but we'll pass all 8) - let func: extern "C" fn(*mut u8, u32, u32, u32, u32, u32, u32, u32, u32) = core::mem::transmute(func_ptr); - func(buffer, args[0] as u32, args[1] as u32, args[2] as u32, args[3] as u32, args[4] as u32, args[5] as u32, args[6] as u32, args[7] as u32); + let func: extern "C" fn(*mut u8, u32, u32, u32, u32, u32, u32, u32, u32) = + core::mem::transmute(func_ptr); + func( + buffer, + args[0] as u32, + args[1] as u32, + args[2] as u32, + args[3] as u32, + args[4] as u32, + args[5] as u32, + args[6] as u32, + args[7] as u32, + ); } } } - + Ok(()) } diff --git a/lp-glsl/crates/lp-jit-util/src/wrapper.rs b/lp-glsl/crates/lp-jit-util/src/wrapper.rs index ac1dbf17a..1637388d9 100644 --- a/lp-glsl/crates/lp-jit-util/src/wrapper.rs +++ b/lp-glsl/crates/lp-jit-util/src/wrapper.rs @@ -106,11 +106,17 @@ impl Clone for StructReturnWrapper { /// /// This is the primary API for wrapping StructReturn functions. /// +/// # Safety +/// - `func_ptr` must be a valid function pointer to a JIT-compiled function +/// - The function signature must match: `fn(*mut T) -> ()` with StructReturn +/// - The calling convention must match the one used when compiling the function +/// - `buffer_size` must be correct for the return type `T` +/// /// # Note /// This function is provided as a convenience API. The main `lp-glsl-compiler` crate uses /// `call_structreturn` directly for performance, but this wrapper can be useful /// for applications that prefer a simpler, higher-level interface. -pub fn wrap_structreturn_function( +pub unsafe fn wrap_structreturn_function( func_ptr: *const u8, buffer_size: usize, call_conv: CallConv, @@ -119,8 +125,9 @@ pub fn wrap_structreturn_function( where T: Copy + Default + 'static, { - let wrapper = - unsafe { StructReturnWrapper::new(func_ptr, buffer_size, call_conv, pointer_type)? }; + unsafe { + let wrapper = StructReturnWrapper::new(func_ptr, buffer_size, call_conv, pointer_type)?; - Ok(Box::new(move || wrapper.call())) + Ok(Box::new(move || wrapper.call())) + } } diff --git a/lp-glsl/crates/lp-jit-util/tests/wrapper.rs b/lp-glsl/crates/lp-jit-util/tests/wrapper.rs index 40e1bbba9..355afc2f6 100644 --- a/lp-glsl/crates/lp-jit-util/tests/wrapper.rs +++ b/lp-glsl/crates/lp-jit-util/tests/wrapper.rs @@ -39,7 +39,8 @@ fn test_wrapper_clone() { #[test] fn test_wrap_function() { let func_ptr = 0x1000 as *const u8; - let wrapped = - wrap_structreturn_function::(func_ptr, 3, CallConv::AppleAarch64, types::I64); + let wrapped = unsafe { + wrap_structreturn_function::(func_ptr, 3, CallConv::AppleAarch64, types::I64) + }; assert!(wrapped.is_ok()); } diff --git a/lp-glsl/crates/lp-riscv-tools/examples/simple_codegen.rs b/lp-glsl/crates/lp-riscv-tools/examples/simple_codegen.rs index 98eec647f..bd1096d08 100644 --- a/lp-glsl/crates/lp-riscv-tools/examples/simple_codegen.rs +++ b/lp-glsl/crates/lp-riscv-tools/examples/simple_codegen.rs @@ -69,16 +69,16 @@ fn main() { Ok(result) => { println!("✓ Function executed successfully!"); println!(" Input: 5 + 7"); - println!(" Result (a0): {}", result); + println!(" Result (a0): {result}"); if result == 12 { println!("\n✓ PASS: Result is correct!"); } else { - println!("\n✗ FAIL: Expected 12, got {}", result); + println!("\n✗ FAIL: Expected 12, got {result}"); } } Err(e) => { - println!("✗ Emulator error: {:?}", e); + println!("✗ Emulator error: {e:?}"); std::process::exit(1); } } diff --git a/lp-glsl/crates/lp-riscv-tools/src/decode.rs b/lp-glsl/crates/lp-riscv-tools/src/decode.rs index 78f2246c8..0dff67a48 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/decode.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/decode.rs @@ -74,8 +74,7 @@ pub fn decode_instruction(inst: u32) -> Result { (0x4, 0x10) => Ok(Inst::Sh2add { rd, rs1, rs2 }), (0x6, 0x10) => Ok(Inst::Sh3add { rd, rs1, rs2 }), _ => Err(format!( - "Unknown R-type instruction: funct3=0x{:x}, funct7=0x{:x}", - funct3, funct7 + "Unknown R-type instruction: funct3=0x{funct3:x}, funct7=0x{funct7:x}" )), } } @@ -184,8 +183,7 @@ pub fn decode_instruction(inst: u32) -> Result { 0x604 => Ok(Inst::Sextb { rd, rs1 }), 0x605 => Ok(Inst::Sexth { rd, rs1 }), _ => Err(format!( - "Unknown I-type instruction: funct3=0x{:x}, funct6=0x{:x}, funct12=0x{:x}", - funct3, funct6, funct12 + "Unknown I-type instruction: funct3=0x{funct3:x}, funct6=0x{funct6:x}, funct12=0x{funct12:x}" )), } } @@ -256,8 +254,7 @@ pub fn decode_instruction(inst: u32) -> Result { 0x287 => Ok(Inst::Orcb { rd, rs1 }), 0x687 => Ok(Inst::Brev8 { rd, rs1 }), _ => Err(format!( - "Unknown I-type instruction: funct3=0x{:x}, funct7=0x{:x}, funct6=0x{:x}, funct12=0x{:x}", - funct3, funct7, funct6, funct12 + "Unknown I-type instruction: funct3=0x{funct3:x}, funct7=0x{funct7:x}, funct6=0x{funct6:x}, funct12=0x{funct12:x}" )), } } @@ -265,8 +262,7 @@ pub fn decode_instruction(inst: u32) -> Result { } } _ => Err(format!( - "Unknown I-type arithmetic instruction: funct3=0x{:x}", - funct3 + "Unknown I-type arithmetic instruction: funct3=0x{funct3:x}" )), } } @@ -518,8 +514,7 @@ pub fn decode_instruction(inst: u32) -> Result { csr, }), _ => Err(format!( - "Unknown CSR instruction: funct3=0x{:x}, inst=0x{:08x}", - funct3, inst + "Unknown CSR instruction: funct3=0x{funct3:x}, inst=0x{inst:08x}" )), } } @@ -534,7 +529,7 @@ pub fn decode_instruction(inst: u32) -> Result { let funct5 = ((inst >> 27) & 0x1f) as u8; if funct3 != 0x2 { - return Err(format!("Unsupported atomic width: funct3=0x{:x}", funct3)); + return Err(format!("Unsupported atomic width: funct3=0x{funct3:x}")); } match funct5 { @@ -545,10 +540,10 @@ pub fn decode_instruction(inst: u32) -> Result { 0x04 => Ok(Inst::AmoxorW { rd, rs1, rs2 }), 0x0c => Ok(Inst::AmoandW { rd, rs1, rs2 }), 0x08 => Ok(Inst::AmoorW { rd, rs1, rs2 }), - _ => Err(format!("Unknown atomic instruction: funct5=0x{:x}", funct5)), + _ => Err(format!("Unknown atomic instruction: funct5=0x{funct5:x}")), } } - _ => Err(format!("Unknown opcode: 0x{:02x}", opcode)), + _ => Err(format!("Unknown opcode: 0x{opcode:02x}")), } } @@ -573,8 +568,7 @@ mod tests { } => { assert_eq!( decoded_rd, rd, - "Register mismatch: expected {:?}, got {:?}", - rd, decoded_rd + "Register mismatch: expected {rd:?}, got {decoded_rd:?}" ); // The decoded imm is the final i32 value (sign-extended and shifted) @@ -731,12 +725,11 @@ mod tests { match decoded { Inst::Srli { rd, rs1, imm } => { - assert_eq!(rd, Gpr::A0, "rd mismatch for shamt={}", shamt); - assert_eq!(rs1, Gpr::A1, "rs1 mismatch for shamt={}", shamt); + assert_eq!(rd, Gpr::A0, "rd mismatch for shamt={shamt}"); + assert_eq!(rs1, Gpr::A1, "rs1 mismatch for shamt={shamt}"); assert_eq!( imm, shamt, - "shift amount mismatch: expected {}, got {}", - shamt, imm + "shift amount mismatch: expected {shamt}, got {imm}" ); } _ => panic!("Expected SRLI, got {:?} for shamt={}", decoded, shamt), @@ -757,12 +750,11 @@ mod tests { match decoded { Inst::Srai { rd, rs1, imm } => { - assert_eq!(rd, Gpr::A0, "rd mismatch for shamt={}", shamt); - assert_eq!(rs1, Gpr::A1, "rs1 mismatch for shamt={}", shamt); + assert_eq!(rd, Gpr::A0, "rd mismatch for shamt={shamt}"); + assert_eq!(rs1, Gpr::A1, "rs1 mismatch for shamt={shamt}"); assert_eq!( imm, shamt, - "shift amount mismatch: expected {}, got {}", - shamt, imm + "shift amount mismatch: expected {shamt}, got {imm}" ); } _ => panic!("Expected SRAI, got {:?} for shamt={}", decoded, shamt), @@ -783,7 +775,7 @@ mod tests { Inst::Srli { rd, rs1, imm } => { assert_eq!(rd, Gpr::A0); assert_eq!(rs1, Gpr::A1); - assert_eq!(imm, 0, "Expected shift amount 0, got {}", imm); + assert_eq!(imm, 0, "Expected shift amount 0, got {imm}"); } _ => panic!("Expected SRLI, got {:?}", inst), } @@ -796,7 +788,7 @@ mod tests { Inst::Srli { rd, rs1, imm } => { assert_eq!(rd, Gpr::A0); assert_eq!(rs1, Gpr::A1); - assert_eq!(imm, 24, "Expected shift amount 24, got {}", imm); + assert_eq!(imm, 24, "Expected shift amount 24, got {imm}"); } _ => panic!("Expected SRLI, got {:?}", inst), } @@ -809,7 +801,7 @@ mod tests { Inst::Srai { rd, rs1, imm } => { assert_eq!(rd, Gpr::A0); assert_eq!(rs1, Gpr::A1); - assert_eq!(imm, 0, "Expected shift amount 0, got {}", imm); + assert_eq!(imm, 0, "Expected shift amount 0, got {imm}"); } _ => panic!("Expected SRAI, got {:?}", inst), } @@ -822,7 +814,7 @@ mod tests { Inst::Srai { rd, rs1, imm } => { assert_eq!(rd, Gpr::A0); assert_eq!(rs1, Gpr::A1); - assert_eq!(imm, 24, "Expected shift amount 24, got {}", imm); + assert_eq!(imm, 24, "Expected shift amount 24, got {imm}"); } _ => panic!("Expected SRAI, got {:?}", inst), } @@ -835,7 +827,7 @@ mod tests { Inst::Srai { rd, rs1, imm } => { assert_eq!(rd, Gpr::A0); assert_eq!(rs1, Gpr::A1); - assert_eq!(imm, 31, "Expected shift amount 31, got {}", imm); + assert_eq!(imm, 31, "Expected shift amount 31, got {imm}"); } _ => panic!("Expected SRAI, got {:?}", inst), } @@ -852,7 +844,7 @@ mod tests { let inst = decode_instruction(0x40055513).expect("Failed to decode"); match inst { Inst::Srai { imm, .. } => { - assert_eq!(imm, 0, "Expected shift amount 0, got {}", imm); + assert_eq!(imm, 0, "Expected shift amount 0, got {imm}"); } _ => panic!("Expected SRAI"), } @@ -861,7 +853,7 @@ mod tests { let inst = decode_instruction(0x41855513).expect("Failed to decode"); match inst { Inst::Srai { imm, .. } => { - assert_eq!(imm, 24, "Expected shift amount 24, got {}", imm); + assert_eq!(imm, 24, "Expected shift amount 24, got {imm}"); } _ => panic!("Expected SRAI"), } @@ -882,12 +874,11 @@ mod tests { match decoded { Inst::Srli { rd, rs1, imm } => { - assert_eq!(rd, Gpr::A0, "rd mismatch for shamt={}", shamt); - assert_eq!(rs1, Gpr::A1, "rs1 mismatch for shamt={}", shamt); + assert_eq!(rd, Gpr::A0, "rd mismatch for shamt={shamt}"); + assert_eq!(rs1, Gpr::A1, "rs1 mismatch for shamt={shamt}"); assert_eq!( imm, shamt, - "shift amount mismatch: expected {}, got {}", - shamt, imm + "shift amount mismatch: expected {shamt}, got {imm}" ); } _ => panic!("Expected SRLI, got {:?} for shamt={}", decoded, shamt), @@ -910,12 +901,11 @@ mod tests { match decoded { Inst::Srai { rd, rs1, imm } => { - assert_eq!(rd, Gpr::A0, "rd mismatch for shamt={}", shamt); - assert_eq!(rs1, Gpr::A1, "rs1 mismatch for shamt={}", shamt); + assert_eq!(rd, Gpr::A0, "rd mismatch for shamt={shamt}"); + assert_eq!(rs1, Gpr::A1, "rs1 mismatch for shamt={shamt}"); assert_eq!( imm, shamt, - "shift amount mismatch: expected {}, got {}", - shamt, imm + "shift amount mismatch: expected {shamt}, got {imm}" ); } _ => panic!("Expected SRAI, got {:?} for shamt={}", decoded, shamt), diff --git a/lp-glsl/crates/lp-riscv-tools/src/decode_rvc.rs b/lp-glsl/crates/lp-riscv-tools/src/decode_rvc.rs index 730a28662..8776133d5 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/decode_rvc.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/decode_rvc.rs @@ -29,8 +29,7 @@ fn decode_c0(inst: u16, funct3: u16) -> Result { 0b010 => decode_c_lw(inst), // C.LW 0b110 => decode_c_sw(inst), // C.SW _ => Err(format!( - "Unknown C0 instruction: funct3={:03b}, inst=0x{:04x}", - funct3, inst + "Unknown C0 instruction: funct3={funct3:03b}, inst=0x{inst:04x}" )), } } @@ -58,8 +57,7 @@ fn decode_c2(inst: u16, funct3: u16) -> Result { 0b100 => decode_c_misc_cr(inst), // C.JR, C.MV, C.JALR, C.ADD 0b110 => decode_c_swsp(inst), // C.SWSP _ => Err(format!( - "Unknown C2 instruction: funct3={:03b}, inst=0x{:04x}", - funct3, inst + "Unknown C2 instruction: funct3={funct3:03b}, inst=0x{inst:04x}" )), } } @@ -268,8 +266,7 @@ fn decode_c_misc_alu(inst: u16) -> Result { (0b100011, 0b10) => Ok(Inst::COr { rd, rs }), (0b100011, 0b11) => Ok(Inst::CAnd { rd, rs }), _ => Err(format!( - "Unknown C.MISC_ALU instruction: funct6={:06b}, inst=0x{:04x}", - funct6, inst + "Unknown C.MISC_ALU instruction: funct6={funct6:06b}, inst=0x{inst:04x}" )), } } @@ -380,8 +377,7 @@ fn decode_c_misc_cr(inst: u16) -> Result { }) } _ => Err(format!( - "Unknown C.MISC_CR instruction: funct4={:04b}, rd_rs1={}, rs2={}, inst=0x{:04x}", - funct4, rd_rs1, rs2, inst + "Unknown C.MISC_CR instruction: funct4={funct4:04b}, rd_rs1={rd_rs1}, rs2={rs2}, inst=0x{inst:04x}" )), } } diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_linker.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_linker.rs index cdb9e686e..ad3926573 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_linker.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_linker.rs @@ -18,8 +18,8 @@ pub enum LinkerError { impl core::fmt::Display for LinkerError { fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { match self { - LinkerError::ParseError(msg) => write!(f, "Parse error: {}", msg), - LinkerError::WriteError(msg) => write!(f, "Write error: {}", msg), + LinkerError::ParseError(msg) => write!(f, "Parse error: {msg}"), + LinkerError::WriteError(msg) => write!(f, "Write error: {msg}"), } } } @@ -30,14 +30,14 @@ impl std::error::Error for LinkerError {} // Implement From for convenience impl From for LinkerError { fn from(err: object::Error) -> Self { - LinkerError::ParseError(format!("{}", err)) + LinkerError::ParseError(format!("{err}")) } } // Implement From for convenience impl From for LinkerError { fn from(err: object::write::Error) -> Self { - LinkerError::WriteError(format!("{}", err)) + LinkerError::WriteError(format!("{err}")) } } diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/layout.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/layout.rs index 276777c8b..562303bc1 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/layout.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/layout.rs @@ -1,7 +1,6 @@ //! Memory layout calculation for ELF loading. use super::memory::{RAM_START, is_ram_address, is_rom_address}; -use crate::debug; use alloc::string::String; use object::{Object, ObjectSection}; @@ -12,7 +11,7 @@ pub struct MemoryLayout { /// Size of RAM buffer needed (in bytes) pub ram_size: usize, /// Entry point address - #[allow(unused)] + #[allow(unused, reason = "Reserved for future use in entry point handling")] pub entry_point: u32, } diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/memory.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/memory.rs index 1f7d3d497..8b40dc346 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/memory.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/memory.rs @@ -23,7 +23,7 @@ pub fn ram_address_to_offset(addr: u64) -> usize { /// Convert a RAM offset to an absolute address. #[inline] -#[allow(unused)] +#[allow(unused, reason = "Utility function for address conversion")] pub fn ram_offset_to_address(offset: usize) -> u64 { RAM_START as u64 + offset as u64 } diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/mod.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/mod.rs index 333f34ec1..82d4d63b9 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/mod.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/mod.rs @@ -13,7 +13,6 @@ mod relocations; mod sections; mod symbols; -use crate::debug; use ::object::{Object, ObjectSection}; use alloc::string::String; use alloc::vec; @@ -179,31 +178,30 @@ mod tests { } } - // Try both debug and release profiles - for profile in ["debug", "release"].iter() { - // Path to the executable - // Try both workspace root and lightplayer/ subdirectory - let exe_path = current_dir - .join("../../../../../lp-app") + // Only use release builds (filetests-setup builds in release mode) + let profile = "release"; + // Path to the executable + // Try both workspace root and lightplayer/ subdirectory + let exe_path = current_dir + .join("../../../../../lp-app") + .join("target") + .join(target) + .join(profile) + .join("lp-builtins-app"); + + // If not found, try workspace root directly (for when running from lightplayer/) + let exe_path = if exe_path.exists() { + exe_path + } else { + current_dir .join("target") .join(target) .join(profile) - .join("lp-builtins-app"); + .join("lp-builtins-app") + }; - // If not found, try workspace root directly (for when running from lightplayer/) - let exe_path = if exe_path.exists() { - exe_path - } else { - current_dir - .join("target") - .join(target) - .join(profile) - .join("lp-builtins-app") - }; - - if exe_path.exists() { - return std::fs::read(&exe_path).ok(); - } + if exe_path.exists() { + return std::fs::read(&exe_path).ok(); } None @@ -259,10 +257,7 @@ mod tests { let max_steps = 10000; loop { if steps >= max_steps { - println!( - "\n=== Emulator exceeded {} steps - possible infinite loop ===", - max_steps - ); + println!("\n=== Emulator exceeded {max_steps} steps - possible infinite loop ==="); println!("PC: 0x{:x}", emu.get_pc()); println!("\n=== Emulator State ==="); println!("{}", emu.dump_state()); @@ -282,12 +277,12 @@ mod tests { println!("Panic message: {}", panic_info.message); if let Some(ref file) = panic_info.file { if let Some(line) = panic_info.line { - println!(" at {}:{}", file, line); + println!(" at {file}:{line}"); } else { - println!(" at {}", file); + println!(" at {file}"); } } else if let Some(line) = panic_info.line { - println!(" at line {}", line); + println!(" at line {line}"); } else { println!( " (no file/line information available, PC: 0x{:x})", @@ -314,8 +309,8 @@ mod tests { } Err(e) => { println!("\n=== Emulator Error ==="); - println!("Error: {}", e); - println!("Steps executed: {}", steps); + println!("Error: {e}"); + println!("Steps executed: {steps}"); println!("PC: 0x{:x}", emu.get_pc()); println!("\n=== Emulator State ==="); println!("{}", emu.dump_state()); diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/layout.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/layout.rs index 1443794c7..f4fc4b2ba 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/layout.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/layout.rs @@ -2,7 +2,6 @@ extern crate alloc; -use crate::debug; use ::object::{Object, ObjectSection}; use alloc::string::String; diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/mod.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/mod.rs index 10940b62f..786e5bd7f 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/mod.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/mod.rs @@ -21,7 +21,6 @@ pub use symbols::{build_object_symbol_map, merge_symbol_maps}; extern crate alloc; -use crate::debug; use alloc::format; use alloc::string::String; use alloc::vec::Vec; @@ -98,7 +97,9 @@ pub fn load_object_file( build_object_symbol_map(&obj, layout.text_placement, layout.data_placement); // Step 7: Merge symbol maps (base takes precedence) - let merged_symbol_map = merge_symbol_maps(symbol_map, &obj_symbol_map); + // Fail fast if there are symbol conflicts + let merged_symbol_map = merge_symbol_maps(symbol_map, &obj_symbol_map) + .map_err(|e| format!("Symbol merge failed: {e}"))?; // Step 8: Apply relocations using merged symbol map apply_object_relocations(&obj, code, ram, &merged_symbol_map, §ion_placement)?; @@ -111,35 +112,41 @@ pub fn load_object_file( let init_address = obj_symbol_map.get("_init").copied(); if let Some(init_addr) = init_address { - // Update __USER_MAIN_PTR in ROM LMA (not RAM) so init code copies the correct value - // The .data section is loaded into ROM at LMA and copied to RAM by init code + // Update __USER_MAIN_PTR in both RAM and ROM LMA + // RAM update: needed because init code may have already run when base executable was loaded + // LMA update: needed for future init runs if let Some(&user_init_ptr_vma) = merged_symbol_map.get("__USER_MAIN_PTR") { if user_init_ptr_vma >= RAM_START { - // Find the .data section LMA by looking for __data_source_start symbol - // or by finding the .data section in the object file we parsed earlier - // Actually, we need to parse the base executable to find .data LMA - // For now, try to find __data_source_start in merged symbol map + let ram_offset = (user_init_ptr_vma - RAM_START) as usize; + + // Always update RAM directly (init code may have already run) + if ram_offset + 4 <= ram.len() { + ram[ram_offset..ram_offset + 4].copy_from_slice(&init_addr.to_le_bytes()); + } + + // Also update LMA if __data_source_start is available (for future init runs) + // Calculate offset within .data section, not from RAM_START if let Some(&data_source_start) = merged_symbol_map.get("__data_source_start") { - // __USER_MAIN_PTR is at RAM offset 0x0 (VMA 0x80000000) - // Calculate its offset within .data section - let ram_offset = (user_init_ptr_vma - RAM_START) as usize; - // The LMA is data_source_start + offset within section - let lma_offset = data_source_start as usize + ram_offset; - - if lma_offset + 4 > code.len() { - return Err(format!( - "__USER_MAIN_PTR LMA 0x{:x} is out of code buffer bounds (len={})", - lma_offset, - code.len() - )); - } - // Write init address as little-endian u32 to ROM LMA - code[lma_offset..lma_offset + 4].copy_from_slice(&init_addr.to_le_bytes()); - } else { - // Fallback: update RAM directly (will be overwritten by init code, but might work if init already ran) - let ram_offset = (user_init_ptr_vma - RAM_START) as usize; - if ram_offset + 4 <= ram.len() { - ram[ram_offset..ram_offset + 4].copy_from_slice(&init_addr.to_le_bytes()); + // Find __data_target_start to calculate offset within .data section + let data_offset = if let Some(&data_target_start) = + merged_symbol_map.get("__data_target_start") + { + // __USER_MAIN_PTR is at user_init_ptr_vma + // __data_target_start is the start of .data section + // Offset within .data = user_init_ptr_vma - data_target_start + (user_init_ptr_vma - data_target_start) as usize + } else { + // Fallback: assume __USER_MAIN_PTR is at start of .data (offset 0) + // This matches the linker script where __USER_MAIN_PTR is first in .data + 0 + }; + + // The LMA is data_source_start + offset within .data section + let lma_offset = data_source_start as usize + data_offset; + + if lma_offset + 4 <= code.len() { + // Write init address as little-endian u32 to ROM LMA + code[lma_offset..lma_offset + 4].copy_from_slice(&init_addr.to_le_bytes()); } } } diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/relocations.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/relocations.rs index c29c2b918..297932ab8 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/relocations.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/relocations.rs @@ -2,7 +2,6 @@ extern crate alloc; -use crate::debug; use ::object::{Object, ObjectSection}; use alloc::string::{String, ToString}; use alloc::vec::Vec; @@ -14,6 +13,21 @@ use super::super::relocations::{ }; use super::sections::ObjectSectionPlacement; +/// Normalize a section name by mapping subsections to their parent section. +/// For example, `.text._init` -> `.text`, `.data.foo` -> `.data`. +fn normalize_section_name(section_name: &str) -> String { + // If the section name contains a dot after the first dot, it's likely a subsection + // Map it to the parent section (everything before the second dot) + if let Some(second_dot) = section_name[1..].find('.') { + // Found a second dot, extract parent section name + let parent_end = 1 + second_dot; + section_name[..parent_end].to_string() + } else { + // No second dot, return as-is + section_name.to_string() + } +} + /// Apply relocations for object file. /// /// Applies all relocations in the object file using the merged symbol map @@ -171,15 +185,25 @@ pub fn apply_object_relocations( // Object files have section addresses starting at 0, so we can use section_vma from relocation let original_section_addr = reloc.section_vma; + // Normalize section name (e.g., ".text._init" -> ".text") for lookup + let normalized_section_name = normalize_section_name(&reloc.section_name); + // Get the adjusted section address and adjust the relocation let mut adjusted_reloc = reloc.clone(); - if let Some(adjusted_info) = adjusted_section_addrs.get(&reloc.section_name) { + if let Some(adjusted_info) = adjusted_section_addrs.get(&normalized_section_name) { // Calculate the adjustment: new_section_addr - original_section_addr let adjustment = adjusted_info.vma.wrapping_sub(original_section_addr); // Adjust the relocation address adjusted_reloc.address = (adjusted_reloc.address as u64).wrapping_add(adjustment) as u32; + // Update section name to normalized version for phase 2 lookup + adjusted_reloc.section_name = normalized_section_name; + } else { + debug!( + "Warning: Section '{}' (normalized: '{}') not found in section address map", + reloc.section_name, normalized_section_name + ); } adjusted_relocations.push(adjusted_reloc); diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/sections.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/sections.rs index c9f46369a..f530e3e9c 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/sections.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/sections.rs @@ -2,10 +2,8 @@ extern crate alloc; -use crate::debug; use ::object::{Object, ObjectSection}; -use alloc::format; -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use super::layout::ObjectLayout; @@ -50,7 +48,12 @@ pub fn load_object_sections( let data_start = layout.data_placement; let mut data_size = 0usize; - // Iterate through sections and load them + // First pass: collect all sections, separating main sections from subsections + let mut text_sections: Vec<(String, Vec)> = Vec::new(); + let mut data_sections: Vec<(String, Vec)> = Vec::new(); + let mut rodata_sections: Vec<(String, Vec)> = Vec::new(); + let mut bss_sections: Vec<(String, usize)> = Vec::new(); + for section in obj.sections() { let section_name = match section.name() { Ok(name) => name, @@ -70,93 +73,160 @@ pub fn load_object_sections( continue; } - match section_name { - ".text" => { - // Load .text section into code buffer - let data = section - .data() - .map_err(|e| format!("Failed to read .text section data: {}", e))?; - + if section_name == ".text" || section_name.starts_with(".text.") { + if section_kind == ::object::SectionKind::Text { + let data = match section.data() { + Ok(d) => d, + Err(_) => continue, + }; if !data.is_empty() { - // Ensure code buffer is large enough - let required_size = (text_start as usize) + data.len(); - if required_size > code.len() { - code.resize(required_size, 0); - } - - // Copy section data - code[text_start as usize..text_start as usize + data.len()] - .copy_from_slice(data); - - text_size = data.len(); + text_sections.push((section_name.to_string(), data.to_vec())); } } - ".data" => { - // Load .data section into RAM buffer - let data = section - .data() - .map_err(|e| format!("Failed to read .data section data: {}", e))?; - + } else if section_name == ".data" || section_name.starts_with(".data.") { + if section_kind == ::object::SectionKind::Data { + let data = match section.data() { + Ok(d) => d, + Err(_) => continue, + }; if !data.is_empty() { - // Ensure RAM buffer is large enough - let required_size = (data_start as usize) + data.len(); - if required_size > ram.len() { - ram.resize(required_size, 0); - } - - // Copy section data - ram[data_start as usize..data_start as usize + data.len()] - .copy_from_slice(data); - - data_size = data.len(); + data_sections.push((section_name.to_string(), data.to_vec())); } } - ".rodata" => { - // Load .rodata section into code buffer (after .text) - let data = section - .data() - .map_err(|e| format!("Failed to read .rodata section data: {}", e))?; - + } else if section_name == ".rodata" || section_name.starts_with(".rodata.") { + if section_kind == ::object::SectionKind::ReadOnlyData { + let data = match section.data() { + Ok(d) => d, + Err(_) => continue, + }; if !data.is_empty() { - // Place .rodata after .text - let rodata_start = text_start + text_size as u32; - let rodata_start_aligned = (rodata_start + 3) & !3; // Align to 4 bytes - - // Ensure code buffer is large enough - let required_size = (rodata_start_aligned as usize) + data.len(); - if required_size > code.len() { - code.resize(required_size, 0); - } - - // Copy section data - code[rodata_start_aligned as usize..rodata_start_aligned as usize + data.len()] - .copy_from_slice(data); - } - } - ".bss" => { - // Zero-initialize .bss section in RAM buffer (after .data) - if section_size > 0 { - // Place .bss after .data - let bss_start = data_start + data_size as u32; - let bss_start_aligned = (bss_start + 3) & !3; // Align to 4 bytes - - // Ensure RAM buffer is large enough - let required_size = (bss_start_aligned as usize) + section_size; - if required_size > ram.len() { - ram.resize(required_size, 0); - } else { - // Zero-initialize the .bss region - ram[bss_start_aligned as usize..bss_start_aligned as usize + section_size] - .fill(0); - } + rodata_sections.push((section_name.to_string(), data.to_vec())); } } - _ => { - // Skip other sections for now + } else if section_name == ".bss" || section_name.starts_with(".bss.") { + if section_kind == ::object::SectionKind::UninitializedData && section_size > 0 { + bss_sections.push((section_name.to_string(), section_size)); } } } + // Sort sections: main section first, then subsections alphabetically + text_sections.sort_by(|a, b| { + if a.0 == ".text" { + std::cmp::Ordering::Less + } else if b.0 == ".text" { + std::cmp::Ordering::Greater + } else { + a.0.cmp(&b.0) + } + }); + data_sections.sort_by(|a, b| { + if a.0 == ".data" { + std::cmp::Ordering::Less + } else if b.0 == ".data" { + std::cmp::Ordering::Greater + } else { + a.0.cmp(&b.0) + } + }); + + // Load .text sections (main + subsections) into code buffer + let mut current_text_offset = text_start as usize; + for (section_name, data) in &text_sections { + // Align to 4 bytes + current_text_offset = (current_text_offset + 3) & !3; + + // Ensure code buffer is large enough + let required_size = current_text_offset + data.len(); + if required_size > code.len() { + code.resize(required_size, 0); + } + + // Copy section data + code[current_text_offset..current_text_offset + data.len()].copy_from_slice(data); + + debug!( + "Loaded section '{}' at offset 0x{:x} ({} bytes)", + section_name, + current_text_offset, + data.len() + ); + + current_text_offset += data.len(); + text_size = current_text_offset - text_start as usize; + } + + // Load .data sections (main + subsections) into RAM buffer + let mut current_data_offset = data_start as usize; + for (section_name, data) in &data_sections { + // Align to 4 bytes + current_data_offset = (current_data_offset + 3) & !3; + + // Ensure RAM buffer is large enough + let required_size = current_data_offset + data.len(); + if required_size > ram.len() { + ram.resize(required_size, 0); + } + + // Copy section data + ram[current_data_offset..current_data_offset + data.len()].copy_from_slice(data); + + debug!( + "Loaded section '{}' at RAM offset 0x{:x} ({} bytes)", + section_name, + current_data_offset, + data.len() + ); + + current_data_offset += data.len(); + data_size = current_data_offset - data_start as usize; + } + + // Load .rodata sections into code buffer (after .text) + for (section_name, data) in &rodata_sections { + // Place .rodata after .text + let rodata_start = text_start + text_size as u32; + let rodata_start_aligned = (rodata_start + 3) & !3; // Align to 4 bytes + + // Ensure code buffer is large enough + let required_size = (rodata_start_aligned as usize) + data.len(); + if required_size > code.len() { + code.resize(required_size, 0); + } + + // Copy section data + code[rodata_start_aligned as usize..rodata_start_aligned as usize + data.len()] + .copy_from_slice(data); + + debug!( + "Loaded section '{}' at offset 0x{:x} ({} bytes)", + section_name, + rodata_start_aligned, + data.len() + ); + } + + // Load .bss sections into RAM buffer (after .data) + for (section_name, section_size) in &bss_sections { + // Place .bss after .data + let bss_start = data_start + data_size as u32; + let bss_start_aligned = (bss_start + 3) & !3; // Align to 4 bytes + + // Ensure RAM buffer is large enough + let required_size = (bss_start_aligned as usize) + section_size; + if required_size > ram.len() { + ram.resize(required_size, 0); + } else { + // Zero-initialize the .bss region + ram[bss_start_aligned as usize..bss_start_aligned as usize + section_size].fill(0); + } + + debug!( + "Initialized section '{}' at RAM offset 0x{:x} ({} bytes)", + section_name, bss_start_aligned, section_size + ); + } + debug!( "Object section loading complete: .text at 0x{:x} ({} bytes), .data at offset 0x{:x} ({} bytes)", text_start, text_size, data_start, data_size diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/symbols.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/symbols.rs index 37e61f4b7..33fd2721c 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/symbols.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/symbols.rs @@ -2,8 +2,8 @@ extern crate alloc; -use crate::debug; use ::object::{Object, ObjectSection, ObjectSymbol, SymbolSection}; +use alloc::format; use alloc::string::{String, ToString}; use alloc::vec::Vec; use hashbrown::HashMap; @@ -46,6 +46,10 @@ pub fn build_object_symbol_map( if name.is_empty() { continue; // Skip unnamed symbols } + // Skip compiler-internal symbols (start with $) + if name.starts_with('$') { + continue; + } let symbol_addr = symbol.address(); let symbol_section = symbol.section(); @@ -68,16 +72,72 @@ pub fn build_object_symbol_map( }; match section_name { - Some(".text") => { - // .text section symbol: adjust by text_placement - // symbol_addr is section-relative offset - text_placement.wrapping_add(symbol_addr as u32) + Some(name) if name == ".text" || name.starts_with(".text.") => { + // .text section or subsection symbol: adjust by text_placement + // Get section VMA to determine if symbol_addr is absolute or relative + let section_vma = symbol_section + .index() + .and_then(|idx| obj.section_by_index(idx).ok()) + .map(|s| s.address()) + .unwrap_or(0); + + // Calculate offset of this subsection within combined .text region + // We need to sum sizes of all .text sections before this one + let mut subsection_offset = 0u32; + if name != ".text" { + // This is a subsection - find its position + for section in obj.sections() { + if let Ok(sec_name) = section.name() { + if sec_name == ".text" || sec_name.starts_with(".text.") { + if sec_name == name { + break; // Found our subsection + } + // Add size of this section (aligned) + let sec_size = section.size() as usize; + subsection_offset = + (subsection_offset + sec_size as u32 + 3) & !3; + } + } + } + } + + if section_vma == 0 { + // Section starts at 0, so symbol_addr is section-relative offset + text_placement + .wrapping_add(subsection_offset) + .wrapping_add(symbol_addr as u32) + } else { + // Section has non-zero VMA - symbol_addr is absolute, need to subtract VMA first + let offset = (symbol_addr - section_vma) as u32; + text_placement + .wrapping_add(subsection_offset) + .wrapping_add(offset) + } } - Some(".data") => { - // .data section symbol: adjust by RAM_START + data_placement + Some(name) if name == ".data" || name.starts_with(".data.") => { + // .data section or subsection symbol: adjust by RAM_START + data_placement + // Calculate offset of this subsection within combined .data region + let mut subsection_offset = 0u32; + if name != ".data" { + // This is a subsection - find its position + for section in obj.sections() { + if let Ok(sec_name) = section.name() { + if sec_name == ".data" || sec_name.starts_with(".data.") { + if sec_name == name { + break; // Found our subsection + } + // Add size of this section (aligned) + let sec_size = section.size() as usize; + subsection_offset = + (subsection_offset + sec_size as u32 + 3) & !3; + } + } + } + } // symbol_addr is section-relative offset RAM_START .wrapping_add(data_placement) + .wrapping_add(subsection_offset) .wrapping_add(symbol_addr as u32) } Some(".rodata") => { @@ -134,6 +194,7 @@ pub fn build_object_symbol_map( /// Merge base and object symbol maps. /// /// Combines symbol maps, with base symbols taking precedence over object symbols. +/// Detects conflicts and returns an error if a symbol exists in both maps with different addresses. /// /// # Arguments /// @@ -142,16 +203,36 @@ pub fn build_object_symbol_map( /// /// # Returns /// -/// Merged symbol map with base symbols taking precedence. +/// Merged symbol map with base symbols taking precedence, or error if conflicts detected. pub fn merge_symbol_maps( base_map: &HashMap, obj_map: &HashMap, -) -> HashMap { - // TODO: Phase 6 - Implement symbol map merging - // For now, just return base map to allow compilation +) -> Result, String> { let mut merged = base_map.clone(); - for (name, addr) in obj_map { - merged.entry(name.clone()).or_insert(*addr); + let mut conflicts = Vec::new(); + + for (name, obj_addr) in obj_map { + if let Some(&base_addr) = base_map.get(name) { + // Only report conflict if both symbols are defined (non-zero addresses) + // Undefined symbols (0x0) in object file should resolve to base executable + if base_addr != *obj_addr && *obj_addr != 0 { + conflicts.push(format!( + "Symbol '{name}' conflict: base executable has 0x{base_addr:08x}, object file has 0x{obj_addr:08x}" + )); + } + // Keep base version (already in merged) + } else { + // New symbol from object file - add it + merged.insert(name.clone(), *obj_addr); + } } - merged + + if !conflicts.is_empty() { + return Err(format!( + "Symbol conflicts detected during linking:\n {}", + conflicts.join("\n ") + )); + } + + Ok(merged) } diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/tests.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/tests.rs index c8e982fc9..ffd8c7af3 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/tests.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/object/tests.rs @@ -42,11 +42,11 @@ mod tests { // Create temporary source file let temp_dir = current_dir.join("target").join("test-objects"); fs::create_dir_all(&temp_dir).ok()?; - let source_path = temp_dir.join(std::format!("{}.rs", name)); + let source_path = temp_dir.join(std::format!("{name}.rs")); fs::write(&source_path, source).ok()?; // Compile to object file - let obj_path = temp_dir.join(std::format!("{}.o", name)); + let obj_path = temp_dir.join(std::format!("{name}.o")); let output = Command::new("rustc") .args(&[ "--target", @@ -98,28 +98,27 @@ mod tests { } } - // Try both debug and release profiles - for profile in ["debug", "release"].iter() { - let exe_path = current_dir - .join("../../../../../../lp-app") + // Only use release builds (filetests-setup builds in release mode) + let profile = "release"; + let exe_path = current_dir + .join("../../../../../../lp-app") + .join("target") + .join(target) + .join(profile) + .join("lp-builtins-app"); + + let exe_path = if exe_path.exists() { + exe_path + } else { + current_dir .join("target") .join(target) .join(profile) - .join("lp-builtins-app"); + .join("lp-builtins-app") + }; - let exe_path = if exe_path.exists() { - exe_path - } else { - current_dir - .join("target") - .join(target) - .join(profile) - .join("lp-builtins-app") - }; - - if exe_path.exists() { - return std::fs::read(&exe_path).ok(); - } + if exe_path.exists() { + return std::fs::read(&exe_path).ok(); } None @@ -195,7 +194,7 @@ mod tests { // Verify _init symbol was found if let Some(init_addr) = obj_info.init_address { assert!(init_addr > 0, "Init address should be valid"); - println!("Object file _init() found at 0x{:x}", init_addr); + println!("Object file _init() found at 0x{init_addr:x}"); } else { println!("No _init symbol found in object file (this is OK for some object files)"); } @@ -330,10 +329,7 @@ mod tests { // Verify last _init wins if let Some(init_addr) = obj2_info.init_address { - println!( - "Second object file's _init() at 0x{:x} (last one wins)", - init_addr - ); + println!("Second object file's _init() at 0x{init_addr:x} (last one wins)"); } } @@ -546,7 +542,7 @@ mod tests { // Handle halt result if let StepResult::Halted = step_result { - println!("Emulator halted at step {}", steps); + println!("Emulator halted at step {steps}"); break; } @@ -554,13 +550,14 @@ mod tests { last_a0 = emu.get_register(Gpr::A0); // Check if we've jumped into __lp_fixed32_sqrt (function was called) - if pc_after >= *sqrt_addr && pc_after < *sqrt_addr + 100 { + // Use a wider range to catch the call (functions can be larger than 100 bytes) + if pc_after >= *sqrt_addr && pc_after < *sqrt_addr + 1000 { called_sqrt = true; } // Check if PC is at halt address (function returned via RET) if pc_after == halt_address { - println!("Function returned after {} steps", steps); + println!("Function returned after {steps} steps"); break; } } @@ -591,7 +588,7 @@ mod tests { } } - println!("Program executed successfully for {} steps", steps); + println!("Program executed successfully for {steps} steps"); assert!(steps > 0, "Program should execute at least one instruction"); assert!(called_sqrt, "__lp_fixed32_sqrt should have been called"); diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/parse.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/parse.rs index 2cf10c604..9bad8eb99 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/parse.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/parse.rs @@ -1,6 +1,5 @@ //! ELF parsing and validation utilities. -use crate::debug; use alloc::format; use alloc::string::String; use object::Object; @@ -10,7 +9,7 @@ pub fn parse_elf(elf_data: &[u8]) -> Result, String> { debug!("=== Parsing ELF file ==="); debug!("ELF size: {} bytes", elf_data.len()); - let obj = object::File::parse(elf_data).map_err(|e| format!("Failed to parse ELF: {}", e))?; + let obj = object::File::parse(elf_data).map_err(|e| format!("Failed to parse ELF: {e}"))?; Ok(obj) } @@ -26,8 +25,7 @@ pub fn validate_elf(obj: &object::File) -> Result<(), String> { } arch => { return Err(format!( - "Unsupported architecture: {:?}. Expected RISC-V 32-bit", - arch + "Unsupported architecture: {arch:?}. Expected RISC-V 32-bit" )); } } @@ -39,8 +37,7 @@ pub fn validate_elf(obj: &object::File) -> Result<(), String> { } endian => { return Err(format!( - "Unsupported endianness: {:?}. Expected little-endian", - endian + "Unsupported endianness: {endian:?}. Expected little-endian" )); } } diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/got.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/got.rs index 424ab6588..242ea8923 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/got.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/got.rs @@ -1,6 +1,5 @@ //! GOT (Global Offset Table) entry tracking and management. -use crate::debug; use alloc::string::String; use hashbrown::HashMap; @@ -8,7 +7,7 @@ use hashbrown::HashMap; #[derive(Debug, Clone)] pub struct GotEntry { /// Symbol name - #[allow(dead_code)] + #[allow(dead_code, reason = "Used for debugging and symbol resolution")] pub symbol_name: String, /// Address where the GOT entry is located pub address: u32, diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/handlers.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/handlers.rs index ec9705dfd..54786a740 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/handlers.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/handlers.rs @@ -1,6 +1,5 @@ //! Individual relocation type handlers. -use crate::debug; use alloc::format; use alloc::string::String; use hashbrown::HashMap; @@ -88,8 +87,7 @@ pub fn handle_got_hi20(ctx: &mut RelocationContext, reloc: &RelocationInfo) -> R let offset = reloc.offset as usize; if offset + 4 > ctx.buffer.len() { return Err(format!( - "GOT_HI20 relocation at offset {} requires 4 bytes", - offset + "GOT_HI20 relocation at offset {offset} requires 4 bytes" )); } @@ -190,8 +188,7 @@ pub fn handle_pcrel_hi20( let offset = reloc.offset as usize; if offset + 4 > ctx.buffer.len() { return Err(format!( - "PCREL_HI20 relocation at offset {} requires 4 bytes", - offset + "PCREL_HI20 relocation at offset {offset} requires 4 bytes" )); } @@ -258,8 +255,7 @@ pub fn handle_pcrel_lo12_i( let offset = reloc.offset as usize; if offset + 4 > ctx.buffer.len() { return Err(format!( - "PCREL_LO12_I relocation at offset {} requires 4 bytes", - offset + "PCREL_LO12_I relocation at offset {offset} requires 4 bytes" )); } @@ -325,8 +321,7 @@ pub fn handle_pcrel_lo12_i( if auipc_buffer_offset >= ctx.buffer.len() || auipc_buffer_offset + 4 > ctx.buffer.len() { return Err(format!( - "Cannot read auipc instruction for PCREL_LO12_I: auipc would be at buffer offset {}", - auipc_buffer_offset + "Cannot read auipc instruction for PCREL_LO12_I: auipc would be at buffer offset {auipc_buffer_offset}" )); } @@ -433,7 +428,7 @@ pub fn handle_pcrel_lo12_i( } /// Handle R_RISCV_32 (1): 32-bit absolute relocation (used for GOT entry initialization). -#[allow(dead_code)] +#[allow(dead_code, reason = "Reserved for future GOT handling")] pub fn handle_abs32( ctx: &mut RelocationContext, reloc: &RelocationInfo, @@ -444,8 +439,7 @@ pub fn handle_abs32( let offset = reloc.offset as usize; if offset + 4 > ctx.buffer.len() { return Err(format!( - "R_RISCV_32 relocation at offset {} requires 4 bytes", - offset + "R_RISCV_32 relocation at offset {offset} requires 4 bytes" )); } @@ -500,8 +494,7 @@ mod tests { let effective_addr = auipc_result.wrapping_add(lo12); assert_eq!( effective_addr, symbol_addr, - "auipc_result=0x{:x}, lo12=0x{:x}, effective=0x{:x}, expected=0x{:x}", - auipc_result, lo12, effective_addr, symbol_addr + "auipc_result=0x{auipc_result:x}, lo12=0x{lo12:x}, effective=0x{effective_addr:x}, expected=0x{symbol_addr:x}" ); } @@ -559,8 +552,7 @@ mod tests { let effective_addr = auipc_result.wrapping_add(lo12); assert_eq!( effective_addr, symbol_addr, - "Failed for auipc_pc=0x{:x}, symbol_addr=0x{:x}: auipc_result=0x{:x}, lo12=0x{:x}, effective=0x{:x}", - auipc_pc, symbol_addr, auipc_result, lo12, effective_addr + "Failed for auipc_pc=0x{auipc_pc:x}, symbol_addr=0x{symbol_addr:x}: auipc_result=0x{auipc_result:x}, lo12=0x{lo12:x}, effective=0x{effective_addr:x}" ); } } diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/mod.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/mod.rs index 47ca4ad60..77ebcc1b6 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/mod.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/mod.rs @@ -14,7 +14,6 @@ pub use phase1::analyze_relocations; pub use phase2::apply_relocations_phase2; pub use section::{BufferSlice, SectionAddressInfo}; -use crate::debug; use alloc::string::String; use hashbrown::HashMap; diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/phase1.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/phase1.rs index 18da8684f..e45d38fd6 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/phase1.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/phase1.rs @@ -1,6 +1,5 @@ //! Phase 1: Analyze relocations and identify GOT entries. -use crate::debug; use alloc::format; use alloc::string::{String, ToString}; use alloc::vec::Vec; @@ -26,10 +25,10 @@ pub struct RelocationInfo { /// Address where relocation is applied (VMA + offset) pub address: u32, /// Section VMA - #[allow(dead_code)] + #[allow(dead_code, reason = "Used for section address tracking")] pub section_vma: u64, /// Section LMA (for .data sections) - #[allow(dead_code)] + #[allow(dead_code, reason = "Used for section load address tracking")] pub section_lma: u64, } diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/phase2.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/phase2.rs index a535cf12b..382dbcd63 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/phase2.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/phase2.rs @@ -1,8 +1,7 @@ //! Phase 2: Apply relocations in dependency order. -use crate::debug; use alloc::format; -use alloc::string::String; +use alloc::string::{String, ToString}; use alloc::vec::Vec; use hashbrown::HashMap; @@ -98,12 +97,24 @@ fn apply_single_relocation( ))?; // Get section address info - let section_info = section_addrs.get(&reloc.section_name).ok_or_else(|| { - format!( - "Section '{}' not found in section address map", - reloc.section_name - ) - })?; + // Try direct lookup first, then try normalized name (for subsections like .text._init -> .text) + let section_info = section_addrs + .get(&reloc.section_name) + .or_else(|| { + // Normalize section name (e.g., ".text._init" -> ".text") for lookup + let normalized = if let Some(second_dot) = reloc.section_name[1..].find('.') { + reloc.section_name[..1 + second_dot].to_string() + } else { + return None; + }; + section_addrs.get(&normalized) + }) + .ok_or_else(|| { + format!( + "Section '{}' not found in section address map", + reloc.section_name + ) + })?; // Determine which buffer to use and get slice let (buffer_slice, load_addr) = match §ion_info.buffer { @@ -145,8 +156,7 @@ fn apply_single_relocation( let offset = reloc.offset as usize; if offset + 4 > buffer_slice.len() { return Err(format!( - "R_RISCV_32 relocation at offset {} requires 4 bytes", - offset + "R_RISCV_32 relocation at offset {offset} requires 4 bytes" )); } diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/section.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/section.rs index 2f72244a5..5689ecc79 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/section.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/relocations/section.rs @@ -1,6 +1,5 @@ //! Section address resolution (VMA/LMA). -use crate::debug; use alloc::string::{String, ToString}; use hashbrown::HashMap; use object::{Object, ObjectSection, ObjectSymbol}; diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/sections.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/sections.rs index c4799b8d1..11f361791 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/sections.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/sections.rs @@ -1,7 +1,6 @@ //! Section loading into memory buffers. use super::memory::{is_ram_address, is_rom_address, ram_address_to_offset}; -use crate::debug; use alloc::format; use alloc::string::{String, ToString}; use object::{Object, ObjectSection, ObjectSymbol}; @@ -112,7 +111,7 @@ pub fn load_sections(obj: &object::File, rom: &mut [u8], _ram: &mut [u8]) -> Res // Get section data let data = section .data() - .map_err(|e| format!("Failed to read section '{}' data: {}", section_name, e))?; + .map_err(|e| format!("Failed to read section '{section_name}' data: {e}"))?; if data.is_empty() { continue; diff --git a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/symbols.rs b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/symbols.rs index 6436ad581..45c812e10 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/elf_loader/symbols.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/elf_loader/symbols.rs @@ -1,7 +1,6 @@ //! Symbol map building for relocations. use super::memory::is_ram_address; -use crate::debug; use alloc::format; use alloc::string::{String, ToString}; use alloc::vec::Vec; @@ -117,5 +116,5 @@ pub fn find_symbol_address( } } } - Err(format!("Symbol '{}' not found", symbol_name)) + Err(format!("Symbol '{symbol_name}' not found")) } diff --git a/lp-glsl/crates/lp-riscv-tools/src/emu/abi_helper.rs b/lp-glsl/crates/lp-riscv-tools/src/emu/abi_helper.rs index 87fd7dbf3..23765564f 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/emu/abi_helper.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/emu/abi_helper.rs @@ -117,9 +117,9 @@ pub fn compute_return_locations( use alloc::format; #[cfg(feature = "std")] use std::format; - return Err(cranelift_codegen::CodegenError::Unsupported( - format!("Invalid return location for return value {}: both reg and stack are None or both are Some", i).into(), - )); + return Err(cranelift_codegen::CodegenError::Unsupported(format!( + "Invalid return location for return value {i}: both reg and stack are None or both are Some" + ))); } } } @@ -204,9 +204,9 @@ pub fn compute_arg_locations( use alloc::format; #[cfg(feature = "std")] use std::format; - return Err(cranelift_codegen::CodegenError::Unsupported( - format!("Invalid argument location for argument {}: both reg and stack are None or both are Some", i).into(), - )); + return Err(cranelift_codegen::CodegenError::Unsupported(format!( + "Invalid argument location for argument {i}: both reg and stack are None or both are Some" + ))); } } } diff --git a/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/debug.rs b/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/debug.rs index 2734bec71..34308f322 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/debug.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/debug.rs @@ -18,7 +18,7 @@ impl Riscv32Emulator { pub fn format_logs(&self) -> String { let mut result = String::new(); for log in &self.log_buffer { - result.push_str(&format!("{}\n", log)); + result.push_str(&format!("{log}\n")); } result } @@ -126,10 +126,8 @@ impl Riscv32Emulator { let zero_count = idx - zero_start; if zero_count > MAX_ZERO_RUN { // Summarize long zero runs - result.push_str(&format!( - " ... ({} zero instructions skipped)\n", - zero_count - )); + result + .push_str(&format!(" ... ({zero_count} zero instructions skipped)\n")); } else { // Show short zero runs for i in 0..zero_count { @@ -148,7 +146,7 @@ impl Riscv32Emulator { // Format non-zero instruction let marker = marker_fn(pc); - result.push_str(&format!("{}{:3}: 0x{:08x}: {}\n", marker, idx, pc, disasm)); + result.push_str(&format!("{marker}{idx:3}: 0x{pc:08x}: {disasm}\n")); } } @@ -156,10 +154,7 @@ impl Riscv32Emulator { if let Some(zero_start) = zero_run_start { let zero_count = instructions.len() - zero_start; if zero_count > MAX_ZERO_RUN { - result.push_str(&format!( - " ... ({} zero instructions skipped)\n", - zero_count - )); + result.push_str(&format!(" ... ({zero_count} zero instructions skipped)\n")); } else { for i in 0..zero_count { let zero_pc = instructions[zero_start + i].0; @@ -180,13 +175,13 @@ impl Riscv32Emulator { /// /// # Arguments /// - /// * `highlight_pc` - Optional PC to highlight in disassembly (for errors) + /// * `highlight_pc` - Optional PC to highlight in disassembly (for errors or current position) /// * `log_count` - Number of recent logs to show (default 20) pub fn format_debug_info(&self, highlight_pc: Option, log_count: usize) -> String { let mut result = String::new(); let code = self.memory.code(); - // Show disassembly only when there's an error PC to highlight + // Show disassembly when there's a PC to highlight (error or current position) if let Some(error_pc) = highlight_pc { // Disassemble all instructions let mut instructions = Vec::new(); @@ -219,10 +214,7 @@ impl Riscv32Emulator { for (idx, (pc, _inst_word, disasm)) in instructions[start..end].iter().enumerate() { let actual_idx = start + idx; let marker = if *pc == error_pc { ">>> " } else { " " }; - result.push_str(&format!( - "{}{:3}: 0x{:08x}: {}\n", - marker, actual_idx, pc, disasm - )); + result.push_str(&format!("{marker}{actual_idx:3}: 0x{pc:08x}: {disasm}\n")); } if end < instructions.len() { @@ -266,11 +258,7 @@ impl Riscv32Emulator { // Writing to String never fails, so unwrap is safe write!( result, - "[{:4}] 0x{:08x}: {:width$}", - cycle, - pc, - disassembly, - width = max_inst_width + "[{cycle:4}] 0x{pc:08x}: {disassembly:max_inst_width$}" ) .unwrap(); @@ -284,11 +272,11 @@ impl Riscv32Emulator { rd_new, .. } => { - write!(result, " ; {}: {} -> {}", rd, rd_old, rd_new).unwrap(); + write!(result, " ; {rd}: {rd_old} -> {rd_new}").unwrap(); if let Some(rs2_val) = rs2_val { - write!(result, " (rs1={}, rs2={})", rs1_val, rs2_val).unwrap(); + write!(result, " (rs1={rs1_val}, rs2={rs2_val})").unwrap(); } else { - write!(result, " (rs1={})", rs1_val).unwrap(); + write!(result, " (rs1={rs1_val})").unwrap(); } } InstLog::Load { @@ -302,8 +290,7 @@ impl Riscv32Emulator { } => { write!( result, - " ; {}: {} -> {} (mem[0x{:08x}] = {}) (rs1={})", - rd, rd_old, rd_new, addr, mem_val, rs1_val + " ; {rd}: {rd_old} -> {rd_new} (mem[0x{addr:08x}] = {mem_val}) (rs1={rs1_val})" ) .unwrap(); } @@ -317,8 +304,7 @@ impl Riscv32Emulator { } => { write!( result, - " ; mem[0x{:08x}]: {} -> {} (rs1={}, rs2={})", - addr, mem_old, mem_new, rs1_val, rs2_val + " ; mem[0x{addr:08x}]: {mem_old} -> {mem_new} (rs1={rs1_val}, rs2={rs2_val})" ) .unwrap(); } @@ -333,25 +319,16 @@ impl Riscv32Emulator { if let Some(target) = target_pc { write!( result, - " ; branch taken: 0x{:08x} -> 0x{:08x} (rs1={}, rs2={})", - pc, target, rs1_val, rs2_val + " ; branch taken: 0x{pc:08x} -> 0x{target:08x} (rs1={rs1_val}, rs2={rs2_val})" ) .unwrap(); } else { - write!( - result, - " ; branch taken (rs1={}, rs2={})", - rs1_val, rs2_val - ) - .unwrap(); + write!(result, " ; branch taken (rs1={rs1_val}, rs2={rs2_val})") + .unwrap(); } } else { - write!( - result, - " ; branch not taken (rs1={}, rs2={})", - rs1_val, rs2_val - ) - .unwrap(); + write!(result, " ; branch not taken (rs1={rs1_val}, rs2={rs2_val})") + .unwrap(); } } InstLog::Jump { @@ -363,18 +340,17 @@ impl Riscv32Emulator { if let Some(rd_new) = rd_new { write!( result, - " ; rd: {} -> {} jump: 0x{:08x} -> 0x{:08x}", - rd_old, rd_new, pc, target_pc + " ; rd: {rd_old} -> {rd_new} jump: 0x{pc:08x} -> 0x{target_pc:08x}" ) .unwrap(); } else { - write!(result, " ; jump: 0x{:08x} -> 0x{:08x}", pc, target_pc).unwrap(); + write!(result, " ; jump: 0x{pc:08x} -> 0x{target_pc:08x}").unwrap(); } } InstLog::Immediate { rd, rd_old, rd_new, .. } => { - write!(result, " ; {}: {} -> {}", rd, rd_old, rd_new).unwrap(); + write!(result, " ; {rd}: {rd_old} -> {rd_new}").unwrap(); } InstLog::System { kind, .. } => match kind { SystemKind::Ecall => write!(result, " ; syscall").unwrap(), diff --git a/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/execution.rs b/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/execution.rs index 30829ea0c..e0e73112c 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/execution.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/execution.rs @@ -144,7 +144,7 @@ impl Riscv32Emulator { // Read panic message from memory let message = read_memory_string(&self.memory, msg_ptr, msg_len).unwrap_or_else(|_| { - format!("", msg_ptr) + format!("") }); // Read file name from memory (if pointer is not null) diff --git a/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/function_call.rs b/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/function_call.rs index 98dd71b09..bb38ed534 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/function_call.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/function_call.rs @@ -39,6 +39,10 @@ impl Riscv32Emulator { args: &[DataValue], signature: &Signature, ) -> Result, EmulatorError> { + // Clear log buffer for clean output on this function call + self.clear_logs(); + self.instruction_count = 0; + // Check if function uses StructReturn if has_struct_return(signature) { // StructReturn: caller must provide struct size @@ -63,7 +67,7 @@ impl Riscv32Emulator { EmulatorError::InvalidInstruction { pc: self.pc, instruction: 0, - reason: format!("Failed to compute return locations: {:?}", e), + reason: format!("Failed to compute return locations: {e:?}"), regs: self.regs, } })?; @@ -76,7 +80,7 @@ impl Riscv32Emulator { .map_err(|e| EmulatorError::InvalidInstruction { pc: self.pc, instruction: 0, - reason: format!("Failed to compute argument locations: {:?}", e), + reason: format!("Failed to compute argument locations: {e:?}"), regs: self.regs, })?; @@ -199,7 +203,7 @@ impl Riscv32Emulator { EmulatorError::InvalidInstruction { pc: self.pc, instruction: 0, - reason: format!("Failed to compute argument locations: {:?}", e), + reason: format!("Failed to compute argument locations: {e:?}"), regs: self.regs, } })?; @@ -314,7 +318,7 @@ fn create_flags_with_multi_ret() -> Result { .map_err(|e| EmulatorError::InvalidInstruction { pc: 0, instruction: 0, - reason: format!("Failed to set flags: {:?}", e), + reason: format!("Failed to set flags: {e:?}"), regs: [0; 32], })?; Ok(Flags::new(builder)) @@ -413,8 +417,7 @@ fn setup_call_stack( pc: emulator.pc, instruction: 0, reason: format!( - "Not enough RAM for stack (need {} bytes, have {} bytes)", - total_stack_space, ram_size + "Not enough RAM for stack (need {total_stack_space} bytes, have {ram_size} bytes)" ), regs: emulator.regs, }); @@ -475,10 +478,7 @@ fn allocate_struct_return_buffer( return Err(EmulatorError::InvalidInstruction { pc: emulator.pc, instruction: 0, - reason: format!( - "Not enough RAM for struct return buffer (need {} bytes)", - aligned_size - ), + reason: format!("Not enough RAM for struct return buffer (need {aligned_size} bytes)"), regs: emulator.regs, }); } @@ -506,7 +506,7 @@ fn place_register_argument( return Err(EmulatorError::InvalidInstruction { pc: emulator.pc, instruction: 0, - reason: format!("Invalid register index: {}", reg_enc), + reason: format!("Invalid register index: {reg_enc}"), regs: emulator.regs, }); } @@ -521,7 +521,7 @@ fn place_register_argument( return Err(EmulatorError::InvalidInstruction { pc: emulator.pc, instruction: 0, - reason: format!("i8 only has 1 slot, got slot_idx {}", slot_idx), + reason: format!("i8 only has 1 slot, got slot_idx {slot_idx}"), regs: emulator.regs, }); } @@ -533,7 +533,7 @@ fn place_register_argument( return Err(EmulatorError::InvalidInstruction { pc: emulator.pc, instruction: 0, - reason: format!("i16 only has 1 slot, got slot_idx {}", slot_idx), + reason: format!("i16 only has 1 slot, got slot_idx {slot_idx}"), regs: emulator.regs, }); } @@ -545,7 +545,7 @@ fn place_register_argument( return Err(EmulatorError::InvalidInstruction { pc: emulator.pc, instruction: 0, - reason: format!("i32 only has 1 slot, got slot_idx {}", slot_idx), + reason: format!("i32 only has 1 slot, got slot_idx {slot_idx}"), regs: emulator.regs, }); } @@ -565,7 +565,7 @@ fn place_register_argument( return Err(EmulatorError::InvalidInstruction { pc: emulator.pc, instruction: 0, - reason: format!("i64 only has 2 slots, got slot_idx {}", slot_idx), + reason: format!("i64 only has 2 slots, got slot_idx {slot_idx}"), regs: emulator.regs, }); } @@ -582,7 +582,7 @@ fn place_register_argument( return Err(EmulatorError::InvalidInstruction { pc: emulator.pc, instruction: 0, - reason: format!("i128 only has 4 slots, got slot_idx {}", slot_idx), + reason: format!("i128 only has 4 slots, got slot_idx {slot_idx}"), regs: emulator.regs, }); } @@ -621,8 +621,7 @@ fn place_stack_argument( pc: emulator.pc, instruction: 0, reason: format!( - "Failed to write i8 stack argument at 0x{:08x}: {}", - stack_addr, e + "Failed to write i8 stack argument at 0x{stack_addr:08x}: {e}" ), regs: emulator.regs, })?; @@ -630,7 +629,7 @@ fn place_stack_argument( return Err(EmulatorError::InvalidInstruction { pc: emulator.pc, instruction: 0, - reason: format!("i8 only has 1 slot, got slot_idx {}", slot_idx), + reason: format!("i8 only has 1 slot, got slot_idx {slot_idx}"), regs: emulator.regs, }); } @@ -644,8 +643,7 @@ fn place_stack_argument( pc: emulator.pc, instruction: 0, reason: format!( - "Failed to write i16 stack argument at 0x{:08x}: {}", - stack_addr, e + "Failed to write i16 stack argument at 0x{stack_addr:08x}: {e}" ), regs: emulator.regs, })?; @@ -653,7 +651,7 @@ fn place_stack_argument( return Err(EmulatorError::InvalidInstruction { pc: emulator.pc, instruction: 0, - reason: format!("i16 only has 1 slot, got slot_idx {}", slot_idx), + reason: format!("i16 only has 1 slot, got slot_idx {slot_idx}"), regs: emulator.regs, }); } @@ -665,8 +663,7 @@ fn place_stack_argument( pc: emulator.pc, instruction: 0, reason: format!( - "Failed to write i32 stack argument at 0x{:08x}: {}", - stack_addr, e + "Failed to write i32 stack argument at 0x{stack_addr:08x}: {e}" ), regs: emulator.regs, } @@ -675,7 +672,7 @@ fn place_stack_argument( return Err(EmulatorError::InvalidInstruction { pc: emulator.pc, instruction: 0, - reason: format!("i32 only has 1 slot, got slot_idx {}", slot_idx), + reason: format!("i32 only has 1 slot, got slot_idx {slot_idx}"), regs: emulator.regs, }); } @@ -695,7 +692,7 @@ fn place_stack_argument( return Err(EmulatorError::InvalidInstruction { pc: emulator.pc, instruction: 0, - reason: format!("i64 only has 2 slots, got slot_idx {}", slot_idx), + reason: format!("i64 only has 2 slots, got slot_idx {slot_idx}"), regs: emulator.regs, }); } @@ -708,8 +705,7 @@ fn place_stack_argument( pc: emulator.pc, instruction: 0, reason: format!( - "Failed to write i64 slot {} stack argument at 0x{:08x}: {}", - slot_idx, stack_addr, e + "Failed to write i64 slot {slot_idx} stack argument at 0x{stack_addr:08x}: {e}" ), regs: emulator.regs, })?; @@ -725,7 +721,7 @@ fn place_stack_argument( return Err(EmulatorError::InvalidInstruction { pc: emulator.pc, instruction: 0, - reason: format!("i128 only has 4 slots, got slot_idx {}", slot_idx), + reason: format!("i128 only has 4 slots, got slot_idx {slot_idx}"), regs: emulator.regs, }); } @@ -738,8 +734,7 @@ fn place_stack_argument( pc: emulator.pc, instruction: 0, reason: format!( - "Failed to write i128 slot {} stack argument at 0x{:08x}: {}", - slot_idx, stack_addr, e + "Failed to write i128 slot {slot_idx} stack argument at 0x{stack_addr:08x}: {e}" ), regs: emulator.regs, })?; @@ -803,7 +798,7 @@ fn extract_register_return_value( return Err(EmulatorError::InvalidInstruction { pc: emulator.pc, instruction: 0, - reason: format!("Invalid register index: {}", reg_enc), + reason: format!("Invalid register index: {reg_enc}"), regs: emulator.regs, }); } @@ -825,10 +820,7 @@ fn extract_stack_return_value( .map_err(|e| EmulatorError::InvalidInstruction { pc: emulator.pc, instruction: 0, - reason: format!( - "Failed to read stack return value at 0x{:08x}: {}", - stack_addr, e - ), + reason: format!("Failed to read stack return value at 0x{stack_addr:08x}: {e}"), regs: emulator.regs, })?; Ok(word_value as u32) @@ -986,8 +978,7 @@ fn extract_struct_return_value( pc: emulator.pc, instruction: 0, reason: format!( - "Failed to read struct return value word {} at 0x{:08x}: {}", - i, addr, e + "Failed to read struct return value word {i} at 0x{addr:08x}: {e}" ), regs: emulator.regs, })?; diff --git a/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/state.rs b/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/state.rs index f8448a754..f4f34fbce 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/state.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/emu/emulator/state.rs @@ -62,6 +62,11 @@ impl Riscv32Emulator { self } + /// Set the maximum number of instructions to execute (mutating method). + pub fn set_max_instructions(&mut self, limit: u64) { + self.max_instructions = limit; + } + /// Set the logging level. pub fn with_log_level(mut self, level: LogLevel) -> Self { self.log_level = level; diff --git a/lp-glsl/crates/lp-riscv-tools/src/emu/error.rs b/lp-glsl/crates/lp-riscv-tools/src/emu/error.rs index 3610c13d5..76e8853a7 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/emu/error.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/emu/error.rs @@ -130,8 +130,7 @@ impl core::fmt::Display for EmulatorError { .. } => write!( f, - "Instruction limit exceeded: executed {} instructions (limit: {}) at PC 0x{:08x}", - executed, limit, pc + "Instruction limit exceeded: executed {executed} instructions (limit: {limit}) at PC 0x{pc:08x}" ), EmulatorError::InvalidMemoryAccess { address, @@ -147,8 +146,7 @@ impl core::fmt::Display for EmulatorError { }; write!( f, - "Invalid memory {} at address 0x{:08x} (size: {} bytes) at PC 0x{:08x}", - kind_str, address, size, pc + "Invalid memory {kind_str} at address 0x{address:08x} (size: {size} bytes) at PC 0x{pc:08x}" ) } EmulatorError::InvalidInstruction { @@ -158,8 +156,7 @@ impl core::fmt::Display for EmulatorError { .. } => write!( f, - "Invalid instruction 0x{:08x} at PC 0x{:08x}: {}", - instruction, pc, reason + "Invalid instruction 0x{instruction:08x} at PC 0x{pc:08x}: {reason}" ), EmulatorError::UnalignedAccess { address, @@ -168,9 +165,8 @@ impl core::fmt::Display for EmulatorError { .. } => write!( f, - "Unaligned memory access at address 0x{:08x} (requires {} byte alignment) at PC \ - 0x{:08x}", - address, alignment, pc + "Unaligned memory access at address 0x{address:08x} (requires {alignment} byte alignment) at PC \ + 0x{pc:08x}" ), EmulatorError::UnknownOpcode { opcode, @@ -179,27 +175,25 @@ impl core::fmt::Display for EmulatorError { .. } => write!( f, - "Unknown opcode 0x{:02x} in instruction 0x{:08x} at PC 0x{:08x}", - opcode, instruction, pc + "Unknown opcode 0x{opcode:02x} in instruction 0x{instruction:08x} at PC 0x{pc:08x}" ), EmulatorError::InvalidRegister { reg, pc, reason } => write!( f, - "Invalid register access: {:?} at PC 0x{:08x}: {}", - reg, pc, reason + "Invalid register access: {reg:?} at PC 0x{pc:08x}: {reason}" ), EmulatorError::Trap { code, pc, .. } => { let trap_name = trap_code_to_string(*code); - write!(f, "Trap: {} at PC 0x{:08x}", trap_name, pc) + write!(f, "Trap: {trap_name} at PC 0x{pc:08x}") } EmulatorError::Panic { info, pc, .. } => { write!(f, "Panic at PC 0x{:08x}: {}", pc, info.message)?; if let Some(ref file) = info.file { - write!(f, "\n at {}", file)?; + write!(f, "\n at {file}")?; if let Some(line) = info.line { - write!(f, ":{}", line)?; + write!(f, ":{line}")?; } } else if let Some(line) = info.line { - write!(f, "\n at line {}", line)?; + write!(f, "\n at line {line}")?; } Ok(()) } diff --git a/lp-glsl/crates/lp-riscv-tools/src/emu/logging.rs b/lp-glsl/crates/lp-riscv-tools/src/emu/logging.rs index cf0fa8335..b0b73df05 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/emu/logging.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/emu/logging.rs @@ -272,7 +272,7 @@ impl fmt::Display for InstLog { // Print cycle count, address and instruction, padding instruction to align semicolons // Format: [cycle] 0xPC: instruction (padded to 30 chars) ; comment - write!(f, "[{:4}] 0x{:08x}: {:30}", cycle, pc, disassembly)?; + write!(f, "[{cycle:4}] 0x{pc:08x}: {disassembly:30}")?; // Format comment on the same line, separated by semicolon match self { @@ -284,11 +284,11 @@ impl fmt::Display for InstLog { rd_new, .. } => { - write!(f, "; {}: {} -> {}", rd, rd_old, rd_new)?; + write!(f, "; {rd}: {rd_old} -> {rd_new}")?; if let Some(rs2_val) = rs2_val { - write!(f, " (rs1={}, rs2={})", rs1_val, rs2_val)?; + write!(f, " (rs1={rs1_val}, rs2={rs2_val})")?; } else { - write!(f, " (rs1={})", rs1_val)?; + write!(f, " (rs1={rs1_val})")?; } } InstLog::Load { @@ -302,8 +302,7 @@ impl fmt::Display for InstLog { } => { write!( f, - "; {}: {} -> {} (mem[0x{:08x}] = {}) (rs1={})", - rd, rd_old, rd_new, addr, mem_val, rs1_val + "; {rd}: {rd_old} -> {rd_new} (mem[0x{addr:08x}] = {mem_val}) (rs1={rs1_val})" )?; } InstLog::Store { @@ -316,8 +315,7 @@ impl fmt::Display for InstLog { } => { write!( f, - "; mem[0x{:08x}]: {} -> {} (rs1={}, rs2={})", - addr, mem_old, mem_new, rs1_val, rs2_val + "; mem[0x{addr:08x}]: {mem_old} -> {mem_new} (rs1={rs1_val}, rs2={rs2_val})" )?; } InstLog::Branch { @@ -331,14 +329,13 @@ impl fmt::Display for InstLog { if let Some(target) = target_pc { write!( f, - "; branch taken: 0x{:08x} -> 0x{:08x} (rs1={}, rs2={})", - pc, target, rs1_val, rs2_val + "; branch taken: 0x{pc:08x} -> 0x{target:08x} (rs1={rs1_val}, rs2={rs2_val})" )?; } else { - write!(f, "; branch taken (rs1={}, rs2={})", rs1_val, rs2_val)?; + write!(f, "; branch taken (rs1={rs1_val}, rs2={rs2_val})")?; } } else { - write!(f, "; branch not taken (rs1={}, rs2={})", rs1_val, rs2_val)?; + write!(f, "; branch not taken (rs1={rs1_val}, rs2={rs2_val})")?; } } InstLog::Jump { @@ -350,17 +347,16 @@ impl fmt::Display for InstLog { if let Some(rd_new) = rd_new { write!( f, - "; rd: {} -> {} jump: 0x{:08x} -> 0x{:08x}", - rd_old, rd_new, pc, target_pc + "; rd: {rd_old} -> {rd_new} jump: 0x{pc:08x} -> 0x{target_pc:08x}" )?; } else { - write!(f, "; jump: 0x{:08x} -> 0x{:08x}", pc, target_pc)?; + write!(f, "; jump: 0x{pc:08x} -> 0x{target_pc:08x}")?; } } InstLog::Immediate { rd, rd_old, rd_new, .. } => { - write!(f, "; {}: {} -> {}", rd, rd_old, rd_new)?; + write!(f, "; {rd}: {rd_old} -> {rd_new}")?; } InstLog::System { kind, .. } => match kind { SystemKind::Ecall => write!(f, "; syscall")?, diff --git a/lp-glsl/crates/lp-riscv-tools/src/format.rs b/lp-glsl/crates/lp-riscv-tools/src/format.rs index 0b5d19b5b..fb40bbcef 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/format.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/format.rs @@ -307,12 +307,11 @@ mod tests { assert_eq!(decoded.rs2, *rs2); assert_eq!(decoded.func, func); - let encoded = decoded.to_riscv_no_opcode() | u32::from(*opcode); + let encoded = decoded.to_riscv_no_opcode() | *opcode; assert_eq!( encoded, inst_word, - "Round-trip failed for R-type: opcode=0x{:02x}, rd={}, rs1={}, rs2={}, \ - funct7=0x{:02x}", - opcode, rd, rs1, rs2, funct7 + "Round-trip failed for R-type: opcode=0x{opcode:02x}, rd={rd}, rs1={rs1}, rs2={rs2}, \ + funct7=0x{funct7:02x}" ); } } @@ -341,16 +340,15 @@ mod tests { assert_eq!(decoded.func, *funct3); assert_eq!( decoded.imm, *imm, - "Immediate mismatch: expected {}, got {}", - imm, decoded.imm + "Immediate mismatch: expected {imm}, got {}", + decoded.imm ); let encoded = decoded.to_riscv_no_opcode() | *opcode; assert_eq!( encoded, inst_word, - "Round-trip failed for I-type: opcode=0x{:02x}, rd={}, rs1={}, imm={}, \ - funct3=0x{:x}", - opcode, rd, rs1, imm, funct3 + "Round-trip failed for I-type: opcode=0x{opcode:02x}, rd={rd}, rs1={rs1}, imm={imm}, \ + funct3=0x{funct3:x}" ); } } @@ -380,16 +378,15 @@ mod tests { assert_eq!(decoded.func, *funct3); assert_eq!( decoded.imm, *imm, - "Immediate mismatch: expected {}, got {}", - imm, decoded.imm + "Immediate mismatch: expected {imm}, got {}", + decoded.imm ); let encoded = decoded.to_riscv_no_opcode() | *opcode; assert_eq!( encoded, inst_word, - "Round-trip failed for S-type: opcode=0x{:02x}, rs1={}, rs2={}, imm={}, \ - funct3=0x{:x}", - opcode, rs1, rs2, imm, funct3 + "Round-trip failed for S-type: opcode=0x{opcode:02x}, rs1={rs1}, rs2={rs2}, imm={imm}, \ + funct3=0x{funct3:x}" ); } } @@ -424,16 +421,15 @@ mod tests { assert_eq!(decoded.func, *funct3); assert_eq!( decoded.imm, *imm, - "Immediate mismatch: expected {}, got {}", - imm, decoded.imm + "Immediate mismatch: expected {imm}, got {}", + decoded.imm ); let encoded = decoded.to_riscv_no_opcode() | *opcode; assert_eq!( encoded, inst_word, - "Round-trip failed for B-type: opcode=0x{:02x}, rs1={}, rs2={}, imm={}, \ - funct3=0x{:x}", - opcode, rs1, rs2, imm, funct3 + "Round-trip failed for B-type: opcode=0x{opcode:02x}, rs1={rs1}, rs2={rs2}, imm={imm}, \ + funct3=0x{funct3:x}" ); } } @@ -464,15 +460,14 @@ mod tests { assert_eq!(decoded.rd, *rd); assert_eq!( decoded.imm, *imm, - "Immediate mismatch: expected {}, got {}", - imm, decoded.imm + "Immediate mismatch: expected {imm}, got {}", + decoded.imm ); let encoded = decoded.to_riscv_no_opcode() | *opcode; assert_eq!( encoded, inst_word, - "Round-trip failed for J-type: opcode=0x{:02x}, rd={}, imm={}", - opcode, rd, imm + "Round-trip failed for J-type: opcode=0x{opcode:02x}, rd={rd}, imm={imm}" ); } } @@ -497,15 +492,14 @@ mod tests { assert_eq!(decoded.rd, *rd); assert_eq!( decoded.imm, *imm, - "Immediate mismatch: expected 0x{:08x}, got 0x{:08x}", - imm_u32, decoded.imm as u32 + "Immediate mismatch: expected 0x{imm_u32:08x}, got 0x{:08x}", + decoded.imm as u32 ); let encoded = decoded.to_riscv_no_opcode() | *opcode; assert_eq!( encoded, inst_word, - "Round-trip failed for U-type: opcode=0x{:02x}, rd={}, imm=0x{:08x}", - opcode, rd, imm_u32 + "Round-trip failed for U-type: opcode=0x{opcode:02x}, rd={rd}, imm=0x{imm_u32:08x}" ); } } @@ -533,15 +527,14 @@ mod tests { let decoded = TypeU::from_riscv(inst_word); assert_eq!( decoded.imm, *imm, - "Immediate mismatch for 0x{:08x}: expected 0x{:08x}, got 0x{:08x}", - imm_u32, imm_u32, decoded.imm as u32 + "Immediate mismatch for 0x{imm_u32:08x}: expected 0x{imm_u32:08x}, got 0x{:08x}", + decoded.imm as u32 ); let encoded = decoded.to_riscv_no_opcode() | opcode; assert_eq!( encoded, inst_word, - "Round-trip failed for U-type with imm=0x{:08x}", - imm_u32 + "Round-trip failed for U-type with imm=0x{imm_u32:08x}" ); } } diff --git a/lp-glsl/crates/lp-riscv-tools/src/inst.rs b/lp-glsl/crates/lp-riscv-tools/src/inst.rs index ff6881da2..c7fe1ea25 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/inst.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/inst.rs @@ -10,7 +10,7 @@ use super::regs::Gpr; fn format_imm(imm: i32) -> alloc::string::String { use alloc::format; if imm >= -32 && imm <= 31 { - format!("{}", imm) + format!("{imm}") } else { format!("0x{:08x}", imm as u32) } @@ -26,7 +26,7 @@ pub fn format_instruction(inst: u32) -> alloc::string::String { match decode_instruction(inst) { Ok(decoded) => decoded.format(), - Err(_) => format!(".word 0x{:08x}", inst), + Err(_) => format!(".word 0x{inst:08x}"), } } @@ -323,16 +323,16 @@ impl Inst { match self { // Arithmetic instructions - Inst::Add { rd, rs1, rs2 } => format!("add {}, {}, {}", rd, rs1, rs2), - Inst::Sub { rd, rs1, rs2 } => format!("sub {}, {}, {}", rd, rs1, rs2), - Inst::Mul { rd, rs1, rs2 } => format!("mul {}, {}, {}", rd, rs1, rs2), - Inst::Mulh { rd, rs1, rs2 } => format!("mulh {}, {}, {}", rd, rs1, rs2), - Inst::Mulhsu { rd, rs1, rs2 } => format!("mulhsu {}, {}, {}", rd, rs1, rs2), - Inst::Mulhu { rd, rs1, rs2 } => format!("mulhu {}, {}, {}", rd, rs1, rs2), - Inst::Div { rd, rs1, rs2 } => format!("div {}, {}, {}", rd, rs1, rs2), - Inst::Divu { rd, rs1, rs2 } => format!("divu {}, {}, {}", rd, rs1, rs2), - Inst::Rem { rd, rs1, rs2 } => format!("rem {}, {}, {}", rd, rs1, rs2), - Inst::Remu { rd, rs1, rs2 } => format!("remu {}, {}, {}", rd, rs1, rs2), + Inst::Add { rd, rs1, rs2 } => format!("add {rd}, {rs1}, {rs2}"), + Inst::Sub { rd, rs1, rs2 } => format!("sub {rd}, {rs1}, {rs2}"), + Inst::Mul { rd, rs1, rs2 } => format!("mul {rd}, {rs1}, {rs2}"), + Inst::Mulh { rd, rs1, rs2 } => format!("mulh {rd}, {rs1}, {rs2}"), + Inst::Mulhsu { rd, rs1, rs2 } => format!("mulhsu {rd}, {rs1}, {rs2}"), + Inst::Mulhu { rd, rs1, rs2 } => format!("mulhu {rd}, {rs1}, {rs2}"), + Inst::Div { rd, rs1, rs2 } => format!("div {rd}, {rs1}, {rs2}"), + Inst::Divu { rd, rs1, rs2 } => format!("divu {rd}, {rs1}, {rs2}"), + Inst::Rem { rd, rs1, rs2 } => format!("rem {rd}, {rs1}, {rs2}"), + Inst::Remu { rd, rs1, rs2 } => format!("remu {rd}, {rs1}, {rs2}"), Inst::Addi { rd, rs1, imm } => format!("addi {}, {}, {}", rd, rs1, format_imm(*imm)), // Load/Store instructions @@ -355,7 +355,7 @@ impl Inst { } Inst::Jalr { rd, rs1, imm } => { if *imm == 0 { - format!("jalr {}, ({})", rd, rs1) + format!("jalr {rd}, ({rs1})") } else { format!("jalr {}, {}({})", rd, format_imm(*imm), rs1) } @@ -368,25 +368,25 @@ impl Inst { Inst::Bgeu { rs1, rs2, imm } => format!("bgeu {}, {}, {}", rs1, rs2, format_imm(*imm)), // Comparison instructions - Inst::Slt { rd, rs1, rs2 } => format!("slt {}, {}, {}", rd, rs1, rs2), + Inst::Slt { rd, rs1, rs2 } => format!("slt {rd}, {rs1}, {rs2}"), Inst::Slti { rd, rs1, imm } => format!("slti {}, {}, {}", rd, rs1, format_imm(*imm)), - Inst::Sltu { rd, rs1, rs2 } => format!("sltu {}, {}, {}", rd, rs1, rs2), + Inst::Sltu { rd, rs1, rs2 } => format!("sltu {rd}, {rs1}, {rs2}"), Inst::Sltiu { rd, rs1, imm } => format!("sltiu {}, {}, {}", rd, rs1, format_imm(*imm)), Inst::Xori { rd, rs1, imm } => format!("xori {}, {}, {}", rd, rs1, format_imm(*imm)), // Logical instructions - Inst::And { rd, rs1, rs2 } => format!("and {}, {}, {}", rd, rs1, rs2), + Inst::And { rd, rs1, rs2 } => format!("and {rd}, {rs1}, {rs2}"), Inst::Andi { rd, rs1, imm } => format!("andi {}, {}, {}", rd, rs1, format_imm(*imm)), - Inst::Or { rd, rs1, rs2 } => format!("or {}, {}, {}", rd, rs1, rs2), + Inst::Or { rd, rs1, rs2 } => format!("or {rd}, {rs1}, {rs2}"), Inst::Ori { rd, rs1, imm } => format!("ori {}, {}, {}", rd, rs1, format_imm(*imm)), - Inst::Xor { rd, rs1, rs2 } => format!("xor {}, {}, {}", rd, rs1, rs2), + Inst::Xor { rd, rs1, rs2 } => format!("xor {rd}, {rs1}, {rs2}"), // Shift instructions - Inst::Sll { rd, rs1, rs2 } => format!("sll {}, {}, {}", rd, rs1, rs2), + Inst::Sll { rd, rs1, rs2 } => format!("sll {rd}, {rs1}, {rs2}"), Inst::Slli { rd, rs1, imm } => format!("slli {}, {}, {}", rd, rs1, format_imm(*imm)), - Inst::Srl { rd, rs1, rs2 } => format!("srl {}, {}, {}", rd, rs1, rs2), + Inst::Srl { rd, rs1, rs2 } => format!("srl {rd}, {rs1}, {rs2}"), Inst::Srli { rd, rs1, imm } => format!("srli {}, {}, {}", rd, rs1, format_imm(*imm)), - Inst::Sra { rd, rs1, rs2 } => format!("sra {}, {}, {}", rd, rs1, rs2), + Inst::Sra { rd, rs1, rs2 } => format!("sra {rd}, {rs1}, {rs2}"), Inst::Srai { rd, rs1, imm } => format!("srai {}, {}, {}", rd, rs1, format_imm(*imm)), // Zbs: Single-bit instructions (immediate) @@ -396,46 +396,46 @@ impl Inst { Inst::Bexti { rd, rs1, imm } => format!("bexti {}, {}, {}", rd, rs1, format_imm(*imm)), // Zbs: Single-bit instructions (register) - Inst::Bclr { rd, rs1, rs2 } => format!("bclr {}, {}, {}", rd, rs1, rs2), - Inst::Bset { rd, rs1, rs2 } => format!("bset {}, {}, {}", rd, rs1, rs2), - Inst::Binv { rd, rs1, rs2 } => format!("binv {}, {}, {}", rd, rs1, rs2), - Inst::Bext { rd, rs1, rs2 } => format!("bext {}, {}, {}", rd, rs1, rs2), + Inst::Bclr { rd, rs1, rs2 } => format!("bclr {rd}, {rs1}, {rs2}"), + Inst::Bset { rd, rs1, rs2 } => format!("bset {rd}, {rs1}, {rs2}"), + Inst::Binv { rd, rs1, rs2 } => format!("binv {rd}, {rs1}, {rs2}"), + Inst::Bext { rd, rs1, rs2 } => format!("bext {rd}, {rs1}, {rs2}"), // Zbb: Count operations - Inst::Clz { rd, rs1 } => format!("clz {}, {}", rd, rs1), - Inst::Ctz { rd, rs1 } => format!("ctz {}, {}", rd, rs1), - Inst::Cpop { rd, rs1 } => format!("cpop {}, {}", rd, rs1), + Inst::Clz { rd, rs1 } => format!("clz {rd}, {rs1}"), + Inst::Ctz { rd, rs1 } => format!("ctz {rd}, {rs1}"), + Inst::Cpop { rd, rs1 } => format!("cpop {rd}, {rs1}"), // Zbb: Sign/zero extend - Inst::Sextb { rd, rs1 } => format!("sext.b {}, {}", rd, rs1), - Inst::Sexth { rd, rs1 } => format!("sext.h {}, {}", rd, rs1), - Inst::Zexth { rd, rs1 } => format!("zext.h {}, {}", rd, rs1), + Inst::Sextb { rd, rs1 } => format!("sext.b {rd}, {rs1}"), + Inst::Sexth { rd, rs1 } => format!("sext.h {rd}, {rs1}"), + Inst::Zexth { rd, rs1 } => format!("zext.h {rd}, {rs1}"), // Zbb: Rotate instructions Inst::Rori { rd, rs1, imm } => format!("rori {}, {}, {}", rd, rs1, format_imm(*imm)), - Inst::Rol { rd, rs1, rs2 } => format!("rol {}, {}, {}", rd, rs1, rs2), - Inst::Ror { rd, rs1, rs2 } => format!("ror {}, {}, {}", rd, rs1, rs2), + Inst::Rol { rd, rs1, rs2 } => format!("rol {rd}, {rs1}, {rs2}"), + Inst::Ror { rd, rs1, rs2 } => format!("ror {rd}, {rs1}, {rs2}"), // Zbb: Byte reverse - Inst::Rev8 { rd, rs1 } => format!("rev8 {}, {}", rd, rs1), - Inst::Brev8 { rd, rs1 } => format!("brev8 {}, {}", rd, rs1), - Inst::Orcb { rd, rs1 } => format!("orcb {}, {}", rd, rs1), + Inst::Rev8 { rd, rs1 } => format!("rev8 {rd}, {rs1}"), + Inst::Brev8 { rd, rs1 } => format!("brev8 {rd}, {rs1}"), + Inst::Orcb { rd, rs1 } => format!("orcb {rd}, {rs1}"), // Zbb: Min/Max - Inst::Min { rd, rs1, rs2 } => format!("min {}, {}, {}", rd, rs1, rs2), - Inst::Minu { rd, rs1, rs2 } => format!("minu {}, {}, {}", rd, rs1, rs2), - Inst::Max { rd, rs1, rs2 } => format!("max {}, {}, {}", rd, rs1, rs2), - Inst::Maxu { rd, rs1, rs2 } => format!("maxu {}, {}, {}", rd, rs1, rs2), + Inst::Min { rd, rs1, rs2 } => format!("min {rd}, {rs1}, {rs2}"), + Inst::Minu { rd, rs1, rs2 } => format!("minu {rd}, {rs1}, {rs2}"), + Inst::Max { rd, rs1, rs2 } => format!("max {rd}, {rs1}, {rs2}"), + Inst::Maxu { rd, rs1, rs2 } => format!("maxu {rd}, {rs1}, {rs2}"), // Zbb: Logical operations - Inst::Andn { rd, rs1, rs2 } => format!("andn {}, {}, {}", rd, rs1, rs2), - Inst::Orn { rd, rs1, rs2 } => format!("orn {}, {}, {}", rd, rs1, rs2), - Inst::Xnor { rd, rs1, rs2 } => format!("xnor {}, {}, {}", rd, rs1, rs2), + Inst::Andn { rd, rs1, rs2 } => format!("andn {rd}, {rs1}, {rs2}"), + Inst::Orn { rd, rs1, rs2 } => format!("orn {rd}, {rs1}, {rs2}"), + Inst::Xnor { rd, rs1, rs2 } => format!("xnor {rd}, {rs1}, {rs2}"), // Zba: Address generation - Inst::Sh1add { rd, rs1, rs2 } => format!("sh1add {}, {}, {}", rd, rs1, rs2), - Inst::Sh2add { rd, rs1, rs2 } => format!("sh2add {}, {}, {}", rd, rs1, rs2), - Inst::Sh3add { rd, rs1, rs2 } => format!("sh3add {}, {}, {}", rd, rs1, rs2), + Inst::Sh1add { rd, rs1, rs2 } => format!("sh1add {rd}, {rs1}, {rs2}"), + Inst::Sh2add { rd, rs1, rs2 } => format!("sh2add {rd}, {rs1}, {rs2}"), + Inst::Sh3add { rd, rs1, rs2 } => format!("sh3add {rd}, {rs1}, {rs2}"), Inst::SlliUw { rd, rs1, imm } => { format!("slli.uw {}, {}, {}", rd, rs1, format_imm(*imm)) } @@ -449,9 +449,9 @@ impl Inst { Inst::Ebreak => alloc::string::String::from("ebreak"), Inst::Fence => alloc::string::String::from("fence"), Inst::FenceI => alloc::string::String::from("fence.i"), - Inst::Csrrw { rd, rs1, csr } => format!("csrrw {}, {}, 0x{:03x}", rd, rs1, csr), - Inst::Csrrs { rd, rs1, csr } => format!("csrrs {}, {}, 0x{:03x}", rd, rs1, csr), - Inst::Csrrc { rd, rs1, csr } => format!("csrrc {}, {}, 0x{:03x}", rd, rs1, csr), + Inst::Csrrw { rd, rs1, csr } => format!("csrrw {rd}, {rs1}, 0x{csr:03x}"), + Inst::Csrrs { rd, rs1, csr } => format!("csrrs {rd}, {rs1}, 0x{csr:03x}"), + Inst::Csrrc { rd, rs1, csr } => format!("csrrc {rd}, {rs1}, 0x{csr:03x}"), Inst::Csrrwi { rd, imm, csr } => { format!("csrrwi {}, {}, 0x{:03x}", rd, format_imm(*imm), csr) } @@ -463,31 +463,31 @@ impl Inst { } // Atomic instructions - Inst::LrW { rd, rs1 } => format!("lr.w {}, ({})", rd, rs1), - Inst::ScW { rd, rs1, rs2 } => format!("sc.w {}, {}, ({})", rd, rs2, rs1), - Inst::AmoswapW { rd, rs1, rs2 } => format!("amoswap.w {}, {}, ({})", rd, rs2, rs1), - Inst::AmoaddW { rd, rs1, rs2 } => format!("amoadd.w {}, {}, ({})", rd, rs2, rs1), - Inst::AmoxorW { rd, rs1, rs2 } => format!("amoxor.w {}, {}, ({})", rd, rs2, rs1), - Inst::AmoandW { rd, rs1, rs2 } => format!("amoand.w {}, {}, ({})", rd, rs2, rs1), - Inst::AmoorW { rd, rs1, rs2 } => format!("amoor.w {}, {}, ({})", rd, rs2, rs1), + Inst::LrW { rd, rs1 } => format!("lr.w {rd}, ({rs1})"), + Inst::ScW { rd, rs1, rs2 } => format!("sc.w {rd}, {rs2}, ({rs1})"), + Inst::AmoswapW { rd, rs1, rs2 } => format!("amoswap.w {rd}, {rs2}, ({rs1})"), + Inst::AmoaddW { rd, rs1, rs2 } => format!("amoadd.w {rd}, {rs2}, ({rs1})"), + Inst::AmoxorW { rd, rs1, rs2 } => format!("amoxor.w {rd}, {rs2}, ({rs1})"), + Inst::AmoandW { rd, rs1, rs2 } => format!("amoand.w {rd}, {rs2}, ({rs1})"), + Inst::AmoorW { rd, rs1, rs2 } => format!("amoor.w {rd}, {rs2}, ({rs1})"), // Compressed instructions Inst::CAddi { rd, imm } => format!("c.addi {}, {}", rd, format_imm(*imm)), Inst::CLi { rd, imm } => format!("c.li {}, {}", rd, format_imm(*imm)), Inst::CLui { rd, imm } => format!("c.lui {}, 0x{:08x}", rd, *imm as u32), - Inst::CMv { rd, rs } => format!("c.mv {}, {}", rd, rs), - Inst::CAdd { rd, rs } => format!("c.add {}, {}", rd, rs), - Inst::CSub { rd, rs } => format!("c.sub {}, {}", rd, rs), - Inst::CAnd { rd, rs } => format!("c.and {}, {}", rd, rs), - Inst::COr { rd, rs } => format!("c.or {}, {}", rd, rs), - Inst::CXor { rd, rs } => format!("c.xor {}, {}", rd, rs), + Inst::CMv { rd, rs } => format!("c.mv {rd}, {rs}"), + Inst::CAdd { rd, rs } => format!("c.add {rd}, {rs}"), + Inst::CSub { rd, rs } => format!("c.sub {rd}, {rs}"), + Inst::CAnd { rd, rs } => format!("c.and {rd}, {rs}"), + Inst::COr { rd, rs } => format!("c.or {rd}, {rs}"), + Inst::CXor { rd, rs } => format!("c.xor {rd}, {rs}"), Inst::CLw { rd, rs, offset } => format!("c.lw {}, {}({})", rd, format_imm(*offset), rs), Inst::CSw { rs1, rs2, offset } => { format!("c.sw {}, {}({})", rs2, format_imm(*offset), rs1) } Inst::CJ { offset } => format!("c.j {}", format_imm(*offset)), - Inst::CJr { rs } => format!("c.jr {}", rs), - Inst::CJalr { rs } => format!("c.jalr {}", rs), + Inst::CJr { rs } => format!("c.jr {rs}"), + Inst::CJalr { rs } => format!("c.jalr {rs}"), Inst::CBeqz { rs, offset } => format!("c.beqz {}, {}", rs, format_imm(*offset)), Inst::CBnez { rs, offset } => format!("c.bnez {}, {}", rs, format_imm(*offset)), Inst::CSlli { rd, imm } => format!("c.slli {}, {}", rd, format_imm(*imm)), diff --git a/lp-glsl/crates/lp-riscv-tools/src/regs.rs b/lp-glsl/crates/lp-riscv-tools/src/regs.rs index 6ca11ead4..59eba431b 100644 --- a/lp-glsl/crates/lp-riscv-tools/src/regs.rs +++ b/lp-glsl/crates/lp-riscv-tools/src/regs.rs @@ -142,7 +142,7 @@ impl Gpr { } } } - Err(alloc::format!("Invalid register name: {}", name)) + Err(alloc::format!("Invalid register name: {name}")) } } } @@ -184,7 +184,7 @@ impl fmt::Display for Gpr { Gpr::T5 => "t5", Gpr::T6 => "t6", }; - write!(f, "{}", name) + write!(f, "{name}") } } diff --git a/lp-glsl/crates/lp-riscv-tools/tests/abi_tests.rs b/lp-glsl/crates/lp-riscv-tools/tests/abi_tests.rs index caf81bfb3..ad7f46411 100644 --- a/lp-glsl/crates/lp-riscv-tools/tests/abi_tests.rs +++ b/lp-glsl/crates/lp-riscv-tools/tests/abi_tests.rs @@ -363,16 +363,14 @@ fn test_return_area_pointer_with_arguments() { } Err(e) => { // Current bug: invalid memory write at address 0x00000001 (a0 contains 1 instead of return area pointer) - let err_str = format!("{:?}", e); + let err_str = format!("{e:?}"); assert!( err_str.contains("InvalidMemoryAccess") && err_str.contains("address: 1"), - "Expected InvalidMemoryAccess at address 1 (bug: a0 contains first arg instead of return area pointer), got: {}", - err_str + "Expected InvalidMemoryAccess at address 1 (bug: a0 contains first arg instead of return area pointer), got: {err_str}" ); // This test will fail until the bug is fixed panic!( - "BUG CONFIRMED: a0 contains first argument (1) instead of return area pointer. Error: {}", - err_str + "BUG CONFIRMED: a0 contains first argument (1) instead of return area pointer. Error: {err_str}" ); } } @@ -415,13 +413,12 @@ fn test_return_area_pointer_no_arguments() { } Err(e) => { // If this fails, it's likely because a0 is 0 or wrong - let err_str = format!("{:?}", e); + let err_str = format!("{e:?}"); assert!( err_str.contains("InvalidMemoryAccess") || err_str.contains("Invalid memory write") || err_str.contains("address: 0"), - "Expected invalid memory write error, got: {}", - err_str + "Expected invalid memory write error, got: {err_str}" ); } } diff --git a/lp-glsl/crates/lp-riscv-tools/tests/instruction_tests.rs b/lp-glsl/crates/lp-riscv-tools/tests/instruction_tests.rs index a5dc80fb4..608825f77 100644 --- a/lp-glsl/crates/lp-riscv-tools/tests/instruction_tests.rs +++ b/lp-glsl/crates/lp-riscv-tools/tests/instruction_tests.rs @@ -10,7 +10,7 @@ fn test_fence_i_decode_encode() { let inst = decode_instruction(0x0010100f).expect("Failed to decode FENCE.I"); match inst { Inst::FenceI => {} - _ => panic!("Expected FenceI, got {:?}", inst), + _ => panic!("Expected FenceI, got {inst:?}"), } // Test FENCE.I encoding @@ -21,7 +21,7 @@ fn test_fence_i_decode_encode() { let decoded = decode_instruction(encoded).expect("Failed to decode encoded FENCE.I"); match decoded { Inst::FenceI => {} - _ => panic!("Expected FenceI after round-trip, got {:?}", decoded), + _ => panic!("Expected FenceI after round-trip, got {decoded:?}"), } } @@ -59,13 +59,13 @@ fn test_fence_vs_fence_i() { let fence = decode_instruction(0x0000000f).expect("Failed to decode FENCE"); match fence { Inst::Fence => {} - _ => panic!("Expected Fence, got {:?}", fence), + _ => panic!("Expected Fence, got {fence:?}"), } let fence_i = decode_instruction(0x0010100f).expect("Failed to decode FENCE.I"); match fence_i { Inst::FenceI => {} - _ => panic!("Expected FenceI, got {:?}", fence_i), + _ => panic!("Expected FenceI, got {fence_i:?}"), } // They should be different @@ -82,7 +82,7 @@ fn test_atomic_instructions() { assert_eq!(rd, Gpr::A0); assert_eq!(rs1, Gpr::Zero); } - _ => panic!("Expected LrW, got {:?}", lr_w), + _ => panic!("Expected LrW, got {lr_w:?}"), } // SC.W: 0x1800252f (sc.w a0, zero, (zero)) @@ -93,7 +93,7 @@ fn test_atomic_instructions() { assert_eq!(rs1, Gpr::Zero); assert_eq!(rs2, Gpr::Zero); } - _ => panic!("Expected ScW, got {:?}", sc_w), + _ => panic!("Expected ScW, got {sc_w:?}"), } } @@ -105,7 +105,7 @@ fn test_compressed_instructions() { let c_nop = decode_instruction(0x0001).expect("Failed to decode C.NOP"); match c_nop { Inst::CNop => {} - _ => panic!("Expected CNop, got {:?}", c_nop), + _ => panic!("Expected CNop, got {c_nop:?}"), } } @@ -130,7 +130,7 @@ fn test_division_by_zero() { match emu.step() { Ok(lp_riscv_tools::StepResult::Halted) => break, Ok(_) => continue, - Err(e) => panic!("Emulator error: {:?}", e), + Err(e) => panic!("Emulator error: {e:?}"), } } diff --git a/lp-glsl/crates/lp-riscv-tools/tests/riscv_nostd_test.rs b/lp-glsl/crates/lp-riscv-tools/tests/riscv_nostd_test.rs index 3c436040a..096763453 100644 --- a/lp-glsl/crates/lp-riscv-tools/tests/riscv_nostd_test.rs +++ b/lp-glsl/crates/lp-riscv-tools/tests/riscv_nostd_test.rs @@ -22,7 +22,7 @@ fn test_riscv_nostd_hello_world() { // Wait for the test to complete with a 60 second timeout (build can take time) match rx.recv_timeout(Duration::from_secs(60)) { Ok(Ok(())) => {} // Success - Ok(Err(e)) => panic!("Test failed: {}", e), + Ok(Err(e)) => panic!("Test failed: {e}"), Err(mpsc::RecvTimeoutError::Timeout) => { panic!("Test timed out after 60 seconds"); } @@ -60,11 +60,11 @@ fn run_nostd_test() -> Result<(), String> { ]) .current_dir(workspace_root) .output() - .map_err(|e| format!("Failed to build: {}", e))?; + .map_err(|e| format!("Failed to build: {e}"))?; if !output.status.success() { let stderr = String::from_utf8_lossy(&output.stderr); - return Err(format!("Build failed:\n{}", stderr)); + return Err(format!("Build failed:\n{stderr}")); } println!(" ✓ Build successful"); println!(); @@ -73,7 +73,7 @@ fn run_nostd_test() -> Result<(), String> { println!("[2/4] Loading ELF binary..."); let elf_path = workspace_root.join("target/riscv32imac-unknown-none-elf/release/embive-program"); - let elf_data = std::fs::read(&elf_path).map_err(|e| format!("Failed to read ELF: {}", e))?; + let elf_data = std::fs::read(&elf_path).map_err(|e| format!("Failed to read ELF: {e}"))?; #[cfg(feature = "std")] let elf_info = load_elf(&elf_data)?; @@ -96,7 +96,7 @@ fn run_nostd_test() -> Result<(), String> { elf_info.code[i + 2], elf_info.code[i + 3], ]); - println!(" 0x{:04x}: 0x{:08x}", i, word); + println!(" 0x{i:04x}: 0x{word:08x}"); } } println!(); @@ -116,18 +116,18 @@ fn run_nostd_test() -> Result<(), String> { Ok(StepResult::Panic(panic_info)) => { let msg = if let Some(ref file) = panic_info.file { if let Some(line) = panic_info.line { - format!("{}:{}", file, line) + format!("{file}:{line}") } else { file.clone() } } else if let Some(line) = panic_info.line { - format!("line {}", line) + format!("line {line}") } else { "unknown location".to_string() }; return Err(format!( - "Guest panicked at PC 0x{:x}: {} ({})", - panic_info.pc, panic_info.message, msg + "Guest panicked at PC 0x{:x}: {} ({msg})", + panic_info.pc, panic_info.message )); } Ok(StepResult::Syscall(info)) => { @@ -158,7 +158,7 @@ fn run_nostd_test() -> Result<(), String> { "panic".to_string() }; - return Err(format!("Guest panicked: {}", msg)); + return Err(format!("Guest panicked: {msg}")); } 2 => { // SYSCALL_WRITE @@ -176,7 +176,7 @@ fn run_nostd_test() -> Result<(), String> { } let text = String::from_utf8_lossy(&bytes); - print!(" {}", text); + print!(" {text}"); std::io::Write::flush(&mut std::io::stdout()).unwrap(); output_lines.push(text.to_string()); } @@ -197,7 +197,7 @@ fn run_nostd_test() -> Result<(), String> { break; } Err(e) => { - return Err(format!("Emulator error: {:?}", e)); + return Err(format!("Emulator error: {e:?}")); } } } @@ -210,24 +210,21 @@ fn run_nostd_test() -> Result<(), String> { // Phase 1: Check for expected hello world message if !full_output.contains("Hello from RISC-V!") { return Err(format!( - "Expected 'Hello from RISC-V!' not found in output:\n{}", - full_output + "Expected 'Hello from RISC-V!' not found in output:\n{full_output}" )); } println!(" ✓ Found expected output: 'Hello from RISC-V!'"); if !full_output.contains("no_std") { return Err(format!( - "Expected 'no_std' not found in output:\n{}", - full_output + "Expected 'no_std' not found in output:\n{full_output}" )); } println!(" ✓ Found 'no_std' mention"); if !full_output.contains("Cranelift") { return Err(format!( - "Expected 'Cranelift' not found in output:\n{}", - full_output + "Expected 'Cranelift' not found in output:\n{full_output}" )); } println!(" ✓ Found 'Cranelift' mention"); @@ -235,8 +232,7 @@ fn run_nostd_test() -> Result<(), String> { // Phase 2: Check that program completed if !full_output.contains("Successfully executed") { return Err(format!( - "Expected success message not found in output:\n{}", - full_output + "Expected success message not found in output:\n{full_output}" )); } println!(" ✓ Program executed successfully"); @@ -247,7 +243,7 @@ fn run_nostd_test() -> Result<(), String> { println!(" ✓ Correct result received: 15 (expected for 5 * 3)"); } Some(other) => { - return Err(format!("Incorrect result: expected 15, got {}", other)); + return Err(format!("Incorrect result: expected 15, got {other}")); } None => { return Err("No result received from program".to_string()); diff --git a/lp-glsl/crates/lp-riscv-tools/tests/trap_tests.rs b/lp-glsl/crates/lp-riscv-tools/tests/trap_tests.rs index eb97c7dcd..f447e9d54 100644 --- a/lp-glsl/crates/lp-riscv-tools/tests/trap_tests.rs +++ b/lp-glsl/crates/lp-riscv-tools/tests/trap_tests.rs @@ -13,8 +13,8 @@ fn test_trap_with_code() { Ok(StepResult::Trap(code)) => { assert_eq!(code, TrapCode::INTEGER_DIVISION_BY_ZERO); } - Ok(other) => panic!("Expected Trap, got {:?}", other), - Err(e) => panic!("Unexpected error: {:?}", e), + Ok(other) => panic!("Expected Trap, got {other:?}"), + Err(e) => panic!("Unexpected error: {e:?}"), } } @@ -25,8 +25,8 @@ fn test_trap_without_metadata() { match emu.step() { Ok(StepResult::Halted) => {} // Expected - Ok(other) => panic!("Expected Halted, got {:?}", other), - Err(e) => panic!("Unexpected error: {:?}", e), + Ok(other) => panic!("Expected Halted, got {other:?}"), + Err(e) => panic!("Unexpected error: {e:?}"), } } @@ -43,8 +43,8 @@ fn test_trap_at_offset_4() { // First step: execute nop match emu.step() { Ok(StepResult::Continue) => {} - Ok(other) => panic!("Expected Continue, got {:?}", other), - Err(e) => panic!("Unexpected error: {:?}", e), + Ok(other) => panic!("Expected Continue, got {other:?}"), + Err(e) => panic!("Unexpected error: {e:?}"), } // Second step: execute ebreak at offset 4 @@ -52,8 +52,8 @@ fn test_trap_at_offset_4() { Ok(StepResult::Trap(code)) => { assert_eq!(code, TrapCode::INTEGER_OVERFLOW); } - Ok(other) => panic!("Expected Trap, got {:?}", other), - Err(e) => panic!("Unexpected error: {:?}", e), + Ok(other) => panic!("Expected Trap, got {other:?}"), + Err(e) => panic!("Unexpected error: {e:?}"), } } @@ -76,8 +76,8 @@ fn test_multiple_traps() { Ok(StepResult::Trap(code)) => { assert_eq!(code, TrapCode::INTEGER_DIVISION_BY_ZERO); } - Ok(other) => panic!("Expected Trap, got {:?}", other), - Err(e) => panic!("Unexpected error: {:?}", e), + Ok(other) => panic!("Expected Trap, got {other:?}"), + Err(e) => panic!("Unexpected error: {e:?}"), } // Reset PC to continue testing @@ -86,8 +86,8 @@ fn test_multiple_traps() { // Second step: execute nop match emu.step() { Ok(StepResult::Continue) => {} - Ok(other) => panic!("Expected Continue, got {:?}", other), - Err(e) => panic!("Unexpected error: {:?}", e), + Ok(other) => panic!("Expected Continue, got {other:?}"), + Err(e) => panic!("Unexpected error: {e:?}"), } // Third step: trap at offset 8 @@ -95,8 +95,8 @@ fn test_multiple_traps() { Ok(StepResult::Trap(code)) => { assert_eq!(code, TrapCode::INTEGER_OVERFLOW); } - Ok(other) => panic!("Expected Trap, got {:?}", other), - Err(e) => panic!("Unexpected error: {:?}", e), + Ok(other) => panic!("Expected Trap, got {other:?}"), + Err(e) => panic!("Unexpected error: {e:?}"), } } @@ -120,8 +120,8 @@ fn test_trap_sorting() { Ok(StepResult::Trap(code)) => { assert_eq!(code, TrapCode::INTEGER_DIVISION_BY_ZERO); } - Ok(other) => panic!("Expected Trap, got {:?}", other), - Err(e) => panic!("Unexpected error: {:?}", e), + Ok(other) => panic!("Expected Trap, got {other:?}"), + Err(e) => panic!("Unexpected error: {e:?}"), } } @@ -150,10 +150,7 @@ fn test_trap_at_absolute_address() { Ok(StepResult::Trap(code)) => { assert_eq!(code, TrapCode::INTEGER_DIVISION_BY_ZERO); } - Ok(other) => panic!( - "Expected Trap, got {:?} - this demonstrates the bug!", - other - ), - Err(e) => panic!("Unexpected error: {:?}", e), + Ok(other) => panic!("Expected Trap, got {other:?} - this demonstrates the bug!"), + Err(e) => panic!("Unexpected error: {e:?}"), } } diff --git a/lp-glsl/plans/2026-01-20-fix-test-failures/00-overview.md b/lp-glsl/plans/2026-01-20-fix-test-failures/00-overview.md new file mode 100644 index 000000000..e042b79c1 --- /dev/null +++ b/lp-glsl/plans/2026-01-20-fix-test-failures/00-overview.md @@ -0,0 +1,25 @@ +# Plan: Fix GLSL Test Failures + +## Overview + +Fix 25 failing tests in the GLSL compiler test suite. Tests fall into 4 categories: +1. JIT tests using unsupported Float format (4 tests) - Simple fix +2. Emulator basic execution returning 0 (3 tests) - Core bug investigation needed +3. Fixed32 transform tests returning 0 (13 tests) - Likely same root cause as #2 +4. Other tests (4 tests) - Individual investigation needed + +## Phases + +1. Fix JIT tests - Update to use Fixed32 format +2. Investigate emulator execution bug - Debug why functions return 0 +3. Fix emulator execution bug - Implement the fix +4. Verify fixed32 tests - Should work after phase 3 +5. Fix remaining tests - Individual fixes for category 4 +6. Cleanup and finalization + +## Success Criteria + +- All 25 failing tests pass +- No regressions in existing passing tests (64 currently pass) +- Code compiles without warnings +- All code formatted with `cargo +nightly fmt` diff --git a/lp-glsl/plans/2026-01-20-fix-test-failures/00-plans-notes.md b/lp-glsl/plans/2026-01-20-fix-test-failures/00-plans-notes.md new file mode 100644 index 000000000..de3a35725 --- /dev/null +++ b/lp-glsl/plans/2026-01-20-fix-test-failures/00-plans-notes.md @@ -0,0 +1,83 @@ +# Plan: Fix GLSL Test Failures + +## Questions + +### Q1: JIT Tests - Float Format Rejection + +**Context**: All JIT tests (`test_jit_int_literal`, `test_jit_int_addition`, `test_jit_float_literal`, `test_jit_bool_literal`) are failing because they use `DecimalFormat::Float`, which is explicitly rejected in `compile_glsl_to_gl_module_jit()` (line 92-97 of `frontend/mod.rs`). + +**Error**: `"Float format is not yet supported. Only Fixed32 format is currently supported. Float format will cause TestCase relocation errors. Use Fixed32 format instead."` + +**Suggested Answer**: Update all JIT tests to use `DecimalFormat::Fixed32` instead of `DecimalFormat::Float`. For integer and bool tests, this should work fine. For float tests, we'll need to convert expected values to fixed-point format. + +**Decision**: Update all JIT tests to use `DecimalFormat::Fixed32` instead of `DecimalFormat::Float`. This is the correct format for the current implementation. Float format support is a future TODO. + +### Q2: Emulator Tests Returning Zero + +**Context**: Multiple emulator tests are returning `0` instead of expected values: +- `test_build_emu_executable`: expects 42, gets 0 +- `test_emu_int_literal`: expects 42, gets 0 +- `test_emu_int_addition`: expects 30, likely gets 0 +- All fixed32 arithmetic tests: expect various values, get 0 + +**Pattern**: The tests compile successfully, but function execution returns 0 instead of the expected value. + +**Possible Causes**: +1. Function address lookup failing (but should error, not return 0) +2. Function execution failing silently +3. Return value extraction wrong (but should error if wrong type) +4. Function not being called correctly +5. Function returning 0 because execution path is wrong + +**Suggested Answer**: Investigate the emulator execution path. Check: +- Are function addresses being populated correctly in `function_addresses`? +- Is `call_function` actually executing the function? +- Are return values being extracted correctly? +- Add debug logging to trace execution + +**Investigation Results**: +- 64 tests pass, 25 fail, 4 ignored - so some tests DO work +- Function addresses are populated correctly from symbol map (lines 324-348 in emu.rs) +- Signature lookup uses `cranelift_signatures` HashMap (correct) +- Return value extraction reads from register a0 (register 10) +- The function call path looks correct, but returns 0 instead of expected value + +**Hypothesis**: The function may be executing but: +1. Not placing return value in a0 correctly +2. Return value being overwritten after function returns +3. Function not executing at all (but should error, not return 0) +4. Signature mismatch causing wrong return location + +**Next Steps**: Add debug logging to trace execution, check if function actually executes, verify return value in a0 register. + +### Q3: Fixed32 Arithmetic Tests + +**Context**: All fixed32 converter tests (`test_fixed32_fadd`, `test_fixed32_fsub`, `test_fixed32_fmul`, `test_fixed32_fneg`, `test_fixed32_fabs`, etc.) are returning 0 instead of expected fixed-point values. + +**Observation**: The CLIF IR transformation looks correct (we can see the transformed IR in test output), but execution returns 0. + +**Suggested Answer**: This is likely the same root cause as Q2 (emulator execution issue). Once we fix the emulator execution, these should work. + +**Decision**: This is likely the same root cause as Q2 (emulator execution issue). Once we fix the emulator execution, these should work. The CLIF IR transformation looks correct based on test output. + +### Q4: Test Categorization + +**Context**: We have 24 failing tests total. Need to categorize them: +1. **JIT Float Format Tests** (4 tests): `test_jit_*` - Need to change to Fixed32 +2. **Emulator Basic Execution** (3 tests): `test_build_emu_executable`, `test_emu_int_*` - Core execution broken +3. **Fixed32 Transform Tests** (13 tests): All `test_fixed32_*` - Likely same as #2 +4. **Other** (4 tests): `test_do_while`, `test_emu_builtin_sqrt_linked`, `test_emu_float_*`, `test_emu_user_fn_fixed32` - Need investigation + +**Suggested Answer**: +- Category 1: Simple fix (change DecimalFormat) +- Category 2: Core bug to investigate +- Category 3: Likely same as Category 2 +- Category 4: Investigate individually + +**Decision**: Fix in order: +1. Category 1 (JIT Float Format) - Simple fix, change DecimalFormat +2. Category 2 (Emulator Basic Execution) - Core bug, needs investigation +3. Category 3 (Fixed32 Tests) - Should fix automatically once #2 is fixed +4. Category 4 (Other Tests) - Investigate individually after #2 is fixed + +No tests should be marked `#[ignore]` unless they're testing future features that aren't implemented yet. diff --git a/lp-glsl/plans/2026-01-20-fix-test-failures/01-phase-fix-jit-tests.md b/lp-glsl/plans/2026-01-20-fix-test-failures/01-phase-fix-jit-tests.md new file mode 100644 index 000000000..5fddacbfe --- /dev/null +++ b/lp-glsl/plans/2026-01-20-fix-test-failures/01-phase-fix-jit-tests.md @@ -0,0 +1,24 @@ +# Phase 1: Fix JIT Tests + +## Description + +Update all JIT tests to use `DecimalFormat::Fixed32` instead of `DecimalFormat::Float`. The Float format is explicitly rejected in `compile_glsl_to_gl_module_jit()` and is not yet supported. + +## Tests to Fix + +- `exec::jit::tests::test_jit_int_literal` +- `exec::jit::tests::test_jit_int_addition` +- `exec::jit::tests::test_jit_float_literal` +- `exec::jit::tests::test_jit_bool_literal` + +## Implementation + +1. Update each test to use `DecimalFormat::Fixed32` instead of `DecimalFormat::Float` +2. For `test_jit_float_literal`: Convert expected float value to fixed-point format for comparison +3. Run tests to verify they pass + +## Success Criteria + +- All 4 JIT tests pass +- Tests compile without errors +- No warnings introduced diff --git a/lp-glsl/plans/2026-01-20-fix-test-failures/02-phase-investigate-emulator-bug.md b/lp-glsl/plans/2026-01-20-fix-test-failures/02-phase-investigate-emulator-bug.md new file mode 100644 index 000000000..1c7c77503 --- /dev/null +++ b/lp-glsl/plans/2026-01-20-fix-test-failures/02-phase-investigate-emulator-bug.md @@ -0,0 +1,50 @@ +# Phase 2: Investigate Emulator Execution Bug + +## Description + +Investigate why emulator tests are returning 0 instead of expected values. The function execution path appears correct, but return values are wrong. + +## Tests Affected + +- `backend::codegen::emu::tests::test_build_emu_executable` - expects 42, gets 0 +- `exec::emu::tests::test_emu_int_literal` - expects 42, gets 0 +- `exec::emu::tests::test_emu_int_addition` - expects 30, likely gets 0 +- All fixed32 arithmetic tests (13 tests) - all return 0 + +## Investigation Steps + +1. Add debug logging to `call_i32` to trace: + - Function address lookup + - Function signature retrieval + - Arguments being passed + - Return values from `call_function` + - Register a0 value after function call + +2. Add debug logging to `build_emu_executable` to verify: + - Function addresses are populated correctly + - Cranelift signatures are stored correctly + - Symbol map contains expected functions + +3. Check if function actually executes: + - Add logging in emulator `call_function` to trace execution + - Verify PC moves through function code + - Check if function returns correctly + +4. Verify return value extraction: + - Check if return value is in register a0 after function returns + - Verify `extract_return_values` is reading correct register + - Check if return location computation is correct + +## Possible Root Causes + +1. Function not executing (but should error, not return 0) +2. Return value not placed in a0 correctly +3. Return value overwritten after function returns +4. Signature mismatch causing wrong return location +5. Function address incorrect (but should error on lookup) + +## Success Criteria + +- Root cause identified +- Evidence collected (logs, register dumps, etc.) +- Fix approach determined diff --git a/lp-glsl/plans/2026-01-20-fix-test-failures/03-phase-fix-emulator-bug.md b/lp-glsl/plans/2026-01-20-fix-test-failures/03-phase-fix-emulator-bug.md new file mode 100644 index 000000000..b2140d2cc --- /dev/null +++ b/lp-glsl/plans/2026-01-20-fix-test-failures/03-phase-fix-emulator-bug.md @@ -0,0 +1,16 @@ +# Phase 3: Fix Emulator Execution Bug + +## Description + +Implement the fix for the emulator execution bug identified in Phase 2. + +## Implementation + +Based on Phase 2 findings, implement the appropriate fix. This phase will be updated once the root cause is identified. + +## Success Criteria + +- `test_build_emu_executable` passes +- `test_emu_int_literal` passes +- `test_emu_int_addition` passes +- No regressions in other tests diff --git a/lp-glsl/plans/2026-01-20-fix-test-failures/04-phase-verify-fixed32-tests.md b/lp-glsl/plans/2026-01-20-fix-test-failures/04-phase-verify-fixed32-tests.md new file mode 100644 index 000000000..3469c50ef --- /dev/null +++ b/lp-glsl/plans/2026-01-20-fix-test-failures/04-phase-verify-fixed32-tests.md @@ -0,0 +1,32 @@ +# Phase 4: Verify Fixed32 Tests + +## Description + +After fixing the emulator execution bug, verify that all fixed32 transform tests now pass. These tests were likely failing due to the same root cause. + +## Tests to Verify + +All `test_fixed32_*` tests: +- `test_fixed32_fadd` +- `test_fixed32_fsub` +- `test_fixed32_fmul` +- `test_fixed32_fneg` +- `test_fixed32_fabs` +- `test_fixed32_fabs_positive` +- `test_fixed32_fcmp_equal` +- `test_fixed32_fcmp_less_than` +- `test_fixed32_fmax` +- `test_fixed32_fmin` +- `test_fixed32_fconst` +- `test_fixed32_call` + +## Implementation + +1. Run all fixed32 tests +2. If any still fail, investigate individually +3. Fix any remaining issues + +## Success Criteria + +- All 13 fixed32 tests pass +- No regressions diff --git a/lp-glsl/plans/2026-01-20-fix-test-failures/05-phase-fix-remaining-tests.md b/lp-glsl/plans/2026-01-20-fix-test-failures/05-phase-fix-remaining-tests.md new file mode 100644 index 000000000..52161deb5 --- /dev/null +++ b/lp-glsl/plans/2026-01-20-fix-test-failures/05-phase-fix-remaining-tests.md @@ -0,0 +1,25 @@ +# Phase 5: Fix Remaining Tests + +## Description + +Fix the remaining 4 tests that don't fit into the previous categories. + +## Tests to Fix + +- `backend::transform::fixed32::transform::tests::test_do_while` +- `exec::emu::tests::test_emu_builtin_sqrt_linked` +- `exec::emu::tests::test_emu_float_addition_fixed32` +- `exec::emu::tests::test_emu_float_constant_fixed32` +- `exec::emu::tests::test_emu_float_multiplication_fixed32` +- `exec::emu::tests::test_emu_user_fn_fixed32` + +## Implementation + +1. Run each test individually to see specific failure +2. Investigate root cause for each +3. Fix issues one by one + +## Success Criteria + +- All remaining tests pass +- No regressions diff --git a/lp-glsl/plans/2026-01-20-fix-test-failures/06-phase-cleanup.md b/lp-glsl/plans/2026-01-20-fix-test-failures/06-phase-cleanup.md new file mode 100644 index 000000000..934441a57 --- /dev/null +++ b/lp-glsl/plans/2026-01-20-fix-test-failures/06-phase-cleanup.md @@ -0,0 +1,21 @@ +# Phase 6: Cleanup and Finalization + +## Description + +Final cleanup phase: remove debug code, fix warnings, ensure all tests pass, format code. + +## Implementation + +1. Remove any debug logging added during investigation +2. Fix all compiler warnings +3. Run full test suite to verify all tests pass +4. Run `cargo +nightly fmt` on all modified files +5. Verify no regressions + +## Success Criteria + +- All 25 previously failing tests pass +- All 64 previously passing tests still pass +- No compiler warnings +- Code properly formatted +- No debug code remaining diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 000000000..73cb934de --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "stable" +components = ["rustfmt", "clippy"] diff --git a/scripts/dev-init.sh b/scripts/dev-init.sh new file mode 100755 index 000000000..b6d6380e0 --- /dev/null +++ b/scripts/dev-init.sh @@ -0,0 +1,123 @@ +#!/bin/bash +# Development initialization script +# Ensures required tools are installed and git hooks are set up + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Script directory (where this script is located) +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +# Project root (parent of scripts directory) +PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +echo "Initializing development environment..." +echo "Project root: $PROJECT_ROOT" +echo "" + +# Function to check if a command exists +check_command() { + if command -v "$1" > /dev/null 2>&1; then + echo -e "${GREEN}✓${NC} $1 is installed" + return 0 + else + echo -e "${RED}✗${NC} $1 is not installed" + return 1 + fi +} + +# Function to check Rust version +check_rust_version() { + local min_version="1.90.0" + if command -v rustc > /dev/null 2>&1; then + # Extract version number from "rustc 1.91.1 (ed61e7d7e 2025-11-07)" format + local rust_version=$(rustc --version | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1) + + if [ -z "$rust_version" ]; then + echo -e "${RED}✗${NC} Could not parse rustc version" + return 1 + fi + + echo -e "${GREEN}✓${NC} rustc version: $rust_version" + + # Compare versions (simple numeric comparison) + local rust_major=$(echo "$rust_version" | cut -d. -f1) + local rust_minor=$(echo "$rust_version" | cut -d. -f2) + local min_major=$(echo "$min_version" | cut -d. -f1) + local min_minor=$(echo "$min_version" | cut -d. -f2) + + # Validate that we got numeric values + if [ -z "$rust_major" ] || [ -z "$rust_minor" ] || [ -z "$min_major" ] || [ -z "$min_minor" ]; then + echo -e "${RED}✗${NC} Could not parse version numbers for comparison" + return 1 + fi + + if [ "$rust_major" -lt "$min_major" ] || ([ "$rust_major" -eq "$min_major" ] && [ "$rust_minor" -lt "$min_minor" ]); then + echo -e "${RED}✗${NC} Rust version $rust_version is below minimum required version $min_version" + echo " Install or update Rust: https://rustup.rs/" + return 1 + fi + return 0 + else + echo -e "${RED}✗${NC} rustc is not installed" + echo " Install Rust: https://rustup.rs/" + return 1 + fi +} + +# Check required tools +echo "Checking required tools..." +MISSING_TOOLS=0 + +# Note: We check for rustc instead of rust (rustc is the actual compiler) +if ! check_command "rustc"; then + echo " Install Rust: https://rustup.rs/" + MISSING_TOOLS=1 +fi + +if ! check_command "cargo"; then + echo " Install Rust (includes cargo): https://rustup.rs/" + MISSING_TOOLS=1 +fi + +if ! check_command "rustup"; then + echo " Install Rust (includes rustup): https://rustup.rs/" + MISSING_TOOLS=1 +fi + +if ! check_command "just"; then + echo " Install just: cargo install just" + echo " Or via package manager: https://github.com/casey/just#installation" + MISSING_TOOLS=1 +fi + +# Check Rust version +if ! check_rust_version; then + MISSING_TOOLS=1 +fi + +if [ $MISSING_TOOLS -eq 1 ]; then + echo "" + echo -e "${RED}Error: Missing required tools. Please install them and run this script again.${NC}" + exit 1 +fi + +echo "" + +# Install RISC-V target +RISC_V_TARGET="riscv32imac-unknown-none-elf" +echo "Checking RISC-V target ($RISC_V_TARGET)..." +if rustup target list --installed | grep -q "^${RISC_V_TARGET}$"; then + echo -e "${GREEN}✓${NC} RISC-V target already installed" +else + echo "Installing RISC-V target..." + rustup target add "$RISC_V_TARGET" + echo -e "${GREEN}✓${NC} RISC-V target installed" +fi + +echo "" +echo -e "${GREEN}Development environment initialized successfully!${NC}" diff --git a/scripts/push.sh b/scripts/push.sh new file mode 100755 index 000000000..4770bd3f9 --- /dev/null +++ b/scripts/push.sh @@ -0,0 +1,408 @@ +#!/bin/bash +set -euo pipefail + +# push.sh: Push current branch and create/update PR with workflow watching +# +# Pushes the current feature branch to origin and creates a PR if it doesn't exist. +# Watches for PR checks and workflows to complete. + +# Colors for output +RED=$'\033[0;31m' +GREEN=$'\033[0;32m' +YELLOW=$'\033[1;33m' +BOLD=$'\033[1m' +GRAY=$'\033[0;90m' +WHITE=$'\033[1;37m' +NC=$'\033[0m' # No Color +SUCCESS=$'\033[1;32m✅' +FAIL=$'\033[1;31m❌' +SPINNER=$'\033[1;33m⏳' + +# Parse arguments +SHOULD_MERGE=false +if [[ "${1:-}" == "--merge" ]] || [[ "${1:-}" == "-m" ]]; then + SHOULD_MERGE=true +fi + +# Main function - orchestrates the entire push workflow +main() { + # Check we're in a git repo + if ! git rev-parse --git-dir > /dev/null 2>&1; then + echo -e "${RED}Error: Not in a git repository${NC}" + exit 1 + fi + + # Change to repo root + cd "$(git rev-parse --show-toplevel)" + + # Check required tools + if ! command -v gh &> /dev/null; then + echo -e "${RED}Error: 'gh' command not found. Please install GitHub CLI.${NC}" + exit 1 + fi + if ! command -v jq &> /dev/null; then + echo -e "${RED}Error: 'jq' command not found. Please install jq.${NC}" + exit 1 + fi + + # Get current branch and repo info + local current_branch + current_branch="$(git rev-parse --abbrev-ref HEAD)" + + # Validate feature branch + check_feature_branch "$current_branch" + + # Get repo URL for branch link + local repo_url + repo_url="$(git remote get-url origin 2>/dev/null | sed 's/\.git$//' | sed 's/^git@github\.com:/https:\/\/github.com\//' || echo "")" + + # Push changes + push_changes "$current_branch" "$repo_url" + + # Ensure PR exists right after pushing + local pr_number pr_url pr_title + pr_url="$(ensure_pr "$current_branch")" + pr_number="$(extract_pr_number "$pr_url")" + + if [[ -n "$pr_number" ]]; then + pr_title="$(gh pr view "$pr_number" --json title --jq -r '.title' 2>/dev/null || echo "")" + fi + + # Wait for validation to complete + if await_validation "$current_branch" "$pr_url"; then + if [[ "$SHOULD_MERGE" == "true" ]]; then + if [[ -n "$pr_number" ]]; then + merge_pr "$pr_number" + else + echo -e "${YELLOW}Warning: Cannot merge PR without PR number${NC}" + fi + else + open_pr "$pr_url" + fi + else + echo -e "${RED}Build failed. Check the logs above for details.${NC}" + exit 1 + fi +} + +# Check that we're on a feature branch +check_feature_branch() { + local branch="$1" + if [[ ! "$branch" =~ ^feature/ ]]; then + echo -e "${RED}Error: Not on a feature branch. Current branch is: $branch${NC}" + echo "Please switch to a feature/* branch before running this script." + exit 1 + fi +} + +# Push changes with force-with-lease +push_changes() { + local branch="$1" + local repo_url="$2" + + local branch_url="" + if [[ -n "$repo_url" ]]; then + branch_url="$repo_url/tree/$branch" + fi + + printf "%s%s Pushing branch... " "${SPINNER}" "${NC}" + + if git push --force-with-lease origin "$branch" > /dev/null 2>&1; then + printf "\r%s%s Pushed branch: %s%s%s\n" "${SUCCESS}" "${NC}" "${GRAY}" "$branch_url" "${NC}" + else + printf "\r%s%s Failed to push branch\n" "${FAIL}" "${NC}" + git push --force-with-lease origin "$branch" + exit 1 + fi +} + +# Ensure PR exists, create if needed, return PR URL +ensure_pr() { + local branch="$1" + local base_branch="main" + + # Check if PR already exists + local existing_pr pr_info + existing_pr="$(gh pr list --head "$branch" --json number,url,title --jq '.[0]' 2>/dev/null || echo "")" + + if [[ -n "$existing_pr" ]] && [[ "$existing_pr" != "null" ]]; then + local pr_url pr_num pr_title + pr_url="$(echo "$existing_pr" | jq -r '.url')" + pr_num="$(echo "$existing_pr" | jq -r '.number')" + pr_title="$(echo "$existing_pr" | jq -r '.title')" + + printf "%s%s Ensuring PR... " "${SPINNER}" "${NC}" + printf "\r%s%s Ensuring PR: @PR %s: %s%s%s\n" "${SUCCESS}" "${NC}" "$pr_num" "${WHITE}" "$pr_title" "${NC}" + echo "$pr_url" + return + fi + + # Create new PR + printf "%s%s Ensuring PR... " "${SPINNER}" "${NC}" + local pr_url + pr_url="$(gh pr create --head "$branch" --base "$base_branch" --title "WIP: $branch" --body "Automated PR for $branch" --draft 2>/dev/null || true)" + + if [[ -z "$pr_url" ]]; then + printf "\r%s%s Failed to create PR\n" "${FAIL}" "${NC}" + exit 1 + fi + + local pr_num pr_title + pr_num="$(extract_pr_number "$pr_url")" + if [[ -n "$pr_num" ]]; then + pr_title="$(gh pr view "$pr_num" --json title --jq -r '.title' 2>/dev/null || echo "")" + printf "\r%s%s Ensuring PR: @PR %s: %s%s%s\n" "${SUCCESS}" "${NC}" "$pr_num" "${WHITE}" "$pr_title" "${NC}" + else + printf "\r%s%s Ensuring PR\n" "${SUCCESS}" "${NC}" + fi + + echo "$pr_url" +} + +# Extract PR number from URL +extract_pr_number() { + local url="$1" + if [[ "$url" =~ /pull/([0-9]+) ]]; then + echo "${BASH_REMATCH[1]}" + fi +} + +# Wait for validation (workflows/checks) to complete +# Returns 0 (success) if successful, 1 (failure) if failed +await_validation() { + local branch="$1" + local pr_url="$2" + + printf "%s%s Awaiting build checks... " "${SPINNER}" "${NC}" + + # Get the commit hash + local commit_hash + commit_hash="$(git rev-parse HEAD)" + + # First, wait for workflow to start (if it hasn't already) + local run_id run_url + run_id="$(wait_for_workflow_start "$commit_hash")" + + if [[ -z "$run_id" ]]; then + printf "\r%s%s No workflow found for commit\n" "${FAIL}" "${NC}" + printf "%sWarning: No workflow found for commit %s%s\n" "${YELLOW}" "$commit_hash" "${NC}" + echo "This might mean workflows haven't started yet, or no workflows are configured." + return 1 + fi + + # Get run URL for display + run_url="$(gh run view "$run_id" --json htmlUrl --jq -r '.htmlUrl' 2>/dev/null || echo "")" + if [[ -z "$run_url" ]]; then + # Fallback: construct URL from repo info + local repo_owner repo_name + repo_owner="$(gh repo view --json owner --jq -r '.owner.login' 2>/dev/null || echo "")" + repo_name="$(gh repo view --json name --jq -r '.name' 2>/dev/null || echo "")" + if [[ -n "$repo_owner" ]] && [[ -n "$repo_name" ]]; then + run_url="https://github.com/$repo_owner/$repo_name/actions/runs/$run_id" + fi + fi + + # Check if workflow already completed (idempotent check) + local run_info + run_info="$(gh run view "$run_id" --json status,conclusion 2>/dev/null || echo "")" + + if [[ -n "$run_info" ]]; then + local status conclusion + status="$(echo "$run_info" | jq -r '.status // "unknown"')" + conclusion="$(echo "$run_info" | jq -r '.conclusion // "none"')" + + if [[ "$status" == "completed" ]] && [[ "$conclusion" == "success" ]]; then + printf "\r%s%s Build checks passed\n" "${SUCCESS}" "${NC}" + if [[ -n "$run_url" ]]; then + printf "Run: %s%s%s\n" "${GRAY}" "$run_url" "${NC}" + fi + return 0 + elif [[ "$status" == "completed" ]] && ([[ "$conclusion" == "failure" ]] || [[ "$conclusion" == "cancelled" ]]); then + printf "\r%s%s Build checks failed\n" "${FAIL}" "${NC}" + if [[ -n "$run_url" ]]; then + printf "Run: %s%s%s\n" "${GRAY}" "$run_url" "${NC}" + fi + handle_workflow_failure "$run_id" "$run_url" "$pr_url" + return 1 + elif [[ "$conclusion" == "failure" ]] || [[ "$conclusion" == "cancelled" ]]; then + # Conclusion set but status might not be "completed" yet + printf "\r%s%s Build checks failed\n" "${FAIL}" "${NC}" + if [[ -n "$run_url" ]]; then + printf "Run: %s%s%s\n" "${GRAY}" "$run_url" "${NC}" + fi + handle_workflow_failure "$run_id" "$run_url" "$pr_url" + return 1 + fi + fi + + # Watch workflow until completion + watch_workflow "$run_id" "$run_url" "$pr_url" +} + +# Wait for workflow to start, return run ID +# Gets the most recent run for the commit +wait_for_workflow_start() { + local commit_hash="$1" + local max_wait=60 # 60 seconds + local elapsed=0 + local interval=2 # Check every 2 seconds + + while [[ $elapsed -lt $max_wait ]]; do + # Get the most recent run (gh run list returns runs sorted by most recent first by default) + # Use databaseId for the run ID (this is the numeric ID used in URLs) + local run_id + run_id="$(gh run list --commit "$commit_hash" --limit 1 --json databaseId --jq '.[0].databaseId // empty' 2>/dev/null || true)" + + if [[ -n "$run_id" ]] && [[ "$run_id" != "null" ]] && [[ "$run_id" != "" ]]; then + echo "$run_id" + return 0 + fi + + sleep "$interval" + elapsed=$((elapsed + interval)) + done + + return 1 +} + +# Watch workflow until completion using gh run watch for live output +# Returns 0 (success) if successful, 1 (failure) if failed +watch_workflow() { + local run_id="$1" + local run_url="$2" + local pr_url="$3" + + echo + printf "%s%s Watching workflow run (live output)...%s\n" "${SPINNER}" "${NC}" "${NC}" + if [[ -n "$run_url" ]]; then + printf "Run: %s%s%s\n" "${GRAY}" "$run_url" "${NC}" + fi + echo + + # Use gh run watch to show live output + # This will stream the workflow logs and exit when the run completes + # --exit-status makes it exit with the same status as the workflow + if gh run watch "$run_id" --exit-status; then + printf "\r%s%s Build checks passed\n" "${SUCCESS}" "${NC}" + if [[ -n "$run_url" ]]; then + printf "Run: %s%s%s\n" "${GRAY}" "$run_url" "${NC}" + fi + return 0 + else + # Check the actual conclusion to see if it failed or was cancelled + local run_info + run_info="$(gh run view "$run_id" --json status,conclusion 2>/dev/null || echo "")" + + if [[ -n "$run_info" ]]; then + local status conclusion + status="$(echo "$run_info" | jq -r '.status // "unknown"')" + conclusion="$(echo "$run_info" | jq -r '.conclusion // "none"')" + + if [[ "$status" == "completed" ]] && [[ "$conclusion" == "success" ]]; then + printf "\r%s%s Build checks passed\n" "${SUCCESS}" "${NC}" + if [[ -n "$run_url" ]]; then + printf "Run: %s%s%s\n" "${GRAY}" "$run_url" "${NC}" + fi + return 0 + fi + fi + + printf "\r%s%s Build checks failed\n" "${FAIL}" "${NC}" + if [[ -n "$run_url" ]]; then + printf "Run: %s%s%s\n" "${GRAY}" "$run_url" "${NC}" + fi + handle_workflow_failure "$run_id" "$run_url" "$pr_url" + return 1 + fi +} + +# Handle workflow failure - download logs and analyze +handle_workflow_failure() { + local run_id="$1" + local run_url="$2" + local pr_url="$3" + + echo + printf "%sDownloading logs...%s\n" "${RED}" "${NC}" + + # Create artifacts directory + local repo_root + repo_root="$(git rev-parse --show-toplevel)" + local artifacts_dir="$repo_root/.ci-artifacts/$run_id" + mkdir -p "$artifacts_dir" + + # Check if logs already exist (idempotent) + local log_file="$artifacts_dir/build.log" + if [[ ! -f "$log_file" ]]; then + if ! gh run view "$run_id" --log > "$log_file" 2>&1; then + # Try alternative method + local zip_file="$artifacts_dir/workflow-logs.zip" + if gh api "repos/:owner/:repo/actions/runs/$run_id/logs" > "$zip_file" 2>/dev/null; then + local logs_dir="$artifacts_dir/logs" + mkdir -p "$logs_dir" + unzip -o "$zip_file" -d "$logs_dir" > /dev/null 2>&1 || true + rm -f "$zip_file" + log_file="$logs_dir" + else + log_file="" + fi + fi + fi + + # Analyze logs for errors and warnings + if [[ -n "$log_file" ]] && [[ -e "$log_file" ]]; then + echo + grep_log_failures "$log_file" + echo + printf "Logs: %s%s%s\n" "${GRAY}" "$log_file" "${NC}" + fi +} + +# Grep logs for common error and warning patterns +grep_log_failures() { + local log_path="$1" + local pattern='(error|failed|failure|exception|fatal|✖|FAIL|ERROR|Error:|Failed:|warning|Warning:|WARNING)' + + local results + if [[ -d "$log_path" ]]; then + # Search recursively in directory + results="$(grep -r -i -E "$pattern" "$log_path" 2>/dev/null | head -30 || true)" + else + # Search in single file + results="$(grep -i -E "$pattern" "$log_path" 2>/dev/null | head -30 || true)" + fi + + if [[ -n "$results" ]]; then + printf "%sErrors/warnings found:%s\n" "${RED}" "${NC}" + echo "$results" | while IFS= read -r line; do + # Truncate very long lines + if [[ ${#line} -gt 200 ]]; then + printf " %s%s...%s\n" "${GRAY}" "${line:0:200}" "${NC}" + else + printf " %s%s%s\n" "${GRAY}" "$line" "${NC}" + fi + done + fi +} + +# Merge PR +merge_pr() { + local pr_number="$1" + printf "%s%s Merging PR #%s... " "${SPINNER}" "${NC}" "$pr_number" + if gh pr merge "$pr_number" --merge --delete-branch=false > /dev/null 2>&1; then + printf "\r%s%s Merged PR #%s\n" "${SUCCESS}" "${NC}" "$pr_number" + else + printf "\r%s%s Failed to merge PR #%s\n" "${FAIL}" "${NC}" "$pr_number" + gh pr merge "$pr_number" --merge --delete-branch=false + exit 1 + fi +} + +# Open PR in browser +open_pr() { + local pr_url="$1" + open "$pr_url" > /dev/null 2>&1 +} + +# Run main function +main "$@" diff --git a/test_round b/test_round new file mode 100755 index 000000000..29bf3fd77 Binary files /dev/null and b/test_round differ