Skip to content

Commit

Permalink
Inlining of nondet_int (#485)
Browse files Browse the repository at this point in the history
* Add IntrinsicsInlining

* Added new nondet_loop.c code + unit test.

---------

Co-authored-by: Thomas Haas <[email protected]>
  • Loading branch information
xeren and ThomasHaas authored Jul 13, 2023
1 parent 0dd7003 commit 037ba25
Show file tree
Hide file tree
Showing 8 changed files with 16,342 additions and 65 deletions.
17 changes: 17 additions & 0 deletions benchmarks/c/miscellaneous/nondet_loop.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#include <stdint.h>
#include <assert.h>

int main()
{
int x = 0;
while (x != 5) {
if (__VERIFIER_nondet_int() == 0) {
x += 2;
} else {
x += 3;
}
}
assert (0);

return 0;
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
package com.dat3m.dartagnan.parsers.program.visitors.boogie;

import com.dat3m.dartagnan.exception.MalformedProgramException;
import com.dat3m.dartagnan.exception.ParsingException;
import com.dat3m.dartagnan.expression.BNonDet;
import com.dat3m.dartagnan.expression.Expression;
import com.dat3m.dartagnan.expression.IExpr;
import com.dat3m.dartagnan.expression.INonDet;
import com.dat3m.dartagnan.expression.type.BooleanType;
import com.dat3m.dartagnan.expression.type.IntegerType;
import com.dat3m.dartagnan.parsers.BoogieParser.Call_cmdContext;
import com.dat3m.dartagnan.program.Register;
import com.dat3m.dartagnan.program.event.EventFactory;
import com.dat3m.dartagnan.program.event.lang.svcomp.BeginAtomic;
import com.google.common.primitives.UnsignedInteger;
import com.google.common.primitives.UnsignedLong;

import java.math.BigInteger;

Expand All @@ -31,12 +23,6 @@ public static boolean handleSvcompFunction(VisitorBoogie visitor, Call_cmdContex
case "__VERIFIER_assume" -> __VERIFIER_assume(visitor, ctx);
case "__VERIFIER_atomic_begin" -> __VERIFIER_atomic_begin(visitor);
case "__VERIFIER_atomic_end" -> __VERIFIER_atomic_end(visitor);
case "__VERIFIER_nondet_bool" -> __VERIFIER_nondet_bool(visitor, ctx);
case "__VERIFIER_nondet_int", "__VERIFIER_nondet_uint",
"__VERIFIER_nondet_unsigned_int", "__VERIFIER_nondet_short",
"__VERIFIER_nondet_ushort", "__VERIFIER_nondet_unsigned_short",
"__VERIFIER_nondet_long", "__VERIFIER_nondet_ulong",
"__VERIFIER_nondet_char", "__VERIFIER_nondet_uchar" -> __VERIFIER_nondet(visitor, ctx, funcName);
default -> {
return false;
}
Expand All @@ -61,54 +47,6 @@ public static void __VERIFIER_atomic_end(VisitorBoogie visitor) {
visitor.currentBeginAtomic = null;
}

private static void __VERIFIER_nondet(VisitorBoogie visitor, Call_cmdContext ctx, String name) {
final String suffix = name.substring("__VERIFIER_nondet_".length());
boolean signed = switch (suffix) {
case "int", "short", "long", "char" -> true;
default -> false;
};
final BigInteger min = switch (suffix) {
case "long" -> BigInteger.valueOf(Long.MIN_VALUE);
case "int" -> BigInteger.valueOf(Integer.MIN_VALUE);
case "short" -> BigInteger.valueOf(Short.MIN_VALUE);
case "char" -> BigInteger.valueOf(Byte.MIN_VALUE);
default -> BigInteger.ZERO;
};
final BigInteger max = switch (suffix) {
case "int" -> BigInteger.valueOf(Integer.MAX_VALUE);
case "uint", "unsigned_int" -> UnsignedInteger.MAX_VALUE.bigIntegerValue();
case "short" -> BigInteger.valueOf(Short.MAX_VALUE);
case "ushort", "unsigned_short" -> BigInteger.valueOf(65535);
case "long" -> BigInteger.valueOf(Long.MAX_VALUE);
case "ulong" -> UnsignedLong.MAX_VALUE.bigIntegerValue();
case "char" -> BigInteger.valueOf(Byte.MAX_VALUE);
case "uchar" -> BigInteger.valueOf(255);
default -> throw new ParsingException(name + " is not supported");
};
final String registerName = ctx.call_params().Ident(0).getText();
final Register register = visitor.getRegister(registerName);
if (register != null) {
if (!(register.getType() instanceof IntegerType type)) {
throw new ParsingException(String.format("Non-integer result register %s.", register));
}
final INonDet expression = visitor.programBuilder.newConstant(type, signed);
expression.setMin(min);
expression.setMax(max);
visitor.addEvent(EventFactory.newLocal(register, expression));
}
}

private static void __VERIFIER_nondet_bool(VisitorBoogie visitor, Call_cmdContext ctx) {
final String registerName = ctx.call_params().Ident(0).getText();
final Register register = visitor.getRegister(registerName);
if (register != null) {
BooleanType booleanType = visitor.types.getBooleanType();
var nondeterministicExpression = new BNonDet(booleanType);
Expression cast = visitor.expressions.makeCast(nondeterministicExpression, register.getType());
visitor.addEvent(EventFactory.newLocal(register, cast));
}
}

private static void __VERIFIER_loop_bound(VisitorBoogie visitor, Call_cmdContext ctx) {
final int bound = ((IExpr) ctx.call_params().exprs().expr(0).accept(visitor)).reduce().getValueAsInt();
visitor.addEvent(EventFactory.Svcomp.newLoopBound(bound));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,10 @@ public Object visitMain(MainContext ctx) {

for (Function func : functions) {
currentFunction = func.getId();
// Skip intrinsics, regardless of being user-defined
if (func.getName().startsWith("__VERIFIER_nondet_")) {
continue;
}
BoogieParser.Proc_declContext funcCtx = procDeclarations.get(func.getName());
if (funcCtx.impl_body() != null) {
visitProc_decl(funcCtx);
Expand Down Expand Up @@ -204,7 +208,7 @@ private boolean doIgnoreProcedure(String procName) {
// These procedures do not get declared in the program, and the body of these procedures is never parsed.
// Calls to any of these procedures must get resolved somehow by the parser (e.g., by removing or replacing the call)
if (procName.startsWith("SMACK") || procName.startsWith("__SMACK") || procName.startsWith("$") || procName.startsWith("llvm")
|| (procName.startsWith("__VERIFIER") && !procName.contains("__VERIFIER_atomic"))
|| (procName.startsWith("__VERIFIER") && !procName.contains("__VERIFIER_atomic") && !procName.startsWith("__VERIFIER_nondet_"))
|| procName.startsWith("boogie") || procName.startsWith("corral")
|| procName.startsWith("assert") || procName.startsWith("malloc") || procName.startsWith("abort")
|| procName.startsWith("reach_error") || procName.startsWith("printf") || procName.startsWith("fopen")) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
package com.dat3m.dartagnan.program.processing;

import com.dat3m.dartagnan.exception.ParsingException;
import com.dat3m.dartagnan.expression.BNonDet;
import com.dat3m.dartagnan.expression.Expression;
import com.dat3m.dartagnan.expression.ExpressionFactory;
import com.dat3m.dartagnan.expression.INonDet;
import com.dat3m.dartagnan.expression.type.BooleanType;
import com.dat3m.dartagnan.expression.type.IntegerType;
import com.dat3m.dartagnan.expression.type.TypeFactory;
import com.dat3m.dartagnan.program.Program;
import com.dat3m.dartagnan.program.Register;
import com.dat3m.dartagnan.program.Thread;
import com.dat3m.dartagnan.program.event.EventFactory;
import com.dat3m.dartagnan.program.event.core.Event;
import com.dat3m.dartagnan.program.event.functions.DirectFunctionCall;
import com.dat3m.dartagnan.program.event.functions.DirectValueFunctionCall;
import com.google.common.primitives.UnsignedInteger;
import com.google.common.primitives.UnsignedLong;
import org.sosy_lab.common.configuration.Configuration;
import org.sosy_lab.common.configuration.InvalidConfigurationException;

import java.math.BigInteger;
import java.util.List;

public class IntrinsicsInlining implements ProgramProcessor {

private int constantId;

private IntrinsicsInlining() {
}

public static IntrinsicsInlining fromConfig(Configuration ignored) throws InvalidConfigurationException {
return new IntrinsicsInlining();
}

@Override
public void run(Program program) {
constantId = 0;
for (Thread thread : program.getThreads()) {
run(thread);
}
}

private void run(Thread thread) {
for (DirectFunctionCall call : thread.getEvents(DirectFunctionCall.class)) {
assert !call.getCallTarget().hasBody();

List<Event> replacement = switch (call.getCallTarget().getName()) {
case "__VERIFIER_nondet_bool",
"__VERIFIER_nondet_int", "__VERIFIER_nondet_uint", "__VERIFIER_nondet_unsigned_int",
"__VERIFIER_nondet_short", "__VERIFIER_nondet_ushort", "__VERIFIER_nondet_unsigned_short",
"__VERIFIER_nondet_long", "__VERIFIER_nondet_ulong",
"__VERIFIER_nondet_char", "__VERIFIER_nondet_uchar" -> inlineNonDet(call);
default -> throw new UnsupportedOperationException(
String.format("Undefined function %s", call.getCallTarget().getName()));
};

replacement.forEach(e -> e.copyAllMetadataFrom(call));
call.replaceBy(replacement);
}
}

private List<Event> inlineNonDet(DirectFunctionCall call) {
TypeFactory types = TypeFactory.getInstance();
ExpressionFactory expressions = ExpressionFactory.getInstance();
assert call instanceof DirectValueFunctionCall;
Register register = ((DirectValueFunctionCall) call).getResultRegister();
String name = call.getCallTarget().getName();
final String separator = "nondet_";
int index = name.indexOf(separator);
assert index > -1;
String suffix = name.substring(index + separator.length());

// Nondeterministic booleans
if (suffix.equals("bool")) {
BooleanType booleanType = types.getBooleanType();
var nondeterministicExpression = new BNonDet(booleanType);
Expression cast = expressions.makeCast(nondeterministicExpression, register.getType());
return List.of(EventFactory.newLocal(register, cast));
}

// Nondeterministic integers
boolean signed = switch (suffix) {
case "int", "short", "long", "char" -> true;
default -> false;
};
final BigInteger min = switch (suffix) {
case "long" -> BigInteger.valueOf(Long.MIN_VALUE);
case "int" -> BigInteger.valueOf(Integer.MIN_VALUE);
case "short" -> BigInteger.valueOf(Short.MIN_VALUE);
case "char" -> BigInteger.valueOf(Byte.MIN_VALUE);
default -> BigInteger.ZERO;
};
final BigInteger max = switch (suffix) {
case "int" -> BigInteger.valueOf(Integer.MAX_VALUE);
case "uint", "unsigned_int" -> UnsignedInteger.MAX_VALUE.bigIntegerValue();
case "short" -> BigInteger.valueOf(Short.MAX_VALUE);
case "ushort", "unsigned_short" -> BigInteger.valueOf(65535);
case "long" -> BigInteger.valueOf(Long.MAX_VALUE);
case "ulong" -> UnsignedLong.MAX_VALUE.bigIntegerValue();
case "char" -> BigInteger.valueOf(Byte.MAX_VALUE);
case "uchar" -> BigInteger.valueOf(255);
default -> throw new UnsupportedOperationException(String.format("%s is not supported", call));
};
if (!(register.getType() instanceof IntegerType type)) {
throw new ParsingException(String.format("Non-integer result register %s.", register));
}
var expression = new INonDet(constantId++, type, signed);
expression.setMin(min);
expression.setMax(max);
call.getFunction().getProgram().addConstant(expression);
return List.of(EventFactory.newLocal(register, expression));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ private ProcessingManager(Configuration config) throws InvalidConfigurationExcep
SimpleSpinLoopDetection.fromConfig(config),
LoopUnrolling.fromConfig(config),
printAfterUnrolling ? DebugPrint.withHeader("After loop unrolling") : null,
IntrinsicsInlining.fromConfig(config),
dynamicPureLoopCutting ? DynamicPureLoopCutting.fromConfig(config) : null,
constantPropagation ? SparseConditionalConstantPropagation.fromConfig(config) : null,
dce ? DeadAssignmentElimination.fromConfig(config) : null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
import java.util.stream.Collectors;

/*
This pass finds and marks simple loops that are totally side effect free (simple spin loops).
This pass finds and marks simple loops that are totally side-effect-free (simple spin loops).
It will also mark side-effect-full loops if they are annotated by a SpinStart event
(we assume the user guarantees the correctness of the annotation)
Expand All @@ -30,6 +30,9 @@ The pass is unable to detect complex types of loops that may spin endlessly (i.e
TODO: Instead of tagging labels as spinning and checking for that tag during loop unrolling
we could let this pass produce LoopBound-Annotations to guide the unrolling implicitly.
TODO 2: Intrinsic calls need to get special treatment as they might be side-effect-full
(for now, all our intrinsics are side-effect-free, so it works fine).
*/
public class SimpleSpinLoopDetection implements ProgramProcessor {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ public static Iterable<Object[]> data() throws IOException {
{"thread_inlining", IMM, PASS, 1},
{"thread_inlining_complex", IMM, PASS, 1},
{"thread_inlining_complex_2", IMM, PASS, 1},
{"thread_local", IMM, PASS, 1}
{"thread_local", IMM, PASS, 1},
{"nondet_loop", IMM, FAIL, 3}
});
}

Expand Down
Loading

0 comments on commit 037ba25

Please sign in to comment.