From 8cf8257c095b76415587723dd4e0bd906fb73d83 Mon Sep 17 00:00:00 2001 From: Yinan Xu Date: Fri, 8 May 2026 15:28:10 +0800 Subject: [PATCH] feat(spec2017): add runspec-based SPEC2017 workload flow Add a SPEC2017 workload integration under workloads/linux/spec2017 that builds rate and speed benchmarks from an external SPEC tree with runcpu, stages selected inputs into Linux workload images, and exports firmware, ELF, kernel, and run-script artifacts under the build and image directories. The helper scripts keep SPEC build output inside this repository, add prepare and ELF-only make targets, split multi-command runs into separate exported images, provide mode-specific RISC-V GCC configs, and support PROFILING-controlled NEMU traps plus per-case DTB memory profiles. --- dts/xiangshan-fpga-noAIA-mem24g.dts.in | 174 ++++ dts/xiangshan-fpga-noAIA-mem8g.dts.in | 174 ++++ scripts/build-firmware-linux.sh | 72 +- workloads/linux/spec2017/README.md | 176 ++++ workloads/linux/spec2017/build.sh | 46 + .../linux/spec2017/prepare-spec-workspace.sh | 64 ++ workloads/linux/spec2017/riscv-gcc15.cfg | 156 ++++ workloads/linux/spec2017/rules.mk | 287 ++++++ workloads/linux/spec2017/spec2017-package.py | 863 ++++++++++++++++++ 9 files changed, 2009 insertions(+), 3 deletions(-) create mode 100644 dts/xiangshan-fpga-noAIA-mem24g.dts.in create mode 100644 dts/xiangshan-fpga-noAIA-mem8g.dts.in create mode 100644 workloads/linux/spec2017/README.md create mode 100755 workloads/linux/spec2017/build.sh create mode 100755 workloads/linux/spec2017/prepare-spec-workspace.sh create mode 100644 workloads/linux/spec2017/riscv-gcc15.cfg create mode 100644 workloads/linux/spec2017/rules.mk create mode 100755 workloads/linux/spec2017/spec2017-package.py diff --git a/dts/xiangshan-fpga-noAIA-mem24g.dts.in b/dts/xiangshan-fpga-noAIA-mem24g.dts.in new file mode 100644 index 0000000..df47ca2 --- /dev/null +++ b/dts/xiangshan-fpga-noAIA-mem24g.dts.in @@ -0,0 +1,174 @@ +/* + * XiangShan FPGA DTS used by the fpga board flow. + * + * 1. AIA is disabled, so Linux should only see the legacy CLINT + PLIC path. + * 2. UART16550 is exposed as the serial console. + */ + +/dts-v1/; + +/ { + compatible = "freechips,rocketchip-unknown-dev"; + model = "xiangshan,Kunminghu-dev"; + #address-cells = <2>; + #size-cells = <2>; + cpus { + #address-cells = <1>; + #size-cells = <0>; + timebase-frequency = <1000000>; + + cpu0: cpu@0 { + compatible = "ICT,xiangshan", "riscv"; + device_type = "cpu"; + reg = <0x0>; + status = "okay"; + + d-cache-block-size = <64>; + d-cache-sets = <256>; + d-cache-size = <65536>; + d-tlb-sets = <1>; + d-tlb-size = <48>; + i-cache-block-size = <64>; + i-cache-sets = <256>; + i-cache-size = <65536>; + i-tlb-sets = <1>; + i-tlb-size = <48>; + mmu-type = "riscv,sv48"; + clock-frequency = <0>; + timebase-frequency = <1000000>; + tlb-split; + + /* + * Keep the Linux-friendly CPU capability description from the + * workload template while using the FPGA board interrupt/peripheral + * map below. + */ + riscv,isa = "rv64imafdcvh_smstateen_sscofpmf_sstc_zicntr_zihpm_svpbmt_sdtrig_smcsrind_sscsrind_svade"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = + "i", "m", "a", "f", "d", "c", "v", "h", + "sdtrig", "sha", "shcounterenw", "shgatpa", + "shlcofideleg", "shtvala", "shvsatpa", "shvstvala", + "shvstvecd", "smcsrind", "smdbltrp", + "smmpm", "smnpm", "smrnmi", "smstateen", + "ss1p13", "ssccptr", "sscofpmf", + "sscounterenw", "sscsrind", "ssdbltrp", "ssnpm", + "sspm", "ssstateen", "ssstrict", "sstc", + "sstvala", "sstvecd", "ssu64xl", "supm", + "sv39", "sv48", "svade", "svbare", "svinval", + "svnapot", "svpbmt", "za64rs", "zacas", "zawrs", + "zba", "zbb", "zbc", "zbkb", "zbkc", "zbkx", + "zbs", "zcb", "zcmop", "zfa", "zfh", "zfhmin", + "zic64b", + "ziccamoa", "ziccif", "zicclsm", "ziccrse", + "zicntr", "zicond", "zicsr", "zifencei", + "zihintntl", "zihintpause", "zihpm", "zimop", + "zkn", "zknd", "zkne", "zknh", "zksed", + "zksh", "zkt", "zvbb", "zvfh", "zvfhmin", + "zvkt", "zvl128b", "zvl32b", "zvl64b"; + riscv,cbom-block-size = <0x40>; + riscv,cboz-block-size = <0x40>; + + next-level-cache = <&l2_cache>; + + intc_cpu0: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + }; + + l2_cache: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-block-size = <64>; + cache-size = <1048576>; + }; + + memory: memory@80000000 { + device_type = "memory"; + /* + * Static 24 GiB memory profile for SPEC2017 and other large initramfs workloads. + */ + reg = <0x0 0x80000000 0x6 0x00000000>; + }; + + soc { + #address-cells = <2>; + #size-cells = <2>; + compatible = "freechips,rocketchip-unknown-soc", "simple-bus"; + ranges; + + /* + * This FPGA image boots without AIA. Describe only the CLINT + PLIC + * interrupt topology that the software stack should probe. + */ + clint: clint@38000000 { + compatible = "riscv,clint0"; + reg = <0x0 0x38000000 0x0 0x10000>; + reg-names = "control"; + interrupts-extended = <&intc_cpu0 3 &intc_cpu0 7>; + }; + + debug-controller@38020000 { + compatible = "sifive,debug-013", "riscv,debug-013"; + debug-attach = "jtag"; + reg = <0x0 0x38020000 0x0 0x1000>; + reg-names = "control"; + interrupts-extended = <&intc_cpu0 65535>; + }; + + PLIC: interrupt-controller@3c000000 { + compatible = "riscv,plic0"; + #interrupt-cells = <1>; + interrupt-controller; + reg = <0x0 0x3c000000 0x0 0x4000000>; + reg-names = "control"; + interrupts-extended = <&intc_cpu0 11 &intc_cpu0 9>; + riscv,max-priority = <7>; + riscv,ndev = <66>; + }; + + /* + * Board-specific FPGA peripheral: the external AXI UART16550 used as + * the Linux console on fpga. Keep the 32-bit MMIO spacing and the + * 50 MHz input clock to match the FPGA wrapper. + */ + uart0: serial@310b0000 { + compatible = "ns16550a"; + reg = <0x0 0x310b0000 0x0 0x10000>; + reg-shift = <0x2>; + reg-io-width = <0x4>; + clock-frequency = <50000000>; + current-speed = <115200>; + //interrupt-parent = <&PLIC>; + //interrupts = <40>; + status = "okay"; + }; + }; + + aliases { + serial0 = &uart0; + }; + + chosen { + /* + * On this FPGA DTS the UART16550 interrupt is not wired into Linux + * (`interrupt-parent` / `interrupts` are intentionally absent above). + * `ttyS0` can still print early boot logs via polling/earlycon, but the + * normal console path becomes unreliable once Linux switches away from + * earlycon. When the UART interrupt is absent, use the SBI console + * (`hvc0`) instead. + */ + bootargs = "console=hvc0 earlycon=sbi loglevel=8"; + stdout-path = "serial0:115200n8"; + linux,initrd-start = <0x0 INITRAMFS_BEGIN>; + linux,initrd-end = <0x0 INITRAMFS_END>; + + opensbi-config { + compatible = "opensbi,config"; + cold-boot-harts = <&cpu0>; + }; + }; +}; diff --git a/dts/xiangshan-fpga-noAIA-mem8g.dts.in b/dts/xiangshan-fpga-noAIA-mem8g.dts.in new file mode 100644 index 0000000..2ffddac --- /dev/null +++ b/dts/xiangshan-fpga-noAIA-mem8g.dts.in @@ -0,0 +1,174 @@ +/* + * XiangShan FPGA DTS used by the fpga board flow. + * + * 1. AIA is disabled, so Linux should only see the legacy CLINT + PLIC path. + * 2. UART16550 is exposed as the serial console. + */ + +/dts-v1/; + +/ { + compatible = "freechips,rocketchip-unknown-dev"; + model = "xiangshan,Kunminghu-dev"; + #address-cells = <2>; + #size-cells = <2>; + cpus { + #address-cells = <1>; + #size-cells = <0>; + timebase-frequency = <1000000>; + + cpu0: cpu@0 { + compatible = "ICT,xiangshan", "riscv"; + device_type = "cpu"; + reg = <0x0>; + status = "okay"; + + d-cache-block-size = <64>; + d-cache-sets = <256>; + d-cache-size = <65536>; + d-tlb-sets = <1>; + d-tlb-size = <48>; + i-cache-block-size = <64>; + i-cache-sets = <256>; + i-cache-size = <65536>; + i-tlb-sets = <1>; + i-tlb-size = <48>; + mmu-type = "riscv,sv48"; + clock-frequency = <0>; + timebase-frequency = <1000000>; + tlb-split; + + /* + * Keep the Linux-friendly CPU capability description from the + * workload template while using the FPGA board interrupt/peripheral + * map below. + */ + riscv,isa = "rv64imafdcvh_smstateen_sscofpmf_sstc_zicntr_zihpm_svpbmt_sdtrig_smcsrind_sscsrind_svade"; + riscv,isa-base = "rv64i"; + riscv,isa-extensions = + "i", "m", "a", "f", "d", "c", "v", "h", + "sdtrig", "sha", "shcounterenw", "shgatpa", + "shlcofideleg", "shtvala", "shvsatpa", "shvstvala", + "shvstvecd", "smcsrind", "smdbltrp", + "smmpm", "smnpm", "smrnmi", "smstateen", + "ss1p13", "ssccptr", "sscofpmf", + "sscounterenw", "sscsrind", "ssdbltrp", "ssnpm", + "sspm", "ssstateen", "ssstrict", "sstc", + "sstvala", "sstvecd", "ssu64xl", "supm", + "sv39", "sv48", "svade", "svbare", "svinval", + "svnapot", "svpbmt", "za64rs", "zacas", "zawrs", + "zba", "zbb", "zbc", "zbkb", "zbkc", "zbkx", + "zbs", "zcb", "zcmop", "zfa", "zfh", "zfhmin", + "zic64b", + "ziccamoa", "ziccif", "zicclsm", "ziccrse", + "zicntr", "zicond", "zicsr", "zifencei", + "zihintntl", "zihintpause", "zihpm", "zimop", + "zkn", "zknd", "zkne", "zknh", "zksed", + "zksh", "zkt", "zvbb", "zvfh", "zvfhmin", + "zvkt", "zvl128b", "zvl32b", "zvl64b"; + riscv,cbom-block-size = <0x40>; + riscv,cboz-block-size = <0x40>; + + next-level-cache = <&l2_cache>; + + intc_cpu0: interrupt-controller { + #interrupt-cells = <1>; + compatible = "riscv,cpu-intc"; + interrupt-controller; + }; + }; + }; + + l2_cache: l2-cache { + compatible = "cache"; + cache-level = <2>; + cache-block-size = <64>; + cache-size = <1048576>; + }; + + memory: memory@80000000 { + device_type = "memory"; + /* + * Static 8 GiB memory profile for SPEC2017 and other large initramfs workloads. + */ + reg = <0x0 0x80000000 0x2 0x00000000>; + }; + + soc { + #address-cells = <2>; + #size-cells = <2>; + compatible = "freechips,rocketchip-unknown-soc", "simple-bus"; + ranges; + + /* + * This FPGA image boots without AIA. Describe only the CLINT + PLIC + * interrupt topology that the software stack should probe. + */ + clint: clint@38000000 { + compatible = "riscv,clint0"; + reg = <0x0 0x38000000 0x0 0x10000>; + reg-names = "control"; + interrupts-extended = <&intc_cpu0 3 &intc_cpu0 7>; + }; + + debug-controller@38020000 { + compatible = "sifive,debug-013", "riscv,debug-013"; + debug-attach = "jtag"; + reg = <0x0 0x38020000 0x0 0x1000>; + reg-names = "control"; + interrupts-extended = <&intc_cpu0 65535>; + }; + + PLIC: interrupt-controller@3c000000 { + compatible = "riscv,plic0"; + #interrupt-cells = <1>; + interrupt-controller; + reg = <0x0 0x3c000000 0x0 0x4000000>; + reg-names = "control"; + interrupts-extended = <&intc_cpu0 11 &intc_cpu0 9>; + riscv,max-priority = <7>; + riscv,ndev = <66>; + }; + + /* + * Board-specific FPGA peripheral: the external AXI UART16550 used as + * the Linux console on fpga. Keep the 32-bit MMIO spacing and the + * 50 MHz input clock to match the FPGA wrapper. + */ + uart0: serial@310b0000 { + compatible = "ns16550a"; + reg = <0x0 0x310b0000 0x0 0x10000>; + reg-shift = <0x2>; + reg-io-width = <0x4>; + clock-frequency = <50000000>; + current-speed = <115200>; + //interrupt-parent = <&PLIC>; + //interrupts = <40>; + status = "okay"; + }; + }; + + aliases { + serial0 = &uart0; + }; + + chosen { + /* + * On this FPGA DTS the UART16550 interrupt is not wired into Linux + * (`interrupt-parent` / `interrupts` are intentionally absent above). + * `ttyS0` can still print early boot logs via polling/earlycon, but the + * normal console path becomes unreliable once Linux switches away from + * earlycon. When the UART interrupt is absent, use the SBI console + * (`hvc0`) instead. + */ + bootargs = "console=hvc0 earlycon=sbi loglevel=8"; + stdout-path = "serial0:115200n8"; + linux,initrd-start = <0x0 INITRAMFS_BEGIN>; + linux,initrd-end = <0x0 INITRAMFS_END>; + + opensbi-config { + compatible = "opensbi,config"; + cold-boot-harts = <&cpu0>; + }; + }; +}; diff --git a/scripts/build-firmware-linux.sh b/scripts/build-firmware-linux.sh index 77f59b7..ebef4de 100644 --- a/scripts/build-firmware-linux.sh +++ b/scripts/build-firmware-linux.sh @@ -8,6 +8,8 @@ KERNEL_IMAGE="$(realpath "$4")" WORKLOAD_BUILD_DIR="$(realpath "$5")" CPIO_ARCHIVE="$WORKLOAD_BUILD_DIR/rootfs.cpio" DEFAULT_DTB="${DEFAULT_DTB:-xiangshan}" +DTB_MEMORY_PROFILE="${DTB_MEMORY_PROFILE:-}" +DTB_MIN_MEMORY_BYTES="${DTB_MIN_MEMORY_BYTES:-}" MEM_BEGIN=$(( 0x80000000 )) DTB_OFFSET_KB=1536 @@ -25,11 +27,62 @@ INITRAMFS_END_HEX=$(printf "0x%x" $(( INITRAMFS_BEGIN_HEX + INITRAMFS_SIZE ))) # Build device tree files DTC="${DTC:-dtc}" +dtb_memory_size_bytes() { + local dts_file="$1" + local cells + cells="$( + awk ' + /device_type[[:space:]]*=[[:space:]]*"memory"/ { in_memory = 1 } + in_memory && /reg[[:space:]]*=/ { + line = $0 + while (line !~ /;/ && (getline more) > 0) { + line = line " " more + } + gsub(/[<>;]/, " ", line) + n = split(line, fields, /[[:space:]]+/) + count = 0 + for (i = 1; i <= n; i++) { + if (fields[i] ~ /^(0x[0-9a-fA-F]+|[0-9]+)$/) { + values[++count] = fields[i] + } + } + if (count >= 4) { + print values[3], values[4] + exit + } + } + ' "$dts_file" + )" + if [ -z "$cells" ]; then + return 1 + fi + set -- $cells + local high="$1" + local low="$2" + printf '%s\n' $(( high * 4294967296 + low )) +} + +check_dtb_memory_size() { + local dts_file="$1" + local min_bytes="$2" + local memory_bytes + if ! memory_bytes="$(dtb_memory_size_bytes "$dts_file")"; then + echo "Cannot determine memory size from DTS: $dts_file" >&2 + exit 1 + fi + if [ "$memory_bytes" -lt "$min_bytes" ]; then + echo "DTS memory is too small: $dts_file describes $memory_bytes bytes, need at least $min_bytes bytes" >&2 + exit 1 + fi +} + build-dtb() { local dt_dir="$WORKLOAD_BUILD_DIR"/dt local dts_template="$1" - local dts_file="$dt_dir/$(basename "$dts_template" .dts.in).dts" - local dtb_file="$dt_dir/$(basename "$dts_template" .dts.in).dtb" + local dts_base + dts_base="$(basename "$dts_template" .dts.in)" + local dts_file="$dt_dir/$dts_base.dts" + local dtb_file="$dt_dir/$dts_base.dtb" mkdir -p "$dt_dir" sed -e "s/INITRAMFS_BEGIN/$INITRAMFS_BEGIN_HEX/g" \ -e "s/INITRAMFS_END/$INITRAMFS_END_HEX/g" \ @@ -42,11 +95,24 @@ done # Assemble the image # Using `xiangshan.dtb` as the default device tree unless DEFAULT_DTB is set. -DEFAULT_DTB_FILE="$WORKLOAD_BUILD_DIR/dt/$DEFAULT_DTB.dtb" +if [ -n "$DTB_MEMORY_PROFILE" ]; then + DEFAULT_DTB_BASE="$DEFAULT_DTB-mem$DTB_MEMORY_PROFILE" +else + DEFAULT_DTB_BASE="$DEFAULT_DTB" +fi +DEFAULT_DTB_FILE="$WORKLOAD_BUILD_DIR/dt/$DEFAULT_DTB_BASE.dtb" +DEFAULT_DTS_FILE="$WORKLOAD_BUILD_DIR/dt/$DEFAULT_DTB_BASE.dts" if ! [ -f "$DEFAULT_DTB_FILE" ]; then echo "Default device tree not found: $DEFAULT_DTB_FILE" >&2 exit 1 fi +if ! [ -f "$DEFAULT_DTS_FILE" ]; then + echo "Default device tree source not found: $DEFAULT_DTS_FILE" >&2 + exit 1 +fi +if [ -n "$DTB_MIN_MEMORY_BYTES" ]; then + check_dtb_memory_size "$DEFAULT_DTS_FILE" "$DTB_MIN_MEMORY_BYTES" +fi dd if="$STARTUP_FILE" of="$WORKLOAD_BUILD_DIR/fw_payload.bin" status=none dd if="$DEFAULT_DTB_FILE" of="$WORKLOAD_BUILD_DIR/fw_payload.bin" bs="$KILOBYTE" seek="$DTB_OFFSET_KB" conv=notrunc status=none dd if="$SBI_BUILD_DIR/build/platform/generic/firmware/fw_jump.bin" of="$WORKLOAD_BUILD_DIR/fw_payload.bin" bs="$KILOBYTE" seek="$SBI_OFFSET_KB" conv=notrunc status=none diff --git a/workloads/linux/spec2017/README.md b/workloads/linux/spec2017/README.md new file mode 100644 index 0000000..94ca70c --- /dev/null +++ b/workloads/linux/spec2017/README.md @@ -0,0 +1,176 @@ +# SPEC CPU2017 Linux workload + +This workload prepares a writable SPEC workspace from `SPEC2017_ISO` first, +then builds cases against that local install. + +Prepare the workspace: + +```sh +make spec2017-prepare SPEC2017_ISO=/path/to/cpu2017.iso +``` + +Build one SPEC CPU2017 case: + +```sh +make linux/spec2017 BENCH=mcf MODE=rate INPUT=ref SPEC2017_ISO=/path/to/cpu2017.iso -jN +make linux/spec2017 BENCH=mcf MODE=speed INPUT=ref SPEC2017_ISO=/path/to/cpu2017.iso -jN +``` + +`MODE=rate` selects `_r` benchmarks and maps `INPUT=ref` to `refrate`. +`MODE=speed` selects `_s` benchmarks and maps `INPUT=ref` to `refspeed`. +Full case names are also accepted: + +```sh +make linux/spec2017 BENCH=mcf_rate_refrate SPEC2017_ISO=/path/to/cpu2017.iso -jN +``` + +The ISO is extracted and installed through a temporary local staging directory, +then copied into the writable workspace +`build/linux-workloads/spec2017/spec-src`. Build output stays under: + +```text +build/linux-workloads/spec2017/ +``` + +Build ELF only: + +```sh +make spec2017-elf BENCH=mcf MODE=rate INPUT=ref SPEC2017_ISO=/path/to/cpu2017.iso -jN +make spec2017-elf BENCH=mcf MODE=speed INPUT=ref SPEC2017_ISO=/path/to/cpu2017.iso -jN +make spec2017-elfs MODE=all INPUT=ref SPEC2017_ISO=/path/to/cpu2017.iso -jN +``` + +ELF output is written to: + +```text +build/linux-workloads/spec2017//elf/.elf +``` + +Export reference rate images. `MODE=rate` is the default: + +```sh +make spec2017-images SPEC2017_ISO=/path/to/cpu2017.iso -jN +``` + +Export reference speed images: + +```sh +make spec2017-images SPEC2017_ISO=/path/to/cpu2017.iso MODE=speed -jN +``` + +The export tree is: + +```text +build/images/spec2017/ + bin/.fw_payload.bin + kernel/.Image + elf/.elf + cmd/.run.sh + gcpt/gcpt.elf + gcpt/gcpt.bin + cfg/riscv-gcc15.cfg +``` + +When SPEC generates multiple run commands for a case, `spec2017-images` exports +one firmware image and one `cmd/.run.sh` per command, plus one shared +ELF for the base case. Variants are named with the base case, command index, +and output label, for example: + +```text +perlbench_rate_refrate_00_checkspam.2500.5.25.11.150.1.1.1.1.fw_payload.bin +``` + +Useful selectors: + +```sh +make spec2017-images SPEC2017_ISO=/path/to/cpu2017.iso SPEC2017_IMAGE_INPUT=test -jN +make spec2017-images SPEC2017_ISO=/path/to/cpu2017.iso SPEC2017_IMAGE_INPUT=all -jN +make spec2017-images SPEC2017_ISO=/path/to/cpu2017.iso SPEC2017_IMAGE_MODE=rate -jN +make spec2017-images SPEC2017_ISO=/path/to/cpu2017.iso SPEC2017_IMAGE_MODE=all -jN +``` + +`SPEC2017_IMAGE_MODE=all` exports both rate and speed images. + +The generated run script emits NEMU profiling traps by default: + +```text +echo "CMD: ..." +nemu-trap 256 +nemu-trap 257 + +nemu-trap +``` + +Disable the begin profiling traps with: + +```sh +PROFILING=0 make spec2017-images SPEC2017_ISO=/path/to/cpu2017.iso -jN +``` + +The final `nemu-trap ` is always emitted. + +The repository provides static `xiangshan-fpga-noAIA` DTS templates for the +SPEC2017 memory profiles used by default. The embedded DTB is selected per +case: + +```text +rate -> 8g +speed -> 24g +``` + +SPEC CPU2017 documents 16 GiB as the minimum system memory for SPECspeed, while +SPECrate only needs 2 GiB per 64-bit copy. This workload also stores benchmark +inputs in initramfs; the largest ref rate input currently needs more than 4 GiB +to unpack and start cleanly, so the default keeps extra headroom for rate too. +Override the selected profile with the available DTS profiles: + +```sh +make spec2017-images SPEC2017_ISO=/path/to/cpu2017.iso SPEC2017_DTB_MEMORY=8g -jN +make spec2017-images SPEC2017_ISO=/path/to/cpu2017.iso SPEC2017_RATE_DTB_MEMORY=8g SPEC2017_SPEED_DTB_MEMORY=24g -jN +``` + +Alternatively, pass a specific DTB basename with `DEFAULT_DTB`. In that mode the +profile suffix is not added automatically, and the firmware script checks that +the selected DTS memory is large enough for the case: + +```sh +make linux/spec2017 BENCH=x264 MODE=rate INPUT=ref \ + DEFAULT_DTB=xiangshan-fpga-noAIA-mem8g \ + SPEC2017_ISO=/path/to/cpu2017.iso -jN +make linux/spec2017 BENCH=x264 MODE=speed INPUT=ref \ + DEFAULT_DTB=xiangshan-fpga-noAIA-mem24g \ + SPEC2017_ISO=/path/to/cpu2017.iso -jN +``` + +The minimum checked size is 8 GiB for rate cases and 24 GiB for speed cases. + +The source templates live in: + +```text +dts/xiangshan-fpga-noAIA-mem8g.dts.in +dts/xiangshan-fpga-noAIA-mem24g.dts.in +``` + +They are compiled into each case's `dt/` directory during firmware assembly. + +## Configuration + +By default, rate cases use: + +```text +workloads/linux/spec2017/riscv-gcc15.cfg +``` + +Speed cases use: + +```text +workloads/linux/spec2017/riscv-gcc15.cfg +``` + +Override both modes with `SPEC2017_CFG=/path/to/config.cfg`, or override the +mode-specific defaults with `SPEC2017_RATE_CFG` and `SPEC2017_SPEED_CFG`. + +`spec2017-prepare` only checks `SPEC2017_ISO` and prepares the local workspace; +it does not depend on benchmark cfg selection. Installation staging uses a +temporary local filesystem; override `SPEC2017_PREPARE_TMPDIR` if `/tmp` is not +suitable. `xorriso` is required for this step. diff --git a/workloads/linux/spec2017/build.sh b/workloads/linux/spec2017/build.sh new file mode 100755 index 0000000..b52c5b1 --- /dev/null +++ b/workloads/linux/spec2017/build.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +set -euo pipefail + +: "${SPEC2017_CASE:?SPEC2017_CASE is required}" +: "${SPEC2017:?SPEC2017 is required}" +: "${SPEC2017_CFG:?SPEC2017_CFG is required}" +: "${CROSS_COMPILE:=riscv64-unknown-linux-gnu-}" +: "${SPEC2017_TUNE:=base}" +: "${SPEC2017_JOBS:=$(nproc)}" +: "${SPEC2017_ELF_ONLY:=false}" +: "${SPEC2017_ALL_RUNS:=false}" +: "${SPEC2017_PROFILING:=1}" +: "${SPEC2017_LOG_DIR:=$WORKLOAD_BUILD_DIR/logs}" +: "${PKG_DIR:=$WORKLOAD_BUILD_DIR/package}" + +mkdir -p "$SPEC2017_LOG_DIR" + +is_true() { + case "$1" in + 1|true|yes|on) return 0 ;; + *) return 1 ;; + esac +} + +python_args=() +if is_true "$SPEC2017_ELF_ONLY"; then + python_args+=(--elf-only) +fi +if is_true "$SPEC2017_ALL_RUNS"; then + python_args+=(--all-runs) +fi + +python3 "$WORKLOAD_DIR/spec2017-package.py" \ + --case "$SPEC2017_CASE" \ + --spec-source "$SPEC2017" \ + --spec-config "$SPEC2017_CFG" \ + --pkg-dir "$PKG_DIR" \ + --out-dir "$WORKLOAD_BUILD_DIR" \ + --cross-compile "$CROSS_COMPILE" \ + --compiler-root "${SPEC2017_COMPILER_ROOT:-}" \ + --gnu-toolchain-root "${SPEC2017_GNU_TOOLCHAIN_ROOT:-}" \ + --log-dir "$SPEC2017_LOG_DIR" \ + --tune "$SPEC2017_TUNE" \ + --jobs "$SPEC2017_JOBS" \ + --profiling "$SPEC2017_PROFILING" \ + "${python_args[@]}" diff --git a/workloads/linux/spec2017/prepare-spec-workspace.sh b/workloads/linux/spec2017/prepare-spec-workspace.sh new file mode 100755 index 0000000..d3e97f2 --- /dev/null +++ b/workloads/linux/spec2017/prepare-spec-workspace.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash +set -euo pipefail + +source_spec_iso="$(realpath "$1")" +prepared_spec_root="$(realpath -m "$2")" + +prepare_dir="$(dirname "$prepared_spec_root")" +mkdir -p "$prepare_dir" +chmod -R u+rwX "$prepared_spec_root" 2>/dev/null || true +rm -rf "$prepared_spec_root" + +temp_root="" +cleanup() { + if [ -n "$temp_root" ]; then + chmod -R u+rwX "$temp_root" 2>/dev/null || true + rm -rf "$temp_root" 2>/dev/null || true + fi +} +trap cleanup EXIT + +if ! command -v xorriso >/dev/null 2>&1; then + echo "xorriso is required to extract $source_spec_iso" >&2 + exit 1 +fi + +tmp_parent="${SPEC2017_PREPARE_TMPDIR:-${TMPDIR:-/tmp}}" +mkdir -p "$tmp_parent" +temp_root="$(mktemp -d "$tmp_parent/spec2017-prepare.XXXXXX")" + +media_root="$temp_root/media" +staged_spec_root="$temp_root/spec-src" +mkdir -p "$media_root" + +xorriso -osirrox on -indev "$source_spec_iso" -extract / "$media_root" >/dev/null + +if ! [ -f "$media_root/install.sh" ]; then + echo "install.sh not found in $source_spec_iso" >&2 + exit 1 +fi + +( + cd "$media_root" + env -u SPEC SPEC_NOCHECK=1 sh ./install.sh -f -d "$staged_spec_root" +) + +mkdir -p "$prepared_spec_root" +cp -R "$staged_spec_root"/. "$prepared_spec_root"/ +chmod -R u+rwX "$prepared_spec_root" + +source_iso_size="$(stat -c '%s' "$source_spec_iso")" +source_iso_mtime="$(stat -c '%Y' "$source_spec_iso")" +cat > "$prepared_spec_root/.workload-builder-prepare.meta" </dev/null + bin/relocate >/dev/null + fi +) diff --git a/workloads/linux/spec2017/riscv-gcc15.cfg b/workloads/linux/spec2017/riscv-gcc15.cfg new file mode 100644 index 0000000..5bd3078 --- /dev/null +++ b/workloads/linux/spec2017/riscv-gcc15.cfg @@ -0,0 +1,156 @@ +#------------------------------------------------------------------------------ +# SPEC CPU2017 RISC-V Linux config for workload-builder. +#------------------------------------------------------------------------------ + +label = riscv-XiangShan-KMHv3-gcc15.1.0 + +action = validate +command_add_redirect = 0 +line_width = 1020 +log_line_width = 1020 +output_format = txt,html,cfg,pdf,csv +preenv = 1 +tune = base +bench_post_setup = sync +mean_anyway = 1 +reportable = 0 +ignore_errors = 1 + +default: + SPECLANG = %{ENV_CROSS_COMPILE} + +intspeed,fpspeed,intrate,fprate: + CC = $(SPECLANG)gcc -std=c99 + CXX = $(SPECLANG)g++ -std=c++03 + FC = $(SPECLANG)gfortran + CC_VERSION_OPTION = --version + CXX_VERSION_OPTION = --version + FC_VERSION_OPTION = --version + +intspeed,fpspeed,intrate,fprate: + PORTABILITY = -DSPEC_LP64 + +500.perlbench_r: + CPORTABILITY = -DSPEC_LINUX_X64 -fno-fast-math + +502.gcc_r: + CPORTABILITY = -fgnu89-inline + LDCFLAGS = -z muldefs + +525.x264_r: + LDCFLAGS = -z muldefs + +520.omnetpp_r: + CXXPORTABILITY = -std=c++98 + +521.wrf_r: + CPORTABILITY = -DSPEC_CASE_FLAG + FPORTABILITY = -fconvert=big-endian + +523.xalancbmk_r: + CXXPORTABILITY = -DSPEC_LINUX -D_FILE_OFFSET_BITS=64 -std=gnu++03 -include cstring -Wno-c++11-narrowing -Wno-template-body -D_GLIBCXX_USE_CXX11_ABI=0 + +527.cam4_r: + CPORTABILITY = -Wno-int-conversion -std=gnu89 -Dlower_case -DFORTRAN_UNDERSCORE -Dappend_utils -Wno-error=implicit-int -Wno-error=implicit-function-declaration -DSPEC_LP64 -Dlinux -Dsun + +526.blender_r: + PORTABILITY = -funsigned-char -DSPEC_LINUX + CPORTABILITY = -std=gnu99 -DSPEC_LP64 -D__LITTLE_ENDIAN__ -Wno-error=narrowing + CXXPORTABILITY = -std=c++11 -Wno-error=narrowing + +508.namd_r: + PORTABILITY = -std=c++98 + +510.parest_r: + CXXPORTABILITY = -std=c++98 + +511.povray_r: + CXXPORTABILITY = -fno-fast-math + CPORTABILITY = -fno-fast-math + +600.perlbench_s: + CPORTABILITY = -DSPEC_LINUX_X64 -fno-fast-math + +602.gcc_s: + CPORTABILITY = -fgnu89-inline + LDCFLAGS = -z muldefs + +625.x264_s: + LDCFLAGS = -z muldefs + +620.omnetpp_s: + CXXPORTABILITY = -std=c++98 + +621.wrf_s: + CPORTABILITY = -DSPEC_CASE_FLAG + FPORTABILITY = -fconvert=big-endian + +623.xalancbmk_s: + CXXPORTABILITY = -DSPEC_LINUX -D_FILE_OFFSET_BITS=64 -std=gnu++03 -include cstring -Wno-c++11-narrowing -Wno-template-body -D_GLIBCXX_USE_CXX11_ABI=0 + +627.cam4_s: + PORTABILITY = -DSPEC_CASE_FLAG + CPORTABILITY = -Wno-implicit-int + +628.pop2_s: + CPORTABILITY = -DSPEC_CASE_FLAG + FPORTABILITY = -fconvert=big-endian + +default: + threads = 1 + copies = 1 + +intspeed=base=default: + COPTIMIZE = -O3 -flto -ffast-math -fopenmp -march=rv64gc_zba_zbb_zbc_zbs -ffp-contract=off -fno-tree-vectorize -fno-tree-loop-vectorize -fno-strict-aliasing + CXXOPTIMIZE = -O3 -flto -ffast-math -fopenmp -march=rv64gc_zba_zbb_zbc_zbs -ffp-contract=off -fno-tree-vectorize -fno-tree-loop-vectorize -fno-strict-aliasing + FOPTIMIZE = -O3 -flto -ffast-math -fopenmp -march=rv64gc_zba_zbb_zbc_zbs -ffp-contract=off -fno-tree-vectorize -fno-tree-loop-vectorize -fno-strict-aliasing -fallow-argument-mismatch + EXTRA_LIBS = -lpthread -lm + EXTRA_LDFLAGS = -static + +fpspeed=base=default: + COPTIMIZE = -O3 -flto -ffast-math -fopenmp -march=rv64gc_zba_zbb_zbc_zbs -ffp-contract=fast -fno-tree-vectorize -fno-tree-loop-vectorize -fno-strict-aliasing + CXXOPTIMIZE = -O3 -flto -ffast-math -fopenmp -march=rv64gc_zba_zbb_zbc_zbs -ffp-contract=fast -fno-tree-vectorize -fno-tree-loop-vectorize -fno-strict-aliasing + FOPTIMIZE = -O3 -flto -ffast-math -fopenmp -march=rv64gc_zba_zbb_zbc_zbs -ffp-contract=fast -fno-tree-vectorize -fno-tree-loop-vectorize -fno-strict-aliasing -fallow-argument-mismatch + EXTRA_LIBS = -lpthread -lm + EXTRA_LDFLAGS = -static + +intrate=base=default: + COPTIMIZE = -O3 -flto -ffast-math -march=rv64gc_zba_zbb_zbc_zbs -ffp-contract=off -fno-tree-vectorize -fno-tree-loop-vectorize -fno-strict-aliasing + CXXOPTIMIZE = -O3 -flto -ffast-math -march=rv64gc_zba_zbb_zbc_zbs -ffp-contract=off -fno-tree-vectorize -fno-tree-loop-vectorize -fno-strict-aliasing + FOPTIMIZE = -O3 -flto -ffast-math -march=rv64gc_zba_zbb_zbc_zbs -ffp-contract=off -fno-tree-vectorize -fno-tree-loop-vectorize -fno-strict-aliasing -fallow-argument-mismatch + EXTRA_LIBS = -lpthread -lm + EXTRA_LDFLAGS = -static + +fprate=base=default: + COPTIMIZE = -O3 -flto -ffast-math -march=rv64gc_zba_zbb_zbc_zbs -ffp-contract=fast -fno-tree-vectorize -fno-tree-loop-vectorize -fno-strict-aliasing + CXXOPTIMIZE = -O3 -flto -ffast-math -march=rv64gc_zba_zbb_zbc_zbs -ffp-contract=fast -fno-tree-vectorize -fno-tree-loop-vectorize -fno-strict-aliasing + FOPTIMIZE = -O3 -flto -ffast-math -march=rv64gc_zba_zbb_zbc_zbs -ffp-contract=fast -fno-tree-vectorize -fno-tree-loop-vectorize -fno-strict-aliasing -fallow-argument-mismatch + EXTRA_LIBS = -lpthread -lm + EXTRA_LDFLAGS = -static + +intrate,intspeed,fpspeed,fprate: + sw_compiler001 = RISC-V GCC cross compiler + sw_base_ptrsize = 64-bit + sw_peak_ptrsize = Not Applicable + +intrate,intspeed,fprate,fpspeed: + hw_vendor = workload-builder + tester = workload-builder + test_sponsor = workload-builder + license_num = 0 + prepared_by = workload-builder + +intrate,intspeed,fprate,fpspeed: + hw_avail = Dec-9999 + sw_avail = Dec-9999 + hw_cpu_nominal_mhz = 3000 + hw_cpu_max_mhz = 3000 + hw_ncores = 1 + hw_nthreadspercore = 1 + hw_ncpuorder = 1 + hw_model = XiangShan Kunminghu + hw_other = None + hw_pcache = N/A + hw_scache = N/A + hw_tcache = N/A + hw_ocache = N/A diff --git a/workloads/linux/spec2017/rules.mk b/workloads/linux/spec2017/rules.mk new file mode 100644 index 0000000..755da1f --- /dev/null +++ b/workloads/linux/spec2017/rules.mk @@ -0,0 +1,287 @@ +SPEC2017_WORKLOAD_DIR := $(abspath $(dir $(lastword $(MAKEFILE_LIST)))) +SPEC2017_REPO_ROOT := $(abspath $(SPEC2017_WORKLOAD_DIR)/../../..) +SPEC2017_SELF_MAKEFILE := $(SPEC2017_WORKLOAD_DIR)/rules.mk +SPEC2017_ROOT_MAKEFILE := $(SPEC2017_REPO_ROOT)/Makefile +SPEC2017_RECURSE_MAKEFILE := $(if $(filter $(SPEC2017_ROOT_MAKEFILE),$(abspath $(firstword $(MAKEFILE_LIST)))),$(SPEC2017_ROOT_MAKEFILE),$(SPEC2017_SELF_MAKEFILE)) +SPEC2017_SCRIPTS_DIR := $(SPEC2017_REPO_ROOT)/scripts +SPEC2017_DTS_DIR := $(SPEC2017_REPO_ROOT)/dts +SPEC2017_BUILD_DIR ?= $(SPEC2017_REPO_ROOT)/build/linux-workloads/spec2017 +SPEC2017_EXPLICIT_CFG := $(if $(filter undefined,$(origin SPEC2017_CFG)),,1) +SPEC2017_RATE_CFG ?= $(SPEC2017_WORKLOAD_DIR)/riscv-gcc15.cfg +SPEC2017_SPEED_CFG ?= $(SPEC2017_WORKLOAD_DIR)/riscv-gcc15.cfg +SPEC2017_HELPER := $(SPEC2017_WORKLOAD_DIR)/spec2017-package.py +SPEC2017_IMAGE_DIR ?= $(SPEC2017_REPO_ROOT)/build/images/spec2017 +SPEC2017_SOURCE_SPEC_ISO := $(SPEC2017_ISO) +SPEC2017_PREPARED_SPEC_ROOT := $(SPEC2017_BUILD_DIR)/spec-src +SPEC2017_SOURCE_SPEC_HASH := $(shell if [ -n "$(SPEC2017_SOURCE_SPEC_ISO)" ] && [ -f "$(SPEC2017_SOURCE_SPEC_ISO)" ]; then stat -c '%n:%s:%Y' "$(SPEC2017_SOURCE_SPEC_ISO)"; else printf '%s\n' "$(SPEC2017_SOURCE_SPEC_ISO)"; fi | sha256sum | cut -d ' ' -f 1) +SPEC2017_PREPARE_STAMP := $(SPEC2017_BUILD_DIR)/spec-src.$(SPEC2017_SOURCE_SPEC_HASH).prepared +SPEC2017_PREPARE_SCRIPT := $(SPEC2017_WORKLOAD_DIR)/prepare-spec-workspace.sh +SPEC2017_CROSS_COMPILE ?= riscv64-unknown-linux-gnu- +SPEC2017_COMPILER_ROOT ?= +SPEC2017_GNU_TOOLCHAIN_ROOT ?= +SPEC2017_EXPLICIT_DEFAULT_DTB := $(if $(DEFAULT_DTB),1,$(if $(filter undefined,$(origin SPEC2017_DEFAULT_DTB)),,1)) +SPEC2017_DEFAULT_DTB ?= $(if $(DEFAULT_DTB),$(DEFAULT_DTB),xiangshan-fpga-noAIA) +SPEC2017_RATE_DTB_MEMORY ?= 8g +SPEC2017_SPEED_DTB_MEMORY ?= 24g +SPEC2017_DTB_MEMORY ?= +SPEC2017_RATE_DTB_MIN_MEMORY_BYTES ?= 8589934592 +SPEC2017_SPEED_DTB_MIN_MEMORY_BYTES ?= 25769803776 +SPEC2017_PROFILING ?= $(if $(PROFILING),$(PROFILING),1) +SPEC2017_TUNE ?= base +SPEC2017_JOBS ?= $(shell nproc) +SPEC2017_INPUT ?= $(if $(INPUT),$(INPUT),ref) +SPEC2017_MODE ?= $(if $(MODE),$(MODE),rate) +SPEC2017_IMAGE_INPUT ?= $(SPEC2017_INPUT) +SPEC2017_IMAGE_MODE ?= $(SPEC2017_MODE) +SPEC2017_PROGRESS_K ?= 1 +SPEC2017_PROGRESS_N ?= 1 +SPEC2017_PROGRESS_PREFIX := [spec2017 $(SPEC2017_PROGRESS_K)/$(SPEC2017_PROGRESS_N)] +SPEC2017_BUILDROOT_DIR ?= $(if $(BUILDROOT_DIR),$(BUILDROOT_DIR),$(SPEC2017_REPO_ROOT)/build/buildroot) +SPEC2017_LINUX_IMAGE ?= $(if $(LINUX_IMAGE),$(LINUX_IMAGE),$(SPEC2017_BUILDROOT_DIR)/output/images/Image) +SPEC2017_GCPT_ELF ?= $(if $(GCPT_ELF),$(GCPT_ELF),$(SPEC2017_REPO_ROOT)/build/LibCheckpointAlpha/build/gcpt) +SPEC2017_GCPT_BIN ?= $(if $(GCPT_BIN),$(GCPT_BIN),$(SPEC2017_REPO_ROOT)/build/LibCheckpointAlpha/build/gcpt.bin) +SPEC2017_SBI_BUILD_DIR ?= $(if $(SBI_BUILD_DIR),$(SBI_BUILD_DIR),$(SPEC2017_REPO_ROOT)/build/opensbi) +SPEC2017_SBI_BIN ?= $(if $(SBI_BIN),$(SBI_BIN),$(SPEC2017_SBI_BUILD_DIR)/build/platform/generic/firmware/fw_jump.bin) +SPEC2017_BUILDROOT_CROSS_COMPILE ?= $(SPEC2017_BUILDROOT_DIR)/output/host/bin/riscv64-linux- +SPEC2017_DTC ?= $(SPEC2017_BUILDROOT_DIR)/output/host/bin/dtc +spec2017_case_dtb_memory = $(if $(SPEC2017_DTB_MEMORY),$(SPEC2017_DTB_MEMORY),$(if $(findstring _speed_,$(1)),$(SPEC2017_SPEED_DTB_MEMORY),$(SPEC2017_RATE_DTB_MEMORY))) +spec2017_case_dtb_profile = $(if $(SPEC2017_EXPLICIT_DEFAULT_DTB),,$(call spec2017_case_dtb_memory,$(1))) +spec2017_case_dtb_name = $(SPEC2017_DEFAULT_DTB)$(if $(call spec2017_case_dtb_profile,$(1)),-mem$(call spec2017_case_dtb_profile,$(1))) +spec2017_case_dtb_tag = $(subst /,_,$(call spec2017_case_dtb_name,$(1))) +spec2017_case_dtb_min_memory_bytes = $(if $(findstring _speed_,$(1)),$(SPEC2017_SPEED_DTB_MIN_MEMORY_BYTES),$(SPEC2017_RATE_DTB_MIN_MEMORY_BYTES)) +spec2017_case_cfg = $(if $(SPEC2017_EXPLICIT_CFG),$(SPEC2017_CFG),$(if $(findstring _speed_,$(1)),$(SPEC2017_SPEED_CFG),$(SPEC2017_RATE_CFG))) +spec2017_case_cfg_hash = $(shell if [ -f "$(abspath $(call spec2017_case_cfg,$(1)))" ]; then sha256sum "$(abspath $(call spec2017_case_cfg,$(1)))" | cut -d ' ' -f 1; else printf 'missing'; fi) +SPEC2017_PYTHON := PYTHONDONTWRITEBYTECODE=1 python3 +SPEC2017_BUILD_VARS_HASH := $(shell printf '%s\n' '$(SPEC2017_PROFILING)' '$(SPEC2017_TUNE)' '$(SPEC2017_CROSS_COMPILE)' '$(SPEC2017_COMPILER_ROOT)' '$(SPEC2017_GNU_TOOLCHAIN_ROOT)' | sha256sum | cut -d ' ' -f 1) +SPEC2017_CASE := $(shell $(SPEC2017_PYTHON) $(SPEC2017_HELPER) --resolve-case --bench '$(BENCH)' --input-set '$(SPEC2017_INPUT)' --mode '$(SPEC2017_MODE)' 2>/dev/null) +SPEC2017_ALL_CASES := $(shell $(SPEC2017_PYTHON) $(SPEC2017_HELPER) --list-cases --input-set all --mode all 2>/dev/null) +SPEC2017_SELECTED_CASES := $(shell $(SPEC2017_PYTHON) $(SPEC2017_HELPER) --list-cases --input-set $(SPEC2017_INPUT) --mode $(SPEC2017_MODE) 2>/dev/null) +SPEC2017_IMAGE_CASES := $(shell $(SPEC2017_PYTHON) $(SPEC2017_HELPER) --list-cases --input-set $(SPEC2017_IMAGE_INPUT) --mode $(SPEC2017_IMAGE_MODE) 2>/dev/null) +SPEC2017_DTS_SOURCES := $(shell find $(SPEC2017_DTS_DIR) -type f 2>/dev/null) + +WORKLOAD_DIRS += $(SPEC2017_BUILD_DIR) + +spec2017-check-spec-iso: + @if [ -z "$(SPEC2017_SOURCE_SPEC_ISO)" ]; then \ + echo "SPEC2017_ISO is required, for example:"; \ + echo " make spec2017-prepare SPEC2017_ISO=/path/to/cpu2017.iso"; \ + echo " make linux/spec2017 BENCH=mcf MODE=rate INPUT=ref SPEC2017_ISO=/path/to/cpu2017.iso -jN"; \ + echo " make spec2017-images SPEC2017_ISO=/path/to/cpu2017.iso -jN"; \ + exit 1; \ + fi; \ + if ! [ -f "$(SPEC2017_SOURCE_SPEC_ISO)" ]; then \ + echo "SPEC2017 ISO path does not exist: $(SPEC2017_SOURCE_SPEC_ISO)"; \ + exit 1; \ + fi + +spec2017-check-spec-config: spec2017-check-spec-iso + @if [ "$(SPEC2017_EXPLICIT_CFG)" = 1 ]; then \ + if ! [ -f "$(SPEC2017_CFG)" ]; then \ + echo "SPEC2017 cfg does not exist: $(SPEC2017_CFG)"; \ + exit 1; \ + fi; \ + else \ + if ! [ -f "$(SPEC2017_RATE_CFG)" ]; then \ + echo "SPEC2017 rate cfg does not exist: $(SPEC2017_RATE_CFG)"; \ + exit 1; \ + fi; \ + if ! [ -f "$(SPEC2017_SPEED_CFG)" ]; then \ + echo "SPEC2017 speed cfg does not exist: $(SPEC2017_SPEED_CFG)"; \ + exit 1; \ + fi; \ + fi; \ + if [ -z "$(call spec2017_case_cfg,$(SPEC2017_CASE))" ] && [ -n "$(BENCH)" ]; then \ + echo "Cannot select SPEC2017 cfg for BENCH=$(BENCH), MODE=$(SPEC2017_MODE), INPUT=$(SPEC2017_INPUT)"; \ + exit 1; \ + fi; \ + case "$(SPEC2017_INPUT)" in \ + ref|refrate|refspeed|train|test|all) ;; \ + *) echo "SPEC2017_INPUT/INPUT must be one of: ref, refrate, refspeed, train, test, all"; exit 1 ;; \ + esac; \ + case "$(SPEC2017_MODE)" in \ + rate|speed|all) ;; \ + *) echo "SPEC2017_MODE/MODE must be one of: rate, speed, all"; exit 1 ;; \ + esac; \ + case "$(SPEC2017_PROFILING)" in \ + 0|1) ;; \ + *) echo "SPEC2017_PROFILING/PROFILING must be 0 or 1"; exit 1 ;; \ + esac + +spec2017-prepare: $(SPEC2017_PREPARE_STAMP) + +$(SPEC2017_PREPARE_STAMP): $(SPEC2017_PREPARE_SCRIPT) | spec2017-check-spec-iso + @printf '$(SPEC2017_PROGRESS_PREFIX) Preparing SPEC workspace at $(SPEC2017_PREPARED_SPEC_ROOT)\n' + @bash "$(SPEC2017_PREPARE_SCRIPT)" "$(SPEC2017_SOURCE_SPEC_ISO)" "$(SPEC2017_PREPARED_SPEC_ROOT)" + @touch "$@" + +define add_spec2017_case +$(SPEC2017_BUILD_DIR)/$(1)/download/sentinel: + @mkdir -p $$(@D) + @touch $$@ + +$(SPEC2017_BUILD_DIR)/$(1)/cfg.$(call spec2017_case_cfg_hash,$(1)).stamp: | spec2017-check-spec-config + @mkdir -p "$$(@D)" + @printf '%s\n' "cfg=$(abspath $(call spec2017_case_cfg,$(1)))" > "$$@" + +$(SPEC2017_BUILD_DIR)/$(1)/build-vars.$(SPEC2017_BUILD_VARS_HASH).stamp: + @mkdir -p "$$(@D)" + @printf '%s\n' "profiling=$(SPEC2017_PROFILING)" > "$$@" + +$(SPEC2017_BUILD_DIR)/$(1)/elf/$(1).elf: $(SPEC2017_PREPARE_STAMP) $(SPEC2017_BUILD_DIR)/$(1)/cfg.$(call spec2017_case_cfg_hash,$(1)).stamp $$(SPEC2017_HELPER) $$(SPEC2017_WORKLOAD_DIR)/build.sh + @mkdir -p "$$(dir $$@)" + @WORKLOAD_DIR="$$(abspath $$(SPEC2017_WORKLOAD_DIR))" \ + WORKLOAD_BUILD_DIR="$$(abspath $(SPEC2017_BUILD_DIR)/$(1))" \ + PKG_DIR="$$(abspath $(SPEC2017_BUILD_DIR)/$(1)/package)" \ + CROSS_COMPILE="$$(SPEC2017_CROSS_COMPILE)" \ + SPEC2017_PROGRESS_K="$$(SPEC2017_PROGRESS_K)" \ + SPEC2017_PROGRESS_N="$$(SPEC2017_PROGRESS_N)" \ + SPEC2017_CASE="$(1)" \ + SPEC2017="$$(SPEC2017_PREPARED_SPEC_ROOT)" \ + SPEC2017_CFG="$(abspath $(call spec2017_case_cfg,$(1)))" \ + SPEC2017_COMPILER_ROOT="$$(SPEC2017_COMPILER_ROOT)" \ + SPEC2017_GNU_TOOLCHAIN_ROOT="$$(SPEC2017_GNU_TOOLCHAIN_ROOT)" \ + SPEC2017_TUNE="$$(SPEC2017_TUNE)" \ + SPEC2017_JOBS="$$(SPEC2017_JOBS)" \ + SPEC2017_ELF_ONLY=1 \ + SPEC2017_PROFILING="$$(SPEC2017_PROFILING)" \ + bash "$$(abspath $$(SPEC2017_WORKLOAD_DIR))/build.sh" + +$(SPEC2017_BUILD_DIR)/$(1)/rootfs.cpio: $(SPEC2017_PREPARE_STAMP) $(SPEC2017_BUILD_DIR)/$(1)/cfg.$(call spec2017_case_cfg_hash,$(1)).stamp $$(SPEC2017_HELPER) $$(SPEC2017_WORKLOAD_DIR)/build.sh $(SPEC2017_BUILD_DIR)/$(1)/download/sentinel $(SPEC2017_BUILD_DIR)/$(1)/build-vars.$(SPEC2017_BUILD_VARS_HASH).stamp $$(SPEC2017_SCRIPTS_DIR)/build-workload-linux.sh + @CROSS_COMPILE="$$(SPEC2017_CROSS_COMPILE)" \ + SPEC2017_PROGRESS_K="$$(SPEC2017_PROGRESS_K)" \ + SPEC2017_PROGRESS_N="$$(SPEC2017_PROGRESS_N)" \ + SPEC2017_CASE="$(1)" \ + SPEC2017="$$(SPEC2017_PREPARED_SPEC_ROOT)" \ + SPEC2017_CFG="$(abspath $(call spec2017_case_cfg,$(1)))" \ + SPEC2017_COMPILER_ROOT="$$(SPEC2017_COMPILER_ROOT)" \ + SPEC2017_GNU_TOOLCHAIN_ROOT="$$(SPEC2017_GNU_TOOLCHAIN_ROOT)" \ + SPEC2017_TUNE="$$(SPEC2017_TUNE)" \ + SPEC2017_JOBS="$$(SPEC2017_JOBS)" \ + SPEC2017_PROFILING="$$(SPEC2017_PROFILING)" \ + bash "$$(SPEC2017_SCRIPTS_DIR)/build-workload-linux.sh" "$$(SPEC2017_WORKLOAD_DIR)" "$(SPEC2017_BUILD_DIR)/$(1)" + +$(SPEC2017_BUILD_DIR)/$(1)/firmware/dtb-$(call spec2017_case_dtb_tag,$(1)).stamp: spec2017-force + @mkdir -p "$$(@D)" + @printf '%s\n' \ + "case=$(1)" \ + "default_dtb=$$(SPEC2017_DEFAULT_DTB)" \ + "profile=$(call spec2017_case_dtb_profile,$(1))" \ + "min_memory_bytes=$(call spec2017_case_dtb_min_memory_bytes,$(1))" > "$$@.tmp" + @if [ -f "$$@" ] && cmp -s "$$@.tmp" "$$@"; then rm "$$@.tmp"; else mv "$$@.tmp" "$$@"; fi + +$(SPEC2017_BUILD_DIR)/$(1)/fw_payload.bin: $$(SPEC2017_DTS_SOURCES) $$(SPEC2017_GCPT_BIN) $$(SPEC2017_SCRIPTS_DIR)/build-firmware-linux.sh $(SPEC2017_BUILD_DIR)/$(1)/rootfs.cpio $$(SPEC2017_LINUX_IMAGE) $$(SPEC2017_SBI_BIN) $(SPEC2017_BUILD_DIR)/$(1)/firmware/dtb-$(call spec2017_case_dtb_tag,$(1)).stamp + @printf '$(SPEC2017_PROGRESS_PREFIX) Assembling firmware for $(1)\n' + @CROSS_COMPILE="$$(SPEC2017_BUILDROOT_CROSS_COMPILE)" \ + DTC="$$(SPEC2017_DTC)" \ + DEFAULT_DTB="$$(SPEC2017_DEFAULT_DTB)" \ + DTB_MEMORY_PROFILE="$(call spec2017_case_dtb_profile,$(1))" \ + DTB_MIN_MEMORY_BYTES="$(call spec2017_case_dtb_min_memory_bytes,$(1))" \ + SPEC2017_PROGRESS_K="$$(SPEC2017_PROGRESS_K)" \ + SPEC2017_PROGRESS_N="$$(SPEC2017_PROGRESS_N)" \ + bash "$$(SPEC2017_SCRIPTS_DIR)/build-firmware-linux.sh" "$$(SPEC2017_GCPT_BIN)" "$$(SPEC2017_SBI_BUILD_DIR)" "$$(SPEC2017_DTS_DIR)" "$$(SPEC2017_LINUX_IMAGE)" "$(SPEC2017_BUILD_DIR)/$(1)" + +linux/$(1): $(SPEC2017_BUILD_DIR)/$(1)/fw_payload.bin + +WORKLOAD_PHONY_TARGETS += linux/$(1) + +$(SPEC2017_IMAGE_DIR)/stamps/$(1).images.stamp: $(SPEC2017_PREPARE_STAMP) $(SPEC2017_BUILD_DIR)/$(1)/cfg.$(call spec2017_case_cfg_hash,$(1)).stamp $$(SPEC2017_HELPER) $$(SPEC2017_WORKLOAD_DIR)/build.sh $(SPEC2017_BUILD_DIR)/$(1)/download/sentinel $(SPEC2017_BUILD_DIR)/$(1)/build-vars.$(SPEC2017_BUILD_VARS_HASH).stamp $$(SPEC2017_DTS_SOURCES) $$(SPEC2017_GCPT_BIN) $$(SPEC2017_GCPT_ELF) $$(SPEC2017_SCRIPTS_DIR)/build-firmware-linux.sh $$(SPEC2017_LINUX_IMAGE) $$(SPEC2017_SBI_BIN) | spec2017-check-spec-config + @printf '$(SPEC2017_PROGRESS_PREFIX) Packaging split run images for $(1)\n' + @WORKLOAD_DIR="$$(abspath $$(SPEC2017_WORKLOAD_DIR))" \ + WORKLOAD_BUILD_DIR="$$(abspath $(SPEC2017_BUILD_DIR)/$(1))" \ + PKG_DIR="$$(abspath $(SPEC2017_BUILD_DIR)/$(1)/package)" \ + CROSS_COMPILE="$$(SPEC2017_CROSS_COMPILE)" \ + SPEC2017_PROGRESS_K="$$(SPEC2017_PROGRESS_K)" \ + SPEC2017_PROGRESS_N="$$(SPEC2017_PROGRESS_N)" \ + SPEC2017_CASE="$(1)" \ + SPEC2017="$$(SPEC2017_PREPARED_SPEC_ROOT)" \ + SPEC2017_CFG="$(abspath $(call spec2017_case_cfg,$(1)))" \ + SPEC2017_COMPILER_ROOT="$$(SPEC2017_COMPILER_ROOT)" \ + SPEC2017_GNU_TOOLCHAIN_ROOT="$$(SPEC2017_GNU_TOOLCHAIN_ROOT)" \ + SPEC2017_TUNE="$$(SPEC2017_TUNE)" \ + SPEC2017_JOBS="$$(SPEC2017_JOBS)" \ + SPEC2017_ALL_RUNS=1 \ + SPEC2017_PROFILING="$$(SPEC2017_PROFILING)" \ + bash "$$(abspath $$(SPEC2017_WORKLOAD_DIR))/build.sh" + @printf '$(SPEC2017_PROGRESS_PREFIX) Exporting $(1) split artifacts to $(SPEC2017_IMAGE_DIR)\n' + @mkdir -p "$(SPEC2017_IMAGE_DIR)/bin" "$(SPEC2017_IMAGE_DIR)/kernel" "$(SPEC2017_IMAGE_DIR)/elf" "$(SPEC2017_IMAGE_DIR)/cmd" "$(SPEC2017_IMAGE_DIR)/cfg" "$(SPEC2017_IMAGE_DIR)/gcpt" "$(SPEC2017_IMAGE_DIR)/logs/build_elf" "$(SPEC2017_IMAGE_DIR)/stamps" + @if [ ! -f "$(SPEC2017_IMAGE_DIR)/cfg/$(notdir $(call spec2017_case_cfg,$(1)))" ]; then \ + cp "$(abspath $(call spec2017_case_cfg,$(1)))" "$(SPEC2017_IMAGE_DIR)/cfg/$(notdir $(call spec2017_case_cfg,$(1)))"; \ + fi + @if [ ! -f "$(SPEC2017_IMAGE_DIR)/gcpt/gcpt.elf" ] || [ ! -f "$(SPEC2017_IMAGE_DIR)/gcpt/gcpt.bin" ]; then \ + cp "$(SPEC2017_GCPT_ELF)" "$(SPEC2017_IMAGE_DIR)/gcpt/gcpt.elf"; \ + cp "$(SPEC2017_GCPT_BIN)" "$(SPEC2017_IMAGE_DIR)/gcpt/gcpt.bin"; \ + fi + @rm -f "$(SPEC2017_IMAGE_DIR)/elf/$(1)"_*.elf + @cp "$(SPEC2017_BUILD_DIR)/$(1)/elf/$(1).elf" "$(SPEC2017_IMAGE_DIR)/elf/$(1).elf" + @cp "$(SPEC2017_BUILD_DIR)/$(1)/logs/build_elf/build.log" "$(SPEC2017_IMAGE_DIR)/logs/build_elf/$(1).log" + @$(SPEC2017_PYTHON) "$(SPEC2017_HELPER)" --list-packaged-variants --out-dir "$(SPEC2017_BUILD_DIR)/$(1)" | while IFS=" " read -r variant build_dir; do \ + printf '$(SPEC2017_PROGRESS_PREFIX) Assembling firmware for %s\n' "$$$$variant"; \ + rm -f "$$$$build_dir/rootfs.cpio"; \ + (cd "$$$$build_dir/package" && find . | fakeroot cpio -o -H newc > "$$$$build_dir/rootfs.cpio" 2>/dev/null); \ + CROSS_COMPILE="$$(SPEC2017_BUILDROOT_CROSS_COMPILE)" \ + DTC="$$(SPEC2017_DTC)" \ + DEFAULT_DTB="$$(SPEC2017_DEFAULT_DTB)" \ + DTB_MEMORY_PROFILE="$(call spec2017_case_dtb_profile,$(1))" \ + DTB_MIN_MEMORY_BYTES="$(call spec2017_case_dtb_min_memory_bytes,$(1))" \ + SPEC2017_PROGRESS_K="$$(SPEC2017_PROGRESS_K)" \ + SPEC2017_PROGRESS_N="$$(SPEC2017_PROGRESS_N)" \ + bash "$$(SPEC2017_SCRIPTS_DIR)/build-firmware-linux.sh" "$$(SPEC2017_GCPT_BIN)" "$$(SPEC2017_SBI_BUILD_DIR)" "$$(SPEC2017_DTS_DIR)" "$$(SPEC2017_LINUX_IMAGE)" "$$$$build_dir"; \ + cp "$$$$build_dir/package/spec/run.sh" "$(SPEC2017_IMAGE_DIR)/cmd/$$$$variant.run.sh"; \ + cp "$$(SPEC2017_LINUX_IMAGE)" "$(SPEC2017_IMAGE_DIR)/kernel/$$$$variant.Image"; \ + cp "$$$$build_dir/fw_payload.bin" "$(SPEC2017_IMAGE_DIR)/bin/$$$$variant.fw_payload.bin"; \ + done + @touch "$$@" +endef + +$(foreach case,$(SPEC2017_ALL_CASES),$(eval $(call add_spec2017_case,$(case)))) + +linux/spec2017: spec2017-check-spec-config + @if [ -z "$(BENCH)" ]; then \ + echo "Usage: make linux/spec2017 BENCH=mcf MODE=rate INPUT=ref SPEC2017_ISO=/path/to/cpu2017.iso -jN"; \ + echo " or: make linux/spec2017 BENCH=mcf_rate_refrate SPEC2017_ISO=/path/to/cpu2017.iso -jN"; \ + exit 1; \ + fi; \ + if [ -z "$(SPEC2017_CASE)" ]; then \ + echo "Cannot resolve SPEC2017 case from BENCH=$(BENCH), MODE=$(SPEC2017_MODE), INPUT=$(SPEC2017_INPUT)"; \ + exit 1; \ + fi + @$(MAKE) --no-print-directory -f "$(SPEC2017_RECURSE_MAKEFILE)" $(SPEC2017_BUILD_DIR)/$(SPEC2017_CASE)/fw_payload.bin + +spec2017-elf: spec2017-check-spec-config + @if [ -z "$(BENCH)" ]; then \ + echo "Usage: make spec2017-elf BENCH=mcf MODE=rate INPUT=ref SPEC2017_ISO=/path/to/cpu2017.iso -jN"; \ + exit 1; \ + fi; \ + if [ -z "$(SPEC2017_CASE)" ]; then \ + echo "Cannot resolve SPEC2017 case from BENCH=$(BENCH), MODE=$(SPEC2017_MODE), INPUT=$(SPEC2017_INPUT)"; \ + exit 1; \ + fi + @$(MAKE) --no-print-directory -f "$(SPEC2017_RECURSE_MAKEFILE)" $(SPEC2017_BUILD_DIR)/$(SPEC2017_CASE)/elf/$(SPEC2017_CASE).elf + +spec2017-elfs: spec2017-check-spec-config + @if [ -z "$(SPEC2017_SELECTED_CASES)" ]; then \ + echo "No SPEC2017 cases selected by SPEC2017_INPUT=$(SPEC2017_INPUT) SPEC2017_MODE=$(SPEC2017_MODE)"; \ + exit 1; \ + fi; \ + total="$(words $(SPEC2017_SELECTED_CASES))"; \ + i=0; \ + for case in $(SPEC2017_SELECTED_CASES); do \ + i=$$((i + 1)); \ + SPEC2017_PROGRESS_K="$$i" SPEC2017_PROGRESS_N="$$total" $(MAKE) --no-print-directory -f "$(SPEC2017_RECURSE_MAKEFILE)" "$(SPEC2017_BUILD_DIR)/$$case/elf/$$case.elf" || exit $$?; \ + done + +spec2017-images: spec2017-check-spec-config + @if [ -z "$(SPEC2017_IMAGE_CASES)" ]; then \ + echo "No SPEC2017 cases selected by SPEC2017_IMAGE_INPUT=$(SPEC2017_IMAGE_INPUT) SPEC2017_IMAGE_MODE=$(SPEC2017_IMAGE_MODE)"; \ + exit 1; \ + fi; \ + rm -rf "$(SPEC2017_IMAGE_DIR)/bin" "$(SPEC2017_IMAGE_DIR)/kernel" "$(SPEC2017_IMAGE_DIR)/elf" "$(SPEC2017_IMAGE_DIR)/cmd" "$(SPEC2017_IMAGE_DIR)/cfg" "$(SPEC2017_IMAGE_DIR)/gcpt" "$(SPEC2017_IMAGE_DIR)/logs" "$(SPEC2017_IMAGE_DIR)/stamps"; \ + total="$(words $(SPEC2017_IMAGE_CASES))"; \ + i=0; \ + for case in $(SPEC2017_IMAGE_CASES); do \ + i=$$((i + 1)); \ + SPEC2017_PROGRESS_K="$$i" SPEC2017_PROGRESS_N="$$total" $(MAKE) --no-print-directory -f "$(SPEC2017_RECURSE_MAKEFILE)" "$(SPEC2017_IMAGE_DIR)/stamps/$$case.images.stamp" || exit $$?; \ + done + @printf '[spec2017 %s/%s] Output written to %s\n' "$(words $(SPEC2017_IMAGE_CASES))" "$(words $(SPEC2017_IMAGE_CASES))" "$(abspath $(SPEC2017_IMAGE_DIR))" + +.PHONY: linux/spec2017 spec2017-check-spec-iso spec2017-check-spec-config spec2017-prepare spec2017-elf spec2017-elfs spec2017-images spec2017-force diff --git a/workloads/linux/spec2017/spec2017-package.py b/workloads/linux/spec2017/spec2017-package.py new file mode 100755 index 0000000..8d27c8a --- /dev/null +++ b/workloads/linux/spec2017/spec2017-package.py @@ -0,0 +1,863 @@ +#!/usr/bin/env python3 +import argparse +import hashlib +import json +import os +import re +import shlex +import shutil +import subprocess +import sys +from collections import deque +from pathlib import Path + + +BENCHMARKS = ( + ("500.perlbench_r", "perlbench", "rate", ("int",)), + ("502.gcc_r", "gcc", "rate", ("int",)), + ("503.bwaves_r", "bwaves", "rate", ("fp",)), + ("505.mcf_r", "mcf", "rate", ("int",)), + ("507.cactuBSSN_r", "cactuBSSN", "rate", ("fp",)), + ("508.namd_r", "namd", "rate", ("fp",)), + ("510.parest_r", "parest", "rate", ("fp",)), + ("511.povray_r", "povray", "rate", ("fp",)), + ("519.lbm_r", "lbm", "rate", ("fp",)), + ("520.omnetpp_r", "omnetpp", "rate", ("int",)), + ("521.wrf_r", "wrf", "rate", ("fp",)), + ("523.xalancbmk_r", "xalancbmk", "rate", ("int",)), + ("525.x264_r", "x264", "rate", ("int",)), + ("526.blender_r", "blender", "rate", ("fp",)), + ("527.cam4_r", "cam4", "rate", ("fp",)), + ("531.deepsjeng_r", "deepsjeng", "rate", ("int",)), + ("538.imagick_r", "imagick", "rate", ("fp",)), + ("541.leela_r", "leela", "rate", ("int",)), + ("544.nab_r", "nab", "rate", ("fp",)), + ("548.exchange2_r", "exchange2", "rate", ("int",)), + ("549.fotonik3d_r", "fotonik3d", "rate", ("fp",)), + ("554.roms_r", "roms", "rate", ("fp",)), + ("557.xz_r", "xz", "rate", ("int",)), + ("600.perlbench_s", "perlbench", "speed", ("int",)), + ("602.gcc_s", "gcc", "speed", ("int",)), + ("603.bwaves_s", "bwaves", "speed", ("fp",)), + ("605.mcf_s", "mcf", "speed", ("int",)), + ("607.cactuBSSN_s", "cactuBSSN", "speed", ("fp",)), + ("619.lbm_s", "lbm", "speed", ("fp",)), + ("620.omnetpp_s", "omnetpp", "speed", ("int",)), + ("621.wrf_s", "wrf", "speed", ("fp",)), + ("623.xalancbmk_s", "xalancbmk", "speed", ("int",)), + ("625.x264_s", "x264", "speed", ("int",)), + ("627.cam4_s", "cam4", "speed", ("fp",)), + ("628.pop2_s", "pop2", "speed", ("fp",)), + ("631.deepsjeng_s", "deepsjeng", "speed", ("int",)), + ("638.imagick_s", "imagick", "speed", ("fp",)), + ("641.leela_s", "leela", "speed", ("int",)), + ("644.nab_s", "nab", "speed", ("fp",)), + ("648.exchange2_s", "exchange2", "speed", ("int",)), + ("649.fotonik3d_s", "fotonik3d", "speed", ("fp",)), + ("654.roms_s", "roms", "speed", ("fp",)), + ("657.xz_s", "xz", "speed", ("int",)), + ("996.specrand_fs", "specrand_f", "speed", ("fp",)), + ("997.specrand_fr", "specrand_f", "rate", ("fp",)), + ("998.specrand_is", "specrand_i", "speed", ("int",)), + ("999.specrand_ir", "specrand_i", "rate", ("int",)), +) + +WORKLOADS_BY_MODE = { + "rate": ("test", "train", "refrate"), + "speed": ("test", "train", "refspeed"), +} + +INPUT_ALIASES = { + "ref": {"rate": "refrate", "speed": "refspeed"}, + "refrate": {"rate": "refrate"}, + "refspeed": {"speed": "refspeed"}, + "test": {"rate": "test", "speed": "test"}, + "train": {"rate": "train", "speed": "train"}, +} + + +def format_cmd(cmd): + return " ".join(shlex.quote(str(part)) for part in cmd) + + +def progress_prefix(): + current = os.environ.get("SPEC2017_PROGRESS_K", "1") + total = os.environ.get("SPEC2017_PROGRESS_N", "1") + return f"[spec2017 {current}/{total}]" + + +def status(message): + print(f"{progress_prefix()} {message}", flush=True) + + +def read_log_tail(log_path, max_lines=40): + with open(log_path, "r", encoding="utf-8", errors="replace") as f: + lines = deque(f, maxlen=max_lines) + return "".join(lines).rstrip() + + +def maybe_read_log_tail(log_path, max_lines=40): + if log_path.is_file(): + tail = read_log_tail(log_path, max_lines=max_lines) + if tail: + return f"\nLast log lines from {log_path}:\n{tail}" + return "" + + +def run(cmd, *, env=None, cwd=None, log_path=None, summary=None, capture=False): + command_text = format_cmd(cmd) + if summary: + status(summary) + if log_path is None: + raise ValueError("log_path is required") + log_path.parent.mkdir(parents=True, exist_ok=True) + with open(log_path, "a", encoding="utf-8") as log: + log.write(f"$ {command_text}\n") + if cwd is not None: + log.write(f"# cwd: {cwd}\n") + log.flush() + result = subprocess.run( + cmd, + cwd=cwd, + env=env, + check=False, + stdout=subprocess.PIPE if capture else log, + stderr=subprocess.STDOUT, + text=True, + ) + if capture and result.stdout: + log.write(result.stdout) + log.write("\n") + if result.returncode != 0: + tail = read_log_tail(log_path) + detail = f"\nLast log lines from {log_path}:\n{tail}" if tail else "" + raise RuntimeError(f"command failed: {command_text}\nSee {log_path}{detail}") + return result.stdout if capture else None + + +def file_sha256(path): + digest = hashlib.sha256() + with open(path, "rb") as f: + for chunk in iter(lambda: f.read(1024 * 1024), b""): + digest.update(chunk) + return digest.hexdigest() + + +def load_json(path): + with open(path, "r", encoding="utf-8") as f: + return json.load(f) + + +def write_json(path, value): + path.parent.mkdir(parents=True, exist_ok=True) + path.write_text(json.dumps(value, indent=2, sort_keys=True) + "\n", encoding="utf-8") + + +def case_name(short, mode, workload): + return f"{short}_{mode}_{workload}" + + +def all_cases(): + cases = {} + for bench_dir, short, mode, tags in BENCHMARKS: + for workload in WORKLOADS_BY_MODE[mode]: + name = case_name(short, mode, workload) + cases[name] = { + "bench_dir": bench_dir, + "short": short, + "mode": mode, + "workload": workload, + "tags": [*tags, mode, workload], + } + return cases + + +def filter_cases(cases, input_set, mode): + selected = {} + for name, case in cases.items(): + if mode not in (None, "", "all") and case["mode"] != mode: + continue + if input_set not in (None, "", "all"): + if input_set == "ref": + if case["workload"] not in ("refrate", "refspeed"): + continue + elif case["workload"] != input_set: + continue + selected[name] = case + return selected + + +def resolve_case_name(bench, input_set, mode): + cases = all_cases() + if not bench: + return "" + if bench in cases: + return bench + if input_set not in INPUT_ALIASES: + raise RuntimeError("SPEC2017_INPUT must be one of: test, train, ref, refrate, refspeed, all") + if mode not in ("rate", "speed"): + raise RuntimeError("MODE/SPEC2017_MODE must be rate or speed for a single BENCH build") + workload = INPUT_ALIASES[input_set].get(mode) + if workload is None: + raise RuntimeError(f"input {input_set!r} is not valid for mode {mode!r}") + matches = [case for case in cases.values() if case["short"] == bench and case["mode"] == mode and case["workload"] == workload] + if len(matches) != 1: + choices = " ".join(sorted(cases)) + raise RuntimeError(f"cannot resolve BENCH={bench!r}; use a full case name. Available cases: {choices}") + return case_name(matches[0]["short"], mode, workload) + + +def resolve_cross_compile(prefix): + gcc_name = prefix if prefix.endswith("gcc") else prefix + "gcc" + gcc_path = Path(gcc_name) + if not gcc_path.is_absolute(): + found = shutil.which(gcc_name) + if found is None: + raise RuntimeError(f"cannot locate compiler for CROSS_COMPILE={prefix!r}") + gcc_path = Path(found) + if not gcc_path.is_file(): + raise RuntimeError(f"compiler does not exist: {gcc_path}") + gcc_path = gcc_path.resolve() + if not gcc_path.name.endswith("gcc"): + raise RuntimeError(f"unexpected compiler name for CROSS_COMPILE={prefix!r}: {gcc_path.name}") + return str(gcc_path)[: -len("gcc")], gcc_path.parent.parent + + +def spec_env(spec_dir): + script = f"cd {shlex.quote(str(spec_dir))} && . ./shrc >/dev/null && env -0" + raw = subprocess.check_output(["bash", "-lc", script]) + env = {} + for item in raw.split(b"\0"): + if not item: + continue + key, value = item.split(b"=", 1) + env[key.decode()] = value.decode() + return env + + +def tree_metadata(source): + source = Path(source).resolve() + archive = source / "install_archives" / "cpu2017.tar.xz" + if archive.is_file(): + return { + "source": str(source), + "archive_size": archive.stat().st_size, + "archive_mtime_ns": archive.stat().st_mtime_ns, + } + return {"source": str(source)} + + +def spec_tree_complete(path): + cpu_root = path / "benchspec" / "CPU" + return (path / "bin" / "specperl").is_file() and all((cpu_root / bench_dir).is_dir() for bench_dir, *_ in BENCHMARKS) + + +def prepared_workspace_meta(path): + meta = path / ".workload-builder-prepare.meta" + if meta.is_file(): + return meta.read_text(encoding="utf-8", errors="replace") + return "" + + +def make_tree_user_writable(path): + for root, dirs, files in os.walk(path, followlinks=False): + root_path = Path(root) + try: + root_path.chmod(root_path.stat().st_mode | 0o700) + except OSError: + pass + for name in dirs: + dir_path = root_path / name + if dir_path.is_symlink(): + continue + try: + dir_path.chmod(dir_path.stat().st_mode | 0o700) + except OSError: + pass + for name in files: + file_path = root_path / name + if file_path.is_symlink(): + continue + try: + file_path.chmod(file_path.stat().st_mode | 0o600) + except OSError: + pass + + +def copy_spec_source(source, dest, log_path): + source = Path(source).resolve() + metadata_path = dest / ".workload-builder-source.json" + desired = tree_metadata(source) + if metadata_path.is_file(): + try: + if load_json(metadata_path) == desired and ( + (dest / "install.sh").is_file() or (dest / "bin" / "runcpu").is_file() + ): + return + except Exception: + pass + if dest.exists(): + shutil.rmtree(dest) + dest.parent.mkdir(parents=True, exist_ok=True) + status(f"Copying SPEC2017 source tree to {dest}") + run(["cp", "-R", "--reflink=auto", str(source), str(dest)], log_path=log_path, summary=f"Copying source tree (log: {log_path})") + make_tree_user_writable(dest) + write_json(metadata_path, desired) + + +def install_spec_tree(source_copy, spec_tree, log_path): + if spec_tree_complete(spec_tree): + return spec_tree + if spec_tree_complete(source_copy): + return source_copy + marker = spec_tree / ".workload-builder-installed" + if marker.is_file() and spec_tree_complete(spec_tree): + return spec_tree + if spec_tree.exists(): + shutil.rmtree(spec_tree) + spec_tree.parent.mkdir(parents=True, exist_ok=True) + status(f"Installing SPEC2017 working tree to {spec_tree}") + env = os.environ.copy() + env["SPEC_NOCHECK"] = "1" + run( + [str(source_copy / "install.sh"), "-f", "-d", str(spec_tree)], + cwd=source_copy, + env=env, + log_path=log_path, + summary=f"Installing SPEC2017 tree (log: {log_path})", + ) + marker.write_text("installed\n", encoding="utf-8") + return spec_tree + + +def prepare_spec_tree(spec_source, out_dir, log_dir): + base_dir = out_dir.parent + source_copy = base_dir / "spec-source" + spec_tree = base_dir / "spec-tree" + prepare_log = log_dir / "prepare-spec-tree.log" + copy_spec_source(spec_source, source_copy, prepare_log) + return install_spec_tree(source_copy, spec_tree, prepare_log) + + +def build_local_config(template_cfg, generated_cfg, output_root, jobs): + text = template_cfg.read_text(encoding="utf-8") + filtered_lines = [] + for line in text.splitlines(): + stripped = line.lstrip() + if stripped.startswith("output_root") and "=" in line: + continue + if stripped.startswith("makeflags") and "=" in line: + continue + filtered_lines.append(line) + generated_cfg.parent.mkdir(parents=True, exist_ok=True) + generated_cfg.write_text( + "\n".join( + [ + "# Auto-generated by spec2017-package.py", + f"# Template: {template_cfg}", + f"output_root = {output_root}", + f"makeflags = -j{jobs}", + "", + *filtered_lines, + "", + ] + ), + encoding="utf-8", + ) + return generated_cfg + + +def shared_build_dir_for(out_dir, bench_dir, tune): + return out_dir.parent / "_bench-builds" / bench_dir / tune + + +def shared_build_metadata(spec_cfg, spec_source, cross_compile, tune, compiler_root, gnu_toolchain_root): + return { + "spec_cfg_sha256": file_sha256(spec_cfg), + "spec_source": str(spec_source), + "spec_source_meta": prepared_workspace_meta(Path(spec_source)), + "cross_compile": cross_compile, + "tune": tune, + "compiler_root": str(compiler_root), + "gnu_toolchain_root": str(gnu_toolchain_root), + } + + +def select_built_elf(exe_dir, tune): + if not exe_dir.is_dir(): + raise RuntimeError(f"runcpu did not create exe directory: {exe_dir}") + candidates = [path for path in exe_dir.iterdir() if path.is_file() and not path.name.endswith(".md5")] + if not candidates: + raise RuntimeError(f"no executable produced by runcpu under {exe_dir}") + preferred = [path for path in candidates if f"_{tune}." in path.name] + if preferred: + candidates = preferred + return max(candidates, key=lambda path: path.stat().st_mtime) + + +def explain_missing_elf(output_root, bench_dir, tune): + build_root = output_root / "benchspec" / "CPU" / bench_dir / "build" + if not build_root.is_dir(): + return "" + build_dirs = sorted(path for path in build_root.iterdir() if path.is_dir()) + if not build_dirs: + return "" + latest = max(build_dirs, key=lambda path: path.stat().st_mtime) + details = [f"Latest build directory: {latest}"] + for name in ("make.err", "make.out"): + tail = maybe_read_log_tail(latest / name) + if tail: + details.append(tail.lstrip("\n")) + break + return "\n" + "\n".join(details) + + +def export_build_log_artifact(build_log, out_dir): + if not build_log.is_file(): + return None + log_dir = out_dir / "logs" / "build_elf" + log_dir.mkdir(parents=True, exist_ok=True) + exported_log = log_dir / build_log.name + shutil.copy2(build_log, exported_log) + return exported_log + + +def runcpu_env(spec_dir, compiler_root, gnu_toolchain_root): + env = os.environ.copy() + env.update(spec_env(spec_dir)) + env["SPEC"] = str(spec_dir) + env["SPEC2017_COMPILER_ROOT"] = str(compiler_root) + env["SPEC2017_GNU_TOOLCHAIN_ROOT"] = str(gnu_toolchain_root) + return env + + +def build_elf(spec_dir, bench_dir, spec_cfg, spec_source, out_dir, log_dir, cross_compile, tune, jobs, compiler_root, gnu_toolchain_root): + shared_dir = shared_build_dir_for(out_dir, bench_dir, tune) + output_root = shared_dir / "runcpu-output" + generated_cfg = shared_dir / "runcpu-config" / spec_cfg.name + shared_log_dir = shared_dir / "logs" + build_log = shared_log_dir / "build.log" + exe_dir = output_root / "benchspec" / "CPU" / bench_dir / "exe" + metadata_path = shared_dir / "build-meta.json" + metadata = shared_build_metadata(spec_cfg, spec_source, cross_compile, tune, compiler_root, gnu_toolchain_root) + if metadata_path.is_file(): + try: + cached_metadata = load_json(metadata_path) + except Exception: + cached_metadata = None + if cached_metadata == metadata: + try: + elf = select_built_elf(exe_dir, tune) + except Exception: + elf = None + if elf is not None: + status(f"Reusing {bench_dir} build from {shared_dir}") + export_build_log_artifact(build_log, out_dir) + return elf, output_root + if output_root.exists(): + shutil.rmtree(output_root) + if generated_cfg.parent.exists(): + shutil.rmtree(generated_cfg.parent) + if shared_log_dir.exists(): + shutil.rmtree(shared_log_dir) + shared_log_dir.mkdir(parents=True, exist_ok=True) + build_local_config(spec_cfg, generated_cfg, output_root, jobs) + status(f"Generated SPEC cfg: {generated_cfg}") + env = runcpu_env(spec_dir, compiler_root, gnu_toolchain_root) + cmd = [ + str(spec_dir / "bin" / "runcpu"), + "--action", + "build", + "--config", + str(generated_cfg), + "--tune", + tune, + "--noreportable", + "--iterations", + "1", + bench_dir, + ] + run(cmd, cwd=spec_dir, env=env, log_path=build_log, summary=f"Building {bench_dir} with runcpu (log: {build_log})") + try: + elf = select_built_elf(exe_dir, tune) + except RuntimeError as exc: + detail = explain_missing_elf(output_root, bench_dir, tune) + raise RuntimeError(str(exc) + detail) from exc + write_json(metadata_path, metadata) + export_build_log_artifact(build_log, out_dir) + return elf, output_root + + +def export_elf_artifact(elf, case_name_value, out_dir): + elf_dir = out_dir / "elf" + elf_dir.mkdir(parents=True, exist_ok=True) + exported_elf = elf_dir / f"{case_name_value}.elf" + shutil.copy2(elf, exported_elf) + return exported_elf + + +def select_run_dir(output_root, bench_dir, tune, workload): + run_root = output_root / "benchspec" / "CPU" / bench_dir / "run" + if not run_root.is_dir(): + raise RuntimeError(f"runcpu did not create run directory root: {run_root}") + pattern = f"run_{tune}_{workload}_" + candidates = [path for path in run_root.iterdir() if path.is_dir() and path.name.startswith(pattern)] + if not candidates: + available = " ".join(sorted(path.name for path in run_root.iterdir() if path.is_dir())) + raise RuntimeError(f"no run directory matching {pattern!r} under {run_root}; available: {available}") + return max(candidates, key=lambda path: path.stat().st_mtime) + + +def prepare_run_dir(spec_dir, bench_dir, workload, generated_output_root, spec_cfg, tune, log_dir, compiler_root, gnu_toolchain_root): + setup_log = log_dir / "runsetup.log" + env = runcpu_env(spec_dir, compiler_root, gnu_toolchain_root) + cmd = [ + str(spec_dir / "bin" / "runcpu"), + "--action", + "runsetup", + "--config", + str(spec_cfg), + "--tune", + tune, + "--size", + workload, + "--copies", + "1", + "--noreportable", + "--iterations", + "1", + bench_dir, + ] + run(cmd, cwd=spec_dir, env=env, log_path=setup_log, summary=f"Preparing run directory for {bench_dir}/{workload} (log: {setup_log})") + return select_run_dir(generated_output_root, bench_dir, tune, workload) + + +def copy_tree_contents(src, dst): + dst.mkdir(parents=True, exist_ok=True) + for item in src.iterdir(): + target = dst / item.name + if item.is_symlink(): + if target.exists() or target.is_symlink(): + target.unlink() + os.symlink(os.readlink(item), target) + elif item.is_dir(): + if target.exists(): + shutil.rmtree(target) + shutil.copytree(item, target, symlinks=True) + else: + shutil.copy2(item, target) + + +def shell_from_specinvoke(spec_dir, run_dir, log_dir): + cmd_file = run_dir / "speccmds.cmd" + if not cmd_file.is_file(): + raise RuntimeError(f"speccmds.cmd not found in run directory: {run_dir}") + log_path = log_dir / "specinvoke-dryrun.log" + env = os.environ.copy() + env.update(spec_env(spec_dir)) + output = run( + [str(spec_dir / "bin" / "specinvoke"), "-nn", str(cmd_file)], + cwd=run_dir, + env=env, + log_path=log_path, + summary=f"Rendering speccmds.cmd to shell (log: {log_path})", + capture=True, + ) + commands = [] + run_dir_ref = f"../{run_dir.name}/" + for line in output.splitlines(): + stripped = line.strip() + if not stripped or stripped.startswith("#") or stripped.startswith("specinvoke exit:"): + continue + if stripped.startswith("export ") or stripped.startswith("unset ") or stripped.startswith("cd "): + continue + if re.match(r"^[A-Za-z_][A-Za-z0-9_]*=", stripped): + continue + line = line.replace(run_dir_ref, "./") + line = line.replace(str(run_dir) + "/", "./") + line = line.replace(str(run_dir), "/spec") + commands.append(line) + if not commands: + raise RuntimeError(f"specinvoke produced no runnable shell commands from {cmd_file}") + return commands + + +def sanitize_name(value): + value = re.sub(r"[^A-Za-z0-9_.+-]+", "_", value) + value = re.sub(r"_+", "_", value).strip("._-") + return value or "run" + + +def command_label(command, index): + match = re.search(r"(?:^|[ \t])(?:1?>|>>)\s*([^ \t]+)", command) + if match: + label = match.group(1).strip("'\"") + if label.endswith(".out"): + label = label[:-4] + return sanitize_name(label) + words = shlex.split(command, posix=True) + if words: + return sanitize_name(Path(words[0]).name) + return f"run{index}" + + +def run_variants(case_name_value, run_commands): + total = len(run_commands) + seen = {} + variants = [] + for index, command in enumerate(run_commands): + label = command_label(command, index) + if total == 1: + name = case_name_value + else: + name = f"{case_name_value}_{index:02d}_{label}" + count = seen.get(name, 0) + seen[name] = count + 1 + if count: + name = f"{name}_{count}" + commands = [command] + # x264 pass 2 depends on stats produced by the immediately previous pass 1 run. + if index > 0 and "--pass 2" in command and "--stats " in command and "--pass 1" in run_commands[index - 1]: + commands = [run_commands[index - 1], command] + variants.append({"name": name, "index": index, "commands": commands}) + return variants + + +def write_variants_metadata(out_dir, variants): + serializable = [] + for variant in variants: + serializable.append( + { + "command_count": len(variant["commands"]), + "name": variant["name"], + "index": variant["index"], + "build_dir": str(variant["build_dir"]), + } + ) + write_json(out_dir / "variants.json", serializable) + + +def write_runtime_files(pkg_dir, case_name_value, run_commands, profiling): + spec_root = pkg_dir / "spec" + run_sh = spec_root / "run.sh" + script_lines = [ + "#!/bin/sh", + "set -e", + "mkdir -p /proc /sys /tmp", + "mount -t proc proc /proc 2>/dev/null || true", + "mount -t sysfs sysfs /sys 2>/dev/null || true", + "cd /spec", + "export LC_ALL=C", + "export OMP_NUM_THREADS=1", + "export OMP_THREAD_LIMIT=1", + "ulimit -s unlimited 2>/dev/null || true", + f"echo '======== BEGIN {case_name_value} ========'", + "date -R || true", + "status=0", + "set +e", + ] + for command in run_commands: + command_lines = [ + "if [ \"$status\" -eq 0 ]; then", + f" spec_cmd={shlex.quote(command)}", + " echo \"CMD: $spec_cmd\"", + ] + if profiling == "1": + command_lines.extend([" nemu-trap 256", " nemu-trap 257"]) + command_lines.extend( + [ + " sh -c \"$spec_cmd\"", + " status=$?", + ] + ) + command_lines.append(" nemu-trap \"$status\"") + command_lines.append("fi") + script_lines.extend( + command_lines + ) + script_lines.extend( + [ + "set -e", + "if [ \"$status\" -ne 0 ]; then", + " echo \"SPEC2017 command failed with status $status\"", + " for log in *.err *.out; do", + " [ -f \"$log\" ] || continue", + " echo \"----- $log -----\"", + " tail -n 40 \"$log\" || true", + " done", + "fi", + "date -R || true", + f"echo '======== END {case_name_value} ========'", + "exit $status", + "", + ] + ) + run_sh.write_text("\n".join(script_lines), encoding="utf-8") + run_sh.chmod(0o755) + etc = pkg_dir / "etc" + etc.mkdir(parents=True, exist_ok=True) + (etc / "inittab").write_text("::once:/bin/sh /spec/run.sh\n", encoding="utf-8") + + +def package_runtime_variant(variant, run_dir, profiling): + build_dir = variant["build_dir"] + pkg_dir = build_dir / "package" + if pkg_dir.exists(): + shutil.rmtree(pkg_dir) + (pkg_dir / "spec").mkdir(parents=True) + status(f"Packaging rootfs for {variant['name']}") + copy_tree_contents(run_dir, pkg_dir / "spec") + write_runtime_files(pkg_dir, variant["name"], variant["commands"], profiling) + + +def package_case(args): + cases = all_cases() + if args.case not in cases: + choices = " ".join(sorted(cases)) + raise RuntimeError(f"unknown SPEC2017 case {args.case!r}; available cases: {choices}") + case = cases[args.case] + spec_source = Path(args.spec_source).resolve() + if not spec_tree_complete(spec_source): + raise RuntimeError(f"SPEC2017 workspace is not prepared: {spec_source}") + spec_cfg = Path(args.spec_config).resolve() + pkg_dir = Path(args.pkg_dir).resolve() if args.pkg_dir else None + out_dir = Path(args.out_dir).resolve() + log_dir = Path(args.log_dir).resolve() if args.log_dir else out_dir / "logs" + spec_dir = spec_source + cross_compile, detected_toolchain_root = resolve_cross_compile(args.cross_compile) + compiler_root = Path(args.compiler_root).resolve() if args.compiler_root else detected_toolchain_root + gnu_toolchain_root = Path(args.gnu_toolchain_root).resolve() if args.gnu_toolchain_root else detected_toolchain_root + elf, output_root = build_elf( + spec_dir, + case["bench_dir"], + spec_cfg, + spec_source, + out_dir, + log_dir, + cross_compile, + args.tune, + args.jobs, + compiler_root, + gnu_toolchain_root, + ) + exported_elf = export_elf_artifact(elf, args.case, out_dir) + status(f"Exported ELF: {exported_elf}") + if args.elf_only: + return + run_dir = prepare_run_dir( + spec_dir, + case["bench_dir"], + case["workload"], + output_root, + shared_build_dir_for(out_dir, case["bench_dir"], args.tune) / "runcpu-config" / spec_cfg.name, + args.tune, + log_dir, + compiler_root, + gnu_toolchain_root, + ) + run_commands = shell_from_specinvoke(spec_dir, run_dir, log_dir) + if args.all_runs: + variants = run_variants(args.case, run_commands) + variant_root = out_dir / "runs" + if variant_root.exists(): + shutil.rmtree(variant_root) + for variant in variants: + variant["build_dir"] = variant_root / variant["name"] + package_runtime_variant(variant, run_dir, args.profiling) + write_variants_metadata(out_dir, variants) + return + if pkg_dir is None: + raise RuntimeError("--pkg-dir is required unless --elf-only is set") + if pkg_dir.exists(): + shutil.rmtree(pkg_dir) + (pkg_dir / "spec").mkdir(parents=True) + status(f"Packaging rootfs for {args.case}") + copy_tree_contents(run_dir, pkg_dir / "spec") + write_runtime_files(pkg_dir, args.case, run_commands, args.profiling) + + +def prepare_only(args): + spec_source = Path(args.spec_source).resolve() + out_dir = Path(args.out_dir).resolve() + log_dir = Path(args.log_dir).resolve() if args.log_dir else out_dir / "logs" + spec_dir = prepare_spec_tree(spec_source, out_dir, log_dir) + status(f"Prepared SPEC2017 working tree: {spec_dir}") + + +def list_packaged_variants(args): + out_dir = Path(args.out_dir).resolve() + variants_path = out_dir / "variants.json" + if not variants_path.is_file(): + raise RuntimeError(f"variant metadata not found: {variants_path}") + variants = load_json(variants_path) + for variant in variants: + print(f"{variant['name']}\t{variant['build_dir']}") + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument("--list-cases", action="store_true") + parser.add_argument("--resolve-case", action="store_true") + parser.add_argument("--prepare-only", action="store_true") + parser.add_argument("--list-packaged-variants", action="store_true") + parser.add_argument("--bench") + parser.add_argument("--input-set", choices=("test", "train", "ref", "refrate", "refspeed", "all"), default="all") + parser.add_argument("--mode", choices=("rate", "speed", "all"), default="all") + parser.add_argument("--case") + parser.add_argument("--spec-source") + parser.add_argument("--spec-config") + parser.add_argument("--pkg-dir") + parser.add_argument("--out-dir") + parser.add_argument("--cross-compile", default="riscv64-unknown-linux-gnu-") + parser.add_argument("--compiler-root") + parser.add_argument("--gnu-toolchain-root") + parser.add_argument("--log-dir") + parser.add_argument("--tune", default="base") + parser.add_argument("--jobs", default=str(os.cpu_count() or 1)) + parser.add_argument("--elf-only", action="store_true") + parser.add_argument("--all-runs", action="store_true") + parser.add_argument("--profiling", choices=("0", "1"), default="1") + args = parser.parse_args() + if args.list_cases: + print(" ".join(filter_cases(all_cases(), args.input_set, args.mode).keys())) + return + if args.resolve_case: + try: + print(resolve_case_name(args.bench, args.input_set, args.mode)) + except Exception as exc: + parser.error(str(exc)) + return + if args.prepare_only: + missing = [name for name in ("spec_source", "out_dir") if getattr(args, name) in (None, "")] + if missing: + parser.error("missing required arguments: " + ", ".join("--" + x.replace("_", "-") for x in missing)) + try: + prepare_only(args) + except Exception as exc: + print(f"{progress_prefix()} error: {exc}", file=sys.stderr) + sys.exit(1) + return + if args.list_packaged_variants: + if not args.out_dir: + parser.error("--out-dir is required with --list-packaged-variants") + try: + list_packaged_variants(args) + except Exception as exc: + print(f"{progress_prefix()} error: {exc}", file=sys.stderr) + sys.exit(1) + return + required = ["case", "spec_source", "spec_config", "out_dir", "cross_compile"] + if not args.elf_only: + required.append("pkg_dir") + missing = [name for name in required if getattr(args, name) in (None, "")] + if missing: + parser.error("missing required arguments: " + ", ".join("--" + x.replace("_", "-") for x in missing)) + try: + package_case(args) + except Exception as exc: + print(f"{progress_prefix()} error: {exc}", file=sys.stderr) + sys.exit(1) + + +if __name__ == "__main__": + main()