From 3168ab55d2167de5f9953bf52c9c5e3421963319 Mon Sep 17 00:00:00 2001
From: Zbynek Slajchrt <zbynek.slajchrt@oracle.com>
Date: Fri, 30 Sep 2016 17:55:43 +0200
Subject: [PATCH] Chimney-sweeping adapted to processing all builtins in batch

---
 .../truffle/r/nodes/test/ChimneySweeping.java | 142 +++++++++++-------
 .../r/nodes/test/DefaultArgsExtractor.java    |  21 ++-
 .../r/nodes/test/RBuiltinDiagnostics.java     |  63 +++++---
 mx.fastr/mx_fastr.py                          |   4 +-
 4 files changed, 145 insertions(+), 85 deletions(-)

diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ChimneySweeping.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ChimneySweeping.java
index a02b86e9fc..1a7c9033ad 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ChimneySweeping.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/ChimneySweeping.java
@@ -36,8 +36,12 @@ import java.util.Map;
 import java.util.Optional;
 import java.util.Set;
 import java.util.SortedMap;
+import java.util.function.Consumer;
+import java.util.function.Function;
 import java.util.stream.Collectors;
 
+import javax.swing.SwingWorker;
+
 import org.junit.Assert;
 
 import com.oracle.truffle.api.dsl.UnsupportedSpecializationException;
@@ -65,8 +69,25 @@ import com.oracle.truffle.r.test.generate.GnuROneShotRSession;
 import com.oracle.truffle.r.test.generate.TestOutputManager;
 import com.oracle.truffle.r.test.generate.TestOutputManager.TestInfo;
 
+/**
+ * Use the following command to sweep all builtins
+ *
+ * <pre>
+ * mx rbdiag --sweep --mnonly --matchLevel=error --maxSweeps=30 --outMaxLev=0
+ * </pre>
+ * 
+ * .
+ *
+ */
 class ChimneySweeping extends SingleBuiltinDiagnostics {
 
+    private Set<String> blacklistedBuiltins = new HashSet<>();
+    {
+        blacklistedBuiltins.add(".dfltWarn");
+        blacklistedBuiltins.add("browser");
+        blacklistedBuiltins.add(".fastr.context.r");
+    }
+
     private static final String TEST_PREFIX = "com.oracle.truffle.r.test.builtins.TestBuiltin_";
     private static final String SWEEP_MODE_ARG = "--sweep";
     private static final String SWEEP_MODE_ARG_SPEC = SWEEP_MODE_ARG + "=";
@@ -74,6 +95,7 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
     private static final String MISSING_AND_NULL_SAMPLES_ONLY_ARG = "--mnonly";
     private static final String OUTPUT_MATCH_LEVEL = "--matchLevel";
     private static final String OUTPUT_MATCH_LEVEL_SPEC = OUTPUT_MATCH_LEVEL + "=";
+    private static final String MAX_SWEEPS_ARG = "--maxSweeps=";
 
     enum ChimneySweepingMode {
         auto,
@@ -109,6 +131,7 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
         OutputMatchLevel outputMatchLevel;
         boolean missingAndNullSamplesOnly;
         boolean performPipelineSelfTest;
+        int maxSweeps;
     }
 
     static class ChimneySweepingSuite extends RBuiltinDiagnostics {
@@ -122,13 +145,13 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
             super(config);
             this.diagConfig = config;
 
-            System.out.println("Loading GnuR ...");
+            print(1, "Loading GnuR ...");
             gnuRSession = new GnuROneShotRSession();
 
-            System.out.println("Loading FastR ...");
+            print(1, "Loading FastR ...");
             fastRSession = FastRSession.create();
 
-            System.out.println("Loading test outputs ...");
+            print(1, "Loading test outputs ...");
             outputManager = loadTestOutputManager();
         }
 
@@ -148,6 +171,7 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
             // The pipeline self-test is disabled when only RMissing and RNull samples are used as
             // these values are not determined via the pipeline static type analysis
             config.performPipelineSelfTest = config.missingAndNullSamplesOnly ? false : !Arrays.stream(args).filter(arg -> NO_SELF_TEST_ARG.equals(arg)).findFirst().isPresent();
+            config.maxSweeps = Arrays.stream(args).filter(arg -> arg.startsWith(MAX_SWEEPS_ARG)).map(x -> Integer.parseInt(x.split("=")[1])).findFirst().orElse(Integer.MAX_VALUE);
             return RBuiltinDiagnostics.initDiagConfig(config, args);
         }
 
