Implement args refinement
Fixes
MODIFY
ir/attrs.cpp
ir/attrs.cpp
@@ -73,6 +73,27 @@ ostream& operator<<(ostream &os, const FnAttrs &attr) {
return os;
}
+bool ParamAttrs::refinedBy(const ParamAttrs &other) const {
+ // check attributes that are properties of the caller
+ unsigned attrs =
+ NonNull |
+ Dereferenceable |
+ NoUndef |
+ Align |
+ NoAlias |
+ DereferenceableOrNull
+ ;
+
+ auto other_params = (other.bits & attrs);
+ if ((bits & other_params) != other_params)
+ return false;
+
+ return derefBytes == other.derefBytes &&
+ derefOrNullBytes == other.derefOrNullBytes &&
+ blockSize == other.blockSize &&
+ align == other.align;
+}
+
bool ParamAttrs::poisonImpliesUB() const {
return has(Dereferenceable) || has(NoUndef) || has(ByVal) ||
has(DereferenceableOrNull);

MODIFY
ir/attrs.h
ir/attrs.h
@@ -30,6 +30,7 @@ public:
bool has(Attribute a) const { return (bits & a) != 0; }
void set(Attribute a) { bits |= (unsigned)a; }
+ bool refinedBy(const ParamAttrs &other) const;
// Returns true if it's UB for the argument to be poison / have a poison elem.
bool poisonImpliesUB() const;

MODIFY
ir/function.cpp
ir/function.cpp
@@ -212,33 +212,6 @@ void Function::addInput(unique_ptr<Value> &&i) {
inputs.emplace_back(move(i));
}
-bool Function::hasSameInputs(const Function &rhs) const {
- auto litr = inputs.begin(), lend = inputs.end();
- auto ritr = rhs.inputs.begin(), rend = rhs.inputs.end();
-
- auto skip_constinputs = [&]() {
- while (litr != lend && dynamic_cast<ConstantInput *>((*litr).get()))
- litr++;
- while (ritr != rend && dynamic_cast<ConstantInput *>((*ritr).get()))
- ritr++;
- };
-
- skip_constinputs();
-
- while (litr != lend && ritr != rend) {
- auto *lv = dynamic_cast<Input *>((*litr).get());
- auto *rv = dynamic_cast<Input *>((*ritr).get());
- if (lv->getType().toString() != rv->getType().toString())
- return false;
-
- ++litr;
- ++ritr;
- skip_constinputs();
- }
-
- return litr == lend && ritr == rend;
-}
-
bool Function::hasReturn() const {
for (auto &i : instrs()) {
if (dynamic_cast<const Return *>(&i))

MODIFY
ir/function.h
ir/function.h
@@ -133,7 +133,6 @@ public:
util::const_strip_unique_ptr<decltype(inputs)> getInputs() const {
return inputs;
}
- bool hasSameInputs(const Function &rhs) const;
Value *getReturnedInput() const { return returned_input; }
void setReturnedInput(Value *v) { returned_input = v; }

MODIFY
tests/alive-tv/attrs/align-intro-withnoundef.srctgt.ll
tests/alive-tv/attrs/align-intro-withnoundef.srctgt.ll
-; Introduction of align AND noundef may introduce UB
-; ERROR: Source is more defined than target
+; ERROR: Parameter attributes not refined
define i8* @src(i8* %p) {
ret i8* %p

MODIFY
tests/alive-tv/attrs/align-intro.srctgt.ll
tests/alive-tv/attrs/align-intro.srctgt.ll
-; Introduction of align isn't immediate UB
-; ERROR: Target is more poisonous than source
+; ERROR: Parameter attributes not refined
define i8* @src(i8* %p) {
ret i8* %p

MODIFY
tests/alive-tv/attrs/dereferenceable_or_null.srctgt.ll
tests/alive-tv/attrs/dereferenceable_or_null.srctgt.ll
@@ -6,4 +6,4 @@ define i32* @tgt(i32* dereferenceable(4) %p) {
ret i32* %p
}
-; ERROR: Source is more defined than target
+; ERROR: Parameter attributes not refined

MODIFY
tests/alive-tv/attrs/dereferenceable_or_null2.srctgt.ll
tests/alive-tv/attrs/dereferenceable_or_null2.srctgt.ll
@@ -5,3 +5,6 @@ define i32* @src(i32* dereferenceable(4) %p) {
define i32* @tgt(i32* dereferenceable_or_null(4) %p) {
ret i32* %p
}
+
+; this one is technically ok, but it's not worth to support it
+; ERROR: Parameter attributes not refined

MODIFY
tests/alive-tv/attrs/nonnull-intro-withnoundef.srctgt.ll
tests/alive-tv/attrs/nonnull-intro-withnoundef.srctgt.ll
-; Introduction of nonnull AND noundef may introduce UB
-; ERROR: Source is more defined than target
+; ERROR: Parameter attributes not refined
define i8* @src(i8* %p) {
ret i8* %p

MODIFY
tests/alive-tv/attrs/nonnull-intro.srctgt.ll
tests/alive-tv/attrs/nonnull-intro.srctgt.ll
-; Introduction of nonnull isn't immediate UB
-; ERROR: Target is more poisonous than source
+; ERROR: Parameter attributes not refined
define i8* @src(i8* %p) {
ret i8* %p

ADD
tests/alive-tv/attrs/nonnull-remove.srctgt.ll
+define i8* @src(i8* nonnull %p) {
+ ret i8* null
+}
+
+define i8* @tgt(i8* %p) {
+ ret i8* null
+}

MODIFY
tests/alive-tv/issue476.srctgt.ll
tests/alive-tv/issue476.srctgt.ll
-; ERROR: Source is more defined than target
+; ERROR: Parameter attributes not refined
define i32 @src(i8* dereferenceable(4) %p) {
ret i32 0

MODIFY
tools/transform.cpp
tools/transform.cpp
@@ -1097,9 +1097,27 @@ pair<unique_ptr<State>, unique_ptr<State>> TransformVerify::exec() const {
}
Errors TransformVerify::verify() const {
- if (!t.src.hasSameInputs(t.tgt)) {
- return { "Unsupported interprocedural transformation: signature mismatch "
- "between src and tgt", false };
+ {
+ auto src_inputs = t.src.getInputs();
+ auto tgt_inputs = t.tgt.getInputs();
+ auto litr = src_inputs.begin(), lend = src_inputs.end();
+ auto ritr = tgt_inputs.begin(), rend = tgt_inputs.end();
+
+ while (litr != lend && ritr != rend) {
+ auto *lv = dynamic_cast<const Input*>(&*litr);
+ auto *rv = dynamic_cast<const Input*>(&*ritr);
+ if (lv->getType().toString() != rv->getType().toString())
+ return { "Signature mismatch between src and tgt", false };
+
+ if (!lv->getAttributes().refinedBy(rv->getAttributes()))
+ return { "Parameter attributes not refined", true };
+
+ ++litr;
+ ++ritr;
+ }
+
+ if (litr != lend || ritr != rend)
+ return { "Signature mismatch between src and tgt", false };
}
// Check sizes of global variables

Implement args refinement
Fixes #771