diff --git a/Test/Passes/pass_group_riscv.mlir b/Test/Passes/pass_group_riscv.mlir new file mode 100644 index 000000000..7e07ec762 --- /dev/null +++ b/Test/Passes/pass_group_riscv.mlir @@ -0,0 +1,10 @@ +// RUN: veir-opt %s -p=riscv | filecheck %s + +"builtin.module"() ({ + "func.func"() <{function_type = (i64, i64) -> i64, sym_name = "foo"}> ({ + ^bb(%a: i64, %b: i64): + %add = "llvm.add"(%a, %b) : (i64, i64) -> i64 + // CHECK: %{{.*}} = "riscv.add"(%{{.*}}, %{{.*}}) : (!riscv.reg, !riscv.reg) -> !riscv.reg + "func.return"(%add) : (i64) -> () + }) : () -> () +}) : () -> () diff --git a/Test/Passes/pipeline_compose.mlir b/Test/Passes/pipeline_compose.mlir new file mode 100644 index 000000000..7c2a1c5f8 --- /dev/null +++ b/Test/Passes/pipeline_compose.mlir @@ -0,0 +1,20 @@ +// RUN: veir-opt %s -p=canonicalize,print-ir -p=riscv,print-ir -p=cse -p=dce 2>&1 | filecheck %s + +// Check that repeated -p flags build one pipeline in the order the flags appear +// on the command line, and that a pass-group name inside a -p list expands in +// place to the group's passes. The print-ir probes (which dump to stderr) witness +// the order: the dump after the first flag must still contain llvm.add, and the +// dump after the riscv group must contain riscv.add. + +// CHECK: IR Dump +// CHECK: "llvm.add" +// CHECK: IR Dump +// CHECK: "riscv.add" + +"builtin.module"() ({ + "func.func"() <{function_type = (i64, i64) -> i64, sym_name = "foo"}> ({ + ^bb(%a: i64, %b: i64): + %add = "llvm.add"(%a, %b) : (i64, i64) -> i64 + "func.return"(%add) : (i64) -> () + }) : () -> () +}) : () -> () diff --git a/VeirOpt.lean b/VeirOpt.lean index d5a1a9f83..bdba86deb 100644 --- a/VeirOpt.lean +++ b/VeirOpt.lean @@ -40,6 +40,26 @@ def availablePasses : Std.HashMap String (Pass OpCode) := |>.insert ModArithToArithPass.name ModArithToArithPass |>.insert CanonicalizePass.name CanonicalizePass +/-- + A map of named pass groups, each expanding to a comma-separated pipeline of pass names. + A group name may be used wherever a pass name is expected in a `-p` list and expands + in place to its member passes. If a name is both a pass and a group, the pass wins. +-/ +def passGroups : Std.HashMap String String := + (Std.HashMap.emptyWithCapacity 2) + |>.insert "O" "canonicalize,instcombine,cse,dce" + |>.insert "riscv" + "isel-br-riscv64,canonicalize,isel-sdag-riscv64,isel-riscv64,riscv-combine,reconcile-cast,dce" + +/-- + A human-readable description of every pass group and the passes it expands to, + used in the usage message. +-/ +def passGroupsUsage : String := + String.intercalate "\n" + ((passGroups.toList.toArray.qsort (·.1 < ·.1)).toList.map fun (name, passes) => + s!" {name}: {passes}") + /-- Arguments for the `veir-opt` command-line tool, parsed from the CLI. -/ @@ -54,27 +74,33 @@ structure VeirOptArgs where disableVerifiers : Bool /-- - Parse the `-p` flag to construct a pass pipeline. - Returns an error if the flag is malformed or if any pass name is unknown. + Parse the `-p` flags to construct a pass pipeline. + `-p` takes a comma-separated list of pass names and pass-group names; a group name + (see `passGroups`) expands in place to its member passes. The flag may appear any + number of times; the resulting pipeline is the concatenation of their passes, in the + order the flags appear on the command line. + Returns an error if a flag is malformed or if any name is neither a pass nor a group. -/ def parsePipelineOption (args : List String) : Except String (PassPipeline OpCode × List String) := do - let (passesFlags, rest) := args.partition (·.startsWith "-p=") - match passesFlags with - | [] => return ({ passes := #[] }, rest) - | [flag] => + let (pipelineFlags, rest) := args.partition (·.startsWith "-p=") + let mut passes : Array (Pass OpCode) := #[] + for flag in pipelineFlags do let arg := (flag.drop 3).toString - match PassPipeline.ofString? availablePasses arg with - | .ok pipeline => return (pipeline, rest) - | .error errMsg => .error s!"Error parsing -p flag: {errMsg}" - | _ => .error "Expected at most one -p flag." + let expanded := String.intercalate "," (arg.splitOn "," |>.map fun name => + if availablePasses.contains name then name + else (passGroups.get? name).getD name) + match PassPipeline.ofString? availablePasses expanded with + | .ok pipeline => passes := passes ++ pipeline.passes + | .error errMsg => throw s!"Error parsing -p flag: {errMsg}" + return ({ passes }, rest) /-- Parse CLI arguments. Returns an error if the arguments are malformed. -/ def parseArgs (args : List String) : Except String VeirOptArgs := do let (flags, positional) := args.partition (·.startsWith "-") - -- Consume the `-p` flag if present. + -- Consume any `-p` flags. let (pipeline, flags) ← parsePipelineOption flags -- Consume `--allow-unregistered-dialect` if present. let allowUnregisteredDialect := flags.contains "--allow-unregistered-dialect" @@ -131,7 +157,10 @@ def main (args : List String) : IO Unit := do match parseArgs args with | .error errMsg => IO.eprintln s!"Error: {errMsg}" - IO.eprintln "Usage: veir-opt [-p=\"pass1,pass2,...\"] [--allow-unregistered-dialect] [--disable-verifiers]" + IO.eprintln "Usage: veir-opt [-p=\"pass1,pass2,...\"]... [--allow-unregistered-dialect] [--disable-verifiers]" + IO.eprintln " -p may be repeated; passes run in the order the flags appear." + IO.eprintln " A pass list may also contain pass-group names, which expand in place:" + IO.eprintln passGroupsUsage IO.Process.exit 1 | .ok { filename, passes, allowUnregisteredDialect, disableVerifiers } => match ← parseOperation filename allowUnregisteredDialect with