diff --git a/benchmarks/c/miscellaneous/funcPtrInStaticMemory.c b/benchmarks/c/miscellaneous/funcPtrInStaticMemory.c new file mode 100644 index 0000000000..588a34b64a --- /dev/null +++ b/benchmarks/c/miscellaneous/funcPtrInStaticMemory.c @@ -0,0 +1,38 @@ +#include +#include +#include + +/* + The test checks if function pointers in static memory are handled correctly. + Expected result: PASS +*/ + +typedef struct { + void* (*funcPtrOne) (void*); + void* (*funcPtrTwo) (void*); +} MyPtrStruct; + +int callCounter = 0; + +void *myFunc1(void* arg) { + assert (arg == 1); + return NULL; +} + +void *myFunc2(void* arg) { + assert (arg == 42 || arg == 123); + callCounter++; + return arg; +} + +MyPtrStruct myStruct = { myFunc1, myFunc2 }; + +int main () { + assert(myStruct.funcPtrOne(1) == NULL); + assert(myStruct.funcPtrTwo(42) == 42); + + pthread_t t; + pthread_create(&t, NULL, myStruct.funcPtrTwo, (void*)123); + pthread_join(t, NULL); + assert (callCounter == 2); +} \ No newline at end of file diff --git a/dartagnan/src/main/java/com/dat3m/dartagnan/program/event/functions/FunctionCall.java b/dartagnan/src/main/java/com/dat3m/dartagnan/program/event/functions/FunctionCall.java index 2a9aa87536..d84d37d783 100644 --- a/dartagnan/src/main/java/com/dat3m/dartagnan/program/event/functions/FunctionCall.java +++ b/dartagnan/src/main/java/com/dat3m/dartagnan/program/event/functions/FunctionCall.java @@ -20,7 +20,7 @@ public abstract class FunctionCall extends AbstractEvent implements RegReader { protected FunctionType funcType; - protected Expression callTarget; // TODO: Generalize to function pointer expressions + protected Expression callTarget; protected List arguments; protected FunctionCall(FunctionType funcType, Expression funcPtr, List arguments) { @@ -54,6 +54,21 @@ protected FunctionCall(FunctionCall other) { public Expression getCallTarget() { return callTarget; } public List getArguments() { return arguments; } + public void setArgument(int index, Expression argument) { + arguments.set(index, argument); + } + + public void setCallTarget(Expression callTarget) { + if (callTarget instanceof Function func) { + Preconditions.checkArgument(func.getFunctionType() == funcType, + "Call target %s has mismatching function type: expected %s", callTarget, funcType); + } + this.callTarget = callTarget; + } + + @Override + public abstract FunctionCall getCopy(); + @Override public Set getRegisterReads() { final Set regReads = new HashSet<>(); @@ -70,5 +85,4 @@ public void transformExpressions(ExpressionVisitor exprTra callTarget = callTarget.accept(exprTransformer); arguments.replaceAll(expression -> expression.accept(exprTransformer)); } - } diff --git a/dartagnan/src/main/java/com/dat3m/dartagnan/program/event/functions/ValueFunctionCall.java b/dartagnan/src/main/java/com/dat3m/dartagnan/program/event/functions/ValueFunctionCall.java index 761ced1e28..8c68a7d5da 100644 --- a/dartagnan/src/main/java/com/dat3m/dartagnan/program/event/functions/ValueFunctionCall.java +++ b/dartagnan/src/main/java/com/dat3m/dartagnan/program/event/functions/ValueFunctionCall.java @@ -36,11 +36,8 @@ public void setResultRegister(Register reg) { @Override protected String defaultString() { - if (isDirectCall()) { - return String.format("%s <- call %s(%s)", resultRegister, ((Function)callTarget).getName(), super.argumentsToString()); - } else { - return String.format("%s <- call %s(%s)", resultRegister, callTarget, super.argumentsToString()); - } + final Object target = isDirectCall() ? ((Function)callTarget).getName() : callTarget; + return String.format("%s <- call %s(%s)", resultRegister, target, super.argumentsToString()); } @Override diff --git a/dartagnan/src/main/java/com/dat3m/dartagnan/program/event/functions/VoidFunctionCall.java b/dartagnan/src/main/java/com/dat3m/dartagnan/program/event/functions/VoidFunctionCall.java index 96211f231a..e88cfa61dd 100644 --- a/dartagnan/src/main/java/com/dat3m/dartagnan/program/event/functions/VoidFunctionCall.java +++ b/dartagnan/src/main/java/com/dat3m/dartagnan/program/event/functions/VoidFunctionCall.java @@ -21,11 +21,8 @@ protected VoidFunctionCall(VoidFunctionCall other) { @Override protected String defaultString() { - if (isDirectCall()) { - return String.format("call %s(%s)", ((Function)callTarget).getName(), super.argumentsToString()); - } else { - return String.format("call %s(%s)", callTarget, super.argumentsToString()); - } + final Object target = isDirectCall() ? ((Function)callTarget).getName() : callTarget; + return String.format("call %s(%s)", target, super.argumentsToString()); } @Override diff --git a/dartagnan/src/main/java/com/dat3m/dartagnan/program/processing/NaiveDevirtualisation.java b/dartagnan/src/main/java/com/dat3m/dartagnan/program/processing/NaiveDevirtualisation.java index 154ae80ed7..8cad0e99bd 100644 --- a/dartagnan/src/main/java/com/dat3m/dartagnan/program/processing/NaiveDevirtualisation.java +++ b/dartagnan/src/main/java/com/dat3m/dartagnan/program/processing/NaiveDevirtualisation.java @@ -7,6 +7,7 @@ import com.dat3m.dartagnan.expression.processing.ExpressionInspector; import com.dat3m.dartagnan.expression.processing.ExpressionVisitor; import com.dat3m.dartagnan.expression.type.IntegerType; +import com.dat3m.dartagnan.expression.type.Type; import com.dat3m.dartagnan.expression.type.TypeFactory; import com.dat3m.dartagnan.program.Function; import com.dat3m.dartagnan.program.Program; @@ -16,9 +17,9 @@ import com.dat3m.dartagnan.program.event.core.Label; import com.dat3m.dartagnan.program.event.core.utils.RegReader; import com.dat3m.dartagnan.program.event.functions.FunctionCall; -import com.dat3m.dartagnan.program.event.functions.ValueFunctionCall; import com.dat3m.dartagnan.program.memory.Memory; import com.dat3m.dartagnan.program.memory.MemoryObject; +import com.google.common.base.Verify; import com.google.common.collect.Iterables; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -35,6 +36,11 @@ - All non-standard uses are replaced by their address values. - Every indirect call is replaced by a switch statement over all registered functions that have a matching type. Each case of the switch statement contains a direct call to the corresponding function. + + TODO: We also need to devirtualize intrinsic functions that expect function pointers. + For now, we only devirtualize pthread_create based on its third parameter. + More generally, we could extend IntrinsicInfo to tell for each intrinsic which parameters are function pointers and + need devirtualization. */ public class NaiveDevirtualisation implements ProgramProcessor { @@ -111,8 +117,7 @@ private boolean assignAddressToFunction(Function func, Map fun private void applyTransformerToEvent(Event e, ExpressionVisitor transformer) { if (e instanceof FunctionCall call) { - if (call.isDirectCall() && call.getCalledFunction().isIntrinsic() - && call.getCalledFunction().getName().contains("pthread_create")) { + if (call.isDirectCall() && call.getCalledFunction().getIntrinsicInfo() == Intrinsics.Info.P_THREAD_CREATE) { // We avoid transforming functions passed as call target to pthread_create // However, we still collect the last argument of the call, because it // is the argument passed to the created thread (which might be a pointer to a function). @@ -131,61 +136,112 @@ private void devirtualise(Function function, Map func2AddressM int devirtCounter = 0; for (FunctionCall call : function.getEvents(FunctionCall.class)) { - if (!call.isDirectCall()) { - final List possibleTargets = func2AddressMap.keySet().stream() - .filter(f -> f.getFunctionType() == call.getCallType()).collect(Collectors.toList()); - - // FIXME: Here we remove the calling function itself so as to avoid trivial recursion. - // However, indirect/mutual recursion is not prevented by this! - if (possibleTargets.removeIf(f -> f == function)) { - logger.warn("Found potentially recursive dynamic call \"{}\". " + - "Dartagnan (unsoundly) assumes the recursive call cannot happen.", call); - } - - if (possibleTargets.isEmpty()) { - logger.warn("Cannot resolve dynamic call \"{}\", no matching functions found.", call); - } + if (!needsDevirtualization(call)) { + continue; + } - logger.trace("Devirtualizing call \"{}\" with possible targets: {}", call, possibleTargets); - - final List