[clang] [clang][dataflow] Propagate locations from result objects to initializers. (PR #87320)

via cfe-commits cfe-commits at lists.llvm.org
Mon Apr 8 05:39:09 PDT 2024


================
@@ -688,22 +689,45 @@ class Environment {
   /// and functions referenced in `FuncDecl`. `FuncDecl` must have a body.
   void initFieldsGlobalsAndFuncs(const FunctionDecl *FuncDecl);
 
+  static PrValueToResultObject
+  buildResultObjectMap(DataflowAnalysisContext *DACtx,
+                       const FunctionDecl *FuncDecl,
+                       RecordStorageLocation *ThisPointeeLoc,
+                       RecordStorageLocation *LocForRecordReturnVal);
+
   // `DACtx` is not null and not owned by this object.
   DataflowAnalysisContext *DACtx;
 
-  // FIXME: move the fields `CallStack`, `ReturnVal`, `ReturnLoc` and
-  // `ThisPointeeLoc` into a separate call-context object, shared between
-  // environments in the same call.
+  // FIXME: move the fields `CallStack`, `ResultObjectMap`, `ReturnVal`,
+  // `ReturnLoc` and `ThisPointeeLoc` into a separate call-context object,
+  // shared between environments in the same call.
   // https://github.com/llvm/llvm-project/issues/59005
 
   // `DeclContext` of the block being analysed if provided.
   std::vector<const DeclContext *> CallStack;
 
-  // Value returned by the function (if it has non-reference return type).
+  // Maps from prvalues of record type to their result objects. Shared between
+  // all environments for the same function.
+  // FIXME: It's somewhat unsatisfactory that we have to use a `shared_ptr`
+  // here, though the cost is acceptable: The overhead of a `shared_ptr` is
+  // incurred when it is copied, and this happens only relatively rarely (when
+  // we fork the environment). The need for a `shared_ptr` will go away once we
+  // introduce a shared call-context object (see above).
+  std::shared_ptr<PrValueToResultObject> ResultObjectMap;
----------------
martinboehme wrote:

> What would be the model for something like:
> 
> ```
> while (cond)
> {
>   f();
> }
> ```
> 
> where `f` returns a value. Do we have the same storage location for the prvalue in all iterations?

Let me clarify what you're asking.

I think what you mean is that `f()` is declared like this, correct?

```cxx
struct S {};
S f();
```

In this case, there is indeed no glvalue of type `S` -- just the prvalue for the call `f()`. Normally, of course, prvalues don't have storage locations, but in this case, because there isn't an actual result object, we treat the `CallExpr` as if it was a `MaterializeTemporaryExpr` (see comment in `ResultObjectVisitor::VisitExpr()`).

(Just making sure up to here that we agree on the setting -- do you agree with all of the above?)

First of all, yes, we use the same storage location on all iterations of the loop -- witness the call to `DACtx.getStableStorageLocation(*E)`.

> In that case, the location is in fact a "summary" for the different iterations.

I'm not sure I would agree with this. Let's assume this was a case where we actually have a `MaterializeTemporaryExpr` (imagine we're accessing some member of `S`, i.e. assume the expression in the loop is actually `f().some_member_fn()`). Then the compiler's codegen would very likely place the temporary in the same storage location on every iteration of the loop. (Sure, nothing in the standard mandates this AFAIK, but it's almost certainly what the compiler is going to do.) So the stable storage location that we use for the expression isn't a "summary" storage location, but it reflects the fact that the generated code, too, would use the same storage location.

https://github.com/llvm/llvm-project/pull/87320


More information about the cfe-commits mailing list