From cd8f8911b53ed0ed946abbbaa446aff67ee7d8d6 Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Sat, 3 Feb 2024 00:36:49 +0100 Subject: [PATCH 01/16] implemented lowering for minvalOp. To be cleaned. --- lib/dialect/CMakeLists.txt | 2 +- lib/dialect/src/ArgumentAnalysisPass.cpp | 316 +++++++++++++++++++++++ lib/dialect/src/EmitFuzzTargetPass.cpp | 72 ++---- lib/dialect/src/Operations.cpp | 8 + lib/dialect/src/Operations.td | 6 + lib/dialect/src/Passes.td | 5 + lib/driver/src/Driver.cpp | 2 + lib/fuzzer/src/fuzz_target.cpp | 3 +- tool/rlc/test/dynamic_analysis.rl | 30 +++ 9 files changed, 391 insertions(+), 53 deletions(-) create mode 100644 lib/dialect/src/ArgumentAnalysisPass.cpp create mode 100644 tool/rlc/test/dynamic_analysis.rl diff --git a/lib/dialect/CMakeLists.txt b/lib/dialect/CMakeLists.txt index b769902a..70b34dd2 100644 --- a/lib/dialect/CMakeLists.txt +++ b/lib/dialect/CMakeLists.txt @@ -1,4 +1,4 @@ -rlcAddLibrary(dialect src/Dialect.cpp src/Types.cpp src/Operations.cpp src/Conversion.cpp src/EmitMain.cpp src/TypeCheck.cpp src/Interfaces.cpp src/SymbolTable.cpp src/ActionArgumentAnalysis.cpp src/LowerActionPass.cpp src/LowerArrayCalls.cpp src/LowerToCf.cpp src/ActionStatementsToCoro.cpp src/OverloadResolver.cpp src/LowerIsOperationsPass.cpp src/InstantiateTemplatesPass.cpp src/LowerAssignPass.cpp src/EmitImplicitAssignPass.cpp src/LowerConstructOpPass.cpp src/EmitImplicitInitPass.cpp src/EmitImplicitDestructorInvocationsPass.cpp src/LowerForFieldOpPass.cpp src/EmitEnumEntitiesPass.cpp src/SortTypeDeclarationsPass.cpp src/AddOutOfBoundsCheckPass.cpp src/PrintIRPass.cpp src/ExtractPreconditionPass.cpp src/LowerAssertsPass.cpp src/AddPreconditionsCheckPass.cpp src/ActionLiveness.cpp src/UncheckedAstToDot.cpp src/RewriteCallSignaturesPass.cpp src/RemoveUselessAllocaPass.cpp src/MembeFunctionsToRegularFunctionsPass.cpp src/LowerInitializerListsPass.cpp src/Enums.cpp src/EmitFuzzTargetPass.cpp) +rlcAddLibrary(dialect src/Dialect.cpp src/Types.cpp src/Operations.cpp src/Conversion.cpp src/EmitMain.cpp src/TypeCheck.cpp src/Interfaces.cpp src/SymbolTable.cpp src/ActionArgumentAnalysis.cpp src/LowerActionPass.cpp src/LowerArrayCalls.cpp src/LowerToCf.cpp src/ActionStatementsToCoro.cpp src/OverloadResolver.cpp src/LowerIsOperationsPass.cpp src/InstantiateTemplatesPass.cpp src/LowerAssignPass.cpp src/EmitImplicitAssignPass.cpp src/LowerConstructOpPass.cpp src/EmitImplicitInitPass.cpp src/EmitImplicitDestructorInvocationsPass.cpp src/LowerForFieldOpPass.cpp src/EmitEnumEntitiesPass.cpp src/SortTypeDeclarationsPass.cpp src/AddOutOfBoundsCheckPass.cpp src/PrintIRPass.cpp src/ExtractPreconditionPass.cpp src/LowerAssertsPass.cpp src/AddPreconditionsCheckPass.cpp src/ActionLiveness.cpp src/UncheckedAstToDot.cpp src/RewriteCallSignaturesPass.cpp src/RemoveUselessAllocaPass.cpp src/MembeFunctionsToRegularFunctionsPass.cpp src/LowerInitializerListsPass.cpp src/Enums.cpp src/EmitFuzzTargetPass.cpp src/ArgumentAnalysisPass.cpp) target_link_libraries(dialect PUBLIC rlc::utils MLIRSupport MLIRDialect MLIRLLVMDialect MLIRLLVMIRTransforms MLIRControlFlowDialect) set(tblgen ${LLVM_BINARY_DIR}/bin/mlir-tblgen) diff --git a/lib/dialect/src/ArgumentAnalysisPass.cpp b/lib/dialect/src/ArgumentAnalysisPass.cpp new file mode 100644 index 00000000..b70e3cc2 --- /dev/null +++ b/lib/dialect/src/ArgumentAnalysisPass.cpp @@ -0,0 +1,316 @@ +#include +#include +#include +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Debug.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/BuiltinOps.h" +#include "mlir/IR/Location.h" +#include "mlir/IR/Region.h" +#include "mlir/IR/Value.h" +#include "mlir/Pass/Pass.h" +#include "mlir/Support/LLVM.h" +#include "rlc/dialect/Dialect.h" +#include "rlc/dialect/Operations.hpp" +#include "rlc/dialect/Types.hpp" + +class ArgumentAnalysis +{ + public: + explicit ArgumentAnalysis(mlir::rlc::FunctionOp op); + + mlir::Value emitMinimumValue(int argIndex, mlir::Value actionEntity, mlir::OpBuilder builder, mlir::Location loc); + + private: + llvm::SmallVector> conjunctions; + mlir::rlc::FunctionOp function; +}; + + +void helper( + llvm::SmallVector> &conjunctions, + llvm::SmallVector currentConjunction, + mlir::Region &precondition + ) { + for (auto expr : llvm::enumerate(currentConjunction)) { + auto definingOp = expr.value().getDefiningOp(); + + // the expr is defined outside of the precondition, it doesn't need to be expanded. + if(definingOp->getParentRegion() != precondition) { + continue; + } + + // TODO add other stopping conditions? + + if(auto andOp = llvm::dyn_cast(definingOp)) { + currentConjunction.erase(currentConjunction.begin() + expr.index()); + currentConjunction.append({andOp.getLhs(), andOp.getRhs()}); + helper(conjunctions, currentConjunction, precondition); + return; + } + + if(auto orOp = llvm::dyn_cast(definingOp)) { + currentConjunction.erase(currentConjunction.begin() + expr.index()); + auto rhs = orOp.getRhs(); + auto lhs = orOp.getLhs(); + + currentConjunction.emplace_back(rhs); + helper(conjunctions, currentConjunction, precondition); + + currentConjunction.pop_back(); + currentConjunction.emplace_back(lhs); + helper(conjunctions, currentConjunction, precondition); + return; + } + } + conjunctions.emplace_back(currentConjunction); +} + +/* + Expands the given value into a disjunction of conjunctions. +*/ +llvm::SmallVector> ExpandToDNF(mlir::Value constraint, mlir::Region &precondition) { + llvm::SmallVector> conjunctions; + helper(conjunctions, {constraint}, precondition); + return conjunctions; +} + + +ArgumentAnalysis::ArgumentAnalysis(mlir::rlc::FunctionOp op) { + auto yield = mlir::dyn_cast(op.getPrecondition().getBlocks().front().back()); + assert(yield->getNumOperands() == 1); + function = op; + conjunctions = ExpandToDNF(yield->getOperand(0), op.getPrecondition()); + llvm::dbgs() << "CONJUNCTIONS: \n"; + for(auto c :conjunctions) { + llvm::dbgs() << "{"; + for (auto t : c) { + llvm::dbgs() << "( "; + t.print(llvm::dbgs()); + llvm::dbgs() << " ),"; + } + llvm::dbgs() << "}\n"; + } + +} + +bool dependsOn(mlir::Value term, mlir::Value argument, mlir::Region &precondition) { + if(term == argument) + return true; + + if(term.isa()) + return false; + + auto definingOp = term.getDefiningOp(); + + if(auto casted = llvm::dyn_cast(definingOp)) + return false; + + if(definingOp->getParentRegion() != precondition) + return false; + + for(auto operand : definingOp->getOperands()) { + if(dependsOn(operand, argument, precondition)) + return true; + } + return false; +} + +mlir::Value recompute(mlir::Value expression, mlir::Value actionEntity, mlir::OpBuilder builder, mlir::Region &precondition) { + if(auto arg = llvm::dyn_cast(expression)) { + assert(arg.getArgNumber() == 0); + return actionEntity; + } + + if (expression.getDefiningOp()->getParentRegion() != precondition) + return expression; + + int resultIndex = -1; + for(auto result : llvm::enumerate(expression.getDefiningOp()->getResults())) { + if(result.value() == expression) + resultIndex = result.index(); + } + assert(resultIndex != -1); + + auto newOp = builder.clone(*expression.getDefiningOp()); + builder.setInsertionPoint(newOp); + for(auto operand : llvm::enumerate(newOp->getOperands())) { + auto newOperand = recompute(operand.value(), actionEntity, builder, precondition); + newOp->setOperand(operand.index(), newOperand); + } + builder.setInsertionPointAfter(newOp); + return newOp->getResult(resultIndex); +} + +//TODO +const int64_t min_int = -800; + +mlir::Value findMinConstraint(mlir::Value constraint, mlir::Value arg, mlir::Value actionEntity, mlir::OpBuilder builder, mlir::Location loc, mlir::Region &precondition) { + auto *definingOp = constraint.getDefiningOp(); + if (definingOp->getOperands().size() != 2) + return builder.create(loc, min_int); + + if (definingOp->getOperand(0) == arg) { + auto rhs = recompute(definingOp->getOperand(1), actionEntity, builder, precondition); + + if (mlir::isa(definingOp)) + { + // could set max here. + } + + if (mlir::isa(definingOp)) + { + // could set max here. + } + + if (mlir::isa(definingOp)) + { + auto one = builder.create(loc, (int64_t)1); + return builder.create(loc, rhs, one); + } + + if (mlir::isa(definingOp)) + { + return rhs; + } + + if (mlir::isa(definingOp)) + { + return rhs; + } + } + + if(definingOp->getOperand(1) == arg) { + auto lhs = recompute(definingOp->getOperand(0), actionEntity, builder, precondition); + + if (mlir::isa(definingOp)) + { + auto one = builder.create(loc, (int64_t)1); + return builder.create(loc, lhs, one); + } + + if (mlir::isa(definingOp)) + { + return lhs; + } + + if (mlir::isa(definingOp)) + { + // could set max here. + } + + if (mlir::isa(definingOp)) + { + // could set max here. + } + + if (mlir::isa(definingOp)) + { + return lhs; + } + } + + return builder.create(loc, min_int); +} + +mlir::Value ArgumentAnalysis::emitMinimumValue(int argIndex, mlir::Value actionEntity, mlir::OpBuilder builder, mlir::Location loc) { + auto arg = function.getPrecondition().getArgument(argIndex); + assert(arg.getType().isa() && "Expected an integer."); + auto minVal = builder.create(loc, arg.getType()); + auto minInt = builder.create(loc, min_int); + builder.create(loc, minVal, minInt); + + for (auto conjunction : conjunctions) { + llvm::SmallVector constraints; + llvm::SmallVector conditions; + for(auto term : conjunction) { + if(dependsOn(term, arg, function.getPrecondition())) { + constraints.emplace_back(term); + } else { + conditions.emplace_back(term); + } + } + + if(constraints.size() > 0) { + llvm::dbgs() << "Well here we are \n"; + for (auto t : constraints) { + llvm::dbgs() << "( "; + t.print(llvm::dbgs()); + llvm::dbgs() << " ),"; + } + llvm::dbgs() << "\n"; + + auto ifStatement = builder.create(loc); + builder.createBlock(&ifStatement.getElseBranch()); + builder.create(loc); + + builder.createBlock(&ifStatement.getCondition()); + mlir::Value condition = builder.create(loc, true); + for( auto c : conditions) { + auto recomputed = recompute(c, actionEntity, builder, function.getPrecondition()); + condition = builder.create(loc, condition, recomputed); + } + + builder.create(loc, condition); + + builder.createBlock(&ifStatement.getTrueBranch()); + for(auto constraint : constraints) { + auto imposed_min = findMinConstraint(constraint, arg, actionEntity, builder, loc, function.getPrecondition()); + auto innerIfStatement = builder.create(loc); + builder.createBlock(&innerIfStatement.getElseBranch()); + builder.create(loc); + + builder.createBlock(&innerIfStatement.getCondition()); + auto g = builder.create(loc, imposed_min, minVal); + builder.create(loc, g.getResult()); + + builder.createBlock(&innerIfStatement.getTrueBranch()); + builder.create(loc, minVal, imposed_min); + builder.create(loc); + builder.setInsertionPointAfter(innerIfStatement); + } + builder.create(loc); + builder.setInsertionPointAfter(ifStatement); + } + } + return minVal; +} + +namespace mlir::rlc +{ +#define GEN_PASS_DEF_ARGUMENTANALYSISPASS +#include "rlc/dialect/Passes.inc" + struct ArgumentAnalysisPass + : public impl::ArgumentAnalysisPassBase + { + void getDependentDialects(mlir::DialectRegistry& registry) const override + { + registry.insert(); + } + + void runOnOperation() override + { + ModuleOp module = getOperation(); + + llvm::SmallVector minValOps; + module.walk([&](mlir::rlc::MinValOp op) { + minValOps.emplace_back(op); + }); + + for (auto minVal : minValOps) + { + auto function = llvm::dyn_cast(minVal.getFunction().getDefiningOp()); + assert( function && "Expected a FunctionOp"); + ArgumentAnalysis analysis(function); + auto min = analysis.emitMinimumValue(minVal.getArgumentIndex(), minVal.getActionEntity(), mlir::OpBuilder(minVal), minVal->getLoc()); + minVal.replaceAllUsesWith(min); + minVal.erase(); + } + + //module.dump(); + } + }; + +} // namespace mlir::rlc \ No newline at end of file diff --git a/lib/dialect/src/EmitFuzzTargetPass.cpp b/lib/dialect/src/EmitFuzzTargetPass.cpp index c5d1d8a1..b3332fc9 100644 --- a/lib/dialect/src/EmitFuzzTargetPass.cpp +++ b/lib/dialect/src/EmitFuzzTargetPass.cpp @@ -207,7 +207,7 @@ static mlir::Value emitChosenActionDeclaration( where arg1,arg2,... are the arguments of the subaction. */ static llvm::SmallVector emitSubactionArgumentDeclarations( - mlir::Value subaction, + mlir::Value subactionFunction, mlir::Value actionEntity, mlir::Value pickArgument, mlir::Value print, @@ -216,67 +216,37 @@ static llvm::SmallVector emitSubactionArgumentDeclarations( mlir::rlc::ModuleBuilder &moduleBuilder ) { auto ip = builder.saveInsertionPoint(); - auto actionStatements = moduleBuilder.actionFunctionValueToActionStatement(subaction); - llvm::SmallVector arguments; // declare the arguments - auto inputs = mlir::dyn_cast(subaction.getType()).getInputs(); + auto inputs = mlir::dyn_cast(subactionFunction.getType()).getInputs(); // The first input is the actionEntity, which does not need to be declared here. - auto inputsExcludingActionEntity = llvm::drop_begin(inputs); + auto inputsExcludingActionEntity = llvm::drop_begin(llvm::enumerate(inputs)); for(auto inputType : inputsExcludingActionEntity) { - - assert(inputType.isa() && "Fuzzing can only handle integer arguments for now."); + assert(inputType.value().isa() && "Fuzzing can only handle integer arguments for now."); - auto argDecl = builder.create( + auto input_min = builder.create( loc, - inputType + mlir::rlc::IntegerType::getInt64(builder.getContext()), + subactionFunction, + (uint8_t)inputType.index(), + actionEntity ); - arguments.emplace_back(argDecl.getResult()); - } - - // for each action statement, if the resumeIndex matches that of the action statement, assign arguments respecting the action statement's constraints. - auto storedResumptionPoint = builder.create( + auto input_max = builder.create( loc, - actionEntity, - 0 + (int64_t)10 // TODO ); - for(auto *actionStatement : actionStatements) { - auto cast = mlir::dyn_cast(*actionStatement); - auto ifStatement = builder.create(loc); - builder.createBlock(&ifStatement.getCondition()); - auto subactionResumptionPoint = builder.create(loc, (int64_t) cast.getResumptionPoint()); - auto eq = builder.create(loc, storedResumptionPoint, subactionResumptionPoint); - builder.create(loc, eq.getResult()); - - builder.createBlock(&ifStatement.getTrueBranch()); - mlir::rlc::ActionArgumentAnalysis analysis(cast); - - for(auto input : llvm::enumerate(cast.getPrecondition().getArguments())) { - auto input_min = builder.create( - loc, - analysis.getBoundsOf(input.value()).getMin() - ); - auto input_max = builder.create( - loc, - analysis.getBoundsOf(input.value()).getMax() - ); - auto call = builder.create( - loc, - pickArgument, - false, - mlir::ValueRange({input_min.getResult(), input_max.getResult()}) - ); - // print the value picked for the argument for debugging purposes. - builder.create(loc, print, false, call.getResult(0)); - builder.create(loc, arguments[input.index()], call.getResult(0)); - } - builder.create(loc); - - builder.createBlock(&ifStatement.getElseBranch()); - builder.create(loc); - builder.setInsertionPointAfter(ifStatement); + auto call = builder.create( + loc, + pickArgument, + false, + mlir::ValueRange({input_min.getResult(), input_max.getResult()}) + ); + // print the value picked for the argument for debugging purposes. + builder.create(loc, print, false, call.getResult(0)); + arguments.emplace_back(call->getResult(0)); } + builder.restoreInsertionPoint(ip); return arguments; } diff --git a/lib/dialect/src/Operations.cpp b/lib/dialect/src/Operations.cpp index a22d9309..c47d0bd9 100644 --- a/lib/dialect/src/Operations.cpp +++ b/lib/dialect/src/Operations.cpp @@ -20,8 +20,10 @@ limitations under the License. #include "llvm/ADT/StringExtras.h" #include "mlir/IR/BuiltinTypes.h" #include "mlir/IR/OpDefinition.h" +#include "mlir/Support/LogicalResult.h" #include "rlc/dialect/ActionLiveness.hpp" #include "rlc/dialect/Dialect.h" +#include "rlc/dialect/SymbolTable.h" #include "rlc/utils/IRange.hpp" #define GET_OP_CLASSES #include "./Operations.inc" @@ -931,6 +933,12 @@ mlir::LogicalResult mlir::rlc::CanOp::typeCheck( return mlir::success(); } +mlir::LogicalResult mlir::rlc::MinValOp::typeCheck( + mlir::rlc::ModuleBuilder &builder) +{ + return mlir::success(); +} + mlir::LogicalResult mlir::rlc::ForFieldStatement::typeCheck( mlir::rlc::ModuleBuilder &builder) { diff --git a/lib/dialect/src/Operations.td b/lib/dialect/src/Operations.td index 9e92d836..a64e4f72 100644 --- a/lib/dialect/src/Operations.td +++ b/lib/dialect/src/Operations.td @@ -1823,3 +1823,9 @@ def RLC_CanOp : RLC_Dialect <"can", [DeclareOpInterfaceMethods]> build($_builder, $_state, resultType, value); }]>]; } + +def RLC_MinValOp : RLC_Dialect <"minval", [DeclareOpInterfaceMethods]> { + let arguments = (ins AnyFunctionType:$function, I8Attr:$argument_index, AnyType:$action_entity); + + let results = (outs RLC_IntegerType:$result); +} diff --git a/lib/dialect/src/Passes.td b/lib/dialect/src/Passes.td index 17fca6fe..7f749389 100644 --- a/lib/dialect/src/Passes.td +++ b/lib/dialect/src/Passes.td @@ -236,3 +236,8 @@ def EmitFuzzTargetPass : Pass<"rlc-emit-fuzz-target-pass", "mlir::ModuleOp"> { "Name of the action to generate a fuzzer for">, ]; } + +def ArgumentAnalysisPass : Pass<"rlc-argument-analysis-pass", "mlir::ModuleOp"> { + let summary = "Lower MinValOp's to expressions that compute the minimum value dynamically."; + let dependentDialects = ["rlc::RLCDialect"]; +} diff --git a/lib/driver/src/Driver.cpp b/lib/driver/src/Driver.cpp index 71295e97..ec5f085d 100644 --- a/lib/driver/src/Driver.cpp +++ b/lib/driver/src/Driver.cpp @@ -114,6 +114,8 @@ namespace mlir::rlc manager.addPass(mlir::rlc::createAddPreconditionsCheckPass()); } + manager.addPass(mlir::rlc::createArgumentAnalysisPass()); + manager.addPass(mlir::rlc::createLowerAssertsPass()); manager.addPass(mlir::rlc::createLowerInitializerListsPass()); diff --git a/lib/fuzzer/src/fuzz_target.cpp b/lib/fuzzer/src/fuzz_target.cpp index d77de541..31dbd185 100644 --- a/lib/fuzzer/src/fuzz_target.cpp +++ b/lib/fuzzer/src/fuzz_target.cpp @@ -59,9 +59,10 @@ extern "C" void rl_fuzzer_get_input__int64_t_r_int64_t(__int64_t *result, const } extern "C" void rl_fuzzer_pick_argument__int64_t_int64_t_r_int64_t(__int64_t *result, const __int64_t *min, __int64_t *max) { - // printf("Picking an integer argument in range [%ld, %ld]\n", *min, *max); + printf("Picking an integer argument in range [%ld, %ld]\n", *min, *max); int num_bits = ceil(log2(*max - *min + 1)); *result = std::abs(consume_bits(fuzz_input, num_bits, byte_offset, bit_offset)) % (*max - *min + 1) + *min; + printf("Picked %ld\n", *result); } extern "C" void rl_fuzzer_is_input_long_enough__r_bool(__int8_t *result) { diff --git a/tool/rlc/test/dynamic_analysis.rl b/tool/rlc/test/dynamic_analysis.rl new file mode 100644 index 00000000..09a86034 --- /dev/null +++ b/tool/rlc/test/dynamic_analysis.rl @@ -0,0 +1,30 @@ +# RUN: rlc %s -o %t -i %stdlib --fuzzer +# RUN: %t +import fuzzer.cpp_functions +import fuzzer.utils + +fun crash_on_five(Int input) -> Int {input != 5}: + return 0 + +fun plus_three(Int input) -> Int: + return input + 3 + +act play() -> Play: + frm current = 0 + act subact(Int x) {x > -5, x < 10} + current = x + + act subact(Int x) {x > (plus_three(current) + 8) / 2, x < 10} + current = x + + crash_on_five(5) + + while current != 7: + act subact(Int x) {x > current, x < 10} + current = x + act that(Int a) {a >= 0, a < 100} + crash_on_five(a) + + + + From 9cb5f6ddb274df851756acf25ff818650e5160c8 Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Sat, 3 Feb 2024 14:00:12 +0100 Subject: [PATCH 02/16] added support for max --- lib/dialect/src/ArgumentAnalysisPass.cpp | 137 +++++++++++++++++------ lib/dialect/src/EmitFuzzTargetPass.cpp | 16 +-- lib/dialect/src/Operations.cpp | 2 +- lib/dialect/src/Operations.td | 4 +- tool/rlc/test/dynamic_analysis.rl | 16 ++- 5 files changed, 121 insertions(+), 54 deletions(-) diff --git a/lib/dialect/src/ArgumentAnalysisPass.cpp b/lib/dialect/src/ArgumentAnalysisPass.cpp index b70e3cc2..cb498547 100644 --- a/lib/dialect/src/ArgumentAnalysisPass.cpp +++ b/lib/dialect/src/ArgumentAnalysisPass.cpp @@ -16,12 +16,17 @@ #include "rlc/dialect/Operations.hpp" #include "rlc/dialect/Types.hpp" +struct DeducedConstraints { + mlir::Value min; + mlir::Value max; +}; + class ArgumentAnalysis { public: explicit ArgumentAnalysis(mlir::rlc::FunctionOp op); - mlir::Value emitMinimumValue(int argIndex, mlir::Value actionEntity, mlir::OpBuilder builder, mlir::Location loc); + DeducedConstraints deduceConstraints(int argIndex, mlir::Value actionEntity, mlir::OpBuilder builder, mlir::Location loc); private: llvm::SmallVector> conjunctions; @@ -146,39 +151,60 @@ mlir::Value recompute(mlir::Value expression, mlir::Value actionEntity, mlir::Op //TODO const int64_t min_int = -800; +const int64_t max_int = 800; -mlir::Value findMinConstraint(mlir::Value constraint, mlir::Value arg, mlir::Value actionEntity, mlir::OpBuilder builder, mlir::Location loc, mlir::Region &precondition) { + +DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value arg, mlir::Value actionEntity, mlir::OpBuilder builder, mlir::Location loc, mlir::Region &precondition) { auto *definingOp = constraint.getDefiningOp(); if (definingOp->getOperands().size() != 2) - return builder.create(loc, min_int); + return { + builder.create(loc, min_int), + builder.create(loc, max_int) + }; if (definingOp->getOperand(0) == arg) { auto rhs = recompute(definingOp->getOperand(1), actionEntity, builder, precondition); if (mlir::isa(definingOp)) { - // could set max here. + auto one = builder.create(loc, (int64_t)1); + return { + builder.create(loc, min_int), + builder.create(loc, rhs, one) + }; } if (mlir::isa(definingOp)) { - // could set max here. + return { + rhs, + builder.create(loc, max_int) + }; } if (mlir::isa(definingOp)) { auto one = builder.create(loc, (int64_t)1); - return builder.create(loc, rhs, one); + return { + builder.create(loc, rhs, one), + builder.create(loc, max_int) + }; } if (mlir::isa(definingOp)) { - return rhs; + return { + rhs, + builder.create(loc, max_int) + }; } if (mlir::isa(definingOp)) { - return rhs; + return { + rhs, + rhs + }; } } @@ -188,40 +214,64 @@ mlir::Value findMinConstraint(mlir::Value constraint, mlir::Value arg, mlir::Val if (mlir::isa(definingOp)) { auto one = builder.create(loc, (int64_t)1); - return builder.create(loc, lhs, one); + return { + builder.create(loc, lhs, one), + builder.create(loc, max_int), + }; } if (mlir::isa(definingOp)) { - return lhs; + return { + lhs, + builder.create(loc, max_int), + }; } if (mlir::isa(definingOp)) { - // could set max here. + auto one = builder.create(loc, (int64_t)1); + return { + builder.create(loc, min_int), + builder.create(loc, lhs, one) + }; } if (mlir::isa(definingOp)) { - // could set max here. + return { + builder.create(loc, min_int), + lhs + }; } if (mlir::isa(definingOp)) { - return lhs; + return { + lhs, + lhs + }; } } - return builder.create(loc, min_int); + return { + builder.create(loc, min_int), + builder.create(loc, max_int) + }; } -mlir::Value ArgumentAnalysis::emitMinimumValue(int argIndex, mlir::Value actionEntity, mlir::OpBuilder builder, mlir::Location loc) { +DeducedConstraints ArgumentAnalysis::deduceConstraints(int argIndex, mlir::Value actionEntity, mlir::OpBuilder builder, mlir::Location loc) { auto arg = function.getPrecondition().getArgument(argIndex); assert(arg.getType().isa() && "Expected an integer."); + auto minVal = builder.create(loc, arg.getType()); auto minInt = builder.create(loc, min_int); builder.create(loc, minVal, minInt); + auto maxVal = builder.create(loc, arg.getType()); + auto maxInt = builder.create(loc, max_int); + builder.create(loc, maxVal, maxInt); + for (auto conjunction : conjunctions) { llvm::SmallVector constraints; llvm::SmallVector conditions; @@ -257,25 +307,41 @@ mlir::Value ArgumentAnalysis::emitMinimumValue(int argIndex, mlir::Value actionE builder.createBlock(&ifStatement.getTrueBranch()); for(auto constraint : constraints) { - auto imposed_min = findMinConstraint(constraint, arg, actionEntity, builder, loc, function.getPrecondition()); - auto innerIfStatement = builder.create(loc); - builder.createBlock(&innerIfStatement.getElseBranch()); + auto imposedConstraints = findImposedConstraints(constraint, arg, actionEntity, builder, loc, function.getPrecondition()); + + // if the minimum imposed by this constraint is greater than the current minimum, set the current minimum. + auto minIfStatement = builder.create(loc); + builder.createBlock(&minIfStatement.getElseBranch()); builder.create(loc); - builder.createBlock(&innerIfStatement.getCondition()); - auto g = builder.create(loc, imposed_min, minVal); + builder.createBlock(&minIfStatement.getCondition()); + auto g = builder.create(loc, imposedConstraints.min, minVal); builder.create(loc, g.getResult()); - builder.createBlock(&innerIfStatement.getTrueBranch()); - builder.create(loc, minVal, imposed_min); + builder.createBlock(&minIfStatement.getTrueBranch()); + builder.create(loc, minVal, imposedConstraints.min); builder.create(loc); - builder.setInsertionPointAfter(innerIfStatement); + builder.setInsertionPointAfter(minIfStatement); + + // if the maximum imposed by this constraint is less than the current maximum, set the current maximum. + auto maxIfStatement = builder.create(loc); + builder.createBlock(&maxIfStatement.getElseBranch()); + builder.create(loc); + + builder.createBlock(&maxIfStatement.getCondition()); + auto l = builder.create(loc, imposedConstraints.max, maxVal); + builder.create(loc, l.getResult()); + + builder.createBlock(&maxIfStatement.getTrueBranch()); + builder.create(loc, maxVal, imposedConstraints.max); + builder.create(loc); + builder.setInsertionPointAfter(maxIfStatement); } builder.create(loc); builder.setInsertionPointAfter(ifStatement); } } - return minVal; + return {minVal, maxVal}; } namespace mlir::rlc @@ -294,22 +360,25 @@ namespace mlir::rlc { ModuleOp module = getOperation(); - llvm::SmallVector minValOps; - module.walk([&](mlir::rlc::MinValOp op) { - minValOps.emplace_back(op); + + llvm::SmallVector argConstraintsOps; + module.walk([&](mlir::rlc::ArgConstraintsOp op) { + argConstraintsOps.emplace_back(op); }); - for (auto minVal : minValOps) + for (auto argConstraints: argConstraintsOps) { - auto function = llvm::dyn_cast(minVal.getFunction().getDefiningOp()); + mlir::OpBuilder builder(argConstraints); + mlir::Location loc = argConstraints->getLoc(); + + auto function = llvm::dyn_cast(argConstraints.getFunction().getDefiningOp()); assert( function && "Expected a FunctionOp"); ArgumentAnalysis analysis(function); - auto min = analysis.emitMinimumValue(minVal.getArgumentIndex(), minVal.getActionEntity(), mlir::OpBuilder(minVal), minVal->getLoc()); - minVal.replaceAllUsesWith(min); - minVal.erase(); + auto deduced = analysis.deduceConstraints(argConstraints.getArgumentIndex(), argConstraints.getActionEntity(), builder, loc); + argConstraints.getMin().replaceAllUsesWith(deduced.min); + argConstraints.getMax().replaceAllUsesWith(deduced.max); + argConstraints.erase(); } - - //module.dump(); } }; diff --git a/lib/dialect/src/EmitFuzzTargetPass.cpp b/lib/dialect/src/EmitFuzzTargetPass.cpp index b3332fc9..fbd6008b 100644 --- a/lib/dialect/src/EmitFuzzTargetPass.cpp +++ b/lib/dialect/src/EmitFuzzTargetPass.cpp @@ -31,6 +31,7 @@ limitations under the License. #include "mlir/IR/OperationSupport.h" #include "mlir/IR/PatternMatch.h" #include "mlir/IR/Region.h" +#include "mlir/IR/TypeRange.h" #include "mlir/IR/Value.h" #include "mlir/IR/ValueRange.h" #include "mlir/Pass/Pass.h" @@ -224,23 +225,22 @@ static llvm::SmallVector emitSubactionArgumentDeclarations( auto inputsExcludingActionEntity = llvm::drop_begin(llvm::enumerate(inputs)); for(auto inputType : inputsExcludingActionEntity) { assert(inputType.value().isa() && "Fuzzing can only handle integer arguments for now."); - - auto input_min = builder.create( + auto argConstraints = builder.create( loc, - mlir::rlc::IntegerType::getInt64(builder.getContext()), + mlir::TypeRange({ + mlir::rlc::IntegerType::getInt64(builder.getContext()), + mlir::rlc::IntegerType::getInt64(builder.getContext()) + }), subactionFunction, (uint8_t)inputType.index(), actionEntity ); - auto input_max = builder.create( - loc, - (int64_t)10 // TODO - ); + auto call = builder.create( loc, pickArgument, false, - mlir::ValueRange({input_min.getResult(), input_max.getResult()}) + mlir::ValueRange({argConstraints.getMin(), argConstraints.getMax()}) ); // print the value picked for the argument for debugging purposes. builder.create(loc, print, false, call.getResult(0)); diff --git a/lib/dialect/src/Operations.cpp b/lib/dialect/src/Operations.cpp index c47d0bd9..09b3ed63 100644 --- a/lib/dialect/src/Operations.cpp +++ b/lib/dialect/src/Operations.cpp @@ -933,7 +933,7 @@ mlir::LogicalResult mlir::rlc::CanOp::typeCheck( return mlir::success(); } -mlir::LogicalResult mlir::rlc::MinValOp::typeCheck( +mlir::LogicalResult mlir::rlc::ArgConstraintsOp::typeCheck( mlir::rlc::ModuleBuilder &builder) { return mlir::success(); diff --git a/lib/dialect/src/Operations.td b/lib/dialect/src/Operations.td index a64e4f72..128d82bf 100644 --- a/lib/dialect/src/Operations.td +++ b/lib/dialect/src/Operations.td @@ -1824,8 +1824,8 @@ def RLC_CanOp : RLC_Dialect <"can", [DeclareOpInterfaceMethods]> }]>]; } -def RLC_MinValOp : RLC_Dialect <"minval", [DeclareOpInterfaceMethods]> { +def RLC_ArgConstraintsOp : RLC_Dialect <"argConstraints", [DeclareOpInterfaceMethods]> { let arguments = (ins AnyFunctionType:$function, I8Attr:$argument_index, AnyType:$action_entity); - let results = (outs RLC_IntegerType:$result); + let results = (outs RLC_IntegerType:$min, RLC_IntegerType: $max); } diff --git a/tool/rlc/test/dynamic_analysis.rl b/tool/rlc/test/dynamic_analysis.rl index 09a86034..030ef0b2 100644 --- a/tool/rlc/test/dynamic_analysis.rl +++ b/tool/rlc/test/dynamic_analysis.rl @@ -9,18 +9,16 @@ fun crash_on_five(Int input) -> Int {input != 5}: fun plus_three(Int input) -> Int: return input + 3 +fun factorial(Int input) -> Int: + if input <= 0: + return 1 + else: + return input * factorial(input - 1) + act play() -> Play: frm current = 0 - act subact(Int x) {x > -5, x < 10} - current = x - - act subact(Int x) {x > (plus_three(current) + 8) / 2, x < 10} - current = x - - crash_on_five(5) - while current != 7: - act subact(Int x) {x > current, x < 10} + act subact(Int x) {x > (plus_three(current) + 8) / 2, x < 10 + factorial(current)} current = x act that(Int a) {a >= 0, a < 100} crash_on_five(a) From c31c19fa67f2d80d7cd455b911927f38d55abb70 Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Sat, 3 Feb 2024 18:42:01 +0100 Subject: [PATCH 03/16] recurse into CallOp's to CanOp results. --- lib/dialect/src/ArgumentAnalysisPass.cpp | 256 +++++++++++++---------- lib/dialect/src/EmitFuzzTargetPass.cpp | 2 +- lib/dialect/src/Operations.td | 2 +- lib/driver/src/Driver.cpp | 4 +- 4 files changed, 155 insertions(+), 109 deletions(-) diff --git a/lib/dialect/src/ArgumentAnalysisPass.cpp b/lib/dialect/src/ArgumentAnalysisPass.cpp index cb498547..e29a1514 100644 --- a/lib/dialect/src/ArgumentAnalysisPass.cpp +++ b/lib/dialect/src/ArgumentAnalysisPass.cpp @@ -10,6 +10,7 @@ #include "mlir/IR/Location.h" #include "mlir/IR/Region.h" #include "mlir/IR/Value.h" +#include "mlir/IR/ValueRange.h" #include "mlir/Pass/Pass.h" #include "mlir/Support/LLVM.h" #include "rlc/dialect/Dialect.h" @@ -21,12 +22,18 @@ struct DeducedConstraints { mlir::Value max; }; +enum TermType { + DEPENDS_ON_ARG, + DEPENDS_ON_OTHER_UNKNOWNS, + KNOWN_VALUE +}; + class ArgumentAnalysis { public: explicit ArgumentAnalysis(mlir::rlc::FunctionOp op); - DeducedConstraints deduceConstraints(int argIndex, mlir::Value actionEntity, mlir::OpBuilder builder, mlir::Location loc); + DeducedConstraints deduceConstraints(int argIndex, mlir::ValueRange knownArgs, mlir::OpBuilder builder, mlir::Location loc); private: llvm::SmallVector> conjunctions; @@ -101,32 +108,45 @@ ArgumentAnalysis::ArgumentAnalysis(mlir::rlc::FunctionOp op) { } -bool dependsOn(mlir::Value term, mlir::Value argument, mlir::Region &precondition) { +TermType decideTermType(mlir::Value term, mlir::Value argument, mlir::ValueRange knownArgs, mlir::Region &precondition) { if(term == argument) - return true; + return DEPENDS_ON_ARG; - if(term.isa()) - return false; + if(auto arg = llvm::dyn_cast(term)) { + if(arg.getArgNumber() < knownArgs.size()) + return KNOWN_VALUE; + + return DEPENDS_ON_OTHER_UNKNOWNS; + } auto definingOp = term.getDefiningOp(); if(auto casted = llvm::dyn_cast(definingOp)) - return false; + return KNOWN_VALUE; if(definingOp->getParentRegion() != precondition) - return false; + return KNOWN_VALUE; + + bool dependsOnArg = false; + bool dependsOnOtherUnkowns = false; for(auto operand : definingOp->getOperands()) { - if(dependsOn(operand, argument, precondition)) - return true; + auto operandType = decideTermType(operand, argument, knownArgs, precondition); + if(operandType == DEPENDS_ON_ARG) + dependsOnArg = true; + else if (operandType == DEPENDS_ON_OTHER_UNKNOWNS) + dependsOnOtherUnkowns = true; } - return false; + + if (dependsOnArg) { return DEPENDS_ON_ARG; } + if (dependsOnOtherUnkowns) { return DEPENDS_ON_OTHER_UNKNOWNS; }; + return KNOWN_VALUE; } -mlir::Value recompute(mlir::Value expression, mlir::Value actionEntity, mlir::OpBuilder builder, mlir::Region &precondition) { +mlir::Value recompute(mlir::Value expression, mlir::ValueRange knownArgs, mlir::OpBuilder builder, mlir::Region &precondition) { if(auto arg = llvm::dyn_cast(expression)) { - assert(arg.getArgNumber() == 0); - return actionEntity; + assert(arg.getArgNumber() < knownArgs.size()); + return knownArgs[arg.getArgNumber()]; } if (expression.getDefiningOp()->getParentRegion() != precondition) @@ -142,7 +162,7 @@ mlir::Value recompute(mlir::Value expression, mlir::Value actionEntity, mlir::Op auto newOp = builder.clone(*expression.getDefiningOp()); builder.setInsertionPoint(newOp); for(auto operand : llvm::enumerate(newOp->getOperands())) { - auto newOperand = recompute(operand.value(), actionEntity, builder, precondition); + auto newOperand = recompute(operand.value(), knownArgs, builder, precondition); newOp->setOperand(operand.index(), newOperand); } builder.setInsertionPointAfter(newOp); @@ -154,113 +174,138 @@ const int64_t min_int = -800; const int64_t max_int = 800; -DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value arg, mlir::Value actionEntity, mlir::OpBuilder builder, mlir::Location loc, mlir::Region &precondition) { +DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value arg, mlir::ValueRange knownArgs, mlir::OpBuilder builder, mlir::Location loc, mlir::Region &precondition) { auto *definingOp = constraint.getDefiningOp(); - if (definingOp->getOperands().size() != 2) - return { - builder.create(loc, min_int), - builder.create(loc, max_int) - }; - - if (definingOp->getOperand(0) == arg) { - auto rhs = recompute(definingOp->getOperand(1), actionEntity, builder, precondition); - - if (mlir::isa(definingOp)) - { - auto one = builder.create(loc, (int64_t)1); - return { - builder.create(loc, min_int), - builder.create(loc, rhs, one) - }; - } + if (definingOp->getOperands().size() == 2) { + if (definingOp->getOperand(0) == arg) { + auto rhs = recompute(definingOp->getOperand(1), knownArgs, builder, precondition); + + if (mlir::isa(definingOp)) + { + auto one = builder.create(loc, (int64_t)1); + return { + builder.create(loc, min_int), + builder.create(loc, rhs, one) + }; + } - if (mlir::isa(definingOp)) - { - return { - rhs, - builder.create(loc, max_int) - }; - } + if (mlir::isa(definingOp)) + { + return { + rhs, + builder.create(loc, max_int) + }; + } - if (mlir::isa(definingOp)) - { - auto one = builder.create(loc, (int64_t)1); - return { - builder.create(loc, rhs, one), - builder.create(loc, max_int) - }; - } + if (mlir::isa(definingOp)) + { + auto one = builder.create(loc, (int64_t)1); + return { + builder.create(loc, rhs, one), + builder.create(loc, max_int) + }; + } - if (mlir::isa(definingOp)) - { - return { - rhs, - builder.create(loc, max_int) - }; - } + if (mlir::isa(definingOp)) + { + return { + rhs, + builder.create(loc, max_int) + }; + } - if (mlir::isa(definingOp)) - { - return { - rhs, - rhs - }; + if (mlir::isa(definingOp)) + { + return { + rhs, + rhs + }; + } } - } - if(definingOp->getOperand(1) == arg) { - auto lhs = recompute(definingOp->getOperand(0), actionEntity, builder, precondition); + if(definingOp->getOperand(1) == arg) { + auto lhs = recompute(definingOp->getOperand(0), knownArgs, builder, precondition); - if (mlir::isa(definingOp)) - { - auto one = builder.create(loc, (int64_t)1); - return { - builder.create(loc, lhs, one), - builder.create(loc, max_int), - }; - } + if (mlir::isa(definingOp)) + { + auto one = builder.create(loc, (int64_t)1); + return { + builder.create(loc, lhs, one), + builder.create(loc, max_int), + }; + } - if (mlir::isa(definingOp)) - { - return { - lhs, - builder.create(loc, max_int), - }; - } + if (mlir::isa(definingOp)) + { + return { + lhs, + builder.create(loc, max_int), + }; + } - if (mlir::isa(definingOp)) - { - auto one = builder.create(loc, (int64_t)1); - return { - builder.create(loc, min_int), - builder.create(loc, lhs, one) - }; - } + if (mlir::isa(definingOp)) + { + auto one = builder.create(loc, (int64_t)1); + return { + builder.create(loc, min_int), + builder.create(loc, lhs, one) + }; + } - if (mlir::isa(definingOp)) - { - return { - builder.create(loc, min_int), - lhs - }; - } + if (mlir::isa(definingOp)) + { + return { + builder.create(loc, min_int), + lhs + }; + } - if (mlir::isa(definingOp)) - { - return { - lhs, - lhs - }; + if (mlir::isa(definingOp)) + { + return { + lhs, + lhs + }; + } } } + if( mlir::rlc::CallOp call = llvm::dyn_cast(definingOp)) + { + if( mlir::rlc::CanOp can = llvm::dyn_cast(call.getCallee().getDefiningOp())) { + llvm::dbgs() << "Underlying function\n"; + auto underlyingFunction = llvm::dyn_cast(*can.getCallee().getDefiningOp()); + underlyingFunction.dump(); + + llvm::SmallVector knownArgsOfUnderlyingFunction; + int argIndex = -1; + for(auto current : llvm::enumerate(call.getArgs())) { + // decide which arguments are known (assuming (in general) known arguments are before unknown arguments.) + if( not current.value().isa()) { + auto newExpr = recompute(current.value(), knownArgs, builder, precondition); + knownArgsOfUnderlyingFunction.emplace_back(newExpr); + continue; + } + // and find the index of the argument we're interested in. + if(current.value() == arg) { + argIndex = current.index(); + break; + } + } + assert(argIndex != -1 && "Expecteed to find the argument."); + ArgumentAnalysis analysis(underlyingFunction); + return analysis.deduceConstraints(argIndex, knownArgsOfUnderlyingFunction, builder, loc); + } + } + + return { builder.create(loc, min_int), builder.create(loc, max_int) }; } -DeducedConstraints ArgumentAnalysis::deduceConstraints(int argIndex, mlir::Value actionEntity, mlir::OpBuilder builder, mlir::Location loc) { +DeducedConstraints ArgumentAnalysis::deduceConstraints(int argIndex, mlir::ValueRange knownArgs, mlir::OpBuilder builder, mlir::Location loc) { auto arg = function.getPrecondition().getArgument(argIndex); assert(arg.getType().isa() && "Expected an integer."); @@ -276,9 +321,10 @@ DeducedConstraints ArgumentAnalysis::deduceConstraints(int argIndex, mlir::Value llvm::SmallVector constraints; llvm::SmallVector conditions; for(auto term : conjunction) { - if(dependsOn(term, arg, function.getPrecondition())) { + TermType type = decideTermType(term, arg, knownArgs, function.getPrecondition()); + if(type == DEPENDS_ON_ARG) { constraints.emplace_back(term); - } else { + } else if (type == KNOWN_VALUE){ conditions.emplace_back(term); } } @@ -299,7 +345,7 @@ DeducedConstraints ArgumentAnalysis::deduceConstraints(int argIndex, mlir::Value builder.createBlock(&ifStatement.getCondition()); mlir::Value condition = builder.create(loc, true); for( auto c : conditions) { - auto recomputed = recompute(c, actionEntity, builder, function.getPrecondition()); + auto recomputed = recompute(c, knownArgs, builder, function.getPrecondition()); condition = builder.create(loc, condition, recomputed); } @@ -307,7 +353,7 @@ DeducedConstraints ArgumentAnalysis::deduceConstraints(int argIndex, mlir::Value builder.createBlock(&ifStatement.getTrueBranch()); for(auto constraint : constraints) { - auto imposedConstraints = findImposedConstraints(constraint, arg, actionEntity, builder, loc, function.getPrecondition()); + auto imposedConstraints = findImposedConstraints(constraint, arg, knownArgs, builder, loc, function.getPrecondition()); // if the minimum imposed by this constraint is greater than the current minimum, set the current minimum. auto minIfStatement = builder.create(loc); @@ -374,7 +420,7 @@ namespace mlir::rlc auto function = llvm::dyn_cast(argConstraints.getFunction().getDefiningOp()); assert( function && "Expected a FunctionOp"); ArgumentAnalysis analysis(function); - auto deduced = analysis.deduceConstraints(argConstraints.getArgumentIndex(), argConstraints.getActionEntity(), builder, loc); + auto deduced = analysis.deduceConstraints(argConstraints.getArgumentIndex(), argConstraints.getKnownArgs(), builder, loc); argConstraints.getMin().replaceAllUsesWith(deduced.min); argConstraints.getMax().replaceAllUsesWith(deduced.max); argConstraints.erase(); diff --git a/lib/dialect/src/EmitFuzzTargetPass.cpp b/lib/dialect/src/EmitFuzzTargetPass.cpp index fbd6008b..666eb333 100644 --- a/lib/dialect/src/EmitFuzzTargetPass.cpp +++ b/lib/dialect/src/EmitFuzzTargetPass.cpp @@ -233,7 +233,7 @@ static llvm::SmallVector emitSubactionArgumentDeclarations( }), subactionFunction, (uint8_t)inputType.index(), - actionEntity + mlir::ValueRange({actionEntity}) ); auto call = builder.create( diff --git a/lib/dialect/src/Operations.td b/lib/dialect/src/Operations.td index 128d82bf..a778e2a2 100644 --- a/lib/dialect/src/Operations.td +++ b/lib/dialect/src/Operations.td @@ -1825,7 +1825,7 @@ def RLC_CanOp : RLC_Dialect <"can", [DeclareOpInterfaceMethods]> } def RLC_ArgConstraintsOp : RLC_Dialect <"argConstraints", [DeclareOpInterfaceMethods]> { - let arguments = (ins AnyFunctionType:$function, I8Attr:$argument_index, AnyType:$action_entity); + let arguments = (ins AnyFunctionType:$function, I8Attr:$argument_index, Variadic:$knownArgs); let results = (outs RLC_IntegerType:$min, RLC_IntegerType: $max); } diff --git a/lib/driver/src/Driver.cpp b/lib/driver/src/Driver.cpp index ec5f085d..e15d6a2b 100644 --- a/lib/driver/src/Driver.cpp +++ b/lib/driver/src/Driver.cpp @@ -107,6 +107,8 @@ namespace mlir::rlc return; } + manager.addPass(mlir::rlc::createArgumentAnalysisPass()); + manager.addPass(mlir::rlc::createExtractPreconditionPass()); if (emitPreconditionChecks) @@ -114,8 +116,6 @@ namespace mlir::rlc manager.addPass(mlir::rlc::createAddPreconditionsCheckPass()); } - manager.addPass(mlir::rlc::createArgumentAnalysisPass()); - manager.addPass(mlir::rlc::createLowerAssertsPass()); manager.addPass(mlir::rlc::createLowerInitializerListsPass()); From 94f95e0e366dd2ffaa82771b23d2642d0e264a03 Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Tue, 6 Feb 2024 21:33:55 +0100 Subject: [PATCH 04/16] organized the implementation to a more readable form. --- lib/dialect/CMakeLists.txt | 2 +- .../rlc/dialect/DynamicArgumentAnalysis.hpp | 45 ++++ ...ss.cpp => DynamicArgumentAnalysisPass.cpp} | 206 ++++++++---------- lib/dialect/src/Passes.td | 4 +- lib/driver/src/Driver.cpp | 2 +- 5 files changed, 139 insertions(+), 120 deletions(-) create mode 100644 lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp rename lib/dialect/src/{ArgumentAnalysisPass.cpp => DynamicArgumentAnalysisPass.cpp} (68%) diff --git a/lib/dialect/CMakeLists.txt b/lib/dialect/CMakeLists.txt index 70b34dd2..dc7274e6 100644 --- a/lib/dialect/CMakeLists.txt +++ b/lib/dialect/CMakeLists.txt @@ -1,4 +1,4 @@ -rlcAddLibrary(dialect src/Dialect.cpp src/Types.cpp src/Operations.cpp src/Conversion.cpp src/EmitMain.cpp src/TypeCheck.cpp src/Interfaces.cpp src/SymbolTable.cpp src/ActionArgumentAnalysis.cpp src/LowerActionPass.cpp src/LowerArrayCalls.cpp src/LowerToCf.cpp src/ActionStatementsToCoro.cpp src/OverloadResolver.cpp src/LowerIsOperationsPass.cpp src/InstantiateTemplatesPass.cpp src/LowerAssignPass.cpp src/EmitImplicitAssignPass.cpp src/LowerConstructOpPass.cpp src/EmitImplicitInitPass.cpp src/EmitImplicitDestructorInvocationsPass.cpp src/LowerForFieldOpPass.cpp src/EmitEnumEntitiesPass.cpp src/SortTypeDeclarationsPass.cpp src/AddOutOfBoundsCheckPass.cpp src/PrintIRPass.cpp src/ExtractPreconditionPass.cpp src/LowerAssertsPass.cpp src/AddPreconditionsCheckPass.cpp src/ActionLiveness.cpp src/UncheckedAstToDot.cpp src/RewriteCallSignaturesPass.cpp src/RemoveUselessAllocaPass.cpp src/MembeFunctionsToRegularFunctionsPass.cpp src/LowerInitializerListsPass.cpp src/Enums.cpp src/EmitFuzzTargetPass.cpp src/ArgumentAnalysisPass.cpp) +rlcAddLibrary(dialect src/Dialect.cpp src/Types.cpp src/Operations.cpp src/Conversion.cpp src/EmitMain.cpp src/TypeCheck.cpp src/Interfaces.cpp src/SymbolTable.cpp src/ActionArgumentAnalysis.cpp src/LowerActionPass.cpp src/LowerArrayCalls.cpp src/LowerToCf.cpp src/ActionStatementsToCoro.cpp src/OverloadResolver.cpp src/LowerIsOperationsPass.cpp src/InstantiateTemplatesPass.cpp src/LowerAssignPass.cpp src/EmitImplicitAssignPass.cpp src/LowerConstructOpPass.cpp src/EmitImplicitInitPass.cpp src/EmitImplicitDestructorInvocationsPass.cpp src/LowerForFieldOpPass.cpp src/EmitEnumEntitiesPass.cpp src/SortTypeDeclarationsPass.cpp src/AddOutOfBoundsCheckPass.cpp src/PrintIRPass.cpp src/ExtractPreconditionPass.cpp src/LowerAssertsPass.cpp src/AddPreconditionsCheckPass.cpp src/ActionLiveness.cpp src/UncheckedAstToDot.cpp src/RewriteCallSignaturesPass.cpp src/RemoveUselessAllocaPass.cpp src/MembeFunctionsToRegularFunctionsPass.cpp src/LowerInitializerListsPass.cpp src/Enums.cpp src/EmitFuzzTargetPass.cpp src/DynamicArgumentAnalysisPass.cpp) target_link_libraries(dialect PUBLIC rlc::utils MLIRSupport MLIRDialect MLIRLLVMDialect MLIRLLVMIRTransforms MLIRControlFlowDialect) set(tblgen ${LLVM_BINARY_DIR}/bin/mlir-tblgen) diff --git a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp new file mode 100644 index 00000000..ad03451e --- /dev/null +++ b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp @@ -0,0 +1,45 @@ +#include +#include +#include "llvm/ADT/SmallVector.h" +#include "mlir/IR/Builders.h" +#include "mlir/IR/Location.h" +#include "mlir/IR/Operation.h" +#include "mlir/IR/Region.h" +#include "mlir/IR/Value.h" +#include "mlir/IR/ValueRange.h" +#include "mlir/Support/LLVM.h" +#include "rlc/dialect/Operations.hpp" + + +struct DeducedConstraints { + mlir::Value min; + mlir::Value max; +}; + +enum TermType { + DEPENDS_ON_ARG, + DEPENDS_ON_OTHER_UNKNOWNS, + KNOWN_VALUE +}; + +class DynamicArgumentAnalysis +{ + public: + explicit DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange knownArgs, mlir::OpBuilder builder, mlir::Location loc); + DeducedConstraints deduceConstraints(int argIndex); + + private: + llvm::SmallVector> expandToDNF(mlir::Value constraint); + TermType decideTermType(mlir::Value term, mlir::Value argument); + mlir::Value compute(mlir::Value expression); + DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value arg); + DeducedConstraints findImposedConstraints(mlir::Operation *binaryOperation, mlir::Value arg); + DeducedConstraints findImposedConstraints(mlir::rlc::CallOp call, mlir::Value arg); + + mlir::rlc::FunctionOp function; + mlir::Region& precondition; + mlir::ValueRange knownArgs; + mlir::OpBuilder builder; + mlir::Location loc; + llvm::SmallVector> conjunctions; +}; diff --git a/lib/dialect/src/ArgumentAnalysisPass.cpp b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp similarity index 68% rename from lib/dialect/src/ArgumentAnalysisPass.cpp rename to lib/dialect/src/DynamicArgumentAnalysisPass.cpp index e29a1514..8ee0b785 100644 --- a/lib/dialect/src/ArgumentAnalysisPass.cpp +++ b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp @@ -4,10 +4,10 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" -#include "llvm/Support/Debug.h" #include "mlir/IR/Builders.h" #include "mlir/IR/BuiltinOps.h" #include "mlir/IR/Location.h" +#include "mlir/IR/Operation.h" #include "mlir/IR/Region.h" #include "mlir/IR/Value.h" #include "mlir/IR/ValueRange.h" @@ -15,30 +15,18 @@ #include "mlir/Support/LLVM.h" #include "rlc/dialect/Dialect.h" #include "rlc/dialect/Operations.hpp" -#include "rlc/dialect/Types.hpp" - -struct DeducedConstraints { - mlir::Value min; - mlir::Value max; -}; - -enum TermType { - DEPENDS_ON_ARG, - DEPENDS_ON_OTHER_UNKNOWNS, - KNOWN_VALUE -}; - -class ArgumentAnalysis -{ - public: - explicit ArgumentAnalysis(mlir::rlc::FunctionOp op); - - DeducedConstraints deduceConstraints(int argIndex, mlir::ValueRange knownArgs, mlir::OpBuilder builder, mlir::Location loc); - - private: - llvm::SmallVector> conjunctions; - mlir::rlc::FunctionOp function; -}; +#include "rlc/dialect/DynamicArgumentAnalysis.hpp" + +DynamicArgumentAnalysis::DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange knownArgs, mlir::OpBuilder builder, mlir::Location loc): + function(op), + precondition(op.getPrecondition()), + knownArgs(knownArgs), + builder(builder), + loc(loc) { + auto yield = mlir::dyn_cast(precondition.getBlocks().front().back()); + assert(yield->getNumOperands() == 1); + conjunctions = expandToDNF(yield->getOperand(0)); +} void helper( @@ -81,34 +69,16 @@ void helper( } /* - Expands the given value into a disjunction of conjunctions. + Expands the given value into a disjunction of conjunctions of terms. + A term is a value that's not the result of on AndOp or an OrOp. */ -llvm::SmallVector> ExpandToDNF(mlir::Value constraint, mlir::Region &precondition) { +llvm::SmallVector> DynamicArgumentAnalysis::expandToDNF(mlir::Value constraint) { llvm::SmallVector> conjunctions; helper(conjunctions, {constraint}, precondition); return conjunctions; } - -ArgumentAnalysis::ArgumentAnalysis(mlir::rlc::FunctionOp op) { - auto yield = mlir::dyn_cast(op.getPrecondition().getBlocks().front().back()); - assert(yield->getNumOperands() == 1); - function = op; - conjunctions = ExpandToDNF(yield->getOperand(0), op.getPrecondition()); - llvm::dbgs() << "CONJUNCTIONS: \n"; - for(auto c :conjunctions) { - llvm::dbgs() << "{"; - for (auto t : c) { - llvm::dbgs() << "( "; - t.print(llvm::dbgs()); - llvm::dbgs() << " ),"; - } - llvm::dbgs() << "}\n"; - } - -} - -TermType decideTermType(mlir::Value term, mlir::Value argument, mlir::ValueRange knownArgs, mlir::Region &precondition) { +TermType DynamicArgumentAnalysis::decideTermType(mlir::Value term, mlir::Value argument) { if(term == argument) return DEPENDS_ON_ARG; @@ -131,7 +101,7 @@ TermType decideTermType(mlir::Value term, mlir::Value argument, mlir::ValueRange bool dependsOnOtherUnkowns = false; for(auto operand : definingOp->getOperands()) { - auto operandType = decideTermType(operand, argument, knownArgs, precondition); + auto operandType = decideTermType(operand, argument); if(operandType == DEPENDS_ON_ARG) dependsOnArg = true; else if (operandType == DEPENDS_ON_OTHER_UNKNOWNS) @@ -143,7 +113,7 @@ TermType decideTermType(mlir::Value term, mlir::Value argument, mlir::ValueRange return KNOWN_VALUE; } -mlir::Value recompute(mlir::Value expression, mlir::ValueRange knownArgs, mlir::OpBuilder builder, mlir::Region &precondition) { +mlir::Value DynamicArgumentAnalysis::compute(mlir::Value expression) { if(auto arg = llvm::dyn_cast(expression)) { assert(arg.getArgNumber() < knownArgs.size()); return knownArgs[arg.getArgNumber()]; @@ -162,7 +132,7 @@ mlir::Value recompute(mlir::Value expression, mlir::ValueRange knownArgs, mlir:: auto newOp = builder.clone(*expression.getDefiningOp()); builder.setInsertionPoint(newOp); for(auto operand : llvm::enumerate(newOp->getOperands())) { - auto newOperand = recompute(operand.value(), knownArgs, builder, precondition); + auto newOperand = compute(operand.value()); newOp->setOperand(operand.index(), newOperand); } builder.setInsertionPointAfter(newOp); @@ -173,14 +143,11 @@ mlir::Value recompute(mlir::Value expression, mlir::ValueRange knownArgs, mlir:: const int64_t min_int = -800; const int64_t max_int = 800; +DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::Operation *binaryOperation, mlir::Value arg) { + if (binaryOperation->getOperand(0) == arg) { + auto rhs = compute(binaryOperation->getOperand(1)); -DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value arg, mlir::ValueRange knownArgs, mlir::OpBuilder builder, mlir::Location loc, mlir::Region &precondition) { - auto *definingOp = constraint.getDefiningOp(); - if (definingOp->getOperands().size() == 2) { - if (definingOp->getOperand(0) == arg) { - auto rhs = recompute(definingOp->getOperand(1), knownArgs, builder, precondition); - - if (mlir::isa(definingOp)) + if (mlir::isa(binaryOperation)) { auto one = builder.create(loc, (int64_t)1); return { @@ -189,7 +156,7 @@ DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value ar }; } - if (mlir::isa(definingOp)) + if (mlir::isa(binaryOperation)) { return { rhs, @@ -197,7 +164,7 @@ DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value ar }; } - if (mlir::isa(definingOp)) + if (mlir::isa(binaryOperation)) { auto one = builder.create(loc, (int64_t)1); return { @@ -206,7 +173,7 @@ DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value ar }; } - if (mlir::isa(definingOp)) + if (mlir::isa(binaryOperation)) { return { rhs, @@ -214,7 +181,7 @@ DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value ar }; } - if (mlir::isa(definingOp)) + if (mlir::isa(binaryOperation)) { return { rhs, @@ -223,10 +190,10 @@ DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value ar } } - if(definingOp->getOperand(1) == arg) { - auto lhs = recompute(definingOp->getOperand(0), knownArgs, builder, precondition); + if(binaryOperation->getOperand(1) == arg) { + auto lhs = compute(binaryOperation->getOperand(0)); - if (mlir::isa(definingOp)) + if (mlir::isa(binaryOperation)) { auto one = builder.create(loc, (int64_t)1); return { @@ -235,7 +202,7 @@ DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value ar }; } - if (mlir::isa(definingOp)) + if (mlir::isa(binaryOperation)) { return { lhs, @@ -243,7 +210,7 @@ DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value ar }; } - if (mlir::isa(definingOp)) + if (mlir::isa(binaryOperation)) { auto one = builder.create(loc, (int64_t)1); return { @@ -252,7 +219,7 @@ DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value ar }; } - if (mlir::isa(definingOp)) + if (mlir::isa(binaryOperation)) { return { builder.create(loc, min_int), @@ -260,7 +227,7 @@ DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value ar }; } - if (mlir::isa(definingOp)) + if (mlir::isa(binaryOperation)) { return { lhs, @@ -268,36 +235,51 @@ DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value ar }; } } + return { + builder.create(loc, min_int), + builder.create(loc, max_int) + }; +} + +DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::rlc::CallOp call, mlir::Value arg) { + if( mlir::rlc::CanOp can = llvm::dyn_cast(call.getCallee().getDefiningOp())) { + auto underlyingFunction = llvm::dyn_cast(*can.getCallee().getDefiningOp()); + llvm::SmallVector knownArgsOfUnderlyingFunction; + + int argIndex = -1; + for(auto current : llvm::enumerate(call.getArgs())) { + // decide which arguments are known (assuming (in general) known arguments are before unknown arguments.) + if( not current.value().isa()) { + auto newExpr = compute(current.value()); + knownArgsOfUnderlyingFunction.emplace_back(newExpr); + continue; + } + // and find the index of the argument we're interested in. + if(current.value() == arg) { + argIndex = current.index(); + break; + } + } + assert(argIndex != -1 && "Expected to find the argument."); + DynamicArgumentAnalysis analysis(underlyingFunction, knownArgsOfUnderlyingFunction, builder, loc); + return analysis.deduceConstraints(argIndex); } + return { + builder.create(loc, min_int), + builder.create(loc, max_int) + }; +} - if( mlir::rlc::CallOp call = llvm::dyn_cast(definingOp)) - { - if( mlir::rlc::CanOp can = llvm::dyn_cast(call.getCallee().getDefiningOp())) { - llvm::dbgs() << "Underlying function\n"; - auto underlyingFunction = llvm::dyn_cast(*can.getCallee().getDefiningOp()); - underlyingFunction.dump(); - - llvm::SmallVector knownArgsOfUnderlyingFunction; - int argIndex = -1; - for(auto current : llvm::enumerate(call.getArgs())) { - // decide which arguments are known (assuming (in general) known arguments are before unknown arguments.) - if( not current.value().isa()) { - auto newExpr = recompute(current.value(), knownArgs, builder, precondition); - knownArgsOfUnderlyingFunction.emplace_back(newExpr); - continue; - } - // and find the index of the argument we're interested in. - if(current.value() == arg) { - argIndex = current.index(); - break; - } - } - assert(argIndex != -1 && "Expecteed to find the argument."); - ArgumentAnalysis analysis(underlyingFunction); - return analysis.deduceConstraints(argIndex, knownArgsOfUnderlyingFunction, builder, loc); - } - } +DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::Value constraint, mlir::Value arg) { + auto *definingOp = constraint.getDefiningOp(); + if (definingOp->getOperands().size() == 2) { + return findImposedConstraints(definingOp, arg); + } + + if( mlir::rlc::CallOp call = llvm::dyn_cast(definingOp)) { + return findImposedConstraints(call, arg); + } return { builder.create(loc, min_int), @@ -305,7 +287,7 @@ DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value ar }; } -DeducedConstraints ArgumentAnalysis::deduceConstraints(int argIndex, mlir::ValueRange knownArgs, mlir::OpBuilder builder, mlir::Location loc) { +DeducedConstraints DynamicArgumentAnalysis::deduceConstraints(int argIndex) { auto arg = function.getPrecondition().getArgument(argIndex); assert(arg.getType().isa() && "Expected an integer."); @@ -320,8 +302,9 @@ DeducedConstraints ArgumentAnalysis::deduceConstraints(int argIndex, mlir::Value for (auto conjunction : conjunctions) { llvm::SmallVector constraints; llvm::SmallVector conditions; + // categorize the terms in the conjunction for(auto term : conjunction) { - TermType type = decideTermType(term, arg, knownArgs, function.getPrecondition()); + TermType type = decideTermType(term, arg); if(type == DEPENDS_ON_ARG) { constraints.emplace_back(term); } else if (type == KNOWN_VALUE){ @@ -329,15 +312,9 @@ DeducedConstraints ArgumentAnalysis::deduceConstraints(int argIndex, mlir::Value } } + // if there are any constraints on unknown args, emit an if statement for this conjunction. if(constraints.size() > 0) { - llvm::dbgs() << "Well here we are \n"; - for (auto t : constraints) { - llvm::dbgs() << "( "; - t.print(llvm::dbgs()); - llvm::dbgs() << " ),"; - } - llvm::dbgs() << "\n"; - + // if the conditions are met, the constraints are active. auto ifStatement = builder.create(loc); builder.createBlock(&ifStatement.getElseBranch()); builder.create(loc); @@ -345,15 +322,15 @@ DeducedConstraints ArgumentAnalysis::deduceConstraints(int argIndex, mlir::Value builder.createBlock(&ifStatement.getCondition()); mlir::Value condition = builder.create(loc, true); for( auto c : conditions) { - auto recomputed = recompute(c, knownArgs, builder, function.getPrecondition()); - condition = builder.create(loc, condition, recomputed); + auto computed = compute(c); + condition = builder.create(loc, condition, computed); } builder.create(loc, condition); builder.createBlock(&ifStatement.getTrueBranch()); for(auto constraint : constraints) { - auto imposedConstraints = findImposedConstraints(constraint, arg, knownArgs, builder, loc, function.getPrecondition()); + auto imposedConstraints = findImposedConstraints(constraint, arg); // if the minimum imposed by this constraint is greater than the current minimum, set the current minimum. auto minIfStatement = builder.create(loc); @@ -392,10 +369,10 @@ DeducedConstraints ArgumentAnalysis::deduceConstraints(int argIndex, mlir::Value namespace mlir::rlc { -#define GEN_PASS_DEF_ARGUMENTANALYSISPASS +#define GEN_PASS_DEF_DYNAMICARGUMENTANALYSISPASS #include "rlc/dialect/Passes.inc" - struct ArgumentAnalysisPass - : public impl::ArgumentAnalysisPassBase + struct DynamicArgumentAnalysisPass + : public impl::DynamicArgumentAnalysisPassBase { void getDependentDialects(mlir::DialectRegistry& registry) const override { @@ -405,8 +382,6 @@ namespace mlir::rlc void runOnOperation() override { ModuleOp module = getOperation(); - - llvm::SmallVector argConstraintsOps; module.walk([&](mlir::rlc::ArgConstraintsOp op) { argConstraintsOps.emplace_back(op); @@ -419,13 +394,12 @@ namespace mlir::rlc auto function = llvm::dyn_cast(argConstraints.getFunction().getDefiningOp()); assert( function && "Expected a FunctionOp"); - ArgumentAnalysis analysis(function); - auto deduced = analysis.deduceConstraints(argConstraints.getArgumentIndex(), argConstraints.getKnownArgs(), builder, loc); + DynamicArgumentAnalysis analysis(function, argConstraints.getKnownArgs(), builder, loc); + auto deduced = analysis.deduceConstraints(argConstraints.getArgumentIndex()); argConstraints.getMin().replaceAllUsesWith(deduced.min); argConstraints.getMax().replaceAllUsesWith(deduced.max); argConstraints.erase(); } } }; - } // namespace mlir::rlc \ No newline at end of file diff --git a/lib/dialect/src/Passes.td b/lib/dialect/src/Passes.td index 7f749389..ef882c71 100644 --- a/lib/dialect/src/Passes.td +++ b/lib/dialect/src/Passes.td @@ -237,7 +237,7 @@ def EmitFuzzTargetPass : Pass<"rlc-emit-fuzz-target-pass", "mlir::ModuleOp"> { ]; } -def ArgumentAnalysisPass : Pass<"rlc-argument-analysis-pass", "mlir::ModuleOp"> { - let summary = "Lower MinValOp's to expressions that compute the minimum value dynamically."; +def DynamicArgumentAnalysisPass : Pass<"rlc-dynamic-argument-analysis-pass", "mlir::ModuleOp"> { + let summary = "Lower ArgConstraintsOp's to expressions that compute the minimum and maximum value dynamically."; let dependentDialects = ["rlc::RLCDialect"]; } diff --git a/lib/driver/src/Driver.cpp b/lib/driver/src/Driver.cpp index e15d6a2b..1415e109 100644 --- a/lib/driver/src/Driver.cpp +++ b/lib/driver/src/Driver.cpp @@ -107,7 +107,7 @@ namespace mlir::rlc return; } - manager.addPass(mlir::rlc::createArgumentAnalysisPass()); + manager.addPass(mlir::rlc::createDynamicArgumentAnalysisPass()); manager.addPass(mlir::rlc::createExtractPreconditionPass()); From 0cb4c4476a967ba16e64b83d432f9969835007da Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Tue, 6 Feb 2024 21:58:36 +0100 Subject: [PATCH 05/16] added explanation. --- .../rlc/dialect/DynamicArgumentAnalysis.hpp | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp index ad03451e..18ecb218 100644 --- a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp +++ b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp @@ -22,6 +22,37 @@ enum TermType { KNOWN_VALUE }; +/* + Given a function and a set of known (bound) arguments for the function, we try to find the + minimum and maximum values the unknown (unbound) arguments can take while satisfying the + function's precondition. + + We first normalize the precondition to be a disjunction of conjunction of terms, where each + term is a value produced by something other than an AndOp or an OrOp. i.e. we express the + precondition in the form (t_1 AND t_2 AND ...) OR (t_3 AND t_4 AND ...). + + Then, to find the minimum and the maximum for a given argument "arg", we emit code as follows: + We emit declarations for current_min and current_max, initialized to minus and plus infinity. + + For each conjunction (t_1 AND t_2 AND ...) we classify the terms as + - conditions: terms depending only on known values + - constraints: terms depending on "arg" and no other unknown values + - others: terms depending on other unknown values. + + We analyse the minimum and maximum values each constraint implies on "arg" in terms of known + dynamic values. Currently, this returns [-infinity, +infinity] for anything but <, >, >=, <=, == + constraints where one of the sides is "arg", and calls to CanOp results. + + Then, we emit an if statement in the form of + if(condition_1 & condition_2....) { + if(imposed_min(constraint_1) > current_min) + current_min = imposed_min(constraint_1) + if(imposed_min(constraint_2) > current_min) + current_min = imposed_min(constraint_2) + ... + } + and similarly for the maximum. +*/ class DynamicArgumentAnalysis { public: From fe26171c63efa0bce1613fa1cef47758fb62a40c Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Tue, 6 Feb 2024 22:11:34 +0100 Subject: [PATCH 06/16] added the blackjack example. --- .../test/fuzzing_wrapped_action_statements.rl | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 tool/rlc/test/fuzzing_wrapped_action_statements.rl diff --git a/tool/rlc/test/fuzzing_wrapped_action_statements.rl b/tool/rlc/test/fuzzing_wrapped_action_statements.rl new file mode 100644 index 00000000..7a00a052 --- /dev/null +++ b/tool/rlc/test/fuzzing_wrapped_action_statements.rl @@ -0,0 +1,114 @@ +import fuzzer.cpp_functions +import fuzzer.utils +import collections.vector + +fun crash() {1 == 0}: + return + +ent Deck: + Vector cards + + fun init(): + self.cards.init() + let i = 1 + while i <= 13 : + # once for each suit. + self.cards.append(i) + self.cards.append(i) + self.cards.append(i) + self.cards.append(i) + i = i + 1 + + fun switch_cards(Int a, Int b) {a >= 0, a < 52, b >= 0, b < 52}: + let temp = self.cards.get(a) + self.cards.set(a, self.cards.get(b)) + self.cards.set(b, temp) + +fun draw(Deck deck, Vector hand): + let card = deck.cards.pop() + hand.append(card) + +fun deal(Deck deck, Vector hand): + let i = 0 + while i < 2: + draw(deck, hand) + i = i + 1 + +fun calculate_points(Vector hand) -> Int: + let total = 0 + let num_ones = 0 + + let i = 0 + while i < hand.size(): + let card = hand.get(i) + if card <= 10: + total = total + card + else: + total = total + 10 + + if card == 1: + num_ones = num_ones + 1 + i = i + 1 + + while num_ones > 0 and total + 10 < 21: + num_ones = num_ones - 1 + total = total + 10 + + if num_ones == 2: + crash() + + return total + +act shuffle(ctx Deck deck) -> Shuffle: + frm done = false + #TODO delet dis. It's only here to showcase being able to use values from the underlying op's frame. + frm last_picked_a = 0 + while !done: + actions: + act stop_shuffle() + done = true + + act switch(Int a, Int b) {a >= last_picked_a, a < deck.cards.size(), b >= 0, b < 52} + deck.switch_cards(a, b) + last_picked_a = last_picked_a + 1 + +act play() -> Blackjack: + frm deck : Deck + frm player_hand : Vector + frm dealer_hand : Vector + + subaction*(deck) s = shuffle(deck) + + deal(deck, player_hand) + deal(deck, dealer_hand) + + frm player_passed = false + frm player_bust = false + + while !player_bust and !player_passed: + actions: + act hit() + draw(deck, player_hand) + if calculate_points(player_hand) > 21: + player_bust = true + + act stand() + player_passed = true + + while calculate_points(dealer_hand) <= 16: + draw(deck, dealer_hand) + +#fun main() -> Int: +# let game = play() +# game.switch(0, 51) +# game.switch(1, 50) +# game.stop_shuffle() +# if(game.player_hand.size() != 2): +# return 1 +# if(game.dealer_hand.size() != 2): +# return 1 +# game.hit() +# +# if(!game.is_done()): +# return 1 +# return 0 From 0eba4074c8c1f07fd052c393dc6ee93b4a9e6347 Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Thu, 8 Feb 2024 16:22:13 +0100 Subject: [PATCH 07/16] fixed how we handle multiple conjunctions. --- .../rlc/dialect/DynamicArgumentAnalysis.hpp | 13 +- .../src/DynamicArgumentAnalysisPass.cpp | 124 ++++++++++++++---- tool/rlc/test/dynamic_analysis.rl | 4 +- 3 files changed, 111 insertions(+), 30 deletions(-) diff --git a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp index 18ecb218..7e7875ab 100644 --- a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp +++ b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp @@ -32,7 +32,7 @@ enum TermType { precondition in the form (t_1 AND t_2 AND ...) OR (t_3 AND t_4 AND ...). Then, to find the minimum and the maximum for a given argument "arg", we emit code as follows: - We emit declarations for current_min and current_max, initialized to minus and plus infinity. + We emit declarations for aggregate_min and aggregate_max, initialized to minus and plus infinity. For each conjunction (t_1 AND t_2 AND ...) we classify the terms as - conditions: terms depending only on known values @@ -45,12 +45,23 @@ enum TermType { Then, we emit an if statement in the form of if(condition_1 & condition_2....) { + current_min = -inf if(imposed_min(constraint_1) > current_min) current_min = imposed_min(constraint_1) if(imposed_min(constraint_2) > current_min) current_min = imposed_min(constraint_2) ... + if ( uninitialized(aggregate_min) || current_min < aggregate_min) + aggregate_min = current_min } + + In other words, we compute + aggregate_min = max([ + min([imposed_min(term) for term in conjunction.constraints]) + for conjunction in constraint + where evaluate(conjunction.conditions) == true + ]) + and similarly for the maximum. */ class DynamicArgumentAnalysis diff --git a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp index 8ee0b785..0f43922f 100644 --- a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp +++ b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp @@ -159,8 +159,8 @@ DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::Operati if (mlir::isa(binaryOperation)) { return { - rhs, - builder.create(loc, max_int) + builder.create(loc, min_int), + rhs }; } @@ -287,6 +287,82 @@ DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::Value c }; } +/* + Emits `if (value > target) target = value` +*/ +void assignIfGreaterthan(mlir::Value value, mlir::Value target, mlir::OpBuilder builder, mlir::Location loc) { + auto ifStatement = builder.create(loc); + builder.createBlock(&ifStatement.getElseBranch()); + builder.create(loc); + + builder.createBlock(&ifStatement.getCondition()); + auto g = builder.create(loc, value, target); + builder.create(loc, g.getResult()); + + builder.createBlock(&ifStatement.getTrueBranch()); + builder.create(loc, target, value); + builder.create(loc); + builder.setInsertionPointAfter(ifStatement); +} + +/* + Emits `if (value < target) target = value` +*/ +void assignIfLessThan(mlir::Value value, mlir::Value target, mlir::OpBuilder builder, mlir::Location loc) { + auto ifStatement = builder.create(loc); + builder.createBlock(&ifStatement.getElseBranch()); + builder.create(loc); + + builder.createBlock(&ifStatement.getCondition()); + auto g = builder.create(loc, value, target); + builder.create(loc, g.getResult()); + + builder.createBlock(&ifStatement.getTrueBranch()); + builder.create(loc, target, value); + builder.create(loc); + builder.setInsertionPointAfter(ifStatement); +} + +void maybeAssignMin(mlir::Value currentMin, mlir::Value aggregateMin, mlir::Value minInt, mlir::OpBuilder builder, mlir::Location loc) { + auto maybeAssignMin = builder.create(loc); + builder.createBlock(&maybeAssignMin.getElseBranch()); + builder.create(loc); + + builder.createBlock(&maybeAssignMin.getCondition()); + auto minUninitialized = builder.create(loc, aggregateMin, minInt); + auto currentMinIsUninitialized = builder.create(loc, currentMin, minInt); + auto currentMinIsInitialized = builder.create(loc, currentMinIsUninitialized.getResult()); + auto currentMinIsSmaller = builder.create(loc, currentMin, aggregateMin); + auto cond = builder.create(loc, minUninitialized, currentMinIsSmaller); + auto cond2 = builder.create(loc, currentMinIsInitialized, cond); + builder.create(loc, cond2.getResult()); + + builder.createBlock(&maybeAssignMin.getTrueBranch()); + builder.create(loc, aggregateMin, currentMin); + builder.create(loc); + builder.setInsertionPointAfter(maybeAssignMin); +} + +void maybeAssignMax(mlir::Value currentMax, mlir::Value aggregateMax, mlir::Value maxInt, mlir::OpBuilder builder, mlir::Location loc) { + auto maybeAssignMax= builder.create(loc); + builder.createBlock(&maybeAssignMax.getElseBranch()); + builder.create(loc); + + builder.createBlock(&maybeAssignMax.getCondition()); + auto maxUninitialized = builder.create(loc, aggregateMax, maxInt); + auto currentMaxIsUninitialized = builder.create(loc, currentMax, maxInt); + auto currentMaxIsInitialized = builder.create(loc, currentMaxIsUninitialized.getResult()); + auto currentMaxIsGreater = builder.create(loc, currentMax, aggregateMax); + auto cond = builder.create(loc, maxUninitialized, currentMaxIsGreater); + auto cond2 = builder.create(loc, currentMaxIsInitialized, cond); + builder.create(loc, cond2.getResult()); + + builder.createBlock(&maybeAssignMax.getTrueBranch()); + builder.create(loc, aggregateMax, currentMax); + builder.create(loc); + builder.setInsertionPointAfter(maybeAssignMax); +} + DeducedConstraints DynamicArgumentAnalysis::deduceConstraints(int argIndex) { auto arg = function.getPrecondition().getArgument(argIndex); assert(arg.getType().isa() && "Expected an integer."); @@ -314,6 +390,12 @@ DeducedConstraints DynamicArgumentAnalysis::deduceConstraints(int argIndex) { // if there are any constraints on unknown args, emit an if statement for this conjunction. if(constraints.size() > 0) { + auto minForThisConjunction = builder.create(loc, arg.getType()); + builder.create(loc, minForThisConjunction, minInt); + + auto maxForThisConjunction = builder.create(loc, arg.getType()); + builder.create(loc, maxForThisConjunction, maxInt); + // if the conditions are met, the constraints are active. auto ifStatement = builder.create(loc); builder.createBlock(&ifStatement.getElseBranch()); @@ -330,36 +412,22 @@ DeducedConstraints DynamicArgumentAnalysis::deduceConstraints(int argIndex) { builder.createBlock(&ifStatement.getTrueBranch()); for(auto constraint : constraints) { - auto imposedConstraints = findImposedConstraints(constraint, arg); - - // if the minimum imposed by this constraint is greater than the current minimum, set the current minimum. - auto minIfStatement = builder.create(loc); - builder.createBlock(&minIfStatement.getElseBranch()); - builder.create(loc); - - builder.createBlock(&minIfStatement.getCondition()); - auto g = builder.create(loc, imposedConstraints.min, minVal); - builder.create(loc, g.getResult()); + auto imposedConstraints = findImposedConstraints(constraint, arg); - builder.createBlock(&minIfStatement.getTrueBranch()); - builder.create(loc, minVal, imposedConstraints.min); - builder.create(loc); - builder.setInsertionPointAfter(minIfStatement); + // if the minimum imposed by this constraint is greater than the current minimum, set the current minimum. + assignIfGreaterthan(imposedConstraints.min, minForThisConjunction, builder, loc); // if the maximum imposed by this constraint is less than the current maximum, set the current maximum. - auto maxIfStatement = builder.create(loc); - builder.createBlock(&maxIfStatement.getElseBranch()); - builder.create(loc); - - builder.createBlock(&maxIfStatement.getCondition()); - auto l = builder.create(loc, imposedConstraints.max, maxVal); - builder.create(loc, l.getResult()); - - builder.createBlock(&maxIfStatement.getTrueBranch()); - builder.create(loc, maxVal, imposedConstraints.max); - builder.create(loc); - builder.setInsertionPointAfter(maxIfStatement); + assignIfLessThan(imposedConstraints.max, maxForThisConjunction, builder, loc); } + + // if minVal is not initialized, or if the min imposed by this conjunction is smaller than it, set minVal + // to the min imposed by this conjunction. + maybeAssignMin(minForThisConjunction, minVal, minInt, builder, loc); + + // similarly for maxVal. + maybeAssignMax(maxForThisConjunction, maxVal, maxInt, builder, loc); + builder.create(loc); builder.setInsertionPointAfter(ifStatement); } diff --git a/tool/rlc/test/dynamic_analysis.rl b/tool/rlc/test/dynamic_analysis.rl index 030ef0b2..b1abd864 100644 --- a/tool/rlc/test/dynamic_analysis.rl +++ b/tool/rlc/test/dynamic_analysis.rl @@ -16,11 +16,13 @@ fun factorial(Int input) -> Int: return input * factorial(input - 1) act play() -> Play: + frm flag = true frm current = 0 + while current != 7: act subact(Int x) {x > (plus_three(current) + 8) / 2, x < 10 + factorial(current)} current = x - act that(Int a) {a >= 0, a < 100} + act that(Int a) {a >= 0, ( flag and a <= 20 ) or (flag and a <= 15), ( flag and a > 3 ) or (flag and a > 1)} crash_on_five(a) From dd9cb275851214ff7b472b3abd62b723b9982eaa Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Wed, 14 Feb 2024 15:18:06 +0100 Subject: [PATCH 08/16] pick arguments one by one, binding previously picked ones each time. --- lib/dialect/src/EmitFuzzTargetPass.cpp | 6 +++++- lib/fuzzer/src/fuzz_target.cpp | 11 +++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lib/dialect/src/EmitFuzzTargetPass.cpp b/lib/dialect/src/EmitFuzzTargetPass.cpp index 666eb333..4d80f6b8 100644 --- a/lib/dialect/src/EmitFuzzTargetPass.cpp +++ b/lib/dialect/src/EmitFuzzTargetPass.cpp @@ -223,6 +223,9 @@ static llvm::SmallVector emitSubactionArgumentDeclarations( auto inputs = mlir::dyn_cast(subactionFunction.getType()).getInputs(); // The first input is the actionEntity, which does not need to be declared here. auto inputsExcludingActionEntity = llvm::drop_begin(llvm::enumerate(inputs)); + // keeps track of arguments that already have been bound. + // the action entity is bound before picking the first input, every input becomes bound when it's picked. + llvm::SmallVector boundArguments {actionEntity}; for(auto inputType : inputsExcludingActionEntity) { assert(inputType.value().isa() && "Fuzzing can only handle integer arguments for now."); auto argConstraints = builder.create( @@ -233,7 +236,7 @@ static llvm::SmallVector emitSubactionArgumentDeclarations( }), subactionFunction, (uint8_t)inputType.index(), - mlir::ValueRange({actionEntity}) + boundArguments ); auto call = builder.create( @@ -245,6 +248,7 @@ static llvm::SmallVector emitSubactionArgumentDeclarations( // print the value picked for the argument for debugging purposes. builder.create(loc, print, false, call.getResult(0)); arguments.emplace_back(call->getResult(0)); + boundArguments.emplace_back(call->getResult(0)); } builder.restoreInsertionPoint(ip); diff --git a/lib/fuzzer/src/fuzz_target.cpp b/lib/fuzzer/src/fuzz_target.cpp index 31dbd185..c6532bee 100644 --- a/lib/fuzzer/src/fuzz_target.cpp +++ b/lib/fuzzer/src/fuzz_target.cpp @@ -60,6 +60,17 @@ extern "C" void rl_fuzzer_get_input__int64_t_r_int64_t(__int64_t *result, const extern "C" void rl_fuzzer_pick_argument__int64_t_int64_t_r_int64_t(__int64_t *result, const __int64_t *min, __int64_t *max) { printf("Picking an integer argument in range [%ld, %ld]\n", *min, *max); + if(*min > *max) { + // The current implementation of constraint analysis allows this to happen. When it does, it's not a fault + // of the program under analysis but a fault of the analysis itself. It shouldn't cause an error. Returning + // any arbitrary value is safe here since any value is either valid by pure chance, or will be discarded by the fuzzer. + // It might be worth tweaking the fuzz target not to call this function in such a case, but I see no justification for that + // effort at the moment. + printf("Invalid range. Picked 0.\n"); + *result = 0; + return; + } + int num_bits = ceil(log2(*max - *min + 1)); *result = std::abs(consume_bits(fuzz_input, num_bits, byte_offset, bit_offset)) % (*max - *min + 1) + *min; printf("Picked %ld\n", *result); From c703544d44ddd1c8d745dfde7e9140d16af7a132 Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Wed, 14 Feb 2024 17:38:34 +0100 Subject: [PATCH 09/16] replaced ArgConstraintsOp with PickedArgOp --- .../rlc/dialect/DynamicArgumentAnalysis.hpp | 6 ++- .../src/DynamicArgumentAnalysisPass.cpp | 49 +++++++++++++------ lib/dialect/src/EmitFuzzTargetPass.cpp | 20 ++------ lib/dialect/src/Operations.cpp | 9 +++- lib/dialect/src/Operations.td | 4 +- 5 files changed, 54 insertions(+), 34 deletions(-) diff --git a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp index 7e7875ab..789ff117 100644 --- a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp +++ b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp @@ -67,10 +67,11 @@ enum TermType { class DynamicArgumentAnalysis { public: - explicit DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange knownArgs, mlir::OpBuilder builder, mlir::Location loc); - DeducedConstraints deduceConstraints(int argIndex); + explicit DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange knownArgs, mlir::Value argPicker, mlir::OpBuilder builder, mlir::Location loc); + mlir::Value pickArg(int argIndex); private: + DeducedConstraints deduceConstraints(int argIndex); llvm::SmallVector> expandToDNF(mlir::Value constraint); TermType decideTermType(mlir::Value term, mlir::Value argument); mlir::Value compute(mlir::Value expression); @@ -81,6 +82,7 @@ class DynamicArgumentAnalysis mlir::rlc::FunctionOp function; mlir::Region& precondition; mlir::ValueRange knownArgs; + mlir::Value argPicker; mlir::OpBuilder builder; mlir::Location loc; llvm::SmallVector> conjunctions; diff --git a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp index 0f43922f..c6d562db 100644 --- a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp +++ b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp @@ -17,10 +17,11 @@ #include "rlc/dialect/Operations.hpp" #include "rlc/dialect/DynamicArgumentAnalysis.hpp" -DynamicArgumentAnalysis::DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange knownArgs, mlir::OpBuilder builder, mlir::Location loc): +DynamicArgumentAnalysis::DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange knownArgs, mlir::Value argPicker, mlir::OpBuilder builder, mlir::Location loc): function(op), precondition(op.getPrecondition()), knownArgs(knownArgs), + argPicker(argPicker), builder(builder), loc(loc) { auto yield = mlir::dyn_cast(precondition.getBlocks().front().back()); @@ -261,7 +262,7 @@ DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::rlc::Ca } } assert(argIndex != -1 && "Expected to find the argument."); - DynamicArgumentAnalysis analysis(underlyingFunction, knownArgsOfUnderlyingFunction, builder, loc); + DynamicArgumentAnalysis analysis(underlyingFunction, knownArgsOfUnderlyingFunction, argPicker, builder, loc); return analysis.deduceConstraints(argIndex); } return { @@ -435,6 +436,17 @@ DeducedConstraints DynamicArgumentAnalysis::deduceConstraints(int argIndex) { return {minVal, maxVal}; } +mlir::Value DynamicArgumentAnalysis::pickArg(int argIndex) { + auto deduced = deduceConstraints(argIndex); + auto call = builder.create( + loc, + argPicker, + false, + mlir::ValueRange({deduced.min, deduced.max}) + ); + return call.getResult(0); +} + namespace mlir::rlc { #define GEN_PASS_DEF_DYNAMICARGUMENTANALYSISPASS @@ -450,23 +462,32 @@ namespace mlir::rlc void runOnOperation() override { ModuleOp module = getOperation(); - llvm::SmallVector argConstraintsOps; - module.walk([&](mlir::rlc::ArgConstraintsOp op) { - argConstraintsOps.emplace_back(op); + + mlir::Value argPicker; + for (auto op : module.getOps()) { + if(op.getUnmangledName().equals("fuzzer_pick_argument")) { + argPicker = op.getResult(); + break; + } + } + + llvm::SmallVector pickedArgOps; + module.walk([&](mlir::rlc::PickedArgOp op) { + pickedArgOps.emplace_back(op); }); - for (auto argConstraints: argConstraintsOps) + for (auto pickedArgOp : pickedArgOps) { - mlir::OpBuilder builder(argConstraints); - mlir::Location loc = argConstraints->getLoc(); + mlir::OpBuilder builder(pickedArgOp); + mlir::Location loc = pickedArgOp->getLoc(); - auto function = llvm::dyn_cast(argConstraints.getFunction().getDefiningOp()); + auto function = llvm::dyn_cast(pickedArgOp.getFunction().getDefiningOp()); assert( function && "Expected a FunctionOp"); - DynamicArgumentAnalysis analysis(function, argConstraints.getKnownArgs(), builder, loc); - auto deduced = analysis.deduceConstraints(argConstraints.getArgumentIndex()); - argConstraints.getMin().replaceAllUsesWith(deduced.min); - argConstraints.getMax().replaceAllUsesWith(deduced.max); - argConstraints.erase(); + DynamicArgumentAnalysis analysis(function, pickedArgOp.getKnownArgs(), argPicker, builder, loc); + auto pickedArg = analysis.pickArg(pickedArgOp.getArgumentIndex()); + + pickedArgOp.getResult().replaceAllUsesWith(pickedArg); + pickedArgOp->erase(); } } }; diff --git a/lib/dialect/src/EmitFuzzTargetPass.cpp b/lib/dialect/src/EmitFuzzTargetPass.cpp index 4d80f6b8..0284da0c 100644 --- a/lib/dialect/src/EmitFuzzTargetPass.cpp +++ b/lib/dialect/src/EmitFuzzTargetPass.cpp @@ -210,7 +210,6 @@ static mlir::Value emitChosenActionDeclaration( static llvm::SmallVector emitSubactionArgumentDeclarations( mlir::Value subactionFunction, mlir::Value actionEntity, - mlir::Value pickArgument, mlir::Value print, mlir::Location loc, mlir::OpBuilder builder, @@ -228,27 +227,19 @@ static llvm::SmallVector emitSubactionArgumentDeclarations( llvm::SmallVector boundArguments {actionEntity}; for(auto inputType : inputsExcludingActionEntity) { assert(inputType.value().isa() && "Fuzzing can only handle integer arguments for now."); - auto argConstraints = builder.create( + auto pickedArg = builder.create( loc, mlir::TypeRange({ - mlir::rlc::IntegerType::getInt64(builder.getContext()), mlir::rlc::IntegerType::getInt64(builder.getContext()) }), subactionFunction, (uint8_t)inputType.index(), boundArguments ); - - auto call = builder.create( - loc, - pickArgument, - false, - mlir::ValueRange({argConstraints.getMin(), argConstraints.getMax()}) - ); // print the value picked for the argument for debugging purposes. - builder.create(loc, print, false, call.getResult(0)); - arguments.emplace_back(call->getResult(0)); - boundArguments.emplace_back(call->getResult(0)); + builder.create(loc, print, false, pickedArg.getResult()); + arguments.emplace_back(pickedArg.getResult()); + boundArguments.emplace_back(pickedArg.getResult()); } builder.restoreInsertionPoint(ip); @@ -278,7 +269,6 @@ static void emitSubactionCases( mlir::OpBuilder builder ) { auto ip = builder.saveInsertionPoint(); - auto pickArgument = findFunction(action->getParentOfType(), "fuzzer_pick_argument"); auto print = findFunction(action->getParentOfType(),"fuzzer_print"); auto skipFuzzInput = findFunction(action->getParentOfType(),"fuzzer_skip_input"); @@ -294,7 +284,7 @@ static void emitSubactionCases( // let arg1 = pickArgument(arg_1_size) // ... builder.createBlock(&ifActionIsChosen.getTrueBranch()); - auto args = emitSubactionArgumentDeclarations(subaction.value(), actionEntity, pickArgument, print, action->getLoc(), builder, moduleBuilder); + auto args = emitSubactionArgumentDeclarations(subaction.value(), actionEntity, print, action->getLoc(), builder, moduleBuilder); args.insert(args.begin(), actionEntity); // the first argument should be the entity itself. // if( !can_subaction_function(arg1, arg2, ...)) diff --git a/lib/dialect/src/Operations.cpp b/lib/dialect/src/Operations.cpp index 09b3ed63..b4a7b734 100644 --- a/lib/dialect/src/Operations.cpp +++ b/lib/dialect/src/Operations.cpp @@ -933,9 +933,16 @@ mlir::LogicalResult mlir::rlc::CanOp::typeCheck( return mlir::success(); } -mlir::LogicalResult mlir::rlc::ArgConstraintsOp::typeCheck( +mlir::LogicalResult mlir::rlc::PickedArgOp::typeCheck( mlir::rlc::ModuleBuilder &builder) { + auto &rewriter = builder.getRewriter(); + rewriter.replaceOpWithNewOp(*this, + getFunction().getType().cast().getInputs()[getArgumentIndex()], + getFunction(), + getArgumentIndex(), + getKnownArgs() + ); return mlir::success(); } diff --git a/lib/dialect/src/Operations.td b/lib/dialect/src/Operations.td index a778e2a2..ab738798 100644 --- a/lib/dialect/src/Operations.td +++ b/lib/dialect/src/Operations.td @@ -1824,8 +1824,8 @@ def RLC_CanOp : RLC_Dialect <"can", [DeclareOpInterfaceMethods]> }]>]; } -def RLC_ArgConstraintsOp : RLC_Dialect <"argConstraints", [DeclareOpInterfaceMethods]> { +def RLC_PickedArgOp : RLC_Dialect <"pickedArg", [DeclareOpInterfaceMethods]> { let arguments = (ins AnyFunctionType:$function, I8Attr:$argument_index, Variadic:$knownArgs); - let results = (outs RLC_IntegerType:$min, RLC_IntegerType: $max); + let results = (outs AnyType:$argument); } From 1d7f24e6dc8eaddc1b82489d77676e02338e48be Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Wed, 14 Feb 2024 23:05:32 +0100 Subject: [PATCH 10/16] laid out the method for picking structs, not yet accounting for already bound struct fields. --- .../rlc/dialect/DynamicArgumentAnalysis.hpp | 16 +- .../src/DynamicArgumentAnalysisPass.cpp | 278 +++++++++++------- lib/dialect/src/EmitFuzzTargetPass.cpp | 3 +- tool/rlc/test/fuzzer_pick_struct.rl | 11 + 4 files changed, 196 insertions(+), 112 deletions(-) create mode 100644 tool/rlc/test/fuzzer_pick_struct.rl diff --git a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp index 789ff117..9d9e713a 100644 --- a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp +++ b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp @@ -1,4 +1,5 @@ #include +#include #include #include "llvm/ADT/SmallVector.h" #include "mlir/IR/Builders.h" @@ -17,7 +18,7 @@ struct DeducedConstraints { }; enum TermType { - DEPENDS_ON_ARG, + DEPENDS_ON_UNBOUND_VALUE, DEPENDS_ON_OTHER_UNKNOWNS, KNOWN_VALUE }; @@ -71,13 +72,16 @@ class DynamicArgumentAnalysis mlir::Value pickArg(int argIndex); private: - DeducedConstraints deduceConstraints(int argIndex); + DeducedConstraints deduceIntegerUnboundValueConstraints(mlir::Value arg, llvm::SmallVector memberAddress); llvm::SmallVector> expandToDNF(mlir::Value constraint); - TermType decideTermType(mlir::Value term, mlir::Value argument); + TermType decideTermType(mlir::Value term, mlir::Value argument, mlir::SmallVector memberAddress); mlir::Value compute(mlir::Value expression); - DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value arg); - DeducedConstraints findImposedConstraints(mlir::Operation *binaryOperation, mlir::Value arg); - DeducedConstraints findImposedConstraints(mlir::rlc::CallOp call, mlir::Value arg); + DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value arg, mlir::SmallVector memberAddress); + DeducedConstraints findImposedConstraints(mlir::Operation *binaryOperation, mlir::Value arg, mlir::SmallVector memberAddress); + DeducedConstraints findImposedConstraints(mlir::rlc::CallOp call, mlir::Value arg, mlir::SmallVector memberAddress); + + mlir::Value pickIntegerUnboundValue(mlir::Value arg, llvm::SmallVector memberAddress); + mlir::Value pickUnboundValue(mlir::Value arg, llvm::SmallVector memberAddress); mlir::rlc::FunctionOp function; mlir::Region& precondition; diff --git a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp index c6d562db..554465ea 100644 --- a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp +++ b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" @@ -16,6 +18,7 @@ #include "rlc/dialect/Dialect.h" #include "rlc/dialect/Operations.hpp" #include "rlc/dialect/DynamicArgumentAnalysis.hpp" +#include "rlc/dialect/Types.hpp" DynamicArgumentAnalysis::DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange knownArgs, mlir::Value argPicker, mlir::OpBuilder builder, mlir::Location loc): function(op), @@ -79,9 +82,28 @@ llvm::SmallVector> DynamicArgumentAnalysis::expan return conjunctions; } -TermType DynamicArgumentAnalysis::decideTermType(mlir::Value term, mlir::Value argument) { - if(term == argument) - return DEPENDS_ON_ARG; +bool matches(mlir::Value term, mlir::Value argument, llvm::SmallVector memberAddress) { + mlir::Value current = term; + // walk the member address in reverse, test if it leads to the argument. + for(uint64_t & index : std::ranges::reverse_view(memberAddress)) { + auto definingOp = current.getDefiningOp(); + if( not llvm::detail::isPresent(definingOp)) + return false; + if(auto memberAccess = mlir::dyn_cast(definingOp)) { + if (memberAccess.getMemberIndex() != index) { + return false; + } + current = memberAccess.getValue(); + } else { + return false; + } + } + return current == argument; +} + +TermType DynamicArgumentAnalysis::decideTermType(mlir::Value term, mlir::Value argument, llvm::SmallVector memberAddress) { + if(matches(term, argument, memberAddress)) + return DEPENDS_ON_UNBOUND_VALUE; if(auto arg = llvm::dyn_cast(term)) { if(arg.getArgNumber() < knownArgs.size()) @@ -102,14 +124,14 @@ TermType DynamicArgumentAnalysis::decideTermType(mlir::Value term, mlir::Value a bool dependsOnOtherUnkowns = false; for(auto operand : definingOp->getOperands()) { - auto operandType = decideTermType(operand, argument); - if(operandType == DEPENDS_ON_ARG) + auto operandType = decideTermType(operand, argument, memberAddress); + if(operandType == DEPENDS_ON_UNBOUND_VALUE) dependsOnArg = true; else if (operandType == DEPENDS_ON_OTHER_UNKNOWNS) dependsOnOtherUnkowns = true; } - if (dependsOnArg) { return DEPENDS_ON_ARG; } + if (dependsOnArg) { return DEPENDS_ON_UNBOUND_VALUE; } if (dependsOnOtherUnkowns) { return DEPENDS_ON_OTHER_UNKNOWNS; }; return KNOWN_VALUE; } @@ -144,105 +166,106 @@ mlir::Value DynamicArgumentAnalysis::compute(mlir::Value expression) { const int64_t min_int = -800; const int64_t max_int = 800; -DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::Operation *binaryOperation, mlir::Value arg) { - if (binaryOperation->getOperand(0) == arg) { - auto rhs = compute(binaryOperation->getOperand(1)); - - if (mlir::isa(binaryOperation)) - { - auto one = builder.create(loc, (int64_t)1); - return { - builder.create(loc, min_int), - builder.create(loc, rhs, one) - }; - } +DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::Operation *binaryOperation, mlir::Value arg, mlir::SmallVector memberAddress) { + if (matches(binaryOperation->getOperand(0), arg, memberAddress)) { + auto rhs = compute(binaryOperation->getOperand(1)); + + if (mlir::isa(binaryOperation)) + { + auto one = builder.create(loc, (int64_t)1); + return { + builder.create(loc, min_int), + builder.create(loc, rhs, one) + }; + } - if (mlir::isa(binaryOperation)) - { - return { - builder.create(loc, min_int), - rhs - }; - } + if (mlir::isa(binaryOperation)) + { + return { + builder.create(loc, min_int), + rhs + }; + } - if (mlir::isa(binaryOperation)) - { - auto one = builder.create(loc, (int64_t)1); - return { - builder.create(loc, rhs, one), - builder.create(loc, max_int) - }; - } + if (mlir::isa(binaryOperation)) + { + auto one = builder.create(loc, (int64_t)1); + return { + builder.create(loc, rhs, one), + builder.create(loc, max_int) + }; + } - if (mlir::isa(binaryOperation)) - { - return { - rhs, - builder.create(loc, max_int) - }; - } + if (mlir::isa(binaryOperation)) + { + return { + rhs, + builder.create(loc, max_int) + }; + } - if (mlir::isa(binaryOperation)) - { - return { - rhs, - rhs - }; - } + if (mlir::isa(binaryOperation)) + { + return { + rhs, + rhs + }; } + } - if(binaryOperation->getOperand(1) == arg) { - auto lhs = compute(binaryOperation->getOperand(0)); + if(matches(binaryOperation->getOperand(1), arg, memberAddress)) { + auto lhs = compute(binaryOperation->getOperand(0)); - if (mlir::isa(binaryOperation)) - { - auto one = builder.create(loc, (int64_t)1); - return { - builder.create(loc, lhs, one), - builder.create(loc, max_int), - }; - } + if (mlir::isa(binaryOperation)) + { + auto one = builder.create(loc, (int64_t)1); + return { + builder.create(loc, lhs, one), + builder.create(loc, max_int), + }; + } - if (mlir::isa(binaryOperation)) - { - return { - lhs, - builder.create(loc, max_int), - }; - } + if (mlir::isa(binaryOperation)) + { + return { + lhs, + builder.create(loc, max_int), + }; + } - if (mlir::isa(binaryOperation)) - { - auto one = builder.create(loc, (int64_t)1); - return { - builder.create(loc, min_int), - builder.create(loc, lhs, one) - }; - } + if (mlir::isa(binaryOperation)) + { + auto one = builder.create(loc, (int64_t)1); + return { + builder.create(loc, min_int), + builder.create(loc, lhs, one) + }; + } - if (mlir::isa(binaryOperation)) - { - return { - builder.create(loc, min_int), - lhs - }; - } + if (mlir::isa(binaryOperation)) + { + return { + builder.create(loc, min_int), + lhs + }; + } - if (mlir::isa(binaryOperation)) - { - return { - lhs, - lhs - }; - } + if (mlir::isa(binaryOperation)) + { + return { + lhs, + lhs + }; } + } return { builder.create(loc, min_int), builder.create(loc, max_int) }; } -DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::rlc::CallOp call, mlir::Value arg) { +DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::rlc::CallOp call, mlir::Value arg, mlir::SmallVector memberAddress) { + // LARGE TODO think about this part. if( mlir::rlc::CanOp can = llvm::dyn_cast(call.getCallee().getDefiningOp())) { auto underlyingFunction = llvm::dyn_cast(*can.getCallee().getDefiningOp()); llvm::SmallVector knownArgsOfUnderlyingFunction; @@ -256,14 +279,14 @@ DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::rlc::Ca continue; } // and find the index of the argument we're interested in. - if(current.value() == arg) { + if(matches(current.value(), arg, memberAddress)) { argIndex = current.index(); break; } } assert(argIndex != -1 && "Expected to find the argument."); DynamicArgumentAnalysis analysis(underlyingFunction, knownArgsOfUnderlyingFunction, argPicker, builder, loc); - return analysis.deduceConstraints(argIndex); + return analysis.deduceIntegerUnboundValueConstraints(underlyingFunction.getPrecondition().getArgument(argIndex), {}); } return { builder.create(loc, min_int), @@ -272,14 +295,14 @@ DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::rlc::Ca } -DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::Value constraint, mlir::Value arg) { +DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::Value constraint, mlir::Value arg, llvm::SmallVector memberAddress) { auto *definingOp = constraint.getDefiningOp(); if (definingOp->getOperands().size() == 2) { - return findImposedConstraints(definingOp, arg); + return findImposedConstraints(definingOp, arg, memberAddress); } if( mlir::rlc::CallOp call = llvm::dyn_cast(definingOp)) { - return findImposedConstraints(call, arg); + return findImposedConstraints(call, arg, memberAddress); } return { @@ -364,15 +387,23 @@ void maybeAssignMax(mlir::Value currentMax, mlir::Value aggregateMax, mlir::Valu builder.setInsertionPointAfter(maybeAssignMax); } -DeducedConstraints DynamicArgumentAnalysis::deduceConstraints(int argIndex) { - auto arg = function.getPrecondition().getArgument(argIndex); - assert(arg.getType().isa() && "Expected an integer."); +mlir::Type getUnboundValueType( mlir::Value arg, llvm::SmallVector memberAddress) { + auto type = arg.getType(); + for (auto index : memberAddress) { + type = type.cast().getBody()[index]; + } + return type; +} + +DeducedConstraints DynamicArgumentAnalysis::deduceIntegerUnboundValueConstraints(mlir::Value arg, llvm::SmallVector memberAddress) { + auto type = getUnboundValueType(arg, memberAddress); + assert(type.isa() && "Expected an integer."); - auto minVal = builder.create(loc, arg.getType()); + auto minVal = builder.create(loc, type); auto minInt = builder.create(loc, min_int); builder.create(loc, minVal, minInt); - auto maxVal = builder.create(loc, arg.getType()); + auto maxVal = builder.create(loc, type); auto maxInt = builder.create(loc, max_int); builder.create(loc, maxVal, maxInt); @@ -381,20 +412,28 @@ DeducedConstraints DynamicArgumentAnalysis::deduceConstraints(int argIndex) { llvm::SmallVector conditions; // categorize the terms in the conjunction for(auto term : conjunction) { - TermType type = decideTermType(term, arg); - if(type == DEPENDS_ON_ARG) { + TermType type = decideTermType(term, arg, memberAddress); + if(type == DEPENDS_ON_UNBOUND_VALUE) { constraints.emplace_back(term); } else if (type == KNOWN_VALUE){ conditions.emplace_back(term); } } + llvm::dbgs() << "CONSTRAINTS: \n"; + for (auto t : constraints) { + llvm::dbgs() << "( "; + t.print(llvm::dbgs()); + llvm::dbgs() << " ),"; + } + llvm::dbgs() << "\n"; + // if there are any constraints on unknown args, emit an if statement for this conjunction. if(constraints.size() > 0) { - auto minForThisConjunction = builder.create(loc, arg.getType()); + auto minForThisConjunction = builder.create(loc, type); builder.create(loc, minForThisConjunction, minInt); - auto maxForThisConjunction = builder.create(loc, arg.getType()); + auto maxForThisConjunction = builder.create(loc, type); builder.create(loc, maxForThisConjunction, maxInt); // if the conditions are met, the constraints are active. @@ -413,7 +452,7 @@ DeducedConstraints DynamicArgumentAnalysis::deduceConstraints(int argIndex) { builder.createBlock(&ifStatement.getTrueBranch()); for(auto constraint : constraints) { - auto imposedConstraints = findImposedConstraints(constraint, arg); + auto imposedConstraints = findImposedConstraints(constraint, arg, memberAddress); // if the minimum imposed by this constraint is greater than the current minimum, set the current minimum. assignIfGreaterthan(imposedConstraints.min, minForThisConjunction, builder, loc); @@ -436,8 +475,8 @@ DeducedConstraints DynamicArgumentAnalysis::deduceConstraints(int argIndex) { return {minVal, maxVal}; } -mlir::Value DynamicArgumentAnalysis::pickArg(int argIndex) { - auto deduced = deduceConstraints(argIndex); +mlir::Value DynamicArgumentAnalysis::pickIntegerUnboundValue(mlir::Value arg, llvm::SmallVector memberAddress) { + auto deduced = deduceIntegerUnboundValueConstraints(arg, memberAddress); auto call = builder.create( loc, argPicker, @@ -447,6 +486,37 @@ mlir::Value DynamicArgumentAnalysis::pickArg(int argIndex) { return call.getResult(0); } +/* + memberAddress is the "path" from the arg to the value we want to pick. + Example: arg = arg2, memberAddress = [2, 1] maps to MemberAccess(MemberAccess(arg,2), 1) +*/ +mlir::Value DynamicArgumentAnalysis::pickUnboundValue(mlir::Value arg, llvm::SmallVector memberAddress) { + auto type = getUnboundValueType(arg, memberAddress); + if(type.isa()) { + return pickIntegerUnboundValue(arg, memberAddress); + } + + if(auto entityType = mlir::dyn_cast(type)) { + auto entity = builder.create(loc, entityType); + for( auto memberType : llvm::enumerate(entityType.getBody())) { + llvm::SmallVector newMemberAddress(memberAddress); + newMemberAddress.emplace_back(memberType.index()); + auto value = pickUnboundValue(arg, newMemberAddress); + auto access = builder.create(loc, entity, memberType.index()); + builder.create(loc, access, value); + } + return entity; + } + + assert(false && "Can only handle unbound values of type int and struct."); + return nullptr; +} + +mlir::Value DynamicArgumentAnalysis::pickArg(int argIndex) { + auto arg = function.getPrecondition().getArgument(argIndex); + return pickUnboundValue(arg, {}); +} + namespace mlir::rlc { #define GEN_PASS_DEF_DYNAMICARGUMENTANALYSISPASS diff --git a/lib/dialect/src/EmitFuzzTargetPass.cpp b/lib/dialect/src/EmitFuzzTargetPass.cpp index 0284da0c..1f5e0af9 100644 --- a/lib/dialect/src/EmitFuzzTargetPass.cpp +++ b/lib/dialect/src/EmitFuzzTargetPass.cpp @@ -226,11 +226,10 @@ static llvm::SmallVector emitSubactionArgumentDeclarations( // the action entity is bound before picking the first input, every input becomes bound when it's picked. llvm::SmallVector boundArguments {actionEntity}; for(auto inputType : inputsExcludingActionEntity) { - assert(inputType.value().isa() && "Fuzzing can only handle integer arguments for now."); auto pickedArg = builder.create( loc, mlir::TypeRange({ - mlir::rlc::IntegerType::getInt64(builder.getContext()) + inputType.value() }), subactionFunction, (uint8_t)inputType.index(), diff --git a/tool/rlc/test/fuzzer_pick_struct.rl b/tool/rlc/test/fuzzer_pick_struct.rl new file mode 100644 index 00000000..7107c4ed --- /dev/null +++ b/tool/rlc/test/fuzzer_pick_struct.rl @@ -0,0 +1,11 @@ +import fuzzer.cpp_functions +import fuzzer.utils + +ent Test: + Int field1 + Int field2 + +act play() -> Play: + act uses_struct(Test t) {t.field1 > 0, t.field1 < 5, t.field2 > 7, t.field2 < 16} + + From 8cdde8335f5fb68f0d01c2cc6e8e649ac16200db Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Thu, 15 Feb 2024 16:12:12 +0100 Subject: [PATCH 11/16] bind values of previously picked struct fields while picking new ones. --- .../rlc/dialect/DynamicArgumentAnalysis.hpp | 77 +++++++++-- .../src/DynamicArgumentAnalysisPass.cpp | 120 +++++++----------- tool/rlc/test/fuzzer_pick_struct.rl | 14 +- 3 files changed, 125 insertions(+), 86 deletions(-) diff --git a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp index 9d9e713a..c18cc22f 100644 --- a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp +++ b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp @@ -1,6 +1,8 @@ #include #include +#include #include +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallVector.h" #include "mlir/IR/Builders.h" #include "mlir/IR/Location.h" @@ -18,7 +20,7 @@ struct DeducedConstraints { }; enum TermType { - DEPENDS_ON_UNBOUND_VALUE, + DEPENDS_ON_UNBOUND, DEPENDS_ON_OTHER_UNKNOWNS, KNOWN_VALUE }; @@ -65,27 +67,82 @@ enum TermType { and similarly for the maximum. */ + + +/* + memberAddress is the "path" from the arg to the value we want to pick. + Example: arg = arg2, memberAddress = [2, 1] maps to MemberAccess(MemberAccess(arg,2), 1) +*/ +struct UnboundValue { + mlir::Value argument; + llvm::SmallVector memberAddress; + + /* + Returns whether this unbound value corressponds to the term. + */ + bool matches(mlir::Value term) { + mlir::Value current = term; + // walk the member address in reverse, test if it leads to the argument. + for(uint64_t & index : std::ranges::reverse_view(memberAddress)) { + auto definingOp = current.getDefiningOp(); + if( not llvm::detail::isPresent(definingOp)) + return false; + if(auto memberAccess = mlir::dyn_cast(definingOp)) { + if (memberAccess.getMemberIndex() != index) { + return false; + } + current = memberAccess.getValue(); + } else { + return false; + } + } + return current == argument; + } + + mlir::Type getType() { + auto type = argument.getType(); + for (auto index : memberAddress) { + type = type.cast().getBody()[index]; + } + return type; + } +}; + class DynamicArgumentAnalysis { public: - explicit DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange knownArgs, mlir::Value argPicker, mlir::OpBuilder builder, mlir::Location loc); + explicit DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange boundArgs, mlir::Value argPicker, mlir::OpBuilder builder, mlir::Location loc); mlir::Value pickArg(int argIndex); private: - DeducedConstraints deduceIntegerUnboundValueConstraints(mlir::Value arg, llvm::SmallVector memberAddress); + DeducedConstraints deduceIntegerUnboundValueConstraints(UnboundValue unbound); llvm::SmallVector> expandToDNF(mlir::Value constraint); - TermType decideTermType(mlir::Value term, mlir::Value argument, mlir::SmallVector memberAddress); + TermType decideTermType(mlir::Value term, UnboundValue unbound); mlir::Value compute(mlir::Value expression); - DeducedConstraints findImposedConstraints(mlir::Value constraint, mlir::Value arg, mlir::SmallVector memberAddress); - DeducedConstraints findImposedConstraints(mlir::Operation *binaryOperation, mlir::Value arg, mlir::SmallVector memberAddress); - DeducedConstraints findImposedConstraints(mlir::rlc::CallOp call, mlir::Value arg, mlir::SmallVector memberAddress); + DeducedConstraints findImposedConstraints(mlir::Value constraint, UnboundValue unbound); + DeducedConstraints findImposedConstraints(mlir::Operation *binaryOperation, UnboundValue unbound); + DeducedConstraints findImposedConstraints(mlir::rlc::CallOp call, UnboundValue unbound); - mlir::Value pickIntegerUnboundValue(mlir::Value arg, llvm::SmallVector memberAddress); - mlir::Value pickUnboundValue(mlir::Value arg, llvm::SmallVector memberAddress); + mlir::Value pickIntegerUnboundValue(UnboundValue unbound); + mlir::Value pickUnboundValue(UnboundValue unbound); mlir::rlc::FunctionOp function; mlir::Region& precondition; - mlir::ValueRange knownArgs; + + llvm::SmallVector> bindings; + /* + If the passed value matches an UnboundValue that has a binding, + return the value bound to it. + Otherwise returns nullptr. + */ + mlir::Value getBoundValue(mlir::Value expr) { + for(auto binding : bindings) { + if(binding.first.matches(expr)) + return binding.second; + } + return nullptr; + } + mlir::Value argPicker; mlir::OpBuilder builder; mlir::Location loc; diff --git a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp index 554465ea..a736c9b9 100644 --- a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp +++ b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp @@ -1,8 +1,7 @@ #include #include -#include -#include #include +#include #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/Casting.h" @@ -20,13 +19,17 @@ #include "rlc/dialect/DynamicArgumentAnalysis.hpp" #include "rlc/dialect/Types.hpp" -DynamicArgumentAnalysis::DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange knownArgs, mlir::Value argPicker, mlir::OpBuilder builder, mlir::Location loc): +DynamicArgumentAnalysis::DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange boundArgs, mlir::Value argPicker, mlir::OpBuilder builder, mlir::Location loc): function(op), precondition(op.getPrecondition()), - knownArgs(knownArgs), argPicker(argPicker), builder(builder), loc(loc) { + for (auto boundArg : llvm::enumerate(boundArgs)) { + UnboundValue arg {op.getPrecondition().getArgument(boundArg.index()), {}}; + bindings.emplace_back(std::pair(arg, boundArg.value())); + } + auto yield = mlir::dyn_cast(precondition.getBlocks().front().back()); assert(yield->getNumOperands() == 1); conjunctions = expandToDNF(yield->getOperand(0)); @@ -82,35 +85,17 @@ llvm::SmallVector> DynamicArgumentAnalysis::expan return conjunctions; } -bool matches(mlir::Value term, mlir::Value argument, llvm::SmallVector memberAddress) { - mlir::Value current = term; - // walk the member address in reverse, test if it leads to the argument. - for(uint64_t & index : std::ranges::reverse_view(memberAddress)) { - auto definingOp = current.getDefiningOp(); - if( not llvm::detail::isPresent(definingOp)) - return false; - if(auto memberAccess = mlir::dyn_cast(definingOp)) { - if (memberAccess.getMemberIndex() != index) { - return false; - } - current = memberAccess.getValue(); - } else { - return false; - } - } - return current == argument; -} +TermType DynamicArgumentAnalysis::decideTermType(mlir::Value term, UnboundValue unbound) { + if(unbound.matches(term)) + return DEPENDS_ON_UNBOUND; -TermType DynamicArgumentAnalysis::decideTermType(mlir::Value term, mlir::Value argument, llvm::SmallVector memberAddress) { - if(matches(term, argument, memberAddress)) - return DEPENDS_ON_UNBOUND_VALUE; + auto boundValue = getBoundValue(term); + if(boundValue != nullptr) + return KNOWN_VALUE; - if(auto arg = llvm::dyn_cast(term)) { - if(arg.getArgNumber() < knownArgs.size()) - return KNOWN_VALUE; - + if(term.isa()) return DEPENDS_ON_OTHER_UNKNOWNS; - } + auto definingOp = term.getDefiningOp(); @@ -124,23 +109,24 @@ TermType DynamicArgumentAnalysis::decideTermType(mlir::Value term, mlir::Value a bool dependsOnOtherUnkowns = false; for(auto operand : definingOp->getOperands()) { - auto operandType = decideTermType(operand, argument, memberAddress); - if(operandType == DEPENDS_ON_UNBOUND_VALUE) + auto operandType = decideTermType(operand, unbound); + if(operandType == DEPENDS_ON_UNBOUND) dependsOnArg = true; else if (operandType == DEPENDS_ON_OTHER_UNKNOWNS) dependsOnOtherUnkowns = true; } - if (dependsOnArg) { return DEPENDS_ON_UNBOUND_VALUE; } + if (dependsOnArg) { return DEPENDS_ON_UNBOUND; } if (dependsOnOtherUnkowns) { return DEPENDS_ON_OTHER_UNKNOWNS; }; return KNOWN_VALUE; } mlir::Value DynamicArgumentAnalysis::compute(mlir::Value expression) { - if(auto arg = llvm::dyn_cast(expression)) { - assert(arg.getArgNumber() < knownArgs.size()); - return knownArgs[arg.getArgNumber()]; - } + auto boundValue = getBoundValue(expression); + if(boundValue != nullptr) + return boundValue; + + assert(not expression.isa() && "The expression to be computed depends on an unbound argument."); if (expression.getDefiningOp()->getParentRegion() != precondition) return expression; @@ -166,8 +152,8 @@ mlir::Value DynamicArgumentAnalysis::compute(mlir::Value expression) { const int64_t min_int = -800; const int64_t max_int = 800; -DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::Operation *binaryOperation, mlir::Value arg, mlir::SmallVector memberAddress) { - if (matches(binaryOperation->getOperand(0), arg, memberAddress)) { +DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::Operation *binaryOperation, UnboundValue unbound) { + if (unbound.matches(binaryOperation->getOperand(0))) { auto rhs = compute(binaryOperation->getOperand(1)); if (mlir::isa(binaryOperation)) @@ -213,7 +199,7 @@ DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::Operati } } - if(matches(binaryOperation->getOperand(1), arg, memberAddress)) { + if(unbound.matches(binaryOperation->getOperand(1))) { auto lhs = compute(binaryOperation->getOperand(0)); if (mlir::isa(binaryOperation)) @@ -264,7 +250,7 @@ DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::Operati }; } -DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::rlc::CallOp call, mlir::Value arg, mlir::SmallVector memberAddress) { +DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::rlc::CallOp call, UnboundValue unbound) { // LARGE TODO think about this part. if( mlir::rlc::CanOp can = llvm::dyn_cast(call.getCallee().getDefiningOp())) { auto underlyingFunction = llvm::dyn_cast(*can.getCallee().getDefiningOp()); @@ -279,14 +265,15 @@ DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::rlc::Ca continue; } // and find the index of the argument we're interested in. - if(matches(current.value(), arg, memberAddress)) { + if(unbound.matches(current.value())) { argIndex = current.index(); break; } } assert(argIndex != -1 && "Expected to find the argument."); DynamicArgumentAnalysis analysis(underlyingFunction, knownArgsOfUnderlyingFunction, argPicker, builder, loc); - return analysis.deduceIntegerUnboundValueConstraints(underlyingFunction.getPrecondition().getArgument(argIndex), {}); + UnboundValue correspongindUnboundValue {underlyingFunction.getPrecondition().getArgument(argIndex), {}}; + return analysis.deduceIntegerUnboundValueConstraints(correspongindUnboundValue); } return { builder.create(loc, min_int), @@ -295,14 +282,14 @@ DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::rlc::Ca } -DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::Value constraint, mlir::Value arg, llvm::SmallVector memberAddress) { +DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::Value constraint, UnboundValue unbound) { auto *definingOp = constraint.getDefiningOp(); if (definingOp->getOperands().size() == 2) { - return findImposedConstraints(definingOp, arg, memberAddress); + return findImposedConstraints(definingOp, unbound); } if( mlir::rlc::CallOp call = llvm::dyn_cast(definingOp)) { - return findImposedConstraints(call, arg, memberAddress); + return findImposedConstraints(call, unbound); } return { @@ -387,16 +374,8 @@ void maybeAssignMax(mlir::Value currentMax, mlir::Value aggregateMax, mlir::Valu builder.setInsertionPointAfter(maybeAssignMax); } -mlir::Type getUnboundValueType( mlir::Value arg, llvm::SmallVector memberAddress) { - auto type = arg.getType(); - for (auto index : memberAddress) { - type = type.cast().getBody()[index]; - } - return type; -} - -DeducedConstraints DynamicArgumentAnalysis::deduceIntegerUnboundValueConstraints(mlir::Value arg, llvm::SmallVector memberAddress) { - auto type = getUnboundValueType(arg, memberAddress); +DeducedConstraints DynamicArgumentAnalysis::deduceIntegerUnboundValueConstraints(UnboundValue unbound) { + auto type = unbound.getType(); assert(type.isa() && "Expected an integer."); auto minVal = builder.create(loc, type); @@ -412,8 +391,8 @@ DeducedConstraints DynamicArgumentAnalysis::deduceIntegerUnboundValueConstraints llvm::SmallVector conditions; // categorize the terms in the conjunction for(auto term : conjunction) { - TermType type = decideTermType(term, arg, memberAddress); - if(type == DEPENDS_ON_UNBOUND_VALUE) { + TermType type = decideTermType(term, unbound); + if(type == DEPENDS_ON_UNBOUND) { constraints.emplace_back(term); } else if (type == KNOWN_VALUE){ conditions.emplace_back(term); @@ -452,7 +431,7 @@ DeducedConstraints DynamicArgumentAnalysis::deduceIntegerUnboundValueConstraints builder.createBlock(&ifStatement.getTrueBranch()); for(auto constraint : constraints) { - auto imposedConstraints = findImposedConstraints(constraint, arg, memberAddress); + auto imposedConstraints = findImposedConstraints(constraint, unbound); // if the minimum imposed by this constraint is greater than the current minimum, set the current minimum. assignIfGreaterthan(imposedConstraints.min, minForThisConjunction, builder, loc); @@ -475,8 +454,8 @@ DeducedConstraints DynamicArgumentAnalysis::deduceIntegerUnboundValueConstraints return {minVal, maxVal}; } -mlir::Value DynamicArgumentAnalysis::pickIntegerUnboundValue(mlir::Value arg, llvm::SmallVector memberAddress) { - auto deduced = deduceIntegerUnboundValueConstraints(arg, memberAddress); +mlir::Value DynamicArgumentAnalysis::pickIntegerUnboundValue(UnboundValue unbound) { + auto deduced = deduceIntegerUnboundValueConstraints(unbound); auto call = builder.create( loc, argPicker, @@ -486,24 +465,23 @@ mlir::Value DynamicArgumentAnalysis::pickIntegerUnboundValue(mlir::Value arg, ll return call.getResult(0); } -/* - memberAddress is the "path" from the arg to the value we want to pick. - Example: arg = arg2, memberAddress = [2, 1] maps to MemberAccess(MemberAccess(arg,2), 1) -*/ -mlir::Value DynamicArgumentAnalysis::pickUnboundValue(mlir::Value arg, llvm::SmallVector memberAddress) { - auto type = getUnboundValueType(arg, memberAddress); +mlir::Value DynamicArgumentAnalysis::pickUnboundValue(UnboundValue unbound) { + auto type = unbound.getType(); if(type.isa()) { - return pickIntegerUnboundValue(arg, memberAddress); + return pickIntegerUnboundValue(unbound); } if(auto entityType = mlir::dyn_cast(type)) { auto entity = builder.create(loc, entityType); for( auto memberType : llvm::enumerate(entityType.getBody())) { - llvm::SmallVector newMemberAddress(memberAddress); + llvm::SmallVector newMemberAddress(unbound.memberAddress); newMemberAddress.emplace_back(memberType.index()); - auto value = pickUnboundValue(arg, newMemberAddress); + UnboundValue newUnboundValue {unbound.argument, newMemberAddress}; + auto value = pickUnboundValue(newUnboundValue); auto access = builder.create(loc, entity, memberType.index()); builder.create(loc, access, value); + // bind the value of this struct field while we pick the next field. + bindings.emplace_back(std::pair(newUnboundValue, value)); } return entity; } @@ -514,7 +492,7 @@ mlir::Value DynamicArgumentAnalysis::pickUnboundValue(mlir::Value arg, llvm::Sma mlir::Value DynamicArgumentAnalysis::pickArg(int argIndex) { auto arg = function.getPrecondition().getArgument(argIndex); - return pickUnboundValue(arg, {}); + return pickUnboundValue({arg, {}}); } namespace mlir::rlc diff --git a/tool/rlc/test/fuzzer_pick_struct.rl b/tool/rlc/test/fuzzer_pick_struct.rl index 7107c4ed..ee3aadff 100644 --- a/tool/rlc/test/fuzzer_pick_struct.rl +++ b/tool/rlc/test/fuzzer_pick_struct.rl @@ -2,10 +2,14 @@ import fuzzer.cpp_functions import fuzzer.utils ent Test: - Int field1 - Int field2 + Int a + Int b act play() -> Play: - act uses_struct(Test t) {t.field1 > 0, t.field1 < 5, t.field2 > 7, t.field2 < 16} - - + act uses_struct(Test t) { + t.a >= 0, + t.a <= 5, + t.b >= -10, + t.a <= 2 or t.b >= 5, + t.b <= 16 + } From 81933b96bc192173b759a239194c14cea0fbcc02 Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Thu, 15 Feb 2024 20:41:38 +0100 Subject: [PATCH 12/16] fixed struct args in wrapped subaction functions. --- .../rlc/dialect/DynamicArgumentAnalysis.hpp | 32 +++++++++++++++++-- .../src/DynamicArgumentAnalysisPass.cpp | 13 ++------ tool/rlc/test/fuzzer_pick_struct.rl | 19 +++++++++++ 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp index c18cc22f..d90d0469 100644 --- a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp +++ b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp @@ -78,10 +78,10 @@ struct UnboundValue { llvm::SmallVector memberAddress; /* - Returns whether this unbound value corressponds to the term. + Returns whether this unbound value corressponds to the expression. */ - bool matches(mlir::Value term) { - mlir::Value current = term; + bool matches(mlir::Value expr) { + mlir::Value current = expr; // walk the member address in reverse, test if it leads to the argument. for(uint64_t & index : std::ranges::reverse_view(memberAddress)) { auto definingOp = current.getDefiningOp(); @@ -99,6 +99,32 @@ struct UnboundValue { return current == argument; } + /* + Returns true if this unbound value is a recursive member of the given expression. + */ + bool isMemberOf(mlir::Value expr) { + mlir::Value current = expr; + llvm::SmallVector indices; + + while(true) { + if(current == argument) { + for(size_t i = 0; i < indices.size(); i++) { + if(indices[i] != memberAddress[i]) + return false; + } + return true; + } + + auto definingOp = current.getDefiningOp(); + if( not llvm::detail::isPresent(definingOp)) + return false; + if(auto memberAccess = mlir::dyn_cast(definingOp)) { + indices.insert(indices.begin(), memberAccess.getMemberIndex()); + current = memberAccess.getValue(); + } else return false; + } + } + mlir::Type getType() { auto type = argument.getType(); for (auto index : memberAddress) { diff --git a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp index a736c9b9..65508482 100644 --- a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp +++ b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp @@ -86,7 +86,7 @@ llvm::SmallVector> DynamicArgumentAnalysis::expan } TermType DynamicArgumentAnalysis::decideTermType(mlir::Value term, UnboundValue unbound) { - if(unbound.matches(term)) + if(unbound.isMemberOf(term)) return DEPENDS_ON_UNBOUND; auto boundValue = getBoundValue(term); @@ -265,14 +265,14 @@ DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::rlc::Ca continue; } // and find the index of the argument we're interested in. - if(unbound.matches(current.value())) { + if(unbound.argument == current.value()) { argIndex = current.index(); break; } } assert(argIndex != -1 && "Expected to find the argument."); DynamicArgumentAnalysis analysis(underlyingFunction, knownArgsOfUnderlyingFunction, argPicker, builder, loc); - UnboundValue correspongindUnboundValue {underlyingFunction.getPrecondition().getArgument(argIndex), {}}; + UnboundValue correspongindUnboundValue {underlyingFunction.getPrecondition().getArgument(argIndex), unbound.memberAddress}; return analysis.deduceIntegerUnboundValueConstraints(correspongindUnboundValue); } return { @@ -399,13 +399,6 @@ DeducedConstraints DynamicArgumentAnalysis::deduceIntegerUnboundValueConstraints } } - llvm::dbgs() << "CONSTRAINTS: \n"; - for (auto t : constraints) { - llvm::dbgs() << "( "; - t.print(llvm::dbgs()); - llvm::dbgs() << " ),"; - } - llvm::dbgs() << "\n"; // if there are any constraints on unknown args, emit an if statement for this conjunction. if(constraints.size() > 0) { diff --git a/tool/rlc/test/fuzzer_pick_struct.rl b/tool/rlc/test/fuzzer_pick_struct.rl index ee3aadff..4f1f18de 100644 --- a/tool/rlc/test/fuzzer_pick_struct.rl +++ b/tool/rlc/test/fuzzer_pick_struct.rl @@ -1,11 +1,24 @@ import fuzzer.cpp_functions import fuzzer.utils +fun crash() {false}: + return + ent Test: Int a Int b +act subact(ctx Test context_struct) -> Subact: + frm subact_frame_var : Int + subact_frame_var = 250 + act uses_context_struct(Test t1) { + t1.a == context_struct.b, + t1.b > context_struct.a, + t1.b < subact_frame_var + } + act play() -> Play: + frm struct_in_frame : Test act uses_struct(Test t) { t.a >= 0, t.a <= 5, @@ -13,3 +26,9 @@ act play() -> Play: t.a <= 2 or t.b >= 5, t.b <= 16 } + struct_in_frame = t + + subaction*(struct_in_frame) s = subact(struct_in_frame) + crash() + + From 1f0c92db12ec078c12133b3a9b4a1b38243c4383 Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Thu, 15 Feb 2024 22:58:16 +0100 Subject: [PATCH 13/16] correctly use the assign operators to assign non-built-in data types. --- .../src/DynamicArgumentAnalysisPass.cpp | 19 ++++++++++++++++++- tool/rlc/test/fuzzer_pick_struct.rl | 11 +++++++---- 2 files changed, 25 insertions(+), 5 deletions(-) diff --git a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp index 65508482..e89adab8 100644 --- a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp +++ b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp @@ -472,7 +472,24 @@ mlir::Value DynamicArgumentAnalysis::pickUnboundValue(UnboundValue unbound) { UnboundValue newUnboundValue {unbound.argument, newMemberAddress}; auto value = pickUnboundValue(newUnboundValue); auto access = builder.create(loc, entity, memberType.index()); - builder.create(loc, access, value); + if(value.getType().isa()) { + builder.create(loc, access, value); + } else { + // find and use the assign operator for this data type. + auto module = access->getParentOfType(); + mlir::Value assign = nullptr; + for (auto op : module.getOps()) { + if(op.getUnmangledName().equals(mlir::rlc::builtinOperatorName()) + && op.getArgumentTypes().size() > 0 + && op.getArgumentTypes()[0] == memberType.value()) { + assign = op.getResult(); + break; + } + } + assert(assign != nullptr && "Can't find the assignment operator for the data type."); + builder.create(loc, assign, false, mlir::ValueRange({access, value})); + } + // bind the value of this struct field while we pick the next field. bindings.emplace_back(std::pair(newUnboundValue, value)); } diff --git a/tool/rlc/test/fuzzer_pick_struct.rl b/tool/rlc/test/fuzzer_pick_struct.rl index 4f1f18de..6d40f846 100644 --- a/tool/rlc/test/fuzzer_pick_struct.rl +++ b/tool/rlc/test/fuzzer_pick_struct.rl @@ -4,9 +4,13 @@ import fuzzer.utils fun crash() {false}: return +ent InnerTest: + Int c + ent Test: Int a Int b + InnerTest inner act subact(ctx Test context_struct) -> Subact: frm subact_frame_var : Int @@ -14,7 +18,8 @@ act subact(ctx Test context_struct) -> Subact: act uses_context_struct(Test t1) { t1.a == context_struct.b, t1.b > context_struct.a, - t1.b < subact_frame_var + t1.b < subact_frame_var, + t1.inner.c == context_struct.b } act play() -> Play: @@ -27,8 +32,6 @@ act play() -> Play: t.b <= 16 } struct_in_frame = t - + act pick_substruct(InnerTest it) subaction*(struct_in_frame) s = subact(struct_in_frame) crash() - - From ffb82ecd5b22b4a92b805ed1707a1697034b2c60 Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Thu, 15 Feb 2024 23:14:54 +0100 Subject: [PATCH 14/16] correctly transfer all bindings when analysing a wrapped precondition. --- .../include/rlc/dialect/DynamicArgumentAnalysis.hpp | 2 +- lib/dialect/src/DynamicArgumentAnalysisPass.cpp | 11 +++++++++++ tool/rlc/test/fuzzer_pick_struct.rl | 2 +- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp index d90d0469..5526cffb 100644 --- a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp +++ b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp @@ -74,7 +74,7 @@ enum TermType { Example: arg = arg2, memberAddress = [2, 1] maps to MemberAccess(MemberAccess(arg,2), 1) */ struct UnboundValue { - mlir::Value argument; + mlir::BlockArgument argument; llvm::SmallVector memberAddress; /* diff --git a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp index e89adab8..0b995fc4 100644 --- a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp +++ b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp @@ -272,6 +272,17 @@ DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::rlc::Ca } assert(argIndex != -1 && "Expected to find the argument."); DynamicArgumentAnalysis analysis(underlyingFunction, knownArgsOfUnderlyingFunction, argPicker, builder, loc); + for(auto binding: bindings) { + if(binding.first.memberAddress.size() != 0) { + // the binding does not just correspond to an argument. + auto indexOfArg = std::distance( + call.getArgs().begin(), + llvm::find(call.getArgs(), unbound.argument)); + // add an equivalent binding using the callee's arg. + UnboundValue newUnboundValue {underlyingFunction.getPrecondition().getArgument(indexOfArg), binding.first.memberAddress}; + analysis.bindings.emplace_back(std::pair(newUnboundValue, binding.second)); + } + } UnboundValue correspongindUnboundValue {underlyingFunction.getPrecondition().getArgument(argIndex), unbound.memberAddress}; return analysis.deduceIntegerUnboundValueConstraints(correspongindUnboundValue); } diff --git a/tool/rlc/test/fuzzer_pick_struct.rl b/tool/rlc/test/fuzzer_pick_struct.rl index 6d40f846..b7908555 100644 --- a/tool/rlc/test/fuzzer_pick_struct.rl +++ b/tool/rlc/test/fuzzer_pick_struct.rl @@ -19,7 +19,7 @@ act subact(ctx Test context_struct) -> Subact: t1.a == context_struct.b, t1.b > context_struct.a, t1.b < subact_frame_var, - t1.inner.c == context_struct.b + t1.inner.c == context_struct.b + t1.b } act play() -> Play: From 95df97e7e58a0ebead744080b13c85f0d3de468c Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Wed, 21 Feb 2024 20:29:43 +0100 Subject: [PATCH 15/16] implement the option to not check legality of subactions. --- lib/dialect/src/EmitFuzzTargetPass.cpp | 136 +++++++++++++---------- lib/dialect/src/Passes.td | 2 + lib/driver/include/rlc/driver/Driver.hpp | 10 ++ lib/driver/src/Driver.cpp | 5 +- tool/rlc/src/Main.cpp | 16 +++ 5 files changed, 108 insertions(+), 61 deletions(-) diff --git a/lib/dialect/src/EmitFuzzTargetPass.cpp b/lib/dialect/src/EmitFuzzTargetPass.cpp index 1f5e0af9..af70d1c3 100644 --- a/lib/dialect/src/EmitFuzzTargetPass.cpp +++ b/lib/dialect/src/EmitFuzzTargetPass.cpp @@ -42,6 +42,11 @@ limitations under the License. #include "rlc/dialect/SymbolTable.h" #include "rlc/dialect/Types.hpp" +struct Options { + bool avoidUnavailableSubactions; + bool analysePreconditions; +}; + static mlir::Value findFunction(mlir::ModuleOp module, llvm::StringRef functionName) { for (auto op : module.getOps()) if(op.getUnmangledName().equals(functionName)) @@ -127,75 +132,86 @@ static mlir::Value emitChosenActionDeclaration( mlir::rlc::ActionFunction action, mlir::Value actionEntity, mlir::Value availableSubactionsVector, + Options options, mlir::rlc::ModuleBuilder &moduleBuilder, mlir::OpBuilder builder ) { auto ip = builder.saveInsertionPoint(); + mlir::Value chosenAction; + if(options.avoidUnavailableSubactions) { + auto clearAvailableSubactions = findFunction(action->getParentOfType(), "fuzzer_clear_available_subactions"); + auto addAvailableSubaction = findFunction(action->getParentOfType(), "fuzzer_add_available_subaction"); + auto pickSubaction = findFunction(action->getParentOfType(), "fuzzer_pick_subaction"); - auto clearAvailableSubactions = findFunction(action->getParentOfType(), "fuzzer_clear_available_subactions"); - auto addAvailableSubaction = findFunction(action->getParentOfType(), "fuzzer_add_available_subaction"); - auto pickSubaction = findFunction(action->getParentOfType(), "fuzzer_pick_subaction"); - - // availableSubactions.clear() - builder.create( - action->getLoc(), - clearAvailableSubactions, - false, - mlir::ValueRange({availableSubactionsVector}) - ); - - // for each subaction, - // if(subAction.resumptionIndex == actionEntity.resumptionIndex) - // availableSubactions.push(subactionID) - int64_t index = 0; - for(auto subactionFunction : action.getActions()) { - auto ifStatement = builder.create(action->getLoc()); - builder.createBlock(&ifStatement.getCondition()); - auto storedResumptionPoint = builder.create( + // availableSubactions.clear() + builder.create( action->getLoc(), - actionEntity, - 0 + clearAvailableSubactions, + false, + mlir::ValueRange({availableSubactionsVector}) ); - // the subactionFunction is available if the stored resumptionIndex matches any of its acitonStatements' resumptionIndex. - auto actionStatements = moduleBuilder.actionFunctionValueToActionStatement(subactionFunction); - mlir::Value lastOperand = - builder.create(action.getLoc(), false); - for(auto *actionStatement : actionStatements) { - auto cast = mlir::dyn_cast(actionStatement); - auto subactionResumptionPoint = builder.create(action.getLoc(), (int64_t) cast.getResumptionPoint()); - auto eq = builder.create(action->getLoc(), storedResumptionPoint, subactionResumptionPoint); - lastOperand = builder.create(action.getLoc(), lastOperand, eq.getResult()); + // for each subaction, + // if(subAction.resumptionIndex == actionEntity.resumptionIndex) + // availableSubactions.push(subactionID) + int64_t index = 0; + for(auto subactionFunction : action.getActions()) { + auto ifStatement = builder.create(action->getLoc()); + builder.createBlock(&ifStatement.getCondition()); + auto storedResumptionPoint = builder.create( + action->getLoc(), + actionEntity, + 0 + ); + + // the subactionFunction is available if the stored resumptionIndex matches any of its acitonStatements' resumptionIndex. + auto actionStatements = moduleBuilder.actionFunctionValueToActionStatement(subactionFunction); + mlir::Value lastOperand = + builder.create(action.getLoc(), false); + for(auto *actionStatement : actionStatements) { + auto cast = mlir::dyn_cast(actionStatement); + auto subactionResumptionPoint = builder.create(action.getLoc(), (int64_t) cast.getResumptionPoint()); + auto eq = builder.create(action->getLoc(), storedResumptionPoint, subactionResumptionPoint); + lastOperand = builder.create(action.getLoc(), lastOperand, eq.getResult()); + } + + builder.create(ifStatement.getLoc(), lastOperand); + + builder.createBlock(&ifStatement.getTrueBranch()); + auto subactionIndex = builder.create(action->getLoc(), index); + builder.create( + action->getLoc(), + addAvailableSubaction, + false, + mlir::ValueRange{availableSubactionsVector, subactionIndex.getResult()} + ); + builder.create(action->getLoc()); + + // construct the false branch that does nothing + auto *falseBranch = builder.createBlock(&ifStatement.getElseBranch()); + builder.create(ifStatement.getLoc()); + + builder.setInsertionPointAfter(ifStatement); + index++; } - - builder.create(ifStatement.getLoc(), lastOperand); - builder.createBlock(&ifStatement.getTrueBranch()); - auto subactionIndex = builder.create(action->getLoc(), index); - builder.create( + // let chosenAction = pick_subaction(availableSubactions) + chosenAction = builder.create( action->getLoc(), - addAvailableSubaction, + pickSubaction, false, - mlir::ValueRange{availableSubactionsVector, subactionIndex.getResult()} - ); - builder.create(action->getLoc()); - - // construct the false branch that does nothing - auto *falseBranch = builder.createBlock(&ifStatement.getElseBranch()); - builder.create(ifStatement.getLoc()); - - builder.setInsertionPointAfter(ifStatement); - index++; + mlir::ValueRange{availableSubactionsVector} + )->getResult(0); + } else { + auto getInput = findFunction(action->getParentOfType(), "fuzzer_get_input"); + auto numSubactions = builder.create(action->getLoc(), (int64_t)action.getActions().size()); + chosenAction = builder.create( + action.getLoc(), + getInput, + false, + mlir::ValueRange{numSubactions} + )->getResult(0); } - - // let chosenAction = pick_subaction(availableSubactions) - auto chosenAction = builder.create( - action->getLoc(), - pickSubaction, - false, - mlir::ValueRange{availableSubactionsVector} - )->getResult(0); - builder.restoreInsertionPoint(ip); return chosenAction; } @@ -346,7 +362,7 @@ static void emitSubactionCases( ... ... */ -static void emitFuzzActionFunction(mlir::rlc::ActionFunction action) { +static void emitFuzzActionFunction(mlir::rlc::ActionFunction action, Options options) { auto loc = action.getLoc(); mlir::OpBuilder builder(action); mlir::rlc::ModuleBuilder moduleBuilder(action->getParentOfType()); @@ -376,7 +392,7 @@ static void emitFuzzActionFunction(mlir::rlc::ActionFunction action) { {mlir::rlc::IntegerType::getInt64(builder.getContext())} ); auto initAvailableSubactions = findFunction(action->getParentOfType(), "fuzzer_init_available_subactions"); - auto availableSubactions = builder.create( + auto availableSubactions = builder.create( // TODO this might end up being unused, shouldn't be emitted in that case. action->getLoc(), initAvailableSubactions, false, @@ -386,7 +402,7 @@ static void emitFuzzActionFunction(mlir::rlc::ActionFunction action) { auto whileStmt = builder.create(loc); emitLoopCondition(action, &whileStmt.getCondition(), entityDeclaration, stopFlag.getResult(), builder); builder.createBlock(&whileStmt.getBody()); - auto chosenAction = emitChosenActionDeclaration(action, entityDeclaration, availableSubactions, moduleBuilder, builder); + auto chosenAction = emitChosenActionDeclaration(action, entityDeclaration, availableSubactions, options, moduleBuilder, builder); emitSubactionCases(action, &whileStmt.getBody(), entityDeclaration, chosenAction, stopFlag.getResult(), moduleBuilder, builder); builder.create(loc); @@ -415,7 +431,7 @@ namespace mlir::rlc // invoke emitFuzzActionFunction on the ActionFunction with the correct unmangledName for(auto op :module.getOps()) { if(op.getUnmangledName().str() == actionToFuzz) - emitFuzzActionFunction(op); + emitFuzzActionFunction(op, {avoidUnavailableSubactions, analysePreconditions}); } } }; diff --git a/lib/dialect/src/Passes.td b/lib/dialect/src/Passes.td index ef882c71..0493d919 100644 --- a/lib/dialect/src/Passes.td +++ b/lib/dialect/src/Passes.td @@ -234,6 +234,8 @@ def EmitFuzzTargetPass : Pass<"rlc-emit-fuzz-target-pass", "mlir::ModuleOp"> { let options = [ Option<"actionToFuzz", "Action to fuzz", "std::string", /*default=*/"\"play\"", "Name of the action to generate a fuzzer for">, + Option<"avoidUnavailableSubactions", "Avoid unavailable subactions", "bool" , /*default=*/"true", "Whether the fuzzer filters out unavailable subaction calls.">, + Option<"analysePreconditions", "Analyse preconditions", "bool" , /*default=*/"true", "Whether the fuzzer analyses subaction preconditions to pick better arguments">, ]; } diff --git a/lib/driver/include/rlc/driver/Driver.hpp b/lib/driver/include/rlc/driver/Driver.hpp index 8c3f5a75..d85851d9 100644 --- a/lib/driver/include/rlc/driver/Driver.hpp +++ b/lib/driver/include/rlc/driver/Driver.hpp @@ -51,6 +51,14 @@ namespace mlir::rlc { emitFuzzer = newEmitFuzzer; } + void setFuzzerAvoidsUnavailableSubactions(bool newValue) + { + fuzzerAvoidsUnavailableSubactions = newValue; + } + void setFuzzerAnalysesPreconditions(bool newValue) + { + fuzzerAnalysesPreconditions = newValue; + } void setIncludeDirs(llvm::SmallVector newIncludeDirs) { @@ -85,6 +93,8 @@ namespace mlir::rlc bool emitPreconditionChecks = true; bool emitBoundChecks = true; bool emitFuzzer = false; + bool fuzzerAvoidsUnavailableSubactions = true; + bool fuzzerAnalysesPreconditions = true; bool skipParsing = false; bool debug = false; diff --git a/lib/driver/src/Driver.cpp b/lib/driver/src/Driver.cpp index 1415e109..8e46dd07 100644 --- a/lib/driver/src/Driver.cpp +++ b/lib/driver/src/Driver.cpp @@ -35,7 +35,10 @@ namespace mlir::rlc manager.addPass(mlir::rlc::createValidateStorageQualifiersPass()); if (emitFuzzer) { - manager.addPass(mlir::rlc::createEmitFuzzTargetPass()); + manager.addPass(mlir::rlc::createEmitFuzzTargetPass({ + .avoidUnavailableSubactions = fuzzerAvoidsUnavailableSubactions, + .analysePreconditions = fuzzerAnalysesPreconditions + })); } if (request == Request::dumpCheckedAST) diff --git a/tool/rlc/src/Main.cpp b/tool/rlc/src/Main.cpp index be5a8617..4b1587e8 100644 --- a/tool/rlc/src/Main.cpp +++ b/tool/rlc/src/Main.cpp @@ -234,6 +234,20 @@ static cl::opt emitFuzzer( cl::cat(astDumperCategory) ); +static cl::opt fuzzerAvoidsUnavailableSubactions( + "fuzzer-avoid-unavailable-subactions", + cl::desc("the emitted fuzzer avoids generating unavailable subaction calls."), + cl::init(true), + cl::cat(astDumperCategory) +); + +static cl::opt fuzzerAnalysesPreconditions( + "fuzzer-analyse-preconditions", + cl::desc("the emitted fuzzer analyses subaction function preconditions."), + cl::init(true), + cl::cat(astDumperCategory) +); + cl::list RPath("rpath", cl::desc("")); @@ -306,6 +320,8 @@ static mlir::rlc::Driver configureDriver( driver.setExtraObjectFile(ExtraObjectFiles); driver.setRPath(RPath); driver.setEmitFuzzer(emitFuzzer); + driver.setFuzzerAvoidsUnavailableSubactions(fuzzerAvoidsUnavailableSubactions); + driver.setFuzzerAnalysesPreconditions(fuzzerAnalysesPreconditions); driver.setTargetInfo(&info); driver.setEmitBoundChecks(emitBoundChecks); From e9fa0607aa9c6a9446b89740dd3f062f84ab58aa Mon Sep 17 00:00:00 2001 From: Cem Cebeci Date: Wed, 21 Feb 2024 21:43:17 +0100 Subject: [PATCH 16/16] implement the option to turn off precondition analysis. --- .../rlc/dialect/DynamicArgumentAnalysis.hpp | 3 ++- .../src/DynamicArgumentAnalysisPass.cpp | 26 ++++++++++++++----- lib/dialect/src/EmitFuzzTargetPass.cpp | 15 ++++------- lib/dialect/src/Passes.td | 6 +++-- lib/driver/src/Driver.cpp | 5 ++-- lib/fuzzer/src/fuzz_target.cpp | 2 +- 6 files changed, 34 insertions(+), 23 deletions(-) diff --git a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp index 5526cffb..55821d9c 100644 --- a/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp +++ b/lib/dialect/include/rlc/dialect/DynamicArgumentAnalysis.hpp @@ -137,7 +137,7 @@ struct UnboundValue { class DynamicArgumentAnalysis { public: - explicit DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange boundArgs, mlir::Value argPicker, mlir::OpBuilder builder, mlir::Location loc); + explicit DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange boundArgs, mlir::Value argPicker, bool analysePreconditions, mlir::OpBuilder builder, mlir::Location loc); mlir::Value pickArg(int argIndex); private: @@ -169,6 +169,7 @@ class DynamicArgumentAnalysis return nullptr; } + bool analysePreconditions; mlir::Value argPicker; mlir::OpBuilder builder; mlir::Location loc; diff --git a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp index 0b995fc4..d6ab5dce 100644 --- a/lib/dialect/src/DynamicArgumentAnalysisPass.cpp +++ b/lib/dialect/src/DynamicArgumentAnalysisPass.cpp @@ -19,9 +19,10 @@ #include "rlc/dialect/DynamicArgumentAnalysis.hpp" #include "rlc/dialect/Types.hpp" -DynamicArgumentAnalysis::DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange boundArgs, mlir::Value argPicker, mlir::OpBuilder builder, mlir::Location loc): +DynamicArgumentAnalysis::DynamicArgumentAnalysis(mlir::rlc::FunctionOp op, mlir::ValueRange boundArgs, mlir::Value argPicker, bool analysePreconditions, mlir::OpBuilder builder, mlir::Location loc): function(op), precondition(op.getPrecondition()), + analysePreconditions(analysePreconditions), argPicker(argPicker), builder(builder), loc(loc) { @@ -271,7 +272,7 @@ DeducedConstraints DynamicArgumentAnalysis::findImposedConstraints(mlir::rlc::Ca } } assert(argIndex != -1 && "Expected to find the argument."); - DynamicArgumentAnalysis analysis(underlyingFunction, knownArgsOfUnderlyingFunction, argPicker, builder, loc); + DynamicArgumentAnalysis analysis(underlyingFunction, knownArgsOfUnderlyingFunction, argPicker, analysePreconditions, builder, loc); for(auto binding: bindings) { if(binding.first.memberAddress.size() != 0) { // the binding does not just correspond to an argument. @@ -459,14 +460,25 @@ DeducedConstraints DynamicArgumentAnalysis::deduceIntegerUnboundValueConstraints } mlir::Value DynamicArgumentAnalysis::pickIntegerUnboundValue(UnboundValue unbound) { - auto deduced = deduceIntegerUnboundValueConstraints(unbound); + if(analysePreconditions) { + auto deduced = deduceIntegerUnboundValueConstraints(unbound); + auto call = builder.create( + loc, + argPicker, + false, + mlir::ValueRange({deduced.min, deduced.max}) + ); + return call.getResult(0); + } + auto min = builder.create(loc, min_int); + auto max = builder.create(loc, max_int); auto call = builder.create( loc, argPicker, false, - mlir::ValueRange({deduced.min, deduced.max}) + mlir::ValueRange({min, max}) ); - return call.getResult(0); + return call->getResult(0); } mlir::Value DynamicArgumentAnalysis::pickUnboundValue(UnboundValue unbound) { @@ -518,11 +530,13 @@ mlir::Value DynamicArgumentAnalysis::pickArg(int argIndex) { namespace mlir::rlc { +#define GEN_PASS_DECL_DYNAMICARGUMENTANALYSISPASS #define GEN_PASS_DEF_DYNAMICARGUMENTANALYSISPASS #include "rlc/dialect/Passes.inc" struct DynamicArgumentAnalysisPass : public impl::DynamicArgumentAnalysisPassBase { + using impl::DynamicArgumentAnalysisPassBase::DynamicArgumentAnalysisPassBase; void getDependentDialects(mlir::DialectRegistry& registry) const override { registry.insert(); @@ -552,7 +566,7 @@ namespace mlir::rlc auto function = llvm::dyn_cast(pickedArgOp.getFunction().getDefiningOp()); assert( function && "Expected a FunctionOp"); - DynamicArgumentAnalysis analysis(function, pickedArgOp.getKnownArgs(), argPicker, builder, loc); + DynamicArgumentAnalysis analysis(function, pickedArgOp.getKnownArgs(), argPicker, analysePreconditions, builder, loc); auto pickedArg = analysis.pickArg(pickedArgOp.getArgumentIndex()); pickedArgOp.getResult().replaceAllUsesWith(pickedArg); diff --git a/lib/dialect/src/EmitFuzzTargetPass.cpp b/lib/dialect/src/EmitFuzzTargetPass.cpp index af70d1c3..e7930900 100644 --- a/lib/dialect/src/EmitFuzzTargetPass.cpp +++ b/lib/dialect/src/EmitFuzzTargetPass.cpp @@ -42,11 +42,6 @@ limitations under the License. #include "rlc/dialect/SymbolTable.h" #include "rlc/dialect/Types.hpp" -struct Options { - bool avoidUnavailableSubactions; - bool analysePreconditions; -}; - static mlir::Value findFunction(mlir::ModuleOp module, llvm::StringRef functionName) { for (auto op : module.getOps()) if(op.getUnmangledName().equals(functionName)) @@ -132,13 +127,13 @@ static mlir::Value emitChosenActionDeclaration( mlir::rlc::ActionFunction action, mlir::Value actionEntity, mlir::Value availableSubactionsVector, - Options options, + bool avoidUnavailableSubactions, mlir::rlc::ModuleBuilder &moduleBuilder, mlir::OpBuilder builder ) { auto ip = builder.saveInsertionPoint(); mlir::Value chosenAction; - if(options.avoidUnavailableSubactions) { + if(avoidUnavailableSubactions) { auto clearAvailableSubactions = findFunction(action->getParentOfType(), "fuzzer_clear_available_subactions"); auto addAvailableSubaction = findFunction(action->getParentOfType(), "fuzzer_add_available_subaction"); auto pickSubaction = findFunction(action->getParentOfType(), "fuzzer_pick_subaction"); @@ -362,7 +357,7 @@ static void emitSubactionCases( ... ... */ -static void emitFuzzActionFunction(mlir::rlc::ActionFunction action, Options options) { +static void emitFuzzActionFunction(mlir::rlc::ActionFunction action, bool avoidUnavailableSubactions) { auto loc = action.getLoc(); mlir::OpBuilder builder(action); mlir::rlc::ModuleBuilder moduleBuilder(action->getParentOfType()); @@ -402,7 +397,7 @@ static void emitFuzzActionFunction(mlir::rlc::ActionFunction action, Options opt auto whileStmt = builder.create(loc); emitLoopCondition(action, &whileStmt.getCondition(), entityDeclaration, stopFlag.getResult(), builder); builder.createBlock(&whileStmt.getBody()); - auto chosenAction = emitChosenActionDeclaration(action, entityDeclaration, availableSubactions, options, moduleBuilder, builder); + auto chosenAction = emitChosenActionDeclaration(action, entityDeclaration, availableSubactions, avoidUnavailableSubactions, moduleBuilder, builder); emitSubactionCases(action, &whileStmt.getBody(), entityDeclaration, chosenAction, stopFlag.getResult(), moduleBuilder, builder); builder.create(loc); @@ -431,7 +426,7 @@ namespace mlir::rlc // invoke emitFuzzActionFunction on the ActionFunction with the correct unmangledName for(auto op :module.getOps()) { if(op.getUnmangledName().str() == actionToFuzz) - emitFuzzActionFunction(op, {avoidUnavailableSubactions, analysePreconditions}); + emitFuzzActionFunction(op, avoidUnavailableSubactions); } } }; diff --git a/lib/dialect/src/Passes.td b/lib/dialect/src/Passes.td index 0493d919..1ffba430 100644 --- a/lib/dialect/src/Passes.td +++ b/lib/dialect/src/Passes.td @@ -234,12 +234,14 @@ def EmitFuzzTargetPass : Pass<"rlc-emit-fuzz-target-pass", "mlir::ModuleOp"> { let options = [ Option<"actionToFuzz", "Action to fuzz", "std::string", /*default=*/"\"play\"", "Name of the action to generate a fuzzer for">, - Option<"avoidUnavailableSubactions", "Avoid unavailable subactions", "bool" , /*default=*/"true", "Whether the fuzzer filters out unavailable subaction calls.">, - Option<"analysePreconditions", "Analyse preconditions", "bool" , /*default=*/"true", "Whether the fuzzer analyses subaction preconditions to pick better arguments">, + Option<"avoidUnavailableSubactions", "Avoid unavailable subactions", "bool" , /*default=*/"true", "Whether the fuzzer filters out unavailable subaction calls."> ]; } def DynamicArgumentAnalysisPass : Pass<"rlc-dynamic-argument-analysis-pass", "mlir::ModuleOp"> { let summary = "Lower ArgConstraintsOp's to expressions that compute the minimum and maximum value dynamically."; let dependentDialects = ["rlc::RLCDialect"]; + let options = [ + Option<"analysePreconditions", "Analyse preconditions", "bool" , /*default=*/"true", "Whether the fuzzer analyses subaction preconditions to pick better arguments"> + ]; } diff --git a/lib/driver/src/Driver.cpp b/lib/driver/src/Driver.cpp index 8e46dd07..dd90ea4d 100644 --- a/lib/driver/src/Driver.cpp +++ b/lib/driver/src/Driver.cpp @@ -36,8 +36,7 @@ namespace mlir::rlc if (emitFuzzer) { manager.addPass(mlir::rlc::createEmitFuzzTargetPass({ - .avoidUnavailableSubactions = fuzzerAvoidsUnavailableSubactions, - .analysePreconditions = fuzzerAnalysesPreconditions + .avoidUnavailableSubactions = fuzzerAvoidsUnavailableSubactions })); } @@ -110,7 +109,7 @@ namespace mlir::rlc return; } - manager.addPass(mlir::rlc::createDynamicArgumentAnalysisPass()); + manager.addPass(mlir::rlc::createDynamicArgumentAnalysisPass({fuzzerAnalysesPreconditions})); manager.addPass(mlir::rlc::createExtractPreconditionPass()); diff --git a/lib/fuzzer/src/fuzz_target.cpp b/lib/fuzzer/src/fuzz_target.cpp index c6532bee..7b79be93 100644 --- a/lib/fuzzer/src/fuzz_target.cpp +++ b/lib/fuzzer/src/fuzz_target.cpp @@ -53,7 +53,7 @@ int64_t consume_bits(const char *Data, int num_bits, int &byte_offset, int &bit_ // TODO this is not completely uniform since the number of possible inputs is not a power of two, think about whether or not that's a problem. extern "C" void rl_fuzzer_get_input__int64_t_r_int64_t(__int64_t *result, const __int64_t *max) { - // printf("Generating input in range [0, %ld)\n", *max); + printf("Generating input in range [0, %ld)\n", *max); int num_bits = ceil(log2(*max)); *result = consume_bits(fuzz_input, num_bits, byte_offset, bit_offset) % *max; }