@@ -194,6 +218,9 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
 
     ChimneySweeping(ChimneySweepingSuite diagSuite, RIntBuiltinDiagFactory builtinFactory) {
         super(diagSuite, builtinFactory);
+
+        print(0, "\n*** Chimney-sweeping of '" + builtinName + "' (" + builtinFactory.getBuiltinNodeClass().getName() + ") ***");
+
         this.kind = builtinFactory.getBuiltinKind();
         this.diagSuite = diagSuite;
         this.validArgsList = extractValidArgsForBuiltin();
@@ -202,17 +229,21 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
 
     @Override
     public void diagnoseBuiltin() throws Exception {
-        super.diagnoseBuiltin();
+        // super.diagnoseBuiltin();
 
-        sweepChimney();
+        if (blacklistedBuiltins.contains(builtinName)) {
+            print(1, "Builtin '" + builtinName + "' blacklisted for chimney-sweeping");
+        } else {
+            sweepChimney();
+        }
     }
 
     @Override
     protected void diagnosePipeline(int i) {
         super.diagnosePipeline(i);
 
-        System.out.println(" Samples:");
-        System.out.println(argSamples.get(i));
+        print(1, " Samples:");
+        print(1, argSamples.get(i));
 
         if (diagSuite.diagConfig.performPipelineSelfTest) {
             checkPipelines(i);
@@ -221,7 +252,7 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
 
     @SuppressWarnings({"rawtypes", "unchecked"})
     private List<Samples<?>> createSamples() {
-        DefaultArgsExtractor defArgExt = new DefaultArgsExtractor(diagSuite.fastRSession);
+        DefaultArgsExtractor defArgExt = new DefaultArgsExtractor(diagSuite.fastRSession, msg -> print(1, msg));
         Map<String, Samples<?>> defaultArgs = defArgExt.extractDefaultArgs(builtinName);
 
         List<Samples<?>> as = new ArrayList<>();
@@ -276,15 +307,15 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
         if (cn != null) {
             Samples<?> samples = argSamples.get(i);
             if (samples.positiveSamples().isEmpty() && samples.negativeSamples().isEmpty()) {
-                System.out.println("No samples");
+                print(1, "No samples");
             } else {
                 testPipeline(cn, samples);
-                System.out.println("Pipeline check OK (" + samples.positiveSamples().size() + "," + samples.negativeSamples().size() + ")");
+                print(1, "Pipeline check OK (" + samples.positiveSamples().size() + "," + samples.negativeSamples().size() + ")");
             }
         }
     }
 
-    private static void testPipeline(CastNode cn, Samples<?> samples) {
+    private void testPipeline(CastNode cn, Samples<?> samples) {
         NodeHandle<CastNode> argCastNodeHandle = TestUtilities.createHandle(cn, (node, args) -> {
             return node.execute(args[0]);
         });
@@ -293,7 +324,7 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
             try {
                 argCastNodeHandle.call(sample);
             } catch (UnsupportedSpecializationException e) {
-                System.out.println("Warning: No specialization to handle arg " + sample + " : " + e.getMessage());
+                print(1, "Warning: No specialization to handle arg " + sample + " : " + e.getMessage());
             } catch (Exception e) {
                 e.printStackTrace();
                 fail("Unexpectedly negative sample: " + sample);
@@ -338,36 +369,24 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
                             a -> a != null && !"".equals(a)).collect(Collectors.toSet());
             Set<RList> args = validArgs.stream().map(a -> evalValidArgs(a, vm)).filter(a -> a != null).collect(Collectors.toSet());
 
-            if (args.isEmpty()) {
-                Object[] nullArgs = new Object[this.argLength];
-                Arrays.fill(nullArgs, RNull.instance);
-                args = Collections.singleton(RDataFactory.createList(nullArgs));
-                System.out.println("No suitable test snippets found. Using the default RNull argument list");
-            }
-
             return args;
         } finally {
             vm.dispose();
         }
     }
 
-    private static RList evalValidArgs(String argsExpr, PolyglotEngine vm) {
+    private RList evalValidArgs(String argsExpr, PolyglotEngine vm) {
         try {
             Value eval = vm.eval(RSource.fromTextInternal(argsExpr, RSource.Internal.UNIT_TEST));
             RList args = (RList) eval.get();
             return args;
         } catch (Exception e) {
-            System.out.println("Warning: Cannot parse arguments: " + argsExpr);
+            print(1, "Warning: Cannot parse arguments: " + argsExpr);
             return null;
         }
     }
 
-    private void sweepChimney() throws IOException {
-        System.out.println("++++++++++++++++++++++");
-        System.out.println("+  Chimney-sweeping  +");
-        System.out.println("++++++++++++++++++++++");
-        System.out.println();
-
+    private void sweepChimney() {
         boolean useDiagonalGen;
 
         long totalCombinations = calculateNumOfSampleCombinations(argSamples);
@@ -389,14 +408,11 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
 
         List<List<Object>> generatedCombinations = generateSampleArgCombinations(argSamples, useDiagonalGen);
 
-        System.out.println("Springboard argument lists: " + validArgsList.size());
-        System.out.println("Used sample combinations: " + generatedCombinations.size() + " (from total " + totalCombinations + ")");
-        System.out.println("Sweeps to perform: " + generatedCombinations.size() * validArgsList.size());
+        print(1, "Springboard argument lists: " + validArgsList.size());
+        print(1, "Used sample combinations: " + generatedCombinations.size() + " (from total " + totalCombinations + ")");
 
-        System.out.println();
-        System.out.println();
-        System.out.println("Press Enter to continue ...");
-        System.in.read();
+        int sweepsToPerform = Math.min(generatedCombinations.size() * validArgsList.size(), diagSuite.diagConfig.maxSweeps);
+        print(0, "Sweeps to perform: " + sweepsToPerform);
 
         evalArgsWithSampleCombinations(generatedCombinations);
     }
@@ -404,8 +420,12 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
     private void evalBuiltin(RList validArgs, List<List<Object>> argSampleCombinations) {
         List<List<Object>> mergedSampleAndValidArgs = mergeValidAndSampleArgs(validArgs, argSampleCombinations);
 
+        boolean isOriginal = true;
         for (List<Object> evalArgs : mergedSampleAndValidArgs) {
-            evalBuiltin(evalArgs);
+            if (!evalBuiltin(evalArgs, isOriginal)) {
+                return;
+            }
+            isOriginal = false;
         }
 
     }
@@ -415,7 +435,11 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
         validArgsList.forEach(validArgs -> evalBuiltin(validArgs, argSampleCombinations));
     }
 
-    private void evalBuiltin(List<Object> args) {
+    private boolean evalBuiltin(List<Object> args, boolean isOriginal) {
+        if (sweepCounter > diagSuite.diagConfig.maxSweeps) {
+            return false;
+        }
+
         StringBuilder sb = new StringBuilder();
         try {
             for (int i = 0; i < args.size(); i++) {
@@ -437,13 +461,17 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
             }
 
             String call;
-            switch (kind) {
-                case INTERNAL:
-                    call = ".Internal(" + builtinName + "(" + sb + "))";
-                    break;
-                default:
-                    call = builtinName + "(" + sb + ")";
-                    break;
+            if ("(".equals(builtinName)) {
+                call = "(" + sb + ")";
+            } else {
+                switch (kind) {
+                    case INTERNAL:
+                        call = ".Internal(" + builtinName + "(" + sb + "))";
+                        break;
+                    default:
+                        call = builtinName + "(" + sb + ")";
+                        break;
+                }
             }
 
             String output;
@@ -456,16 +484,24 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
 
             List<String> outputPair = Arrays.asList(output, outputGnu);
 
+            if (isOriginal && !output.equals(outputGnu)) {
+                // The original test may not be passing (e.g. marked by Ignored.Inknown etc.).
+                // Skip these arguments.
+                System.out.print('I');
+                print(1, "Ignoring original test arguments: " + sb);
+                return false;
+            }
+
             if (compareOutputs(output, outputGnu)) {
                 System.out.print('.');
             } else if (!printedOutputPairs.contains(outputPair)) {
-                System.out.println("\n#" + sweepCounter + "> " + call);
-                System.out.println("\n====== FastR output ======");
-                System.out.println(output);
+                print(0, "\n#" + sweepCounter + "> " + call);
+                print(0, "\n====== FastR output ======");
+                print(0, output);
 
-                System.out.println("====== GnuR output ======");
-                System.out.println(outputGnu);
-                System.out.println("==========================");
+                print(0, "====== GnuR output ======");
+                print(0, outputGnu);
+                print(0, "==========================");
 
                 printedOutputPairs.add(outputPair);
             } else {
@@ -473,17 +509,17 @@ class ChimneySweeping extends SingleBuiltinDiagnostics {
             }
 
         } catch (Throwable e) {
-            // throw new RuntimeException(e);
-            // e.printStackTrace();
             if (!printedErrors.contains(e.getMessage())) {
                 String call = ".Internal(" + builtinName + "(" + sb + "))";
-                System.out.println("\n[" + sweepCounter + "]> " + call);
-                System.out.println("ERROR: " + e.getMessage());
+                print(0, "\n[" + sweepCounter + "]> " + call);
+                print(0, "ERROR: " + e.getMessage());
                 printedErrors.add(e.getMessage());
             }
         } finally {
             sweepCounter++;
         }
+
+        return true;
     }
 
     private boolean compareOutputs(String output, String outputGnu) {
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/DefaultArgsExtractor.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/DefaultArgsExtractor.java
index c11be9eb79..a4551f6255 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/DefaultArgsExtractor.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/DefaultArgsExtractor.java
@@ -26,6 +26,8 @@ import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
 
 import com.oracle.truffle.api.source.Source;
 import com.oracle.truffle.api.vm.PolyglotEngine;
@@ -47,20 +49,24 @@ import com.oracle.truffle.r.test.generate.FastRSession;
 class DefaultArgsExtractor {
 
     private final FastRSession fastRSession;
+    private final Consumer<Object> printer;
 
-    DefaultArgsExtractor(FastRSession fastRSession) {
+    DefaultArgsExtractor(FastRSession fastRSession, Consumer<Object> printer) {
         this.fastRSession = fastRSession;
+        this.printer = printer;
     }
 
     Map<String, Samples<?>> extractDefaultArgs(String functionName) {
         final PolyglotEngine vm = fastRSession.checkContext(null).createVM();
 
+        HashMap<String, Samples<?>> samplesMap = new HashMap<>();
         try {
-            HashMap<String, Samples<?>> samplesMap = new HashMap<>();
 
             Source source = RSource.fromTextInternal("formals(" + functionName + ")",
                             RSource.Internal.UNIT_TEST);
+
             Value defArgVal = vm.eval(source);
+
             if (defArgVal.get() instanceof RPairList) {
                 RPairList formals = (RPairList) defArgVal.get();
                 RStringVector names = formals.getNames(null);
@@ -75,8 +81,8 @@ class DefaultArgsExtractor {
                             Value eval = vm.eval(RSource.fromTextInternal(deparsedDefVal,
                                             RSource.Internal.UNIT_TEST));
                             defVal = eval.get();
-                        } catch (Exception e) {
-                            System.out.println("Warning: Unable to evaluate the default value of argument " + name + ". Expression: " + deparsedDefVal);
+                        } catch (Throwable t) {
+                            printer.accept("Warning: Unable to evaluate the default value of argument " + name + ". Expression: " + deparsedDefVal);
                             continue;
                         }
 
@@ -106,11 +112,12 @@ class DefaultArgsExtractor {
 
             }
 
-            return samplesMap;
-        } catch (Exception e) {
-            throw new RuntimeException(e);
+        } catch (Throwable t) {
+            printer.accept("Warning: Unable to evaluate formal arguments of function " + functionName);
         } finally {
             vm.dispose();
         }
+
+        return samplesMap;
     }
 }
diff --git a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/RBuiltinDiagnostics.java b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/RBuiltinDiagnostics.java
index 86bcced497..274b3ae20a 100644
--- a/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/RBuiltinDiagnostics.java
+++ b/com.oracle.truffle.r.nodes.test/src/com/oracle/truffle/r/nodes/test/RBuiltinDiagnostics.java
@@ -63,11 +63,14 @@ import com.oracle.truffle.r.runtime.nodes.RNode;
 
 public class RBuiltinDiagnostics {
 
+    private static final String OUTPUT_MAX_LEVEL_ARG = "--outMaxLev=";
+
     static class DiagConfig {
         boolean verbose;
         boolean ignoreRNull;
         boolean ignoreRMissing;
         long maxTotalCombinations = 500L;
+        int outputMaxLevel;
     }
 
     static {
@@ -89,6 +92,7 @@ public class RBuiltinDiagnostics {
         diagConfig.verbose = Arrays.stream(args).filter(arg -> "-v".equals(arg)).findFirst().isPresent();
         diagConfig.ignoreRNull = Arrays.stream(args).filter(arg -> "-n".equals(arg)).findFirst().isPresent();
         diagConfig.ignoreRMissing = Arrays.stream(args).filter(arg -> "-m".equals(arg)).findFirst().isPresent();
+        diagConfig.outputMaxLevel = Arrays.stream(args).filter(arg -> arg.startsWith(OUTPUT_MAX_LEVEL_ARG)).map(x -> Integer.parseInt(x.split("=")[1])).findFirst().orElse(Integer.MAX_VALUE);
         return diagConfig;
     }
 
@@ -117,7 +121,7 @@ public class RBuiltinDiagnostics {
             try {
                 bdf = RExtBuiltinDiagFactory.create(builtinName);
             } catch (Exception e) {
-                System.out.println("No builtin '" + builtinName + "' found");
+                print(0, "No builtin '" + builtinName + "' found");
                 return;
             }
         } else {
@@ -126,8 +130,8 @@ public class RBuiltinDiagnostics {
 
         createBuiltinDiagnostics(bdf).diagnoseBuiltin();
 
-        System.out.println("Finished");
-        System.out.println("--------");
+        print(0, "Finished");
+        print(0, "--------");
 
         System.exit(0);
     }
@@ -138,16 +142,23 @@ public class RBuiltinDiagnostics {
             try {
                 createBuiltinDiagnostics(new RIntBuiltinDiagFactory((bf))).diagnoseBuiltin();
             } catch (Exception e) {
-                System.out.println(bf.getName() + " failed: " + e.getMessage());
+                e.printStackTrace();
+                print(0, bf.getName() + " failed: " + e.getMessage());
             }
         }
 
-        System.out.println("Finished");
-        System.out.println("--------");
+        print(0, "Finished");
+        print(0, "--------");
 
         System.exit(0);
     }
 
+    protected void print(int level, Object x) {
+        if (level <= diagConfig.outputMaxLevel) {
+            System.out.println(x);
+        }
+    }
+
     static class SingleBuiltinDiagnostics {
         private final RBuiltinDiagnostics diagSuite;
         final RBuiltinDiagFactory builtinFactory;
@@ -167,7 +178,7 @@ public class RBuiltinDiagnostics {
 
             String[] pn = builtinFactory.getParameterNames();
             this.argLength = pn.length;
-            this.parameterNames = Arrays.stream(pn).map(n -> n.isEmpty() ? null : n).toArray(String[]::new);
+            this.parameterNames = Arrays.stream(pn).map(n -> n == null || n.isEmpty() ? null : n).toArray(String[]::new);
 
             this.castNodes = getCastNodesFromBuiltin();
 
@@ -182,6 +193,10 @@ public class RBuiltinDiagnostics {
             this.nonCoveredArgsSet = combineArguments();
         }
 
+        protected void print(int level, Object x) {
+            diagSuite.print(level, x);
+        }
+
         private HashMap<Method, List<Set<Cast>>> createConvResultTypePerSpecialization() {
             HashMap<Method, List<Set<Cast>>> convResultTypes = new HashMap<>();
 
@@ -214,29 +229,29 @@ public class RBuiltinDiagnostics {
         }
 
         public void diagnoseBuiltin() throws Exception {
-            System.out.println("****************************************************************************");
-            System.out.println("Builtin: " + builtinName + " (" + builtinFactory.getBuiltinNodeClass().getName() + ")");
-            System.out.println("****************************************************************************");
+            print(0, "****************************************************************************");
+            print(0, "Builtin: " + builtinName + " (" + builtinFactory.getBuiltinNodeClass().getName() + ")");
+            print(0, "****************************************************************************");
 
-            System.out.println("Argument cast pipelines binding:");
+            print(0, "Argument cast pipelines binding:");
             for (int i = 0; i < argLength; i++) {
                 diagnosePipeline(i);
             }
 
-            System.out.println("\nUnhandled argument combinations: " + nonCoveredArgsSet.size());
-            System.out.println("");
+            print(0, "\nUnhandled argument combinations: " + nonCoveredArgsSet.size());
+            print(0, "");
 
             printDeadSpecs();
 
             if (diagSuite.diagConfig.verbose) {
                 for (List<Type> uncoveredArgs : nonCoveredArgsSet) {
-                    System.out.println(uncoveredArgs.stream().map(t -> typeName(t)).collect(Collectors.toList()));
+                    print(0, uncoveredArgs.stream().map(t -> typeName(t)).collect(Collectors.toList()));
                 }
             }
         }
 
         private void printDeadSpecs() {
-            System.out.println("Dead specializations: ");
+            print(0, "Dead specializations: ");
             for (Map.Entry<Method, List<Set<Cast>>> resTpPerSpec : convResultTypePerSpec.entrySet()) {
                 List<Set<Cast>> argsCasts = resTpPerSpec.getValue();
                 List<Integer> missingCasts = new ArrayList<>();
@@ -248,33 +263,33 @@ public class RBuiltinDiagnostics {
                 }
 
                 if (!missingCasts.isEmpty()) {
-                    System.out.println("   " + methodName(resTpPerSpec.getKey(), missingCasts));
+                    print(0, "   " + methodName(resTpPerSpec.getKey(), missingCasts));
                 }
             }
 
-            System.out.println("");
+            print(0, "");
         }
 
         protected void diagnosePipeline(int i) {
             TypeExpr argResultSet = argResultSets.get(i);
-            System.out.println("\n Pipeline for '" + parameterNames[i] + "' (arg[" + i + "]):");
-            System.out.println("  Result types union:");
+            print(0, "\n Pipeline for '" + parameterNames[i] + "' (arg[" + i + "]):");
+            print(0, "  Result types union:");
             Set<Type> argSetNorm = argResultSet.normalize();
-            System.out.println("   " + argSetNorm.stream().map(argType -> typeName(argType)).collect(Collectors.toSet()));
-            System.out.println("  Bound result types:");
+            print(0, "   " + argSetNorm.stream().map(argType -> typeName(argType)).collect(Collectors.toSet()));
+            print(0, "  Bound result types:");
             final int curParIndex = i;
             Set<Type> unboundArgTypes = new HashSet<>(argSetNorm);
             for (Map.Entry<Method, List<Set<Cast>>> entry : convResultTypePerSpec.entrySet()) {
                 Set<Cast> argCastInSpec = entry.getValue().get(i);
                 argCastInSpec.stream().forEach(
                                 partialCast -> {
-                                    System.out.println("   " + partialCast.coverage() + " (" + typeName(partialCast.inputType()) + "->" + typeName(partialCast.resultType()) + ")" + " in " +
+                                    print(0, "   " + partialCast.coverage() + " (" + typeName(partialCast.inputType()) + "->" + typeName(partialCast.resultType()) + ")" + " in " +
                                                     methodName(entry.getKey(), Collections.singleton(curParIndex)));
                                     unboundArgTypes.remove(partialCast.inputType());
                                 });
             }
-            System.out.println("  Unbound types:");
-            System.out.println("   " + unboundArgTypes.stream().map(argType -> typeName(argType)).collect(Collectors.toSet()));
+            print(0, "  Unbound types:");
+            print(0, "   " + unboundArgTypes.stream().map(argType -> typeName(argType)).collect(Collectors.toSet()));
 
         }
 
diff --git a/mx.fastr/mx_fastr.py b/mx.fastr/mx_fastr.py
index 2b63d8eecd..c446cbe52e 100644
--- a/mx.fastr/mx_fastr.py
+++ b/mx.fastr/mx_fastr.py
@@ -466,6 +466,8 @@ def rbdiag(args):
     --sweep=total	Performs the 'chimney-sweeping'. The total sample selection method is used.
     --matchLevel=same	Outputs produced by FastR and GnuR must be same (default)
     --matchLevel=error	Outputs are considered matching if none or both outputs contain an error
+    --maxSweeps=N		Sets the maximum number of sweeps
+    --outMaxLev=N		Sets the maximum output detail level for report messages. Use 0 for the basic messages only.
     
 	If no builtin is specified, all registered builtins are diagnosed.
 	An external builtin is specified by the fully qualified name of its node class.
@@ -529,7 +531,7 @@ _commands = {
     'junitnopkgs' : [junit_nopkgs, ['options']],
     'unittest' : [unittest, ['options']],
     'rbcheck' : [rbcheck, '--filter [gnur-only,fastr-only,both,both-diff]'],
-    'rbdiag' : [rbdiag, '(builtin)* [-v] [-n] [-m] [--sweep | --sweep=lite | --sweep=total] [--mnonly] [--noSelfTest] [--matchLevel=same | --matchLevel=error]'],
+    'rbdiag' : [rbdiag, '(builtin)* [-v] [-n] [-m] [--sweep | --sweep=lite | --sweep=total] [--mnonly] [--noSelfTest] [--matchLevel=same | --matchLevel=error] [--maxSweeps=N] [--outMaxLev=N]'],
     'rcmplib' : [rcmplib, ['options']],
     'rrepl' : [rrepl, '[options]'],
     'rembed' : [rembed, '[options]'],
-- 
GitLab