================
@@ -47,114 +138,96 @@ mlir::Value
CIRBasicAliasAnalysis::getUnderlyingObject(mlir::Value val) {
break;
}
- // Pointer stride: only strip through when we can prove the access stays
- // within the bounds of the underlying allocation.
+ // A stride moves the pointer by `stride * sizeof(pointee)` bytes.
if (auto strideOp = mlir::dyn_cast<cir::PtrStrideOp>(defOp)) {
- auto constOp = strideOp.getStride().getDefiningOp<cir::ConstantOp>();
- if (constOp) {
- if (auto intAttr = mlir::dyn_cast<cir::IntAttr>(constOp.getValue())) {
- APInt stride = intAttr.getValue();
-
- // Zero stride is trivially in-bounds.
- if (stride.isZero()) {
- LDBG() << "Walking past zero-strided PtrStrideOp";
- val = strideOp.getBase();
- continue;
- }
- }
- }
- // Dynamic stride or unverifiable bounds — stop here conservatively.
- LDBG() << "Non-zero or dynamic PtrStrideOp, stopping";
- break;
+ LDBG() << "Walking past PtrStrideOp";
+ addToOffset(offset,
+ scaleOffset(getConstantIndex(strideOp.getStride()),
+ getTypeSizeInBytes(strideOp.getElementType(),
+ dataLayout)));
+ val = strideOp.getBase();
+ continue;
}
- // Handle special cases for zero-offset sub-object accesses.
- if (auto op = mlir::dyn_cast<cir::GetMemberOp>(defOp)) {
- if (op.getIndex() == 0) {
- LDBG() << "GetMemberOp[0], following to underlying object";
- val = op.getAddr();
- continue;
- } else {
- LDBG() << "GetMemberOp, non-zero index, stopping";
- break;
- }
+ // A record member sits at a fixed offset given by the record layout.
+ if (auto memberOp = mlir::dyn_cast<cir::GetMemberOp>(defOp)) {
+ LDBG() << "Walking past GetMemberOp";
+ auto recordTy =
+ mlir::cast<cir::RecordType>(memberOp.getAddrTy().getPointee());
+ std::optional<int64_t> memberOffset;
+ if (!recordTy.isIncomplete())
+ memberOffset =
+ recordTy.getElementOffset(dataLayout, memberOp.getIndex());
+ addToOffset(offset, memberOffset);
+ val = memberOp.getAddr();
+ continue;
}
- if (auto op = mlir::dyn_cast<cir::GetElementOp>(defOp)) {
- cir::IntAttr index;
- if (auto constOp = op.getIndex().getDefiningOp<cir::ConstantOp>())
- index = mlir::dyn_cast<cir::IntAttr>(constOp.getValue());
- if (index && index.getValue().isZero()) {
- LDBG() << "GetElementOp[0], following to underlying object";
- val = op.getBase();
- continue;
- }
- LDBG() << "GetElementOp, non-zero or dynamic index, stopping";
- break;
+
+ // An array element sits at `index * sizeof(element)` bytes into the array.
+ if (auto elementOp = mlir::dyn_cast<cir::GetElementOp>(defOp)) {
+ LDBG() << "Walking past GetElementOp";
+ addToOffset(offset,
+ scaleOffset(getConstantIndex(elementOp.getIndex()),
+ getTypeSizeInBytes(elementOp.getElementType(),
+ dataLayout)));
+ val = elementOp.getBase();
+ continue;
}
- if (auto op = mlir::dyn_cast<cir::BaseClassAddrOp>(defOp)) {
- // A zero byte offset means the base subobject starts at the same address
- // as the derived object.
- if (op.getOffset().isZero()) {
- LDBG() << "BaseClassAddrOp[0], following to underlying object";
- val = op.getDerivedAddr();
- continue;
- }
- LDBG() << "BaseClassAddrOp, non-zero offset, stopping";
- break;
+
+ // A base class subobject starts the given number of bytes into the derived
+ // object.
+ if (auto baseOp = mlir::dyn_cast<cir::BaseClassAddrOp>(defOp)) {
+ LDBG() << "Walking past BaseClassAddrOp";
+ addToOffset(offset, baseOp.getOffset().tryZExtValue());
----------------
andykaylor wrote:
> > The alternative is we return `MayAlias` for any pointer with a
> > BaseClassAddrOp that doesn't assume non-null in its pointer use-def chain.
> > So for a case like this...
> > ```
> > void foo() { bar(nullptr); }
> > int bar(Derived *p) {
> > Base2 *p2 = static_cast<Base2*>(p); // return null
> > if (p2 == null)
> > return -1;
> > p->x = 0; // Writes 4 bytes at p, offset zero
> > return p2->y; // Reads 4 bytes at p, offset four
> > }
> > ```
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> >
> > ...we'd return `MayAlias`, even though we could say `NoAlias` if ignored
> > the possibility of the static_cast returning null. For this trivial case
> > that wouldn't matter, but I'm sure you can see how it could in other cases.
>
> This section/example I'm not really getting? But my understanding of the
> alias-analysis is limited.
My point there was that when the operation isn't assuming the input operand is
non-null, we have two choices: (1) ignore the possibility that it's null, or
(2) report that we cannot determine whether the result (and anything else
derived from it) aliases with anything else. The latter behavior would be
conservatively correct, but it would potentially block optimization
opportunities in cases where we could prove `NoAlias` in the non-UB case. I
believe that ignoring the possibility of a null pointer will be correct for all
non-UB cases since we will only ever get an alias query starting at a memory
access. We never query alias for pointer comparisons or casts.
https://github.com/llvm/llvm-project/pull/219047
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits