19
19
//
20
20
21
21
#include < algorithm>
22
+ #include < type_traits>
22
23
23
24
#include < ir/abstract.h>
24
25
#include < ir/cost.h>
@@ -578,10 +579,30 @@ struct OptimizeInstructions
578
579
if (right->type == Type::i32) {
579
580
uint32_t c = right->value .geti32 ();
580
581
if (IsPowerOf2 (c)) {
581
- if (binary->op == MulInt32) {
582
- return optimizePowerOf2Mul (binary, c);
583
- } else if (binary->op == RemUInt32) {
584
- return optimizePowerOf2URem (binary, c);
582
+ switch (binary->op ) {
583
+ case MulInt32:
584
+ return optimizePowerOf2Mul (binary, c);
585
+ case RemUInt32:
586
+ return optimizePowerOf2URem (binary, c);
587
+ case DivUInt32:
588
+ return optimizePowerOf2UDiv (binary, c);
589
+ default :
590
+ break ;
591
+ }
592
+ }
593
+ }
594
+ if (right->type == Type::i64) {
595
+ uint64_t c = right->value .geti64 ();
596
+ if (IsPowerOf2 (c)) {
597
+ switch (binary->op ) {
598
+ case MulInt64:
599
+ return optimizePowerOf2Mul (binary, c);
600
+ case RemUInt64:
601
+ return optimizePowerOf2URem (binary, c);
602
+ case DivUInt64:
603
+ return optimizePowerOf2UDiv (binary, c);
604
+ default :
605
+ break ;
585
606
}
586
607
}
587
608
}
@@ -1265,22 +1286,37 @@ struct OptimizeInstructions
1265
1286
// but it's still worth doing since
1266
1287
// * Often shifts are more common than muls.
1267
1288
// * The constant is smaller.
1268
- Expression* optimizePowerOf2Mul (Binary* binary, uint32_t c) {
1269
- uint32_t shifts = CountTrailingZeroes (c);
1270
- binary->op = ShlInt32;
1271
- binary->right ->cast <Const>()->value = Literal (int32_t (shifts));
1289
+ template <typename T> Expression* optimizePowerOf2Mul (Binary* binary, T c) {
1290
+ static_assert (std::is_same<T, uint32_t >::value ||
1291
+ std::is_same<T, uint64_t >::value,
1292
+ " type mismatch" );
1293
+ auto shifts = CountTrailingZeroes<T>(c);
1294
+ binary->op = std::is_same<T, uint32_t >::value ? ShlInt32 : ShlInt64;
1295
+ binary->right ->cast <Const>()->value = Literal (static_cast <T>(shifts));
1272
1296
return binary;
1273
1297
}
1274
1298
1275
- // Optimize an unsigned divide by a power of two on the right,
1276
- // which can be an AND mask
1299
+ // Optimize an unsigned divide / remainder by a power of two on the right
1277
1300
// This doesn't shrink code size, and VMs likely optimize it anyhow,
1278
1301
// but it's still worth doing since
1279
1302
// * Usually ands are more common than urems.
1280
1303
// * The constant is slightly smaller.
1281
- Expression* optimizePowerOf2URem (Binary* binary, uint32_t c) {
1282
- binary->op = AndInt32;
1283
- binary->right ->cast <Const>()->value = Literal (int32_t (c - 1 ));
1304
+ template <typename T> Expression* optimizePowerOf2UDiv (Binary* binary, T c) {
1305
+ static_assert (std::is_same<T, uint32_t >::value ||
1306
+ std::is_same<T, uint64_t >::value,
1307
+ " type mismatch" );
1308
+ auto shifts = CountTrailingZeroes<T>(c);
1309
+ binary->op = std::is_same<T, uint32_t >::value ? ShrUInt32 : ShrUInt64;
1310
+ binary->right ->cast <Const>()->value = Literal (static_cast <T>(shifts));
1311
+ return binary;
1312
+ }
1313
+
1314
+ template <typename T> Expression* optimizePowerOf2URem (Binary* binary, T c) {
1315
+ static_assert (std::is_same<T, uint32_t >::value ||
1316
+ std::is_same<T, uint64_t >::value,
1317
+ " type mismatch" );
1318
+ binary->op = std::is_same<T, uint32_t >::value ? AndInt32 : AndInt64;
1319
+ binary->right ->cast <Const>()->value = Literal (c - 1 );
1284
1320
return binary;
1285
1321
}
1286
1322
@@ -1327,8 +1363,9 @@ struct OptimizeInstructions
1327
1363
auto type = binary->right ->type ;
1328
1364
auto * right = binary->right ->cast <Const>();
1329
1365
if (type.isInteger ()) {
1366
+ auto constRight = right->value .getInteger ();
1330
1367
// operations on zero
1331
- if (right-> value == Literal::makeFromInt32 ( 0 , type) ) {
1368
+ if (constRight == 0LL ) {
1332
1369
if (binary->op == Abstract::getBinary (type, Abstract::Shl) ||
1333
1370
binary->op == Abstract::getBinary (type, Abstract::ShrU) ||
1334
1371
binary->op == Abstract::getBinary (type, Abstract::ShrS) ||
@@ -1344,16 +1381,62 @@ struct OptimizeInstructions
1344
1381
return Builder (*getModule ()).makeUnary (EqZInt64, binary->left );
1345
1382
}
1346
1383
}
1384
+ // operations on one
1385
+ if (constRight == 1LL ) {
1386
+ // (signed)x % 1 ==> 0
1387
+ if (binary->op == Abstract::getBinary (type, Abstract::RemS) &&
1388
+ !EffectAnalyzer (getPassOptions (), features, binary->left )
1389
+ .hasSideEffects ()) {
1390
+ right->value = Literal::makeSingleZero (type);
1391
+ return right;
1392
+ }
1393
+ }
1347
1394
// operations on all 1s
1348
- // TODO: shortcut method to create an all-ones?
1349
- if (right->value == Literal (int32_t (-1 )) ||
1350
- right->value == Literal (int64_t (-1 ))) {
1395
+ if (constRight == -1LL ) {
1351
1396
if (binary->op == Abstract::getBinary (type, Abstract::And)) {
1397
+ // x & -1 ==> x
1352
1398
return binary->left ;
1353
1399
} else if (binary->op == Abstract::getBinary (type, Abstract::Or) &&
1354
1400
!EffectAnalyzer (getPassOptions (), features, binary->left )
1355
1401
.hasSideEffects ()) {
1402
+ // x | -1 ==> -1
1356
1403
return binary->right ;
1404
+ } else if (binary->op == Abstract::getBinary (type, Abstract::RemS) &&
1405
+ !EffectAnalyzer (getPassOptions (), features, binary->left )
1406
+ .hasSideEffects ()) {
1407
+ // (signed)x % -1 ==> 0
1408
+ right->value = Literal::makeSingleZero (type);
1409
+ return right;
1410
+ } else if (binary->op == Abstract::getBinary (type, Abstract::GtU) &&
1411
+ !EffectAnalyzer (getPassOptions (), features, binary->left )
1412
+ .hasSideEffects ()) {
1413
+ // (unsigned)x > -1 ==> 0
1414
+ right->value = Literal::makeSingleZero (Type::i32);
1415
+ right->type = Type::i32;
1416
+ return right;
1417
+ } else if (binary->op == Abstract::getBinary (type, Abstract::LtU)) {
1418
+ // (unsigned)x < -1 ==> x != -1
1419
+ // friendlier to JS emitting as we don't need to write an unsigned
1420
+ // -1 value which is large.
1421
+ binary->op = Abstract::getBinary (type, Abstract::Ne);
1422
+ return binary;
1423
+ } else if (binary->op == DivUInt32) {
1424
+ // (unsigned)x / -1 ==> x == -1
1425
+ binary->op = Abstract::getBinary (type, Abstract::Eq);
1426
+ return binary;
1427
+ } else if (binary->op == Abstract::getBinary (type, Abstract::Mul)) {
1428
+ // x * -1 ==> 0 - x
1429
+ binary->op = Abstract::getBinary (type, Abstract::Sub);
1430
+ right->value = Literal::makeSingleZero (type);
1431
+ std::swap (binary->left , binary->right );
1432
+ return binary;
1433
+ } else if (binary->op == Abstract::getBinary (type, Abstract::LeU) &&
1434
+ !EffectAnalyzer (getPassOptions (), features, binary->left )
1435
+ .hasSideEffects ()) {
1436
+ // (unsigned)x <= -1 ==> 1
1437
+ right->value = Literal::makeFromInt32 (1 , Type::i32);
1438
+ right->type = Type::i32;
1439
+ return right;
1357
1440
}
1358
1441
}
1359
1442
// wasm binary encoding uses signed LEBs, which slightly favor negative
@@ -1364,7 +1447,7 @@ struct OptimizeInstructions
1364
1447
// subtractions than the more common additions).
1365
1448
if (binary->op == Abstract::getBinary (type, Abstract::Add) ||
1366
1449
binary->op == Abstract::getBinary (type, Abstract::Sub)) {
1367
- auto value = right-> value . getInteger () ;
1450
+ auto value = constRight ;
1368
1451
if (value == 0x40 || value == 0x2000 || value == 0x100000 ||
1369
1452
value == 0x8000000 || value == 0x400000000LL ||
1370
1453
value == 0x20000000000LL || value == 0x1000000000000LL ||
0 commit comments