diff options
| -rw-r--r-- | compiler/optimizing/data_type.h | 6 | ||||
| -rw-r--r-- | compiler/optimizing/instruction_simplifier.cc | 76 | ||||
| -rw-r--r-- | compiler/optimizing/select_generator.cc | 17 | ||||
| -rw-r--r-- | libartbase/base/allocator.cc | 4 | ||||
| -rw-r--r-- | libartbase/base/allocator.h | 16 | ||||
| -rw-r--r-- | runtime/vdex_file.cc | 1 | ||||
| -rw-r--r-- | test/645-checker-abs-simd/src/Main.java | 5 | ||||
| -rw-r--r-- | test/678-checker-simd-saturation/src/Main.java | 23 | ||||
| -rw-r--r-- | test/679-checker-minmax/src/Main.java | 130 | ||||
| -rw-r--r-- | test/681-checker-abs/src/Main.java | 105 |
10 files changed, 345 insertions, 38 deletions
diff --git a/compiler/optimizing/data_type.h b/compiler/optimizing/data_type.h index 4a6c91459f..be26e67af3 100644 --- a/compiler/optimizing/data_type.h +++ b/compiler/optimizing/data_type.h @@ -210,6 +210,12 @@ class DataType { static bool IsTypeConversionImplicit(Type input_type, Type result_type); static bool IsTypeConversionImplicit(int64_t value, Type result_type); + static bool IsZeroExtension(Type input_type, Type result_type) { + return IsIntOrLongType(result_type) && + IsUnsignedType(input_type) && + Size(result_type) > Size(input_type); + } + static const char* PrettyDescriptor(Type type); private: diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 676fe6bcb7..d3cf9568c2 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -67,7 +67,6 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { bool TryCombineVecMultiplyAccumulate(HVecMul* mul); void VisitShift(HBinaryOperation* shift); - void VisitEqual(HEqual* equal) OVERRIDE; void VisitNotEqual(HNotEqual* equal) OVERRIDE; void VisitBooleanNot(HBooleanNot* bool_not) OVERRIDE; @@ -78,6 +77,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void VisitNullCheck(HNullCheck* instruction) OVERRIDE; void VisitArrayLength(HArrayLength* instruction) OVERRIDE; void VisitCheckCast(HCheckCast* instruction) OVERRIDE; + void VisitAbs(HAbs* instruction) OVERRIDE; void VisitAdd(HAdd* instruction) OVERRIDE; void VisitAnd(HAnd* instruction) OVERRIDE; void VisitCondition(HCondition* instruction) OVERRIDE; @@ -903,6 +903,30 @@ static bool AreLowerPrecisionArgs(DataType::Type to_type, HInstruction* a, HInst to_type == DataType::Type::kInt64); } +// Returns an acceptable substitution for "a" on the select +// construct "a <cmp> b ? c : .." during MIN/MAX recognition. +static HInstruction* AllowInMinMax(IfCondition cmp, + HInstruction* a, + HInstruction* b, + HInstruction* c) { + int64_t value = 0; + if (IsInt64AndGet(b, /*out*/ &value) && + (((cmp == kCondLT || cmp == kCondLE) && c->IsMax()) || + ((cmp == kCondGT || cmp == kCondGE) && c->IsMin()))) { + HConstant* other = c->AsBinaryOperation()->GetConstantRight(); + if (other != nullptr && a == c->AsBinaryOperation()->GetLeastConstantLeft()) { + int64_t other_value = Int64FromConstant(other); + bool is_max = (cmp == kCondLT || cmp == kCondLE); + // Allow the max for a < 100 ? max(a, -100) : .. + // or the min for a > -100 ? min(a, 100) : .. + if (is_max ? (value >= other_value) : (value <= other_value)) { + return c; + } + } + } + return nullptr; +} + void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { HInstruction* replace_with = nullptr; HInstruction* condition = select->GetCondition(); @@ -946,9 +970,17 @@ void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { DataType::Type t_type = true_value->GetType(); DataType::Type f_type = false_value->GetType(); // Here we have a <cmp> b ? true_value : false_value. - // Test if both values are compatible integral types (resulting - // MIN/MAX/ABS type will be int or long, like the condition). + // Test if both values are compatible integral types (resulting MIN/MAX/ABS + // type will be int or long, like the condition). Replacements are general, + // but assume conditions prefer constants on the right. if (DataType::IsIntegralType(t_type) && DataType::Kind(t_type) == DataType::Kind(f_type)) { + // Allow a < 100 ? max(a, -100) : .. + // or a > -100 ? min(a, 100) : .. + // to use min/max instead of a to detect nested min/max expressions. + HInstruction* new_a = AllowInMinMax(cmp, a, b, true_value); + if (new_a != nullptr) { + a = new_a; + } // Try to replace typical integral MIN/MAX/ABS constructs. if ((cmp == kCondLT || cmp == kCondLE || cmp == kCondGT || cmp == kCondGE) && ((a == true_value && b == false_value) || @@ -957,19 +989,16 @@ void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { // or a > b ? a : b (MAX) or a > b ? b : a (MIN). bool is_min = (cmp == kCondLT || cmp == kCondLE) == (a == true_value); replace_with = NewIntegralMinMax(GetGraph()->GetAllocator(), a, b, select, is_min); - } else if (true_value->IsNeg()) { - HInstruction* negated = true_value->InputAt(0); - if ((cmp == kCondLT || cmp == kCondLE) && - (a == negated && a == false_value && IsInt64Value(b, 0))) { - // Found a < 0 ? -a : a which can be replaced by ABS(a). - replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), false_value, select); - } - } else if (false_value->IsNeg()) { - HInstruction* negated = false_value->InputAt(0); - if ((cmp == kCondGT || cmp == kCondGE) && - (a == true_value && a == negated && IsInt64Value(b, 0))) { - // Found a > 0 ? a : -a which can be replaced by ABS(a). - replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), true_value, select); + } else if (((cmp == kCondLT || cmp == kCondLE) && true_value->IsNeg()) || + ((cmp == kCondGT || cmp == kCondGE) && false_value->IsNeg())) { + bool negLeft = (cmp == kCondLT || cmp == kCondLE); + HInstruction* the_negated = negLeft ? true_value->InputAt(0) : false_value->InputAt(0); + HInstruction* not_negated = negLeft ? false_value : true_value; + if (a == the_negated && a == not_negated && IsInt64Value(b, 0)) { + // Found a < 0 ? -a : a + // or a > 0 ? a : -a + // which can be replaced by ABS(a). + replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), a, select); } } else if (true_value->IsSub() && false_value->IsSub()) { HInstruction* true_sub1 = true_value->InputAt(0); @@ -981,8 +1010,8 @@ void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { ((cmp == kCondLT || cmp == kCondLE) && (a == true_sub2 && b == true_sub1 && a == false_sub1 && b == false_sub2))) && AreLowerPrecisionArgs(t_type, a, b)) { - // Found a > b ? a - b : b - a or - // a < b ? b - a : a - b + // Found a > b ? a - b : b - a + // or a < b ? b - a : a - b // which can be replaced by ABS(a - b) for lower precision operands a, b. replace_with = NewIntegralAbs(GetGraph()->GetAllocator(), true_value, select); } @@ -1241,6 +1270,17 @@ void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruct } } +void InstructionSimplifierVisitor::VisitAbs(HAbs* instruction) { + HInstruction* input = instruction->GetInput(); + if (DataType::IsZeroExtension(input->GetType(), instruction->GetResultType())) { + // Zero extension from narrow to wide can never set sign bit in the wider + // operand, making the subsequent Abs redundant (e.g., abs(b & 0xff) for byte b). + instruction->ReplaceWith(input); + instruction->GetBlock()->RemoveInstruction(instruction); + RecordSimplification(); + } +} + void InstructionSimplifierVisitor::VisitAdd(HAdd* instruction) { HConstant* input_cst = instruction->GetConstantRight(); HInstruction* input_other = instruction->GetLeastConstantLeft(); diff --git a/compiler/optimizing/select_generator.cc b/compiler/optimizing/select_generator.cc index 66e51421ca..3f52bdd13c 100644 --- a/compiler/optimizing/select_generator.cc +++ b/compiler/optimizing/select_generator.cc @@ -43,12 +43,16 @@ static bool IsSimpleBlock(HBasicBlock* block) { for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* instruction = it.Current(); if (instruction->IsControlFlow()) { - if (num_instructions > kMaxInstructionsInBranch) { - return false; - } return instruction->IsGoto() || instruction->IsReturn(); } else if (instruction->CanBeMoved() && !instruction->HasSideEffects()) { - num_instructions++; + if (instruction->IsSelect() && + instruction->AsSelect()->GetCondition()->GetBlock() == block) { + // Count one HCondition and HSelect in the same block as a single instruction. + // This enables finding nested selects. + continue; + } else if (++num_instructions > kMaxInstructionsInBranch) { + return false; // bail as soon as we exceed number of allowed instructions + } } else { return false; } @@ -97,6 +101,7 @@ void HSelectGenerator::Run() { HBasicBlock* true_block = if_instruction->IfTrueSuccessor(); HBasicBlock* false_block = if_instruction->IfFalseSuccessor(); DCHECK_NE(true_block, false_block); + if (!IsSimpleBlock(true_block) || !IsSimpleBlock(false_block) || !BlocksMergeTogether(true_block, false_block)) { @@ -107,10 +112,10 @@ void HSelectGenerator::Run() { // If the branches are not empty, move instructions in front of the If. // TODO(dbrazdil): This puts an instruction between If and its condition. // Implement moving of conditions to first users if possible. - if (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) { + while (!true_block->IsSingleGoto() && !true_block->IsSingleReturn()) { true_block->GetFirstInstruction()->MoveBefore(if_instruction); } - if (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) { + while (!false_block->IsSingleGoto() && !false_block->IsSingleReturn()) { false_block->GetFirstInstruction()->MoveBefore(if_instruction); } DCHECK(true_block->IsSingleGoto() || true_block->IsSingleReturn()); diff --git a/libartbase/base/allocator.cc b/libartbase/base/allocator.cc index 17da789b36..c7be4e0161 100644 --- a/libartbase/base/allocator.cc +++ b/libartbase/base/allocator.cc @@ -76,7 +76,7 @@ namespace TrackedAllocators { // These globals are safe since they don't have any non-trivial destructors. Atomic<size_t> g_bytes_used[kAllocatorTagCount]; -volatile size_t g_max_bytes_used[kAllocatorTagCount]; +Atomic<size_t> g_max_bytes_used[kAllocatorTagCount]; Atomic<uint64_t> g_total_bytes_used[kAllocatorTagCount]; void Dump(std::ostream& os) { @@ -84,7 +84,7 @@ void Dump(std::ostream& os) { os << "Dumping native memory usage\n"; for (size_t i = 0; i < kAllocatorTagCount; ++i) { uint64_t bytes_used = g_bytes_used[i].load(std::memory_order_relaxed); - uint64_t max_bytes_used = g_max_bytes_used[i]; + uint64_t max_bytes_used = g_max_bytes_used[i].load(std::memory_order_relaxed); uint64_t total_bytes_used = g_total_bytes_used[i].load(std::memory_order_relaxed); if (total_bytes_used != 0) { os << static_cast<AllocatorTag>(i) << " active=" << bytes_used << " max=" diff --git a/libartbase/base/allocator.h b/libartbase/base/allocator.h index 7ddbacf716..662f78e448 100644 --- a/libartbase/base/allocator.h +++ b/libartbase/base/allocator.h @@ -71,12 +71,14 @@ std::ostream& operator<<(std::ostream& os, const AllocatorTag& tag); namespace TrackedAllocators { +// We use memory_order_relaxed updates of the following counters. Values are treated as approximate +// wherever concurrent updates are possible. // Running count of number of bytes used for this kind of allocation. Increased by allocations, // decreased by deallocations. extern Atomic<size_t> g_bytes_used[kAllocatorTagCount]; // Largest value of bytes used seen. -extern volatile size_t g_max_bytes_used[kAllocatorTagCount]; +extern Atomic<size_t> g_max_bytes_used[kAllocatorTagCount]; // Total number of bytes allocated of this kind. extern Atomic<uint64_t> g_total_bytes_used[kAllocatorTagCount]; @@ -84,15 +86,17 @@ extern Atomic<uint64_t> g_total_bytes_used[kAllocatorTagCount]; void Dump(std::ostream& os); inline void RegisterAllocation(AllocatorTag tag, size_t bytes) { - g_total_bytes_used[tag].fetch_add(bytes, std::memory_order_seq_cst); - size_t new_bytes = g_bytes_used[tag].fetch_add(bytes, std::memory_order_seq_cst) + bytes; - if (g_max_bytes_used[tag] < new_bytes) { - g_max_bytes_used[tag] = new_bytes; + g_total_bytes_used[tag].fetch_add(bytes, std::memory_order_relaxed); + size_t new_bytes = g_bytes_used[tag].fetch_add(bytes, std::memory_order_relaxed) + bytes; + size_t max_bytes = g_max_bytes_used[tag].load(std::memory_order_relaxed); + while (max_bytes < new_bytes + && !g_max_bytes_used[tag].compare_exchange_weak(max_bytes /* updated */, new_bytes, + std::memory_order_relaxed)) { } } inline void RegisterFree(AllocatorTag tag, size_t bytes) { - g_bytes_used[tag].fetch_sub(bytes, std::memory_order_seq_cst); + g_bytes_used[tag].fetch_sub(bytes, std::memory_order_relaxed); } } // namespace TrackedAllocators diff --git a/runtime/vdex_file.cc b/runtime/vdex_file.cc index ec4dc417d3..838d7f14bc 100644 --- a/runtime/vdex_file.cc +++ b/runtime/vdex_file.cc @@ -317,6 +317,7 @@ ArrayRef<const uint8_t> VdexFile::GetQuickenedInfoOf(const DexFile& dex_file, if (quickening_info.empty()) { return ArrayRef<const uint8_t>(); } + CHECK_LT(dex_method_idx, dex_file.NumMethodIds()); const uint32_t quickening_offset = GetQuickenInfoOffsetTable(dex_file, quickening_info).GetOffset(dex_method_idx); if (quickening_offset == 0u) { diff --git a/test/645-checker-abs-simd/src/Main.java b/test/645-checker-abs-simd/src/Main.java index 870a403ff5..819304a9e9 100644 --- a/test/645-checker-abs-simd/src/Main.java +++ b/test/645-checker-abs-simd/src/Main.java @@ -48,10 +48,7 @@ public class Main { } /// CHECK-START: void Main.doitChar(char[]) loop_optimization (before) - /// CHECK-DAG: Phi loop:<<Loop:B\d+>> outer_loop:none - /// CHECK-DAG: ArrayGet loop:<<Loop>> outer_loop:none - /// CHECK-DAG: Abs loop:<<Loop>> outer_loop:none - /// CHECK-DAG: ArraySet loop:<<Loop>> outer_loop:none + /// CHECK-NOT: Abs // /// CHECK-START: void Main.doitChar(char[]) loop_optimization (after) /// CHECK-NOT: VecAbs diff --git a/test/678-checker-simd-saturation/src/Main.java b/test/678-checker-simd-saturation/src/Main.java index decc691789..7a22ca175d 100644 --- a/test/678-checker-simd-saturation/src/Main.java +++ b/test/678-checker-simd-saturation/src/Main.java @@ -397,7 +397,22 @@ public class Main { } } - // TODO: recognize the more common if-else too. + /// CHECK-START: void Main.satAlt2(short[], short[], short[]) loop_optimization (before) + /// CHECK-DAG: <<Clp1:i\d+>> IntConstant -32768 loop:none + /// CHECK-DAG: <<Clp2:i\d+>> IntConstant 32767 loop:none + /// CHECK-DAG: <<Get1:s\d+>> ArrayGet [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:s\d+>> ArrayGet [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:i\d+>> Add [<<Get1>>,<<Get2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Max:i\d+>> Max [<<Add>>,<<Clp1>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Min:i\d+>> Min [<<Max>>,<<Clp2>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Conv:s\d+>> TypeConversion [<<Min>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: ArraySet [{{l\d+}},<<Phi>>,<<Conv>>] loop:<<Loop>> outer_loop:none + // + /// CHECK-START-{ARM,ARM64}: void Main.satAlt2(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:d\d+>> VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none public static void satAlt2(short[] a, short[] b, short[] c) { int n = Math.min(a.length, Math.min(b.length, c.length)); for (int i = 0; i < n; i++) { @@ -411,7 +426,11 @@ public class Main { } } - // TODO: recognize conditional too. + /// CHECK-START-{ARM,ARM64}: void Main.satAlt3(short[], short[], short[]) loop_optimization (after) + /// CHECK-DAG: <<Get1:d\d+>> VecLoad [{{l\d+}},<<Phi:i\d+>>] loop:<<Loop:B\d+>> outer_loop:none + /// CHECK-DAG: <<Get2:d\d+>> VecLoad [{{l\d+}},<<Phi>>] loop:<<Loop>> outer_loop:none + /// CHECK-DAG: <<Add:d\d+>> VecSaturationAdd [<<Get1>>,<<Get2>>] packed_type:Int16 loop:<<Loop>> outer_loop:none + /// CHECK-DAG: VecStore [{{l\d+}},<<Phi>>,<<Add>>] loop:<<Loop>> outer_loop:none public static void satAlt3(short[] a, short[] b, short[] c) { int n = Math.min(a.length, Math.min(b.length, c.length)); for (int i = 0; i < n; i++) { diff --git a/test/679-checker-minmax/src/Main.java b/test/679-checker-minmax/src/Main.java index 38085bbd7b..4f0261c0a3 100644 --- a/test/679-checker-minmax/src/Main.java +++ b/test/679-checker-minmax/src/Main.java @@ -19,6 +19,10 @@ */ public class Main { + // + // Different types. + // + /// CHECK-START: int Main.min1(int, int) instruction_simplifier$after_inlining (before) /// CHECK-DAG: <<Cnd:z\d+>> GreaterThanOrEqual [<<Op1:i\d+>>,<<Op2:i\d+>>] /// CHECK-DAG: <<Sel:i\d+>> Select [<<Op1>>,<<Op2>>,<<Cnd>>] @@ -229,7 +233,116 @@ public class Main { return a >= b ? a : b; } + + // + // Complications. + // + + // TODO: coming soon, under discussion + public static int min0(int[] a, int[] b) { + // Repeat of array references needs finding the common subexpressions + // prior to doing the select and min/max recognition. + return a[0] <= b[0] ? a[0] : b[0]; + } + + // TODO: coming soon, under discussion + public static int max0(int[] a, int[] b) { + // Repeat of array references needs finding the common subexpressions + // prior to doing the select and min/max recognition. + return a[0] >= b[0] ? a[0] : b[0]; + } + + /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <<Par:i\d+>> ParameterValue + /// CHECK-DAG: <<P100:i\d+>> IntConstant 100 + /// CHECK-DAG: <<M100:i\d+>> IntConstant -100 + /// CHECK-DAG: <<Cnd1:z\d+>> LessThanOrEqual [<<Par>>,<<P100>>] + /// CHECK-DAG: <<Sel1:i\d+>> Select [<<P100>>,<<Par>>,<<Cnd1>>] + /// CHECK-DAG: <<Cnd2:z\d+>> GreaterThanOrEqual [<<Sel1>>,<<M100>>] + /// CHECK-DAG: <<Sel2:i\d+>> Select [<<M100>>,<<Sel1>>,<<Cnd2>>] + /// CHECK-DAG: Return [<<Sel2>>] + // + /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <<Par:i\d+>> ParameterValue + /// CHECK-DAG: <<P100:i\d+>> IntConstant 100 + /// CHECK-DAG: <<M100:i\d+>> IntConstant -100 + /// CHECK-DAG: <<Min:i\d+>> Min [<<Par>>,<<P100>>] + /// CHECK-DAG: <<Max:i\d+>> Max [<<Min>>,<<M100>>] + /// CHECK-DAG: Return [<<Max>>] + // + /// CHECK-START: int Main.minmax1(int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int minmax1(int x) { + // Simple if-if gives clean select sequence. + if (x > 100) { + x = 100; + } + if (x < -100) { + x = -100; + } + return x; + } + + /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (before) + /// CHECK-DAG: <<Par:i\d+>> ParameterValue + /// CHECK-DAG: <<P100:i\d+>> IntConstant 100 + /// CHECK-DAG: <<M100:i\d+>> IntConstant -100 + /// CHECK-DAG: <<Cnd1:z\d+>> LessThanOrEqual [<<Par>>,<<P100>>] + /// CHECK-DAG: <<Cnd2:z\d+>> GreaterThanOrEqual [<<Par>>,<<M100>>] + /// CHECK-DAG: <<Sel1:i\d+>> Select [<<M100>>,<<Par>>,<<Cnd2>>] + /// CHECK-DAG: <<Sel2:i\d+>> Select [<<P100>>,<<Sel1>>,<<Cnd1>>] + /// CHECK-DAG: Return [<<Sel2>>] + // + /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <<Par:i\d+>> ParameterValue + /// CHECK-DAG: <<P100:i\d+>> IntConstant 100 + /// CHECK-DAG: <<M100:i\d+>> IntConstant -100 + /// CHECK-DAG: <<Max:i\d+>> Max [<<Par>>,<<M100>>] + /// CHECK-DAG: <<Min:i\d+>> Min [<<Max>>,<<P100>>] + /// CHECK-DAG: Return [<<Min>>] + // + /// CHECK-START: int Main.minmax2(int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int minmax2(int x) { + // Simple if-else requires inspecting bounds of resulting selects. + if (x > 100) { + x = 100; + } else if (x < -100) { + x = -100; + } + return x; + } + + /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <<Par:i\d+>> ParameterValue + /// CHECK-DAG: <<P100:i\d+>> IntConstant 100 + /// CHECK-DAG: <<M100:i\d+>> IntConstant -100 + /// CHECK-DAG: <<Max:i\d+>> Max [<<Par>>,<<M100>>] + /// CHECK-DAG: <<Min:i\d+>> Min [<<Max>>,<<P100>>] + /// CHECK-DAG: Return [<<Min>>] + // + /// CHECK-START: int Main.minmax3(int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int minmax3(int x) { + return (x > 100) ? 100 : ((x < -100) ? -100 : x); + } + + /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <<Par:i\d+>> ParameterValue + /// CHECK-DAG: <<P100:i\d+>> IntConstant 100 + /// CHECK-DAG: <<M100:i\d+>> IntConstant -100 + /// CHECK-DAG: <<Min:i\d+>> Min [<<Par>>,<<P100>>] + /// CHECK-DAG: <<Max:i\d+>> Max [<<Min>>,<<M100>>] + /// CHECK-DAG: Return [<<Max>>] + // + /// CHECK-START: int Main.minmax4(int) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: Select + public static int minmax4(int x) { + return (x < -100) ? -100 : ((x > 100) ? 100 : x); + } + public static void main(String[] args) { + // Types. expectEquals(10, min1(10, 20)); expectEquals(10, min2(10, 20)); expectEquals(10, min3(10, 20)); @@ -244,6 +357,23 @@ public class Main { expectEquals(20, max5((short) 10, (short) 20)); expectEquals(20, max6((byte) 10, (byte) 20)); expectEquals(20L, max7(10L, 20L)); + // Complications. + int[] a = { 10 }; + int[] b = { 20 }; + expectEquals(10, min0(a, b)); + expectEquals(20, max0(a, b)); + expectEquals(-100, minmax1(-200)); + expectEquals(10, minmax1(10)); + expectEquals(100, minmax1(200)); + expectEquals(-100, minmax2(-200)); + expectEquals(10, minmax2(10)); + expectEquals(100, minmax2(200)); + expectEquals(-100, minmax3(-200)); + expectEquals(10, minmax3(10)); + expectEquals(100, minmax3(200)); + expectEquals(-100, minmax4(-200)); + expectEquals(10, minmax4(10)); + expectEquals(100, minmax4(200)); System.out.println("passed"); } diff --git a/test/681-checker-abs/src/Main.java b/test/681-checker-abs/src/Main.java index 8064b1dac1..d1ba7c6851 100644 --- a/test/681-checker-abs/src/Main.java +++ b/test/681-checker-abs/src/Main.java @@ -19,6 +19,38 @@ */ public class Main { + /// CHECK-START: int Main.absI(int) instruction_simplifier (before) + /// CHECK-DAG: <<Par:i\d+>> ParameterValue + /// CHECK-DAG: <<Abs:i\d+>> InvokeStaticOrDirect [<<Par>>] intrinsic:MathAbsInt + /// CHECK-DAG: Return [<<Abs>>] + // + /// CHECK-START: int Main.absI(int) instruction_simplifier (after) + /// CHECK-DAG: <<Par:i\d+>> ParameterValue + /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Par>>] + /// CHECK-DAG: Return [<<Abs>>] + // + /// CHECK-START: int Main.absI(int) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + public static int absI(int a) { + return Math.abs(a); + } + + /// CHECK-START: long Main.absL(long) instruction_simplifier (before) + /// CHECK-DAG: <<Par:j\d+>> ParameterValue + /// CHECK-DAG: <<Abs:j\d+>> InvokeStaticOrDirect [<<Par>>] intrinsic:MathAbsLong + /// CHECK-DAG: Return [<<Abs>>] + // + /// CHECK-START: long Main.absL(long) instruction_simplifier (after) + /// CHECK-DAG: <<Par:j\d+>> ParameterValue + /// CHECK-DAG: <<Abs:j\d+>> Abs [<<Par>>] + /// CHECK-DAG: Return [<<Abs>>] + // + /// CHECK-START: long Main.absL(long) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + public static long absL(long a) { + return Math.abs(a); + } + /// CHECK-START: int Main.abs1(int) instruction_simplifier$after_inlining (before) /// CHECK-DAG: <<Par:i\d+>> ParameterValue /// CHECK-DAG: <<Zer:i\d+>> IntConstant 0 @@ -152,7 +184,74 @@ public class Main { return a >= 0 ? a : -a; } + // + // Nop zero extension. + // + + /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (before) + /// CHECK-DAG: <<Par:b\d+>> ParameterValue + /// CHECK-DAG: <<Msk:i\d+>> IntConstant 255 + /// CHECK-DAG: <<And:i\d+>> [<<Par>>,<<Msk>>] + /// CHECK-DAG: <<Abs:i\d+>> InvokeStaticOrDirect [<<And>>] intrinsic:MathAbsInt + /// CHECK-DAG: Return [<<Abs>>] + // + /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (after) + /// CHECK-DAG: <<Par:b\d+>> ParameterValue + /// CHECK-DAG: <<Cnv:a\d+>> TypeConversion [<<Par>>] + /// CHECK-DAG: Return [<<Cnv>>] + // + /// CHECK-START: int Main.zabs1(byte) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: Abs + public static int zabs1(byte a) { + return Math.abs(a & 0xff); + } + + /// CHECK-START: int Main.zabs2(short) instruction_simplifier (before) + /// CHECK-DAG: <<Par:s\d+>> ParameterValue + /// CHECK-DAG: <<Msk:i\d+>> IntConstant 65535 + /// CHECK-DAG: <<And:i\d+>> [<<Msk>>,<<Par>>] + /// CHECK-DAG: <<Abs:i\d+>> InvokeStaticOrDirect [<<And>>] intrinsic:MathAbsInt + /// CHECK-DAG: Return [<<Abs>>] + // + /// CHECK-START: int Main.zabs2(short) instruction_simplifier (after) + /// CHECK-DAG: <<Par:s\d+>> ParameterValue + /// CHECK-DAG: <<Cnv:c\d+>> TypeConversion [<<Par>>] + /// CHECK-DAG: Return [<<Cnv>>] + // + /// CHECK-START: int Main.zabs2(short) instruction_simplifier (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: Abs + public static int zabs2(short a) { + return Math.abs(a & 0xffff); + } + + /// CHECK-START: int Main.zabs3(char) instruction_simplifier (before) + /// CHECK-DAG: <<Par:c\d+>> ParameterValue + /// CHECK-DAG: <<Abs:i\d+>> InvokeStaticOrDirect [<<Par>>] intrinsic:MathAbsInt + /// CHECK-DAG: Return [<<Abs>>] + // + /// CHECK-START: int Main.zabs3(char) instruction_simplifier (after) + /// CHECK-DAG: <<Par:c\d+>> ParameterValue + /// CHECK-DAG: <<Abs:i\d+>> Abs [<<Par>>] + /// CHECK-DAG: Return [<<Abs>>] + // + /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_inlining (after) + /// CHECK-DAG: <<Par:c\d+>> ParameterValue + /// CHECK-DAG: Return [<<Par>>] + // + /// CHECK-START: int Main.zabs3(char) instruction_simplifier$after_inlining (after) + /// CHECK-NOT: InvokeStaticOrDirect + /// CHECK-NOT: Abs + public static int zabs3(char a) { + return Math.abs(a); + } + public static void main(String[] args) { + expectEquals(10, absI(-10)); + expectEquals(20, absI(20)); + expectEquals(10L, absL(-10L)); + expectEquals(20L, absL(20L)); expectEquals(10, abs1(-10)); expectEquals(20, abs1(20)); expectEquals(10, abs2(-10)); @@ -167,6 +266,12 @@ public class Main { expectEquals(20, abs6((byte) 20)); expectEquals(10L, abs7(-10L)); expectEquals(20L, abs7(20L)); + expectEquals(1, zabs1((byte) 1)); + expectEquals(0xff, zabs1((byte) -1)); + expectEquals(1, zabs2((short) 1)); + expectEquals(0xffff, zabs2((short) -1)); + expectEquals(1, zabs3((char) 1)); + expectEquals(0xffff, zabs3((char) -1)); System.out.println("passed"); } |