goblint / cil

C Intermediate Language
https://goblint.github.io/cil/
Other
40 stars 16 forks source link

Keep side-effect-less expression statements when wanted #145

Open sim642 opened 1 year ago

sim642 commented 1 year ago

This is an even simpler version of #140.

CIL removes side-effect-less standalone expressions completely, but this makes Goblint unsound as it misses a race. For example in

#include <pthread.h>
#include <stdio.h>

int myglobal;
pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;

void *t_fun(void *arg) {
  pthread_mutex_lock(&mutex1);
  myglobal; // RACE!
  pthread_mutex_unlock(&mutex1);
  return NULL;
}

int main(void) {
  pthread_t id;
  pthread_create(&id, NULL, t_fun, NULL);
  pthread_mutex_lock(&mutex2);
  myglobal=myglobal+1; // RACE!
  pthread_mutex_unlock(&mutex2);
  pthread_join (id, NULL);
  return 0;
}

Obviously any sensible compiler just optimizes away, but I don't think the standard demands such optimization. It just says the expression is evaluated:

The expression in an expression statement is evaluated as a void expression for its side effects.

The semantic descriptions in this International Standard describe the behavior of an abstract machine in which issues of optimization are irrelevant.

I think the problem is that the CIL AST has no construct that corresponds to this. It's only Cabs which as A.COMPUTATION.

jerhard commented 1 year ago

We discussed how to handle this at GobCon today.

We considered two options:

  1. Compile such an expression as a call to a special function like __builtin_constant_p
  2. Introduce a constructor for such expressions ( “pure_eval”) in stmtkind / instruction in CIL, and add a transfer function.

In the discussion we favored the second option as cleaner.