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/aot_optimizer.h" | 5 #include "vm/aot_optimizer.h" |
6 | 6 |
7 #include "vm/bit_vector.h" | 7 #include "vm/bit_vector.h" |
8 #include "vm/branch_optimizer.h" | 8 #include "vm/branch_optimizer.h" |
9 #include "vm/cha.h" | 9 #include "vm/cha.h" |
10 #include "vm/compiler.h" | 10 #include "vm/compiler.h" |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
115 const Class& cls = Class::Handle(Z, I->object_store()->object_class()); | 115 const Class& cls = Class::Handle(Z, I->object_store()->object_class()); |
116 const Array& args_desc_array = Array::Handle( | 116 const Array& args_desc_array = Array::Handle( |
117 Z, | 117 Z, |
118 ArgumentsDescriptor::New(call->ArgumentCount(), call->argument_names())); | 118 ArgumentsDescriptor::New(call->ArgumentCount(), call->argument_names())); |
119 ArgumentsDescriptor args_desc(args_desc_array); | 119 ArgumentsDescriptor args_desc(args_desc_array); |
120 const Function& function = | 120 const Function& function = |
121 Function::Handle(Z, Resolver::ResolveDynamicForReceiverClass( | 121 Function::Handle(Z, Resolver::ResolveDynamicForReceiverClass( |
122 cls, call->function_name(), args_desc)); | 122 cls, call->function_name(), args_desc)); |
123 ASSERT(!function.IsNull()); | 123 ASSERT(!function.IsNull()); |
124 | 124 |
125 ZoneGrowableArray<PushArgumentInstr*>* args = | 125 const Function& target = Function::ZoneHandle(Z, function.raw()); |
126 new (Z) ZoneGrowableArray<PushArgumentInstr*>(call->ArgumentCount()); | 126 StaticCallInstr* static_call = StaticCallInstr::FromCall(Z, call, target); |
127 for (intptr_t i = 0; i < call->ArgumentCount(); i++) { | |
128 args->Add(call->PushArgumentAt(i)); | |
129 } | |
130 StaticCallInstr* static_call = new (Z) StaticCallInstr( | |
131 call->token_pos(), Function::ZoneHandle(Z, function.raw()), | |
132 call->argument_names(), args, call->deopt_id()); | |
133 static_call->set_result_cid(kTypeCid); | 127 static_call->set_result_cid(kTypeCid); |
134 call->ReplaceWith(static_call, current_iterator()); | 128 call->ReplaceWith(static_call, current_iterator()); |
135 return true; | 129 return true; |
136 } | 130 } |
137 | 131 |
138 | 132 |
139 // Optimize instance calls using cid. This is called after optimizer | 133 // Optimize instance calls using cid. This is called after optimizer |
140 // converted instance calls to instructions. Any remaining | 134 // converted instance calls to instructions. Any remaining |
141 // instance calls are either megamorphic calls, cannot be optimized or | 135 // instance calls are either megamorphic calls, cannot be optimized or |
142 // have no runtime type feedback collected. | 136 // have no runtime type feedback collected. |
(...skipping 502 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
645 PushArgumentInstr* arg = | 639 PushArgumentInstr* arg = |
646 new (Z) PushArgumentInstr(new (Z) Value(left->ArgumentAt(0))); | 640 new (Z) PushArgumentInstr(new (Z) Value(left->ArgumentAt(0))); |
647 InsertBefore(call, arg, NULL, FlowGraph::kEffect); | 641 InsertBefore(call, arg, NULL, FlowGraph::kEffect); |
648 args->Add(arg); | 642 args->Add(arg); |
649 arg = new (Z) PushArgumentInstr(new (Z) Value(right->ArgumentAt(0))); | 643 arg = new (Z) PushArgumentInstr(new (Z) Value(right->ArgumentAt(0))); |
650 InsertBefore(call, arg, NULL, FlowGraph::kEffect); | 644 InsertBefore(call, arg, NULL, FlowGraph::kEffect); |
651 args->Add(arg); | 645 args->Add(arg); |
652 StaticCallInstr* static_call = | 646 StaticCallInstr* static_call = |
653 new (Z) StaticCallInstr(call->token_pos(), have_same_runtime_type, | 647 new (Z) StaticCallInstr(call->token_pos(), have_same_runtime_type, |
654 Object::null_array(), // argument_names | 648 Object::null_array(), // argument_names |
655 args, call->deopt_id()); | 649 args, call->deopt_id(), call->CallCount()); |
656 static_call->set_result_cid(kBoolCid); | 650 static_call->set_result_cid(kBoolCid); |
657 ReplaceCall(call, static_call); | 651 ReplaceCall(call, static_call); |
658 return true; | 652 return true; |
659 } | 653 } |
660 | 654 |
661 return false; | 655 return false; |
662 } | 656 } |
663 | 657 |
664 | 658 |
665 bool AotOptimizer::TryReplaceWithEqualityOp(InstanceCallInstr* call, | 659 bool AotOptimizer::TryReplaceWithEqualityOp(InstanceCallInstr* call, |
(...skipping 845 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1511 Library::Handle(Z, Library::InternalLibrary()); | 1505 Library::Handle(Z, Library::InternalLibrary()); |
1512 const String& target_name = Symbols::_classRangeCheck(); | 1506 const String& target_name = Symbols::_classRangeCheck(); |
1513 const Function& target = Function::ZoneHandle( | 1507 const Function& target = Function::ZoneHandle( |
1514 Z, dart_internal.LookupFunctionAllowPrivate(target_name)); | 1508 Z, dart_internal.LookupFunctionAllowPrivate(target_name)); |
1515 ASSERT(!target.IsNull()); | 1509 ASSERT(!target.IsNull()); |
1516 ASSERT(target.IsRecognized() && target.always_inline()); | 1510 ASSERT(target.IsRecognized() && target.always_inline()); |
1517 | 1511 |
1518 StaticCallInstr* new_call = | 1512 StaticCallInstr* new_call = |
1519 new (Z) StaticCallInstr(call->token_pos(), target, | 1513 new (Z) StaticCallInstr(call->token_pos(), target, |
1520 Object::null_array(), // argument_names | 1514 Object::null_array(), // argument_names |
1521 args, call->deopt_id()); | 1515 args, call->deopt_id(), call->CallCount()); |
1522 Environment* copy = call->env()->DeepCopy( | 1516 Environment* copy = call->env()->DeepCopy( |
1523 Z, call->env()->Length() - call->ArgumentCount()); | 1517 Z, call->env()->Length() - call->ArgumentCount()); |
1524 for (intptr_t i = 0; i < args->length(); ++i) { | 1518 for (intptr_t i = 0; i < args->length(); ++i) { |
1525 copy->PushValue(new (Z) Value((*args)[i]->value()->definition())); | 1519 copy->PushValue(new (Z) Value((*args)[i]->value()->definition())); |
1526 } | 1520 } |
1527 call->RemoveEnvironment(); | 1521 call->RemoveEnvironment(); |
1528 ReplaceCall(call, new_call); | 1522 ReplaceCall(call, new_call); |
1529 copy->DeepCopyTo(Z, new_call); | 1523 copy->DeepCopyTo(Z, new_call); |
1530 return; | 1524 return; |
1531 } | 1525 } |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1603 const String& target_name = Symbols::_classIdEqualsAssert(); | 1597 const String& target_name = Symbols::_classIdEqualsAssert(); |
1604 const Function& target = Function::ZoneHandle( | 1598 const Function& target = Function::ZoneHandle( |
1605 Z, dart_internal.LookupFunctionAllowPrivate(target_name)); | 1599 Z, dart_internal.LookupFunctionAllowPrivate(target_name)); |
1606 ASSERT(!target.IsNull()); | 1600 ASSERT(!target.IsNull()); |
1607 ASSERT(target.IsRecognized()); | 1601 ASSERT(target.IsRecognized()); |
1608 ASSERT(target.always_inline()); | 1602 ASSERT(target.always_inline()); |
1609 | 1603 |
1610 StaticCallInstr* new_call = | 1604 StaticCallInstr* new_call = |
1611 new (Z) StaticCallInstr(call->token_pos(), target, | 1605 new (Z) StaticCallInstr(call->token_pos(), target, |
1612 Object::null_array(), // argument_names | 1606 Object::null_array(), // argument_names |
1613 args, call->deopt_id()); | 1607 args, call->deopt_id(), call->CallCount()); |
1614 Environment* copy = | 1608 Environment* copy = |
1615 call->env()->DeepCopy(Z, call->env()->Length() - call->ArgumentCount()); | 1609 call->env()->DeepCopy(Z, call->env()->Length() - call->ArgumentCount()); |
1616 for (intptr_t i = 0; i < args->length(); ++i) { | 1610 for (intptr_t i = 0; i < args->length(); ++i) { |
1617 copy->PushValue(new (Z) Value((*args)[i]->value()->definition())); | 1611 copy->PushValue(new (Z) Value((*args)[i]->value()->definition())); |
1618 } | 1612 } |
1619 call->RemoveEnvironment(); | 1613 call->RemoveEnvironment(); |
1620 ReplaceCall(call, new_call); | 1614 ReplaceCall(call, new_call); |
1621 copy->DeepCopyTo(Z, new_call); | 1615 copy->DeepCopyTo(Z, new_call); |
1622 return; | 1616 return; |
1623 } | 1617 } |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1666 const String& target_name = Symbols::_classRangeAssert(); | 1660 const String& target_name = Symbols::_classRangeAssert(); |
1667 const Function& target = Function::ZoneHandle( | 1661 const Function& target = Function::ZoneHandle( |
1668 Z, dart_internal.LookupFunctionAllowPrivate(target_name)); | 1662 Z, dart_internal.LookupFunctionAllowPrivate(target_name)); |
1669 ASSERT(!target.IsNull()); | 1663 ASSERT(!target.IsNull()); |
1670 ASSERT(target.IsRecognized()); | 1664 ASSERT(target.IsRecognized()); |
1671 ASSERT(target.always_inline()); | 1665 ASSERT(target.always_inline()); |
1672 | 1666 |
1673 StaticCallInstr* new_call = | 1667 StaticCallInstr* new_call = |
1674 new (Z) StaticCallInstr(call->token_pos(), target, | 1668 new (Z) StaticCallInstr(call->token_pos(), target, |
1675 Object::null_array(), // argument_names | 1669 Object::null_array(), // argument_names |
1676 args, call->deopt_id()); | 1670 args, call->deopt_id(), call->CallCount()); |
1677 Environment* copy = call->env()->DeepCopy( | 1671 Environment* copy = call->env()->DeepCopy( |
1678 Z, call->env()->Length() - call->ArgumentCount()); | 1672 Z, call->env()->Length() - call->ArgumentCount()); |
1679 for (intptr_t i = 0; i < args->length(); ++i) { | 1673 for (intptr_t i = 0; i < args->length(); ++i) { |
1680 copy->PushValue(new (Z) Value((*args)[i]->value()->definition())); | 1674 copy->PushValue(new (Z) Value((*args)[i]->value()->definition())); |
1681 } | 1675 } |
1682 call->RemoveEnvironment(); | 1676 call->RemoveEnvironment(); |
1683 ReplaceCall(call, new_call); | 1677 ReplaceCall(call, new_call); |
1684 copy->DeepCopyTo(Z, new_call); | 1678 copy->DeepCopyTo(Z, new_call); |
1685 return; | 1679 return; |
1686 } | 1680 } |
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1836 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); | 1830 const Function& target = Function::Handle(Z, unary_checks.GetTargetAt(0)); |
1837 const bool polymorphic_target = MethodRecognizer::PolymorphicTarget(target); | 1831 const bool polymorphic_target = MethodRecognizer::PolymorphicTarget(target); |
1838 has_one_target = !polymorphic_target; | 1832 has_one_target = !polymorphic_target; |
1839 } | 1833 } |
1840 | 1834 |
1841 if (has_one_target) { | 1835 if (has_one_target) { |
1842 RawFunction::Kind function_kind = | 1836 RawFunction::Kind function_kind = |
1843 Function::Handle(Z, unary_checks.GetTargetAt(0)).kind(); | 1837 Function::Handle(Z, unary_checks.GetTargetAt(0)).kind(); |
1844 if (!flow_graph()->InstanceCallNeedsClassCheck(instr, function_kind)) { | 1838 if (!flow_graph()->InstanceCallNeedsClassCheck(instr, function_kind)) { |
1845 CallTargets* targets = CallTargets::Create(Z, unary_checks); | 1839 CallTargets* targets = CallTargets::Create(Z, unary_checks); |
1846 PolymorphicInstanceCallInstr* call = | 1840 ASSERT(targets->HasSingleTarget()); |
1847 new (Z) PolymorphicInstanceCallInstr(instr, *targets, | 1841 const Function& target = targets->FirstTarget(); |
1848 /* with_checks = */ false, | 1842 StaticCallInstr* call = StaticCallInstr::FromCall(Z, instr, target); |
1849 /* complete = */ true); | |
1850 instr->ReplaceWith(call, current_iterator()); | 1843 instr->ReplaceWith(call, current_iterator()); |
1851 return; | 1844 return; |
1852 } | 1845 } |
1853 } | 1846 } |
1854 switch (instr->token_kind()) { | 1847 switch (instr->token_kind()) { |
1855 case Token::kEQ: | 1848 case Token::kEQ: |
1856 case Token::kLT: | 1849 case Token::kLT: |
1857 case Token::kLTE: | 1850 case Token::kLTE: |
1858 case Token::kGT: | 1851 case Token::kGT: |
1859 case Token::kGTE: { | 1852 case Token::kGTE: { |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1902 Class::Handle(Z, isolate()->class_table()->At(receiver_cid)); | 1895 Class::Handle(Z, isolate()->class_table()->At(receiver_cid)); |
1903 | 1896 |
1904 const Array& args_desc_array = | 1897 const Array& args_desc_array = |
1905 Array::Handle(Z, ArgumentsDescriptor::New(instr->ArgumentCount(), | 1898 Array::Handle(Z, ArgumentsDescriptor::New(instr->ArgumentCount(), |
1906 instr->argument_names())); | 1899 instr->argument_names())); |
1907 ArgumentsDescriptor args_desc(args_desc_array); | 1900 ArgumentsDescriptor args_desc(args_desc_array); |
1908 Function& function = Function::Handle( | 1901 Function& function = Function::Handle( |
1909 Z, Resolver::ResolveDynamicForReceiverClass( | 1902 Z, Resolver::ResolveDynamicForReceiverClass( |
1910 receiver_class, instr->function_name(), args_desc)); | 1903 receiver_class, instr->function_name(), args_desc)); |
1911 if (!function.IsNull()) { | 1904 if (!function.IsNull()) { |
1912 CallTargets* targets = new (Z) CallTargets(); | 1905 const Function& target = Function::ZoneHandle(Z, function.raw()); |
1913 Function& target = Function::ZoneHandle(Z, function.raw()); | 1906 StaticCallInstr* call = StaticCallInstr::FromCall(Z, instr, target); |
1914 targets->Add(CidRangeTarget(receiver_class.id(), receiver_class.id(), | |
1915 &target, /*count = */ 1)); | |
1916 PolymorphicInstanceCallInstr* call = | |
1917 new (Z) PolymorphicInstanceCallInstr(instr, *targets, | |
1918 /* with_checks = */ false, | |
1919 /* complete = */ true); | |
1920 instr->ReplaceWith(call, current_iterator()); | 1907 instr->ReplaceWith(call, current_iterator()); |
1921 return; | 1908 return; |
1922 } | 1909 } |
1923 } | 1910 } |
1924 | 1911 |
1925 Definition* callee_receiver = instr->ArgumentAt(0); | 1912 Definition* callee_receiver = instr->ArgumentAt(0); |
1926 const Function& function = flow_graph_->function(); | 1913 const Function& function = flow_graph_->function(); |
1927 Class& receiver_class = Class::Handle(Z); | 1914 Class& receiver_class = Class::Handle(Z); |
1928 | 1915 |
1929 if (function.IsDynamicFunction() && | 1916 if (function.IsDynamicFunction() && |
(...skipping 88 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2018 ic_data.AddReceiverCheck(cls.id(), single_target); | 2005 ic_data.AddReceiverCheck(cls.id(), single_target); |
2019 instr->set_ic_data(&ic_data); | 2006 instr->set_ic_data(&ic_data); |
2020 | 2007 |
2021 if (TryInlineFieldAccess(instr)) { | 2008 if (TryInlineFieldAccess(instr)) { |
2022 return; | 2009 return; |
2023 } | 2010 } |
2024 } | 2011 } |
2025 | 2012 |
2026 // We have computed that there is only a single target for this call | 2013 // We have computed that there is only a single target for this call |
2027 // within the whole hierarchy. Replace InstanceCall with StaticCall. | 2014 // within the whole hierarchy. Replace InstanceCall with StaticCall. |
2028 ZoneGrowableArray<PushArgumentInstr*>* args = new (Z) | 2015 const Function& target = Function::ZoneHandle(Z, single_target.raw()); |
2029 ZoneGrowableArray<PushArgumentInstr*>(instr->ArgumentCount()); | 2016 StaticCallInstr* call = StaticCallInstr::FromCall(Z, instr, target); |
2030 for (intptr_t i = 0; i < instr->ArgumentCount(); i++) { | |
2031 args->Add(instr->PushArgumentAt(i)); | |
2032 } | |
2033 StaticCallInstr* call = new (Z) StaticCallInstr( | |
2034 instr->token_pos(), Function::ZoneHandle(Z, single_target.raw()), | |
2035 instr->argument_names(), args, instr->deopt_id()); | |
2036 instr->ReplaceWith(call, current_iterator()); | 2017 instr->ReplaceWith(call, current_iterator()); |
2037 return; | 2018 return; |
2038 } else if ((ic_data.raw() != ICData::null()) && | 2019 } else if ((ic_data.raw() != ICData::null()) && |
2039 !ic_data.NumberOfChecksIs(0)) { | 2020 !ic_data.NumberOfChecksIs(0)) { |
2040 CallTargets* targets = CallTargets::Create(Z, ic_data); | 2021 CallTargets* targets = CallTargets::Create(Z, ic_data); |
2041 PolymorphicInstanceCallInstr* call = | 2022 PolymorphicInstanceCallInstr* call = |
2042 new (Z) PolymorphicInstanceCallInstr(instr, *targets, | 2023 new (Z) PolymorphicInstanceCallInstr(instr, *targets, |
2043 /* with_checks = */ true, | |
2044 /* complete = */ true); | 2024 /* complete = */ true); |
2045 instr->ReplaceWith(call, current_iterator()); | 2025 instr->ReplaceWith(call, current_iterator()); |
2046 return; | 2026 return; |
2047 } | 2027 } |
2048 } | 2028 } |
2049 } | 2029 } |
2050 | 2030 |
2051 // More than one target. Generate generic polymorphic call without | 2031 // More than one target. Generate generic polymorphic call without |
2052 // deoptimization. | 2032 // deoptimization. |
2053 if (instr->ic_data()->NumberOfUsedChecks() > 0) { | 2033 if (instr->ic_data()->NumberOfUsedChecks() > 0) { |
2054 ASSERT(!FLAG_polymorphic_with_deopt); | 2034 ASSERT(!FLAG_polymorphic_with_deopt); |
2055 // OK to use checks with PolymorphicInstanceCallInstr since no | 2035 // OK to use checks with PolymorphicInstanceCallInstr since no |
2056 // deoptimization is allowed. | 2036 // deoptimization is allowed. |
2057 CallTargets* targets = CallTargets::Create(Z, *instr->ic_data()); | 2037 CallTargets* targets = CallTargets::Create(Z, *instr->ic_data()); |
2058 PolymorphicInstanceCallInstr* call = | 2038 PolymorphicInstanceCallInstr* call = |
2059 new (Z) PolymorphicInstanceCallInstr(instr, *targets, | 2039 new (Z) PolymorphicInstanceCallInstr(instr, *targets, |
2060 /* with_checks = */ true, | |
2061 /* complete = */ false); | 2040 /* complete = */ false); |
2062 instr->ReplaceWith(call, current_iterator()); | 2041 instr->ReplaceWith(call, current_iterator()); |
2063 return; | 2042 return; |
2064 } | 2043 } |
2065 } | 2044 } |
2066 | 2045 |
2067 | 2046 |
2068 void AotOptimizer::VisitPolymorphicInstanceCall( | 2047 void AotOptimizer::VisitPolymorphicInstanceCall( |
2069 PolymorphicInstanceCallInstr* call) { | 2048 PolymorphicInstanceCallInstr* call) { |
2070 if (call->with_checks()) { | 2049 const intptr_t receiver_cid = |
2071 const intptr_t receiver_cid = | 2050 call->PushArgumentAt(0)->value()->Type()->ToCid(); |
2072 call->PushArgumentAt(0)->value()->Type()->ToCid(); | 2051 if (receiver_cid != kDynamicCid) { |
2073 if (receiver_cid != kDynamicCid) { | 2052 const Class& receiver_class = |
2074 const Class& receiver_class = | 2053 Class::Handle(Z, isolate()->class_table()->At(receiver_cid)); |
2075 Class::Handle(Z, isolate()->class_table()->At(receiver_cid)); | |
2076 | 2054 |
2077 const Array& args_desc_array = Array::Handle( | 2055 const Array& args_desc_array = Array::Handle( |
2078 Z, ArgumentsDescriptor::New(call->ArgumentCount(), | 2056 Z, ArgumentsDescriptor::New(call->ArgumentCount(), |
2079 call->instance_call()->argument_names())); | 2057 call->instance_call()->argument_names())); |
2080 ArgumentsDescriptor args_desc(args_desc_array); | 2058 ArgumentsDescriptor args_desc(args_desc_array); |
2081 const Function& function = Function::Handle( | 2059 const Function& function = Function::Handle( |
2082 Z, Resolver::ResolveDynamicForReceiverClass( | 2060 Z, |
2083 receiver_class, call->instance_call()->function_name(), | 2061 Resolver::ResolveDynamicForReceiverClass( |
2084 args_desc)); | 2062 receiver_class, call->instance_call()->function_name(), args_desc)); |
2085 if (!function.IsNull()) { | 2063 if (!function.IsNull()) { |
2086 call->set_with_checks(false); | 2064 // Only one target. Replace by static call. |
2087 } | 2065 StaticCallInstr* new_call = StaticCallInstr::FromCall(Z, call, function); |
| 2066 call->ReplaceWith(new_call, current_iterator()); |
2088 } | 2067 } |
2089 } | 2068 } |
2090 } | 2069 } |
2091 | 2070 |
2092 | 2071 |
2093 void AotOptimizer::VisitStaticCall(StaticCallInstr* call) { | 2072 void AotOptimizer::VisitStaticCall(StaticCallInstr* call) { |
2094 if (!IsAllowedForInlining(call->deopt_id())) { | 2073 if (!IsAllowedForInlining(call->deopt_id())) { |
2095 // Inlining disabled after a speculative inlining attempt. | 2074 // Inlining disabled after a speculative inlining attempt. |
2096 return; | 2075 return; |
2097 } | 2076 } |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2255 FlowGraph::kEffect); | 2234 FlowGraph::kEffect); |
2256 current_iterator()->RemoveCurrentFromGraph(); | 2235 current_iterator()->RemoveCurrentFromGraph(); |
2257 } | 2236 } |
2258 } | 2237 } |
2259 } | 2238 } |
2260 } | 2239 } |
2261 | 2240 |
2262 #endif // DART_PRECOMPILER | 2241 #endif // DART_PRECOMPILER |
2263 | 2242 |
2264 } // namespace dart | 2243 } // namespace dart |
OLD | NEW |