Skip to content

Instantly share code, notes, and snippets.

@ConnorNelson
Last active September 19, 2025 05:34
Show Gist options
  • Select an option

  • Save ConnorNelson/af7ea0945dab81921ed6522dc840b701 to your computer and use it in GitHub Desktop.

Select an option

Save ConnorNelson/af7ea0945dab81921ed6522dc840b701 to your computer and use it in GitHub Desktop.
Compare disassembly of different optimization levels of various packages in nixpkgs
{
description = "Pick-any package with selectable -O* (without rebuilding the world)";
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
outputs =
{ self, nixpkgs }:
let
system = "x86_64-linux";
pkgs = import nixpkgs { inherit system; };
lib = pkgs.lib;
mkWithCFlags =
flags: drv:
if lib.isDerivation drv then
drv.overrideAttrs (old: {
NIX_CFLAGS_COMPILE = lib.concatStringsSep " " (
lib.optional (old ? NIX_CFLAGS_COMPILE) old.NIX_CFLAGS_COMPILE ++ flags
);
})
else
drv;
mkLevel = flags: lib.mapAttrs (_: mkWithCFlags flags) pkgs;
levels = {
O0 = [ "-O0" "-frecord-gcc-switches" ];
O1 = [ "-O1" "-frecord-gcc-switches" ];
O2 = [ "-O2" "-frecord-gcc-switches" ];
O3 = [ "-O3" "-frecord-gcc-switches" ];
Og = [ "-Og" "-frecord-gcc-switches" ];
Os = [ "-Os" "-frecord-gcc-switches" ];
Ofast = [ "-Ofast" "-frecord-gcc-switches" ];
};
opt = {
O0 = mkLevel levels.O0;
O1 = mkLevel levels.O1;
O2 = mkLevel levels.O2;
O3 = mkLevel levels.O3;
Og = mkLevel levels.Og;
Os = mkLevel levels.Os;
Ofast = mkLevel levels.Ofast;
};
odis = pkgs.writeShellApplication {
name = "odis";
runtimeInputs = [
pkgs.nix # to run `nix build`
pkgs.binutils # objdump
pkgs.gawk # awk
pkgs.gnugrep # grep (not strictly required but handy)
pkgs.coreutils # head/sed/cut etc.
pkgs.icdiff # pretty diff when two levels given
];
text = ''
set -euo pipefail
usage() {
cat <<EOF
Usage:
odis -p <package> [-b <bin>] [-f <func>] [-n <lines>] <Olevel> [<Olevel2>]
Examples:
odis -p curl O0
odis -p curl O3
odis -p wget -f main O0 O3
odis -p coreutils -b ls Os O2
Flags:
-p Nix package attr name (required), e.g. curl, wget, coreutils
-b Binary name (defaults to package name), e.g. ls for coreutils
-f Function to disassemble (default: main)
-n Number of lines to print (default: 40)
Notes:
* Uses .#opt.<Olevel>.<package> from this flake.
* Output is Intel syntax, .text only, no addresses/hex, no headers.
* With two levels, shows an icdiff side-by-side diff.
EOF
}
PKG=""
BIN=""
FUNC="main"
LINES=20
while getopts ":p:b:f:n:h" opt; do
case "$opt" in
p) PKG="$OPTARG" ;;
b) BIN="$OPTARG" ;;
f) FUNC="$OPTARG" ;;
n) LINES="$OPTARG" ;;
h) usage; exit 0 ;;
*) usage; exit 1 ;;
esac
done
shift $((OPTIND-1))
if [ -z "''${PKG}" ]; then
echo "error: -p <package> is required" >&2
usage; exit 1
fi
if [ -z "''${BIN:-}" ]; then
BIN="$PKG"
fi
if [ "$#" -lt 1 ] || [ "$#" -gt 2 ]; then
usage; exit 1
fi
O1="$1"
O2="''${2-}"
build_path() {
local level="$1"
nix build ".#opt.$level.$PKG.bin" --print-out-paths 2>/dev/null || \
nix build ".#opt.$level.$PKG" --print-out-paths
}
dump() {
# dump first $LINES lines of $FUNC, clean, and silence SIGPIPE noise
local level="$1"
local out
out="$(build_path "$level")"
objdump -d -M intel --section=.text --disassemble="$FUNC" \
--no-addresses --no-show-raw-insn "$out/bin/$BIN" 2>/dev/null \
| awk '/^Disassembly of section .text:/{f=1;next} f' \
| head -n "$LINES"
}
if [ -n "$O2" ]; then
icdiff <(dump "$O1") <(dump "$O2")
else
dump "$O1"
fi
'';
};
in
{
packages.${system} = pkgs // {
inherit opt odis;
};
devShells.${system}.default = pkgs.mkShell {
packages = with pkgs; [
icdiff
binutils
gawk
gnugrep
coreutils
# nix
odis
];
shellHook = ''
echo "Try: odis -p curl O0 O3"
'';
};
};
}
@ConnorNelson
Copy link
Author

ConnorNelson commented Sep 19, 2025

This was mostly generated by ChatGPT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment