2424#include < pass.h>
2525#include < wasm-s-parser.h>
2626#include < support/threads.h>
27+ #include < ir/abstract.h>
2728#include < ir/utils.h>
2829#include < ir/cost.h>
2930#include < ir/effects.h>
@@ -541,9 +542,11 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
541542 }
542543 }
543544 }
544- return optimizeAddedConstants (binary);
545+ auto * ret = optimizeAddedConstants (binary);
546+ if (ret) return ret;
545547 } else if (binary->op == SubInt32) {
546- return optimizeAddedConstants (binary);
548+ auto * ret = optimizeAddedConstants (binary);
549+ if (ret) return ret;
547550 }
548551 // a bunch of operations on a constant right side can be simplified
549552 if (auto * right = binary->right ->dynCast <Const>()) {
@@ -567,19 +570,9 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
567570 }
568571 }
569572 }
570- // some math operations have trivial results TODO: many more
571- if (right->value == Literal (int32_t (0 ))) {
572- if (binary->op == ShlInt32 || binary->op == ShrUInt32 || binary->op == ShrSInt32 || binary->op == OrInt32) {
573- return binary->left ;
574- } else if ((binary->op == MulInt32 || binary->op == AndInt32) &&
575- !EffectAnalyzer (getPassOptions (), binary->left ).hasSideEffects ()) {
576- return binary->right ;
577- }
578- } else if (right->value == Literal (int32_t (1 ))) {
579- if (binary->op == MulInt32) {
580- return binary->left ;
581- }
582- }
573+ // some math operations have trivial results
574+ Expression* ret = optimizeWithConstantOnRight (binary);
575+ if (ret) return ret;
583576 // the square of some operations can be merged
584577 if (auto * left = binary->left ->dynCast <Binary>()) {
585578 if (left->op == binary->op ) {
@@ -645,6 +638,10 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
645638 if (binary->op == AndInt32 || binary->op == OrInt32) {
646639 return conditionalizeExpensiveOnBitwise (binary);
647640 }
641+ // relation/comparisons allow for math optimizations
642+ if (binary->isRelational ()) {
643+ return optimizeRelational (binary);
644+ }
648645 } else if (auto * unary = curr->dynCast <Unary>()) {
649646 // de-morgan's laws
650647 if (unary->op == EqZInt32) {
@@ -1098,6 +1095,115 @@ struct OptimizeInstructions : public WalkerPass<PostWalker<OptimizeInstructions,
10981095 }
10991096 return false ;
11001097 }
1098+
1099+ // optimize trivial math operations, given that the right side of a binary
1100+ // is a constant
1101+ // TODO: templatize on type?
1102+ Expression* optimizeWithConstantOnRight (Binary* binary) {
1103+ auto type = binary->right ->type ;
1104+ auto * right = binary->right ->cast <Const>();
1105+ if (isIntegerType (type)) {
1106+ // operations on zero
1107+ if (right->value == LiteralUtils::makeLiteralFromInt32 (0 , type)) {
1108+ if (binary->op == Abstract::getBinary (type, Abstract::Shl) ||
1109+ binary->op == Abstract::getBinary (type, Abstract::ShrU) ||
1110+ binary->op == Abstract::getBinary (type, Abstract::ShrS) ||
1111+ binary->op == Abstract::getBinary (type, Abstract::Or)) {
1112+ return binary->left ;
1113+ } else if ((binary->op == Abstract::getBinary (type, Abstract::Mul) ||
1114+ binary->op == Abstract::getBinary (type, Abstract::And)) &&
1115+ !EffectAnalyzer (getPassOptions (), binary->left ).hasSideEffects ()) {
1116+ return binary->right ;
1117+ }
1118+ }
1119+ // wasm binary encoding uses signed LEBs, which slightly favor negative
1120+ // numbers: -64 is more efficient than +64 etc. we therefore prefer
1121+ // x - -64 over x + 64
1122+ if (binary->op == Abstract::getBinary (type, Abstract::Add) ||
1123+ binary->op == Abstract::getBinary (type, Abstract::Sub)) {
1124+ auto value = right->value .getInteger ();
1125+ if (value == 0x40 ||
1126+ value == 0x2000 ||
1127+ value == 0x100000 ||
1128+ value == 0x8000000 ||
1129+ value == 0x400000000LL ||
1130+ value == 0x20000000000LL ||
1131+ value == 0x1000000000000LL ||
1132+ value == 0x80000000000000LL ||
1133+ value == 0x4000000000000000LL ) {
1134+ right->value = right->value .neg ();
1135+ if (binary->op == Abstract::getBinary (type, Abstract::Add)) {
1136+ binary->op = Abstract::getBinary (type, Abstract::Sub);
1137+ } else {
1138+ binary->op = Abstract::getBinary (type, Abstract::Add);
1139+ }
1140+ }
1141+ }
1142+ }
1143+ // note that this is correct even on floats with a NaN on the left,
1144+ // as a NaN would skip the computation and just return the NaN,
1145+ // and that is precisely what we do here. but, the same with -1
1146+ // (change to a negation) would be incorrect for that reason.
1147+ if (right->value == LiteralUtils::makeLiteralFromInt32 (1 , type)) {
1148+ if (binary->op == Abstract::getBinary (type, Abstract::Mul) ||
1149+ binary->op == Abstract::getBinary (type, Abstract::DivS) ||
1150+ binary->op == Abstract::getBinary (type, Abstract::DivU)) {
1151+ return binary->left ;
1152+ }
1153+ }
1154+ return nullptr ;
1155+ }
1156+
1157+ // integer math, even on 2s complement, allows stuff like
1158+ // x + 5 == 7
1159+ // =>
1160+ // x == 2
1161+ // TODO: templatize on type?
1162+ Expression* optimizeRelational (Binary* binary) {
1163+ // TODO: inequalities can also work, if the constants do not overflow
1164+ auto type = binary->right ->type ;
1165+ if (binary->op ==Abstract::getBinary (type, Abstract::Eq) ||
1166+ binary->op ==Abstract::getBinary (type, Abstract::Ne)) {
1167+ if (isIntegerType (binary->left ->type )) {
1168+ if (auto * left = binary->left ->dynCast <Binary>()) {
1169+ if (left->op == Abstract::getBinary (type, Abstract::Add) ||
1170+ left->op == Abstract::getBinary (type, Abstract::Sub)) {
1171+ if (auto * leftConst = left->right ->dynCast <Const>()) {
1172+ if (auto * rightConst = binary->right ->dynCast <Const>()) {
1173+ return combineRelationalConstants (binary, left, leftConst, nullptr , rightConst);
1174+ } else if (auto * rightBinary = binary->right ->dynCast <Binary>()) {
1175+ if (rightBinary->op == Abstract::getBinary (type, Abstract::Add) ||
1176+ rightBinary->op == Abstract::getBinary (type, Abstract::Sub)) {
1177+ if (auto * rightConst = rightBinary->right ->dynCast <Const>()) {
1178+ return combineRelationalConstants (binary, left, leftConst, rightBinary, rightConst);
1179+ }
1180+ }
1181+ }
1182+ }
1183+ }
1184+ }
1185+ }
1186+ }
1187+ return nullptr ;
1188+ }
1189+
1190+ // given a relational binary with a const on both sides, combine the constants
1191+ // left is also a binary, and has a constant; right may be just a constant, in which
1192+ // case right is nullptr
1193+ Expression* combineRelationalConstants (Binary* binary, Binary* left, Const* leftConst, Binary* right, Const* rightConst) {
1194+ auto type = binary->right ->type ;
1195+ // we fold constants to the right
1196+ Literal extra = leftConst->value ;
1197+ if (left->op == Abstract::getBinary (type, Abstract::Sub)) {
1198+ extra = extra.neg ();
1199+ }
1200+ if (right && right->op == Abstract::getBinary (type, Abstract::Sub)) {
1201+ extra = extra.neg ();
1202+ }
1203+ rightConst->value = rightConst->value .sub (extra);
1204+ binary->left = left->left ;
1205+ return binary;
1206+ }
11011207};
11021208
11031209Pass *createOptimizeInstructionsPass () {
0 commit comments