| OLD | NEW |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 #include "vm/globals.h" | 5 #include "vm/globals.h" |
| 6 #if defined(TARGET_ARCH_IA32) | 6 #if defined(TARGET_ARCH_IA32) |
| 7 | 7 |
| 8 #include "vm/assembler.h" | 8 #include "vm/assembler.h" |
| 9 #include "vm/compiler.h" | 9 #include "vm/compiler.h" |
| 10 #include "vm/dart_entry.h" | 10 #include "vm/dart_entry.h" |
| (...skipping 1260 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1271 // - If receiver is Smi -> load Smi class. | 1271 // - If receiver is Smi -> load Smi class. |
| 1272 // - If receiver is not-Smi -> load receiver's class. | 1272 // - If receiver is not-Smi -> load receiver's class. |
| 1273 // - Check if 'num_args' (including receiver) match any IC data group. | 1273 // - Check if 'num_args' (including receiver) match any IC data group. |
| 1274 // - Match found -> jump to target. | 1274 // - Match found -> jump to target. |
| 1275 // - Match not found -> jump to IC miss. | 1275 // - Match not found -> jump to IC miss. |
| 1276 void StubCode::GenerateNArgsCheckInlineCacheStub( | 1276 void StubCode::GenerateNArgsCheckInlineCacheStub( |
| 1277 Assembler* assembler, | 1277 Assembler* assembler, |
| 1278 intptr_t num_args, | 1278 intptr_t num_args, |
| 1279 const RuntimeEntry& handle_ic_miss, | 1279 const RuntimeEntry& handle_ic_miss, |
| 1280 Token::Kind kind, | 1280 Token::Kind kind, |
| 1281 RangeCollectionMode range_collection_mode, | |
| 1282 bool optimized) { | 1281 bool optimized) { |
| 1283 ASSERT(num_args > 0); | 1282 ASSERT(num_args > 0); |
| 1284 #if defined(DEBUG) | 1283 #if defined(DEBUG) |
| 1285 { Label ok; | 1284 { Label ok; |
| 1286 // Check that the IC data array has NumArgsTested() == num_args. | 1285 // Check that the IC data array has NumArgsTested() == num_args. |
| 1287 // 'NumArgsTested' is stored in the least significant bits of 'state_bits'. | 1286 // 'NumArgsTested' is stored in the least significant bits of 'state_bits'. |
| 1288 __ movl(EBX, FieldAddress(ECX, ICData::state_bits_offset())); | 1287 __ movl(EBX, FieldAddress(ECX, ICData::state_bits_offset())); |
| 1289 ASSERT(ICData::NumArgsTestedShift() == 0); // No shift needed. | 1288 ASSERT(ICData::NumArgsTestedShift() == 0); // No shift needed. |
| 1290 __ andl(EBX, Immediate(ICData::NumArgsTestedMask())); | 1289 __ andl(EBX, Immediate(ICData::NumArgsTestedMask())); |
| 1291 __ cmpl(EBX, Immediate(num_args)); | 1290 __ cmpl(EBX, Immediate(num_args)); |
| 1292 __ j(EQUAL, &ok, Assembler::kNearJump); | 1291 __ j(EQUAL, &ok, Assembler::kNearJump); |
| 1293 __ Stop("Incorrect stub for IC data"); | 1292 __ Stop("Incorrect stub for IC data"); |
| 1294 __ Bind(&ok); | 1293 __ Bind(&ok); |
| 1295 } | 1294 } |
| 1296 #endif // DEBUG | 1295 #endif // DEBUG |
| 1297 | 1296 |
| 1298 Label stepping, done_stepping; | 1297 Label stepping, done_stepping; |
| 1299 if (FLAG_support_debugger && !optimized) { | 1298 if (FLAG_support_debugger && !optimized) { |
| 1300 __ Comment("Check single stepping"); | 1299 __ Comment("Check single stepping"); |
| 1301 __ LoadIsolate(EAX); | 1300 __ LoadIsolate(EAX); |
| 1302 __ cmpb(Address(EAX, Isolate::single_step_offset()), Immediate(0)); | 1301 __ cmpb(Address(EAX, Isolate::single_step_offset()), Immediate(0)); |
| 1303 __ j(NOT_EQUAL, &stepping); | 1302 __ j(NOT_EQUAL, &stepping); |
| 1304 __ Bind(&done_stepping); | 1303 __ Bind(&done_stepping); |
| 1305 } | 1304 } |
| 1306 __ Comment("Range feedback collection"); | |
| 1307 Label not_smi_or_overflow; | 1305 Label not_smi_or_overflow; |
| 1308 if (range_collection_mode == kCollectRanges) { | |
| 1309 ASSERT((num_args == 1) || (num_args == 2)); | |
| 1310 if (num_args == 2) { | |
| 1311 __ movl(EAX, Address(ESP, + 2 * kWordSize)); | |
| 1312 __ UpdateRangeFeedback(EAX, 0, ECX, EBX, EDI, EDX, ¬_smi_or_overflow); | |
| 1313 } | |
| 1314 | |
| 1315 __ movl(EAX, Address(ESP, + 1 * kWordSize)); | |
| 1316 __ UpdateRangeFeedback(EAX, (num_args - 1), ECX, EBX, EDI, EDX, | |
| 1317 ¬_smi_or_overflow); | |
| 1318 } | |
| 1319 if (kind != Token::kILLEGAL) { | 1306 if (kind != Token::kILLEGAL) { |
| 1320 EmitFastSmiOp(assembler, kind, num_args, ¬_smi_or_overflow); | 1307 EmitFastSmiOp(assembler, kind, num_args, ¬_smi_or_overflow); |
| 1321 } | 1308 } |
| 1322 __ Bind(¬_smi_or_overflow); | 1309 __ Bind(¬_smi_or_overflow); |
| 1323 | 1310 |
| 1324 __ Comment("Extract ICData initial values and receiver cid"); | 1311 __ Comment("Extract ICData initial values and receiver cid"); |
| 1325 // ECX: IC data object (preserved). | 1312 // ECX: IC data object (preserved). |
| 1326 // Load arguments descriptor into EDX. | 1313 // Load arguments descriptor into EDX. |
| 1327 __ movl(EDX, FieldAddress(ECX, ICData::arguments_descriptor_offset())); | 1314 __ movl(EDX, FieldAddress(ECX, ICData::arguments_descriptor_offset())); |
| 1328 // Loop that checks if there is an IC data match. | 1315 // Loop that checks if there is an IC data match. |
| (...skipping 98 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1427 __ movl(EDI, Immediate(Smi::RawValue(Smi::kMaxValue))); | 1414 __ movl(EDI, Immediate(Smi::RawValue(Smi::kMaxValue))); |
| 1428 __ cmovno(EDI, EAX); | 1415 __ cmovno(EDI, EAX); |
| 1429 __ StoreIntoSmiField(Address(EBX, count_offset), EDI); | 1416 __ StoreIntoSmiField(Address(EBX, count_offset), EDI); |
| 1430 } | 1417 } |
| 1431 | 1418 |
| 1432 __ movl(EAX, Address(EBX, target_offset)); | 1419 __ movl(EAX, Address(EBX, target_offset)); |
| 1433 __ Bind(&call_target_function); | 1420 __ Bind(&call_target_function); |
| 1434 __ Comment("Call target"); | 1421 __ Comment("Call target"); |
| 1435 // EAX: Target function. | 1422 // EAX: Target function. |
| 1436 __ movl(EBX, FieldAddress(EAX, Function::entry_point_offset())); | 1423 __ movl(EBX, FieldAddress(EAX, Function::entry_point_offset())); |
| 1437 if (range_collection_mode == kCollectRanges) { | 1424 __ jmp(EBX); |
| 1438 __ EnterStubFrame(); | |
| 1439 __ pushl(ECX); | |
| 1440 const intptr_t arg_offset_words = num_args + | |
| 1441 Assembler::kEnterStubFramePushedWords + | |
| 1442 1; // ECX | |
| 1443 for (intptr_t i = 0; i < num_args; i++) { | |
| 1444 __ movl(EDI, Address(ESP, arg_offset_words * kWordSize)); | |
| 1445 __ pushl(EDI); | |
| 1446 } | |
| 1447 __ call(EBX); | |
| 1448 | |
| 1449 __ movl(ECX, Address(EBP, kFirstLocalSlotFromFp * kWordSize)); | |
| 1450 Label done; | |
| 1451 __ UpdateRangeFeedback(EAX, 2, ECX, EBX, EDI, EDX, &done); | |
| 1452 __ Bind(&done); | |
| 1453 __ LeaveFrame(); | |
| 1454 __ ret(); | |
| 1455 } else { | |
| 1456 __ jmp(EBX); | |
| 1457 } | |
| 1458 | 1425 |
| 1459 if (FLAG_support_debugger && !optimized) { | 1426 if (FLAG_support_debugger && !optimized) { |
| 1460 __ Bind(&stepping); | 1427 __ Bind(&stepping); |
| 1461 __ EnterStubFrame(); | 1428 __ EnterStubFrame(); |
| 1462 __ pushl(ECX); | 1429 __ pushl(ECX); |
| 1463 __ CallRuntime(kSingleStepHandlerRuntimeEntry, 0); | 1430 __ CallRuntime(kSingleStepHandlerRuntimeEntry, 0); |
| 1464 __ popl(ECX); | 1431 __ popl(ECX); |
| 1465 __ LeaveFrame(); | 1432 __ LeaveFrame(); |
| 1466 __ jmp(&done_stepping); | 1433 __ jmp(&done_stepping); |
| 1467 } | 1434 } |
| 1468 } | 1435 } |
| 1469 | 1436 |
| 1470 | 1437 |
| 1471 // Use inline cache data array to invoke the target or continue in inline | 1438 // Use inline cache data array to invoke the target or continue in inline |
| 1472 // cache miss handler. Stub for 1-argument check (receiver class). | 1439 // cache miss handler. Stub for 1-argument check (receiver class). |
| 1473 // ECX: Inline cache data object. | 1440 // ECX: Inline cache data object. |
| 1474 // TOS(0): Return address. | 1441 // TOS(0): Return address. |
| 1475 // Inline cache data object structure: | 1442 // Inline cache data object structure: |
| 1476 // 0: function-name | 1443 // 0: function-name |
| 1477 // 1: N, number of arguments checked. | 1444 // 1: N, number of arguments checked. |
| 1478 // 2 .. (length - 1): group of checks, each check containing: | 1445 // 2 .. (length - 1): group of checks, each check containing: |
| 1479 // - N classes. | 1446 // - N classes. |
| 1480 // - 1 target function. | 1447 // - 1 target function. |
| 1481 void StubCode::GenerateOneArgCheckInlineCacheStub(Assembler* assembler) { | 1448 void StubCode::GenerateOneArgCheckInlineCacheStub(Assembler* assembler) { |
| 1482 GenerateUsageCounterIncrement(assembler, EBX); | 1449 GenerateUsageCounterIncrement(assembler, EBX); |
| 1483 GenerateNArgsCheckInlineCacheStub(assembler, 1, | 1450 GenerateNArgsCheckInlineCacheStub(assembler, 1, |
| 1484 kInlineCacheMissHandlerOneArgRuntimeEntry, | 1451 kInlineCacheMissHandlerOneArgRuntimeEntry, |
| 1485 Token::kILLEGAL, | 1452 Token::kILLEGAL); |
| 1486 kIgnoreRanges); | |
| 1487 } | 1453 } |
| 1488 | 1454 |
| 1489 | 1455 |
| 1490 void StubCode::GenerateTwoArgsCheckInlineCacheStub(Assembler* assembler) { | 1456 void StubCode::GenerateTwoArgsCheckInlineCacheStub(Assembler* assembler) { |
| 1491 GenerateUsageCounterIncrement(assembler, EBX); | 1457 GenerateUsageCounterIncrement(assembler, EBX); |
| 1492 GenerateNArgsCheckInlineCacheStub(assembler, 2, | 1458 GenerateNArgsCheckInlineCacheStub(assembler, 2, |
| 1493 kInlineCacheMissHandlerTwoArgsRuntimeEntry, | 1459 kInlineCacheMissHandlerTwoArgsRuntimeEntry, |
| 1494 Token::kILLEGAL, | 1460 Token::kILLEGAL); |
| 1495 kIgnoreRanges); | |
| 1496 } | 1461 } |
| 1497 | 1462 |
| 1498 | 1463 |
| 1499 void StubCode::GenerateSmiAddInlineCacheStub(Assembler* assembler) { | 1464 void StubCode::GenerateSmiAddInlineCacheStub(Assembler* assembler) { |
| 1500 GenerateUsageCounterIncrement(assembler, EBX); | 1465 GenerateUsageCounterIncrement(assembler, EBX); |
| 1501 GenerateNArgsCheckInlineCacheStub(assembler, 2, | 1466 GenerateNArgsCheckInlineCacheStub(assembler, 2, |
| 1502 kInlineCacheMissHandlerTwoArgsRuntimeEntry, | 1467 kInlineCacheMissHandlerTwoArgsRuntimeEntry, |
| 1503 Token::kADD, | 1468 Token::kADD); |
| 1504 kCollectRanges); | |
| 1505 } | 1469 } |
| 1506 | 1470 |
| 1507 | 1471 |
| 1508 void StubCode::GenerateSmiSubInlineCacheStub(Assembler* assembler) { | 1472 void StubCode::GenerateSmiSubInlineCacheStub(Assembler* assembler) { |
| 1509 GenerateUsageCounterIncrement(assembler, EBX); | 1473 GenerateUsageCounterIncrement(assembler, EBX); |
| 1510 GenerateNArgsCheckInlineCacheStub(assembler, 2, | 1474 GenerateNArgsCheckInlineCacheStub(assembler, 2, |
| 1511 kInlineCacheMissHandlerTwoArgsRuntimeEntry, | 1475 kInlineCacheMissHandlerTwoArgsRuntimeEntry, |
| 1512 Token::kSUB, | 1476 Token::kSUB); |
| 1513 kCollectRanges); | |
| 1514 } | 1477 } |
| 1515 | 1478 |
| 1516 | 1479 |
| 1517 void StubCode::GenerateSmiEqualInlineCacheStub(Assembler* assembler) { | 1480 void StubCode::GenerateSmiEqualInlineCacheStub(Assembler* assembler) { |
| 1518 GenerateUsageCounterIncrement(assembler, EBX); | 1481 GenerateUsageCounterIncrement(assembler, EBX); |
| 1519 GenerateNArgsCheckInlineCacheStub(assembler, 2, | 1482 GenerateNArgsCheckInlineCacheStub(assembler, 2, |
| 1520 kInlineCacheMissHandlerTwoArgsRuntimeEntry, | 1483 kInlineCacheMissHandlerTwoArgsRuntimeEntry, |
| 1521 Token::kEQ, | 1484 Token::kEQ); |
| 1522 kIgnoreRanges); | |
| 1523 } | 1485 } |
| 1524 | 1486 |
| 1525 | 1487 |
| 1526 void StubCode::GenerateUnaryRangeCollectingInlineCacheStub( | 1488 void StubCode::GenerateUnaryRangeCollectingInlineCacheStub( |
| 1527 Assembler* assembler) { | 1489 Assembler* assembler) { |
| 1528 GenerateUsageCounterIncrement(assembler, EBX); | 1490 GenerateUsageCounterIncrement(assembler, EBX); |
| 1529 GenerateNArgsCheckInlineCacheStub(assembler, 1, | 1491 GenerateNArgsCheckInlineCacheStub(assembler, 1, |
| 1530 kInlineCacheMissHandlerOneArgRuntimeEntry, | 1492 kInlineCacheMissHandlerOneArgRuntimeEntry, |
| 1531 Token::kILLEGAL, | 1493 Token::kILLEGAL); |
| 1532 kCollectRanges); | |
| 1533 } | 1494 } |
| 1534 | 1495 |
| 1535 | 1496 |
| 1536 void StubCode::GenerateBinaryRangeCollectingInlineCacheStub( | 1497 void StubCode::GenerateBinaryRangeCollectingInlineCacheStub( |
| 1537 Assembler* assembler) { | 1498 Assembler* assembler) { |
| 1538 GenerateUsageCounterIncrement(assembler, EBX); | 1499 GenerateUsageCounterIncrement(assembler, EBX); |
| 1539 GenerateNArgsCheckInlineCacheStub(assembler, 2, | 1500 GenerateNArgsCheckInlineCacheStub(assembler, 2, |
| 1540 kInlineCacheMissHandlerTwoArgsRuntimeEntry, | 1501 kInlineCacheMissHandlerTwoArgsRuntimeEntry, |
| 1541 Token::kILLEGAL, | 1502 Token::kILLEGAL); |
| 1542 kCollectRanges); | |
| 1543 } | 1503 } |
| 1544 | 1504 |
| 1545 | 1505 |
| 1546 // Use inline cache data array to invoke the target or continue in inline | 1506 // Use inline cache data array to invoke the target or continue in inline |
| 1547 // cache miss handler. Stub for 1-argument check (receiver class). | 1507 // cache miss handler. Stub for 1-argument check (receiver class). |
| 1548 // EDI: function which counter needs to be incremented. | 1508 // EDI: function which counter needs to be incremented. |
| 1549 // ECX: Inline cache data object. | 1509 // ECX: Inline cache data object. |
| 1550 // TOS(0): Return address. | 1510 // TOS(0): Return address. |
| 1551 // Inline cache data object structure: | 1511 // Inline cache data object structure: |
| 1552 // 0: function-name | 1512 // 0: function-name |
| 1553 // 1: N, number of arguments checked. | 1513 // 1: N, number of arguments checked. |
| 1554 // 2 .. (length - 1): group of checks, each check containing: | 1514 // 2 .. (length - 1): group of checks, each check containing: |
| 1555 // - N classes. | 1515 // - N classes. |
| 1556 // - 1 target function. | 1516 // - 1 target function. |
| 1557 void StubCode::GenerateOneArgOptimizedCheckInlineCacheStub( | 1517 void StubCode::GenerateOneArgOptimizedCheckInlineCacheStub( |
| 1558 Assembler* assembler) { | 1518 Assembler* assembler) { |
| 1559 GenerateOptimizedUsageCounterIncrement(assembler); | 1519 GenerateOptimizedUsageCounterIncrement(assembler); |
| 1560 GenerateNArgsCheckInlineCacheStub(assembler, 1, | 1520 GenerateNArgsCheckInlineCacheStub(assembler, 1, |
| 1561 kInlineCacheMissHandlerOneArgRuntimeEntry, | 1521 kInlineCacheMissHandlerOneArgRuntimeEntry, |
| 1562 Token::kILLEGAL, | 1522 Token::kILLEGAL, |
| 1563 kIgnoreRanges, | |
| 1564 true /* optimized */); | 1523 true /* optimized */); |
| 1565 } | 1524 } |
| 1566 | 1525 |
| 1567 | 1526 |
| 1568 void StubCode::GenerateTwoArgsOptimizedCheckInlineCacheStub( | 1527 void StubCode::GenerateTwoArgsOptimizedCheckInlineCacheStub( |
| 1569 Assembler* assembler) { | 1528 Assembler* assembler) { |
| 1570 GenerateOptimizedUsageCounterIncrement(assembler); | 1529 GenerateOptimizedUsageCounterIncrement(assembler); |
| 1571 GenerateNArgsCheckInlineCacheStub(assembler, 2, | 1530 GenerateNArgsCheckInlineCacheStub(assembler, 2, |
| 1572 kInlineCacheMissHandlerTwoArgsRuntimeEntry, | 1531 kInlineCacheMissHandlerTwoArgsRuntimeEntry, |
| 1573 Token::kILLEGAL, | 1532 Token::kILLEGAL, |
| 1574 kIgnoreRanges, | |
| 1575 true /* optimized */); | 1533 true /* optimized */); |
| 1576 } | 1534 } |
| 1577 | 1535 |
| 1578 | 1536 |
| 1579 // Intermediary stub between a static call and its target. ICData contains | 1537 // Intermediary stub between a static call and its target. ICData contains |
| 1580 // the target function and the call count. | 1538 // the target function and the call count. |
| 1581 // ECX: ICData | 1539 // ECX: ICData |
| 1582 void StubCode::GenerateZeroArgsUnoptimizedStaticCallStub(Assembler* assembler) { | 1540 void StubCode::GenerateZeroArgsUnoptimizedStaticCallStub(Assembler* assembler) { |
| 1583 GenerateUsageCounterIncrement(assembler, EBX); | 1541 GenerateUsageCounterIncrement(assembler, EBX); |
| 1584 | 1542 |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1638 __ LeaveFrame(); | 1596 __ LeaveFrame(); |
| 1639 __ jmp(&done_stepping, Assembler::kNearJump); | 1597 __ jmp(&done_stepping, Assembler::kNearJump); |
| 1640 } | 1598 } |
| 1641 } | 1599 } |
| 1642 | 1600 |
| 1643 | 1601 |
| 1644 void StubCode::GenerateOneArgUnoptimizedStaticCallStub(Assembler* assembler) { | 1602 void StubCode::GenerateOneArgUnoptimizedStaticCallStub(Assembler* assembler) { |
| 1645 GenerateUsageCounterIncrement(assembler, EBX); | 1603 GenerateUsageCounterIncrement(assembler, EBX); |
| 1646 GenerateNArgsCheckInlineCacheStub( | 1604 GenerateNArgsCheckInlineCacheStub( |
| 1647 assembler, 1, kStaticCallMissHandlerOneArgRuntimeEntry, | 1605 assembler, 1, kStaticCallMissHandlerOneArgRuntimeEntry, |
| 1648 Token::kILLEGAL, | 1606 Token::kILLEGAL); |
| 1649 kIgnoreRanges); | |
| 1650 } | 1607 } |
| 1651 | 1608 |
| 1652 | 1609 |
| 1653 void StubCode::GenerateTwoArgsUnoptimizedStaticCallStub(Assembler* assembler) { | 1610 void StubCode::GenerateTwoArgsUnoptimizedStaticCallStub(Assembler* assembler) { |
| 1654 GenerateUsageCounterIncrement(assembler, EBX); | 1611 GenerateUsageCounterIncrement(assembler, EBX); |
| 1655 GenerateNArgsCheckInlineCacheStub(assembler, 2, | 1612 GenerateNArgsCheckInlineCacheStub(assembler, 2, |
| 1656 kStaticCallMissHandlerTwoArgsRuntimeEntry, | 1613 kStaticCallMissHandlerTwoArgsRuntimeEntry, |
| 1657 Token::kILLEGAL, | 1614 Token::kILLEGAL); |
| 1658 kIgnoreRanges); | |
| 1659 } | 1615 } |
| 1660 | 1616 |
| 1661 | 1617 |
| 1662 // Stub for compiling a function and jumping to the compiled code. | 1618 // Stub for compiling a function and jumping to the compiled code. |
| 1663 // ECX: IC-Data (for methods). | 1619 // ECX: IC-Data (for methods). |
| 1664 // EDX: Arguments descriptor. | 1620 // EDX: Arguments descriptor. |
| 1665 // EAX: Function. | 1621 // EAX: Function. |
| 1666 void StubCode::GenerateLazyCompileStub(Assembler* assembler) { | 1622 void StubCode::GenerateLazyCompileStub(Assembler* assembler) { |
| 1667 __ EnterStubFrame(); | 1623 __ EnterStubFrame(); |
| 1668 __ pushl(EDX); // Preserve arguments descriptor array. | 1624 __ pushl(EDX); // Preserve arguments descriptor array. |
| (...skipping 439 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2108 | 2064 |
| 2109 | 2065 |
| 2110 | 2066 |
| 2111 void StubCode::GenerateFrameAwaitingMaterializationStub(Assembler* assembler) { | 2067 void StubCode::GenerateFrameAwaitingMaterializationStub(Assembler* assembler) { |
| 2112 __ int3(); | 2068 __ int3(); |
| 2113 } | 2069 } |
| 2114 | 2070 |
| 2115 } // namespace dart | 2071 } // namespace dart |
| 2116 | 2072 |
| 2117 #endif // defined TARGET_ARCH_IA32 | 2073 #endif // defined TARGET_ARCH_IA32 |
| OLD | NEW |