summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ir/abstract.h17
-rw-r--r--src/passes/OptimizeInstructions.cpp221
2 files changed, 139 insertions, 99 deletions
diff --git a/src/ir/abstract.h b/src/ir/abstract.h
index 3071e6dd6..687412455 100644
--- a/src/ir/abstract.h
+++ b/src/ir/abstract.h
@@ -44,6 +44,7 @@ enum Op {
Or,
Xor,
// Relational
+ EqZ,
Eq,
Ne,
LtS,
@@ -62,10 +63,22 @@ enum Op {
inline UnaryOp getUnary(Type type, Op op) {
switch (type.getSingle()) {
case Type::i32: {
- return InvalidUnary;
+ switch (op) {
+ case EqZ:
+ return EqZInt32;
+ default:
+ return InvalidUnary;
+ }
+ break;
}
case Type::i64: {
- return InvalidUnary;
+ switch (op) {
+ case EqZ:
+ return EqZInt64;
+ default:
+ return InvalidUnary;
+ }
+ break;
}
case Type::f32: {
switch (op) {
diff --git a/src/passes/OptimizeInstructions.cpp b/src/passes/OptimizeInstructions.cpp
index 2e59775c0..860ea3ac7 100644
--- a/src/passes/OptimizeInstructions.cpp
+++ b/src/passes/OptimizeInstructions.cpp
@@ -666,87 +666,13 @@ struct OptimizeInstructions
}
}
} else if (auto* unary = curr->dynCast<Unary>()) {
- // de-morgan's laws
if (unary->op == EqZInt32) {
if (auto* inner = unary->value->dynCast<Binary>()) {
- switch (inner->op) {
- case EqInt32:
- inner->op = NeInt32;
- return inner;
- case NeInt32:
- inner->op = EqInt32;
- return inner;
- case LtSInt32:
- inner->op = GeSInt32;
- return inner;
- case LtUInt32:
- inner->op = GeUInt32;
- return inner;
- case LeSInt32:
- inner->op = GtSInt32;
- return inner;
- case LeUInt32:
- inner->op = GtUInt32;
- return inner;
- case GtSInt32:
- inner->op = LeSInt32;
- return inner;
- case GtUInt32:
- inner->op = LeUInt32;
- return inner;
- case GeSInt32:
- inner->op = LtSInt32;
- return inner;
- case GeUInt32:
- inner->op = LtUInt32;
- return inner;
-
- case EqInt64:
- inner->op = NeInt64;
- return inner;
- case NeInt64:
- inner->op = EqInt64;
- return inner;
- case LtSInt64:
- inner->op = GeSInt64;
- return inner;
- case LtUInt64:
- inner->op = GeUInt64;
- return inner;
- case LeSInt64:
- inner->op = GtSInt64;
- return inner;
- case LeUInt64:
- inner->op = GtUInt64;
- return inner;
- case GtSInt64:
- inner->op = LeSInt64;
- return inner;
- case GtUInt64:
- inner->op = LeUInt64;
- return inner;
- case GeSInt64:
- inner->op = LtSInt64;
- return inner;
- case GeUInt64:
- inner->op = LtUInt64;
- return inner;
-
- case EqFloat32:
- inner->op = NeFloat32;
- return inner;
- case NeFloat32:
- inner->op = EqFloat32;
- return inner;
-
- case EqFloat64:
- inner->op = NeFloat64;
- return inner;
- case NeFloat64:
- inner->op = EqFloat64;
- return inner;
-
- default: {}
+ // Try to invert a relational operation using De Morgan's law
+ auto op = invertBinaryOp(inner->op);
+ if (op != InvalidBinary) {
+ inner->op = op;
+ return inner;
}
}
// eqz of a sign extension can be of zero-extension
@@ -809,16 +735,6 @@ struct OptimizeInstructions
}
} else if (auto* select = curr->dynCast<Select>()) {
select->condition = optimizeBoolean(select->condition);
- auto* condition = select->condition->dynCast<Unary>();
- if (condition && condition->op == EqZInt32) {
- // flip select to remove eqz, if we can reorder
- EffectAnalyzer ifTrue(getPassOptions(), features, select->ifTrue);
- EffectAnalyzer ifFalse(getPassOptions(), features, select->ifFalse);
- if (!ifTrue.invalidates(ifFalse)) {
- select->condition = condition->value;
- std::swap(select->ifTrue, select->ifFalse);
- }
- }
if (auto* c = select->condition->dynCast<Const>()) {
// constant condition, we can just pick the right side (barring side
// effects)
@@ -841,6 +757,42 @@ struct OptimizeInstructions
}
}
}
+ if (auto* constTrue = select->ifTrue->dynCast<Const>()) {
+ if (auto* constFalse = select->ifFalse->dynCast<Const>()) {
+ if (select->type == Type::i32 || select->type == Type::i64) {
+ auto trueValue = constTrue->value.getInteger();
+ auto falseValue = constFalse->value.getInteger();
+ if ((trueValue == 1LL && falseValue == 0LL) ||
+ (trueValue == 0LL && falseValue == 1LL)) {
+ Builder builder(*getModule());
+ Expression* condition = select->condition;
+ if (trueValue == 0LL) {
+ condition =
+ optimizeBoolean(builder.makeUnary(EqZInt32, condition));
+ }
+ if (!Properties::emitsBoolean(condition)) {
+ // expr ? 1 : 0 ==> !!expr
+ condition = builder.makeUnary(
+ EqZInt32, builder.makeUnary(EqZInt32, condition));
+ }
+ return select->type == Type::i64
+ ? builder.makeUnary(ExtendUInt32, condition)
+ : condition;
+ }
+ }
+ }
+ }
+ if (auto* condition = select->condition->dynCast<Unary>()) {
+ if (condition->op == EqZInt32) {
+ // flip select to remove eqz, if we can reorder
+ EffectAnalyzer ifTrue(getPassOptions(), features, select->ifTrue);
+ EffectAnalyzer ifFalse(getPassOptions(), features, select->ifFalse);
+ if (!ifTrue.invalidates(ifFalse)) {
+ select->condition = condition->value;
+ std::swap(select->ifTrue, select->ifFalse);
+ }
+ }
+ }
if (ExpressionAnalyzer::equal(select->ifTrue, select->ifFalse)) {
// sides are identical, fold
EffectAnalyzer value(getPassOptions(), features, select->ifTrue);
@@ -974,11 +926,21 @@ private:
Expression* optimizeBoolean(Expression* boolean) {
// TODO use a general getFallthroughs
if (auto* unary = boolean->dynCast<Unary>()) {
- if (unary && unary->op == EqZInt32) {
- auto* unary2 = unary->value->dynCast<Unary>();
- if (unary2 && unary2->op == EqZInt32) {
- // double eqz
- return unary2->value;
+ if (unary) {
+ if (unary->op == EqZInt32) {
+ auto* unary2 = unary->value->dynCast<Unary>();
+ if (unary2 && unary2->op == EqZInt32) {
+ // double eqz
+ return unary2->value;
+ }
+ if (auto* binary = unary->value->dynCast<Binary>()) {
+ // !(x <=> y) ==> x <!=> y
+ auto op = invertBinaryOp(binary->op);
+ if (op != InvalidBinary) {
+ binary->op = op;
+ return binary;
+ }
+ }
}
}
} else if (auto* binary = boolean->dynCast<Binary>()) {
@@ -989,18 +951,23 @@ private:
return binary->right;
}
}
- }
- if (binary->op == OrInt32) {
+ } else if (binary->op == OrInt32) {
// an or flowing into a boolean context can consider each input as
// boolean
binary->left = optimizeBoolean(binary->left);
binary->right = optimizeBoolean(binary->right);
} else if (binary->op == NeInt32) {
- // x != 0 is just x if it's used as a bool
if (auto* num = binary->right->dynCast<Const>()) {
+ // x != 0 is just x if it's used as a bool
if (num->value.geti32() == 0) {
return binary->left;
}
+ // TODO: Perhaps use it for separate final pass???
+ // x != -1 ==> x ^ -1
+ // if (num->value.geti32() == -1) {
+ // binary->op = XorInt32;
+ // return binary;
+ // }
}
}
if (auto* ext = Properties::getSignExtValue(binary)) {
@@ -1611,6 +1578,66 @@ private:
return nullptr;
}
}
+
+ BinaryOp invertBinaryOp(BinaryOp op) {
+ // use de-morgan's laws
+ switch (op) {
+ case EqInt32:
+ return NeInt32;
+ case NeInt32:
+ return EqInt32;
+ case LtSInt32:
+ return GeSInt32;
+ case LtUInt32:
+ return GeUInt32;
+ case LeSInt32:
+ return GtSInt32;
+ case LeUInt32:
+ return GtUInt32;
+ case GtSInt32:
+ return LeSInt32;
+ case GtUInt32:
+ return LeUInt32;
+ case GeSInt32:
+ return LtSInt32;
+ case GeUInt32:
+ return LtUInt32;
+
+ case EqInt64:
+ return NeInt64;
+ case NeInt64:
+ return EqInt64;
+ case LtSInt64:
+ return GeSInt64;
+ case LtUInt64:
+ return GeUInt64;
+ case LeSInt64:
+ return GtSInt64;
+ case LeUInt64:
+ return GtUInt64;
+ case GtSInt64:
+ return LeSInt64;
+ case GtUInt64:
+ return LeUInt64;
+ case GeSInt64:
+ return LtSInt64;
+ case GeUInt64:
+ return LtUInt64;
+
+ case EqFloat32:
+ return NeFloat32;
+ case NeFloat32:
+ return EqFloat32;
+
+ case EqFloat64:
+ return NeFloat64;
+ case NeFloat64:
+ return EqFloat64;
+
+ default:
+ return InvalidBinary;
+ }
+ }
};
Pass* createOptimizeInstructionsPass() { return new OptimizeInstructions(); }