diff options
18 files changed, 639 insertions, 184 deletions
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 6ffd1aa686..42d955ef9e 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -2430,6 +2430,22 @@ void InstructionCodeGeneratorARM64::HandleBinaryOp(HBinaryOperation* instr) { __ Orr(dst, lhs, rhs); } else if (instr->IsSub()) { __ Sub(dst, lhs, rhs); + } else if (instr->IsRol()) { + if (rhs.IsImmediate()) { + uint32_t shift = (-rhs.GetImmediate()) & (lhs.GetSizeInBits() - 1); + __ Ror(dst, lhs, shift); + } else { + UseScratchRegisterScope temps(GetVIXLAssembler()); + + // Ensure shift distance is in the same size register as the result. If + // we are rotating a long and the shift comes in a w register originally, + // we don't need to sxtw for use as an x since the shift distances are + // all & reg_bits - 1. + Register right = RegisterFrom(instr->GetLocations()->InAt(1), type); + Register negated = (type == DataType::Type::kInt32) ? temps.AcquireW() : temps.AcquireX(); + __ Neg(negated, right); + __ Ror(dst, lhs, negated); + } } else if (instr->IsRor()) { if (rhs.IsImmediate()) { uint32_t shift = rhs.GetImmediate() & (lhs.GetSizeInBits() - 1); @@ -6411,6 +6427,14 @@ void InstructionCodeGeneratorARM64::VisitReturnVoid([[maybe_unused]] HReturnVoid codegen_->GenerateFrameExit(); } +void LocationsBuilderARM64::VisitRol(HRol* rol) { + HandleBinaryOp(rol); +} + +void InstructionCodeGeneratorARM64::VisitRol(HRol* rol) { + HandleBinaryOp(rol); +} + void LocationsBuilderARM64::VisitRor(HRor* ror) { HandleBinaryOp(ror); } diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 1afca6b8d6..fb8f8c0153 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -5280,17 +5280,22 @@ void InstructionCodeGeneratorARMVIXL::VisitDivZeroCheck(HDivZeroCheck* instructi } } -void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) { - LocationSummary* locations = ror->GetLocations(); - vixl32::Register in = InputRegisterAt(ror, 0); +void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HBinaryOperation* rotate) { + LocationSummary* locations = rotate->GetLocations(); + vixl32::Register in = InputRegisterAt(rotate, 0); Location rhs = locations->InAt(1); - vixl32::Register out = OutputRegister(ror); + vixl32::Register out = OutputRegister(rotate); if (rhs.IsConstant()) { // Arm32 and Thumb2 assemblers require a rotation on the interval [1,31], // so map all rotations to a +ve. equivalent in that range. // (e.g. left *or* right by -2 bits == 30 bits in the same direction.) uint32_t rot = CodeGenerator::GetInt32ValueOf(rhs.GetConstant()) & 0x1F; + + if (rotate->IsRol()) { + rot = -rot; + } + if (rot) { // Rotate, mapping left rotations to right equivalents if necessary. // (e.g. left by 2 bits == right by 30.) @@ -5299,7 +5304,16 @@ void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) { __ Mov(out, in); } } else { - __ Ror(out, in, RegisterFrom(rhs)); + if (rotate->IsRol()) { + UseScratchRegisterScope temps(GetVIXLAssembler()); + + vixl32::Register negated = temps.Acquire(); + __ Rsb(negated, RegisterFrom(rhs), 0); + __ Ror(out, in, negated); + } else { + DCHECK(rotate->IsRor()); + __ Ror(out, in, RegisterFrom(rhs)); + } } } @@ -5307,8 +5321,8 @@ void InstructionCodeGeneratorARMVIXL::HandleIntegerRotate(HRor* ror) { // rotates by swapping input regs (effectively rotating by the first 32-bits of // a larger rotation) or flipping direction (thus treating larger right/left // rotations as sub-word sized rotations in the other direction) as appropriate. -void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) { - LocationSummary* locations = ror->GetLocations(); +void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HBinaryOperation* rotate) { + LocationSummary* locations = rotate->GetLocations(); vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0)); vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0)); Location rhs = locations->InAt(1); @@ -5317,6 +5331,11 @@ void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) { if (rhs.IsConstant()) { uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant()); + + if (rotate->IsRol()) { + rot = -rot; + } + // Map all rotations to +ve. equivalents on the interval [0,63]. rot &= kMaxLongShiftDistance; // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate @@ -5341,7 +5360,17 @@ void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) { vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1)); vixl32::Label end; vixl32::Label shift_by_32_plus_shift_right; - vixl32::Label* final_label = codegen_->GetFinalLabel(ror, &end); + vixl32::Label* final_label = codegen_->GetFinalLabel(rotate, &end); + + // Negate rhs, taken from VisitNeg + if (rotate->IsRol()) { + Location negated = locations->GetTemp(2); + Location in = rhs; + + __ Rsb(RegisterFrom(negated), RegisterFrom(in), 0); + + rhs = negated; + } __ And(shift_right, RegisterFrom(rhs), 0x1F); __ Lsrs(shift_left, RegisterFrom(rhs), 6); @@ -5374,11 +5403,11 @@ void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) { } } -void LocationsBuilderARMVIXL::VisitRor(HRor* ror) { +void LocationsBuilderARMVIXL::HandleRotate(HBinaryOperation* rotate) { LocationSummary* locations = - new (GetGraph()->GetAllocator()) LocationSummary(ror, LocationSummary::kNoCall); - HInstruction* shift = ror->InputAt(1); - switch (ror->GetResultType()) { + new (GetGraph()->GetAllocator()) LocationSummary(rotate, LocationSummary::kNoCall); + HInstruction* shift = rotate->InputAt(1); + switch (rotate->GetResultType()) { case DataType::Type::kInt32: { locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RegisterOrConstant(shift)); @@ -5391,25 +5420,39 @@ void LocationsBuilderARMVIXL::VisitRor(HRor* ror) { locations->SetInAt(1, Location::ConstantLocation(shift)); } else { locations->SetInAt(1, Location::RequiresRegister()); - locations->AddRegisterTemps(2); + + if (rotate->IsRor()) { + locations->AddRegisterTemps(2); + } else { + DCHECK(rotate->IsRol()); + locations->AddRegisterTemps(3); + } } locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); break; } default: - LOG(FATAL) << "Unexpected operation type " << ror->GetResultType(); + LOG(FATAL) << "Unexpected operation type " << rotate->GetResultType(); } } -void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) { - DataType::Type type = ror->GetResultType(); +void LocationsBuilderARMVIXL::VisitRol(HRol* rol) { + HandleRotate(rol); +} + +void LocationsBuilderARMVIXL::VisitRor(HRor* ror) { + HandleRotate(ror); +} + +void InstructionCodeGeneratorARMVIXL::HandleRotate(HBinaryOperation* rotate) { + DataType::Type type = rotate->GetResultType(); switch (type) { case DataType::Type::kInt32: { - HandleIntegerRotate(ror); + HandleIntegerRotate(rotate); break; } case DataType::Type::kInt64: { - HandleLongRotate(ror); + HandleLongRotate(rotate); break; } default: @@ -5418,6 +5461,14 @@ void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) { } } +void InstructionCodeGeneratorARMVIXL::VisitRol(HRol* rol) { + HandleRotate(rol); +} + +void InstructionCodeGeneratorARMVIXL::VisitRor(HRor* ror) { + HandleRotate(ror); +} + void LocationsBuilderARMVIXL::HandleShift(HBinaryOperation* op) { DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); diff --git a/compiler/optimizing/code_generator_arm_vixl.h b/compiler/optimizing/code_generator_arm_vixl.h index 0c2e79c134..00b5a69b1d 100644 --- a/compiler/optimizing/code_generator_arm_vixl.h +++ b/compiler/optimizing/code_generator_arm_vixl.h @@ -390,13 +390,12 @@ class LocationsBuilderARMVIXL : public HGraphVisitor { void HandleInvoke(HInvoke* invoke); void HandleBitwiseOperation(HBinaryOperation* operation, Opcode opcode); void HandleCondition(HCondition* condition); - void HandleIntegerRotate(LocationSummary* locations); - void HandleLongRotate(LocationSummary* locations); void HandleShift(HBinaryOperation* operation); void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, WriteBarrierKind write_barrier_kind); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); + void HandleRotate(HBinaryOperation* rotate); Location ArithmeticZeroOrFpuRegister(HInstruction* input); Location ArmEncodableConstantOrRegister(HInstruction* constant, Opcode opcode); @@ -446,8 +445,9 @@ class InstructionCodeGeneratorARMVIXL : public InstructionCodeGenerator { void GenerateAddLongConst(Location out, Location first, uint64_t value); void HandleBitwiseOperation(HBinaryOperation* operation); void HandleCondition(HCondition* condition); - void HandleIntegerRotate(HRor* ror); - void HandleLongRotate(HRor* ror); + void HandleIntegerRotate(HBinaryOperation* rotate); + void HandleLongRotate(HBinaryOperation* rotate); + void HandleRotate(HBinaryOperation* rotate); void HandleShift(HBinaryOperation* operation); void GenerateWideAtomicStore(vixl::aarch32::Register addr, diff --git a/compiler/optimizing/code_generator_riscv64.cc b/compiler/optimizing/code_generator_riscv64.cc index bfc693f76b..43f855a915 100644 --- a/compiler/optimizing/code_generator_riscv64.cc +++ b/compiler/optimizing/code_generator_riscv64.cc @@ -2331,6 +2331,7 @@ void LocationsBuilderRISCV64::HandleShift(HBinaryOperation* instruction) { DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr() || + instruction->IsRol() || instruction->IsRor()); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction); @@ -2353,7 +2354,9 @@ void InstructionCodeGeneratorRISCV64::HandleShift(HBinaryOperation* instruction) DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr() || + instruction->IsRol() || instruction->IsRor()); + LocationSummary* locations = instruction->GetLocations(); DataType::Type type = instruction->GetType(); @@ -2366,6 +2369,9 @@ void InstructionCodeGeneratorRISCV64::HandleShift(HBinaryOperation* instruction) if (rs2_location.IsConstant()) { int64_t imm = CodeGenerator::GetInt64ValueOf(rs2_location.GetConstant()); + if (instruction->IsRol()) { + imm = -imm; + } uint32_t shamt = imm & (type == DataType::Type::kInt32 ? kMaxIntShiftDistance : kMaxLongShiftDistance); @@ -2380,6 +2386,8 @@ void InstructionCodeGeneratorRISCV64::HandleShift(HBinaryOperation* instruction) __ Sraiw(rd, rs1, shamt); } else if (instruction->IsUShr()) { __ Srliw(rd, rs1, shamt); + } else if (instruction->IsRol()) { + __ Roriw(rd, rs1, shamt); } else { DCHECK(instruction->IsRor()); __ Roriw(rd, rs1, shamt); @@ -2391,6 +2399,8 @@ void InstructionCodeGeneratorRISCV64::HandleShift(HBinaryOperation* instruction) __ Srai(rd, rs1, shamt); } else if (instruction->IsUShr()) { __ Srli(rd, rs1, shamt); + } else if (instruction->IsRol()) { + __ Rori(rd, rs1, shamt); } else { DCHECK(instruction->IsRor()); __ Rori(rd, rs1, shamt); @@ -2405,6 +2415,8 @@ void InstructionCodeGeneratorRISCV64::HandleShift(HBinaryOperation* instruction) __ Sraw(rd, rs1, rs2); } else if (instruction->IsUShr()) { __ Srlw(rd, rs1, rs2); + } else if (instruction->IsRol()) { + __ Rolw(rd, rs1, rs2); } else { DCHECK(instruction->IsRor()); __ Rorw(rd, rs1, rs2); @@ -2416,6 +2428,8 @@ void InstructionCodeGeneratorRISCV64::HandleShift(HBinaryOperation* instruction) __ Sra(rd, rs1, rs2); } else if (instruction->IsUShr()) { __ Srl(rd, rs1, rs2); + } else if (instruction->IsRol()) { + __ Rol(rd, rs1, rs2); } else { DCHECK(instruction->IsRor()); __ Ror(rd, rs1, rs2); @@ -5006,6 +5020,14 @@ void InstructionCodeGeneratorRISCV64::VisitReturnVoid([[maybe_unused]] HReturnVo codegen_->GenerateFrameExit(); } +void LocationsBuilderRISCV64::VisitRol(HRol* instruction) { + HandleShift(instruction); +} + +void InstructionCodeGeneratorRISCV64::VisitRol(HRol* instruction) { + HandleShift(instruction); +} + void LocationsBuilderRISCV64::VisitRor(HRor* instruction) { HandleShift(instruction); } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 5b2d0d4535..7e3c6216eb 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -5052,11 +5052,19 @@ void InstructionCodeGeneratorX86::GenerateUShrLong(const Location& loc, Register __ Bind(&done); } +void LocationsBuilderX86::VisitRol(HRol* rol) { + HandleRotate(rol); +} + void LocationsBuilderX86::VisitRor(HRor* ror) { + HandleRotate(ror); +} + +void LocationsBuilderX86::HandleRotate(HBinaryOperation* rotate) { LocationSummary* locations = - new (GetGraph()->GetAllocator()) LocationSummary(ror, LocationSummary::kNoCall); + new (GetGraph()->GetAllocator()) LocationSummary(rotate, LocationSummary::kNoCall); - switch (ror->GetResultType()) { + switch (rotate->GetResultType()) { case DataType::Type::kInt64: // Add the temporary needed. locations->AddTemp(Location::RequiresRegister()); @@ -5064,39 +5072,62 @@ void LocationsBuilderX86::VisitRor(HRor* ror) { case DataType::Type::kInt32: locations->SetInAt(0, Location::RequiresRegister()); // The shift count needs to be in CL (unless it is a constant). - locations->SetInAt(1, Location::ByteRegisterOrConstant(ECX, ror->InputAt(1))); + locations->SetInAt(1, Location::ByteRegisterOrConstant(ECX, rotate->InputAt(1))); locations->SetOut(Location::SameAsFirstInput()); break; default: - LOG(FATAL) << "Unexpected operation type " << ror->GetResultType(); + LOG(FATAL) << "Unexpected operation type " << rotate->GetResultType(); UNREACHABLE(); } } +void InstructionCodeGeneratorX86::VisitRol(HRol* rol) { + HandleRotate(rol); +} + void InstructionCodeGeneratorX86::VisitRor(HRor* ror) { - LocationSummary* locations = ror->GetLocations(); + HandleRotate(ror); +} + +void InstructionCodeGeneratorX86::HandleRotate(HBinaryOperation* rotate) { + LocationSummary* locations = rotate->GetLocations(); Location first = locations->InAt(0); Location second = locations->InAt(1); - if (ror->GetResultType() == DataType::Type::kInt32) { + if (rotate->GetResultType() == DataType::Type::kInt32) { Register first_reg = first.AsRegister<Register>(); if (second.IsRegister()) { Register second_reg = second.AsRegister<Register>(); - __ rorl(first_reg, second_reg); + if (rotate->IsRol()) { + __ roll(first_reg, second_reg); + } else { + DCHECK(rotate->IsRor()); + __ rorl(first_reg, second_reg); + } } else { Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftDistance); - __ rorl(first_reg, imm); + if (rotate->IsRol()) { + __ roll(first_reg, imm); + } else { + DCHECK(rotate->IsRor()); + __ rorl(first_reg, imm); + } } return; } - DCHECK_EQ(ror->GetResultType(), DataType::Type::kInt64); + DCHECK_EQ(rotate->GetResultType(), DataType::Type::kInt64); Register first_reg_lo = first.AsRegisterPairLow<Register>(); Register first_reg_hi = first.AsRegisterPairHigh<Register>(); Register temp_reg = locations->GetTemp(0).AsRegister<Register>(); if (second.IsRegister()) { Register second_reg = second.AsRegister<Register>(); DCHECK_EQ(second_reg, ECX); + + if (rotate->IsRol()) { + __ negl(second_reg); + } + __ movl(temp_reg, first_reg_hi); __ shrd(first_reg_hi, first_reg_lo, second_reg); __ shrd(first_reg_lo, temp_reg, second_reg); @@ -5105,7 +5136,12 @@ void InstructionCodeGeneratorX86::VisitRor(HRor* ror) { __ cmovl(kNotEqual, first_reg_hi, first_reg_lo); __ cmovl(kNotEqual, first_reg_lo, temp_reg); } else { - int32_t shift_amt = second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftDistance; + int32_t value = second.GetConstant()->AsIntConstant()->GetValue(); + if (rotate->IsRol()) { + value = -value; + } + int32_t shift_amt = value & kMaxLongShiftDistance; + if (shift_amt == 0) { // Already fine. return; diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index bb6fa2a298..321ee92e2e 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -249,6 +249,7 @@ class LocationsBuilderX86 : public HGraphVisitor { void HandleBitwiseOperation(HBinaryOperation* instruction); void HandleInvoke(HInvoke* invoke); void HandleCondition(HCondition* condition); + void HandleRotate(HBinaryOperation* rotate); void HandleShift(HBinaryOperation* instruction); void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, @@ -339,6 +340,7 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator { bool value_can_be_null, WriteBarrierKind write_barrier_kind); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); + void HandleRotate(HBinaryOperation* rotate); // Generate a heap reference load using one register `out`: // diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 6a7f9b1264..51ded684c6 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -5035,55 +5035,91 @@ void InstructionCodeGeneratorX86_64::HandleShift(HBinaryOperation* op) { } } -void LocationsBuilderX86_64::VisitRor(HRor* ror) { +void LocationsBuilderX86_64::HandleRotate(HBinaryOperation* rotate) { LocationSummary* locations = - new (GetGraph()->GetAllocator()) LocationSummary(ror, LocationSummary::kNoCall); + new (GetGraph()->GetAllocator()) LocationSummary(rotate, LocationSummary::kNoCall); - switch (ror->GetResultType()) { + switch (rotate->GetResultType()) { case DataType::Type::kInt32: case DataType::Type::kInt64: { locations->SetInAt(0, Location::RequiresRegister()); // The shift count needs to be in CL (unless it is a constant). - locations->SetInAt(1, Location::ByteRegisterOrConstant(RCX, ror->InputAt(1))); + locations->SetInAt(1, Location::ByteRegisterOrConstant(RCX, rotate->InputAt(1))); locations->SetOut(Location::SameAsFirstInput()); break; } default: - LOG(FATAL) << "Unexpected operation type " << ror->GetResultType(); + LOG(FATAL) << "Unexpected operation type " << rotate->GetResultType(); UNREACHABLE(); } } -void InstructionCodeGeneratorX86_64::VisitRor(HRor* ror) { - LocationSummary* locations = ror->GetLocations(); +void InstructionCodeGeneratorX86_64::HandleRotate(HBinaryOperation* rotate) { + LocationSummary* locations = rotate->GetLocations(); CpuRegister first_reg = locations->InAt(0).AsRegister<CpuRegister>(); Location second = locations->InAt(1); - switch (ror->GetResultType()) { + switch (rotate->GetResultType()) { case DataType::Type::kInt32: if (second.IsRegister()) { CpuRegister second_reg = second.AsRegister<CpuRegister>(); - __ rorl(first_reg, second_reg); + if (rotate->IsRor()) { + __ rorl(first_reg, second_reg); + } else { + DCHECK(rotate->IsRol()); + __ roll(first_reg, second_reg); + } } else { Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftDistance); - __ rorl(first_reg, imm); + if (rotate->IsRor()) { + __ rorl(first_reg, imm); + } else { + DCHECK(rotate->IsRol()); + __ roll(first_reg, imm); + } } break; case DataType::Type::kInt64: if (second.IsRegister()) { CpuRegister second_reg = second.AsRegister<CpuRegister>(); - __ rorq(first_reg, second_reg); + if (rotate->IsRor()) { + __ rorq(first_reg, second_reg); + } else { + DCHECK(rotate->IsRol()); + __ rolq(first_reg, second_reg); + } } else { Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftDistance); - __ rorq(first_reg, imm); + if (rotate->IsRor()) { + __ rorq(first_reg, imm); + } else { + DCHECK(rotate->IsRol()); + __ rolq(first_reg, imm); + } } break; default: - LOG(FATAL) << "Unexpected operation type " << ror->GetResultType(); + LOG(FATAL) << "Unexpected operation type " << rotate->GetResultType(); UNREACHABLE(); } } +void InstructionCodeGeneratorX86_64::VisitRor(HRor* ror) { + HandleRotate(ror); +} + +void LocationsBuilderX86_64::VisitRol(HRol* rol) { + HandleRotate(rol); +} + +void LocationsBuilderX86_64::VisitRor(HRor* ror) { + HandleRotate(ror); +} + +void InstructionCodeGeneratorX86_64::VisitRol(HRol* rol) { + HandleRotate(rol); +} + void LocationsBuilderX86_64::VisitShl(HShl* shl) { HandleShift(shl); } diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 03b73abd62..b3eb1a0373 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -247,6 +247,7 @@ class LocationsBuilderX86_64 : public HGraphVisitor { void HandleBitwiseOperation(HBinaryOperation* operation); void HandleCondition(HCondition* condition); void HandleShift(HBinaryOperation* operation); + void HandleRotate(HBinaryOperation* rotate); void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, WriteBarrierKind write_barrier_kind); @@ -320,6 +321,7 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator { void GenerateDivRemIntegral(HBinaryOperation* instruction); void HandleCondition(HCondition* condition); void HandleShift(HBinaryOperation* operation); + void HandleRotate(HBinaryOperation* rotate); void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index ad0d0fb2ca..82f98ed5ea 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -1404,7 +1404,7 @@ void GraphChecker::VisitBinaryOperation(HBinaryOperation* op) { DataType::Type result_type = op->GetType(); // Type consistency between inputs. - if (op->IsUShr() || op->IsShr() || op->IsShl() || op->IsRor()) { + if (op->IsUShr() || op->IsShr() || op->IsShl() || op->IsRol() || op->IsRor()) { if (DataType::Kind(rhs_type) != DataType::Type::kInt32) { AddError(StringPrintf("Shift/rotate operation %s %d has a non-int kind second input: " "%s of type %s.", @@ -1428,7 +1428,7 @@ void GraphChecker::VisitBinaryOperation(HBinaryOperation* op) { op->GetId(), DataType::PrettyDescriptor(result_type))); } - } else if (op->IsUShr() || op->IsShr() || op->IsShl() || op->IsRor()) { + } else if (op->IsUShr() || op->IsShr() || op->IsShl() || op->IsRol() || op->IsRor()) { // Only check the first input (value), as the second one (distance) // must invariably be of kind `int`. if (result_type != DataType::Kind(lhs_type)) { diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index f3676bbc02..55e3267427 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1953,14 +1953,16 @@ bool HInstructionBuilder::BuildSimpleIntrinsic(ArtMethod* method, ReceiverArg receiver_arg = method->IsStatic() ? ReceiverArg::kNone : ReceiverArg::kNullCheckedArg; HInstruction* instruction = nullptr; switch (intrinsic) { - case Intrinsics::kIntegerRotateRight: case Intrinsics::kIntegerRotateLeft: - // For rotate left, we negate the distance below. + instruction = new (allocator_) HRol(kInt32, /*value=*/ nullptr, /*distance=*/ nullptr); + break; + case Intrinsics::kIntegerRotateRight: instruction = new (allocator_) HRor(kInt32, /*value=*/ nullptr, /*distance=*/ nullptr); break; - case Intrinsics::kLongRotateRight: case Intrinsics::kLongRotateLeft: - // For rotate left, we negate the distance below. + instruction = new (allocator_) HRol(kInt64, /*value=*/ nullptr, /*distance=*/ nullptr); + break; + case Intrinsics::kLongRotateRight: instruction = new (allocator_) HRor(kInt64, /*value=*/ nullptr, /*distance=*/ nullptr); break; case Intrinsics::kIntegerCompare: @@ -2079,15 +2081,6 @@ bool HInstructionBuilder::BuildSimpleIntrinsic(ArtMethod* method, } switch (intrinsic) { - case Intrinsics::kIntegerRotateLeft: - case Intrinsics::kLongRotateLeft: { - // Negate the distance value for rotate left. - DCHECK(instruction->IsRor()); - HNeg* neg = new (allocator_) HNeg(kInt32, instruction->InputAt(1u)); - AppendInstruction(neg); - instruction->SetRawInputAt(1u, neg); - break; - } case Intrinsics::kFloatIsNaN: case Intrinsics::kDoubleIsNaN: // Set the second input to be the same as first. diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc index a050f86363..7dccc036b5 100644 --- a/compiler/optimizing/instruction_simplifier_arm.cc +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -74,6 +74,7 @@ class InstructionSimplifierArmVisitor final : public HGraphVisitor { void VisitArraySet(HArraySet* instruction) override; void VisitMul(HMul* instruction) override; void VisitOr(HOr* instruction) override; + void VisitRol(HRol* instruction) override; void VisitShl(HShl* instruction) override; void VisitShr(HShr* instruction) override; void VisitSub(HSub* instruction) override; @@ -263,6 +264,11 @@ void InstructionSimplifierArmVisitor::VisitOr(HOr* instruction) { } } +void InstructionSimplifierArmVisitor::VisitRol(HRol* instruction) { + UnfoldRotateLeft(instruction); + RecordSimplification(); +} + void InstructionSimplifierArmVisitor::VisitShl(HShl* instruction) { if (instruction->InputAt(1)->IsConstant()) { TryMergeIntoUsersShifterOperand(instruction); diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index f57448d70f..8dd64e59ee 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -77,6 +77,7 @@ class InstructionSimplifierArm64Visitor final : public HGraphVisitor { void VisitArraySet(HArraySet* instruction) override; void VisitMul(HMul* instruction) override; void VisitOr(HOr* instruction) override; + void VisitRol(HRol* instruction) override; void VisitShl(HShl* instruction) override; void VisitShr(HShr* instruction) override; void VisitSub(HSub* instruction) override; @@ -234,6 +235,11 @@ void InstructionSimplifierArm64Visitor::VisitOr(HOr* instruction) { } } +void InstructionSimplifierArm64Visitor::VisitRol(HRol* rol) { + UnfoldRotateLeft(rol); + RecordSimplification(); +} + void InstructionSimplifierArm64Visitor::VisitShl(HShl* instruction) { if (instruction->InputAt(1)->IsConstant()) { TryMergeIntoUsersShifterOperand(instruction); diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc index b7d76da548..7f575c0348 100644 --- a/compiler/optimizing/instruction_simplifier_shared.cc +++ b/compiler/optimizing/instruction_simplifier_shared.cc @@ -316,4 +316,23 @@ bool TryReplaceSubSubWithSubAdd(HSub* last_sub) { } } +void UnfoldRotateLeft(HRol* rol) { + HBasicBlock* block = rol->GetBlock(); + HGraph* graph = block->GetGraph(); + ArenaAllocator* allocator = graph->GetAllocator(); + HRor* ror; + + if (rol->GetRight()->IsConstant()) { + int32_t value = rol->GetRight()->AsIntConstant()->GetValue(); + HIntConstant* negated = graph->GetIntConstant(-value); + ror = new (allocator) HRor(rol->GetType(), rol->GetLeft(), negated); + } else { + HNeg* neg = new (allocator) HNeg(DataType::Type::kInt32, rol->GetRight()); + block->InsertInstructionBefore(neg, rol); + ror = new (allocator) HRor(rol->GetType(), rol->GetLeft(), neg); + } + + block->ReplaceAndRemoveInstructionWith(rol, ror); +} + } // namespace art diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h index de70ec5a8a..8e61109b0e 100644 --- a/compiler/optimizing/instruction_simplifier_shared.h +++ b/compiler/optimizing/instruction_simplifier_shared.h @@ -77,6 +77,14 @@ bool TryExtractVecArrayAccessAddress(HVecMemoryOperation* access, HInstruction* // Add(c, Sub(b, a)) bool TryReplaceSubSubWithSubAdd(HSub* last_sub); +// ARM does not contain instruction ROL so replace +// ROL dest, a, distance +// with +// NEG neg, distance +// ROR dest, a, neg +// before GVN to give it a chance to deduplicate the instructions, if it's able. +void UnfoldRotateLeft(HRol* rol); + } // namespace art #endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ diff --git a/compiler/optimizing/instruction_simplifier_x86.cc b/compiler/optimizing/instruction_simplifier_x86.cc index 5a4345d589..e1c783e5b8 100644 --- a/compiler/optimizing/instruction_simplifier_x86.cc +++ b/compiler/optimizing/instruction_simplifier_x86.cc @@ -48,6 +48,7 @@ class InstructionSimplifierX86Visitor final : public HGraphVisitor { } void VisitAnd(HAnd * instruction) override; + void VisitRol(HRol* instruction) override; void VisitXor(HXor* instruction) override; private: @@ -57,6 +58,10 @@ class InstructionSimplifierX86Visitor final : public HGraphVisitor { void InstructionSimplifierX86Visitor::VisitAnd(HAnd* instruction) { + if (!HasAVX2()) { + return; + } + if (TryCombineAndNot(instruction)) { RecordSimplification(); } else if (instruction->GetResultType() == DataType::Type::kInt32) { @@ -66,7 +71,26 @@ void InstructionSimplifierX86Visitor::VisitAnd(HAnd* instruction) { } } +void InstructionSimplifierX86Visitor::VisitRol(HRol* rol) { + if (rol->GetType() != DataType::Type::kInt64) { + return; + } + + HBasicBlock* block = rol->GetBlock(); + HGraph* graph = block->GetGraph(); + ArenaAllocator* allocator = graph->GetAllocator(); + + HNeg* neg = new (allocator) HNeg(DataType::Type::kInt32, rol->GetRight()); + block->InsertInstructionBefore(neg, rol); + HRor* ror = new (allocator) HRor(rol->GetType(), rol->GetLeft(), neg); + block->ReplaceAndRemoveInstructionWith(rol, ror); +} + void InstructionSimplifierX86Visitor::VisitXor(HXor* instruction) { + if (!HasAVX2()) { + return; + } + if (instruction->GetResultType() == DataType::Type::kInt32) { if (TryGenerateMaskUptoLeastSetBit(instruction)) { RecordSimplification(); @@ -76,11 +100,8 @@ void InstructionSimplifierX86Visitor::VisitXor(HXor* instruction) { bool InstructionSimplifierX86::Run() { InstructionSimplifierX86Visitor visitor(graph_, codegen_, stats_); - if (visitor.HasAVX2()) { - visitor.VisitReversePostOrder(); - return true; - } - return false; + visitor.VisitReversePostOrder(); + return true; } } // namespace x86 diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 588f4d7ce2..32ae89eeea 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1829,7 +1829,7 @@ HConstant* HBinaryOperation::TryStaticEvaluation(HInstruction* left, HInstructio } else if (left->IsLongConstant()) { if (right->IsIntConstant()) { // The binop(long, int) case is only valid for shifts and rotations. - DCHECK(IsShl() || IsShr() || IsUShr() || IsRor()) << DebugName(); + DCHECK(IsShl() || IsShr() || IsUShr() || IsRol() || IsRor()) << DebugName(); return Evaluate(left->AsLongConstant(), right->AsIntConstant()); } else if (right->IsLongConstant()) { return Evaluate(left->AsLongConstant(), right->AsLongConstant()); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index eb6d9ecad4..2477c9fe51 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1593,6 +1593,7 @@ class HLoopInformationOutwardIterator : public ValueObject { M(Rem, BinaryOperation) \ M(Return, Instruction) \ M(ReturnVoid, Instruction) \ + M(Rol, BinaryOperation) \ M(Ror, BinaryOperation) \ M(Shl, BinaryOperation) \ M(Shr, BinaryOperation) \ @@ -5998,6 +5999,31 @@ class HRor final : public HBinaryOperation { DEFAULT_COPY_CONSTRUCTOR(Ror); }; +class HRol final : public HBinaryOperation { + public: + HRol(DataType::Type result_type, HInstruction* value, HInstruction* distance) + : HBinaryOperation(kRol, result_type, value, distance) {} + + template <typename T> + static T Compute(T value, int32_t distance, int32_t max_shift_value) { + return HRor::Compute(value, -distance, max_shift_value); + } + + HConstant* Evaluate(HIntConstant* value, HIntConstant* distance) const override { + return GetBlock()->GetGraph()->GetIntConstant( + Compute(value->GetValue(), distance->GetValue(), kMaxIntShiftDistance), GetDexPc()); + } + HConstant* Evaluate(HLongConstant* value, HIntConstant* distance) const override { + return GetBlock()->GetGraph()->GetLongConstant( + Compute(value->GetValue(), distance->GetValue(), kMaxLongShiftDistance), GetDexPc()); + } + + DECLARE_INSTRUCTION(Rol); + + protected: + DEFAULT_COPY_CONSTRUCTOR(Rol); +}; + // The value of a parameter in this method. Its location depends on // the calling convention. class HParameterValue final : public HExpression<0> { diff --git a/test/567-checker-builder-intrinsics/src/TestRotate.java b/test/567-checker-builder-intrinsics/src/TestRotate.java index 2037ccf655..00a4fc6b60 100644 --- a/test/567-checker-builder-intrinsics/src/TestRotate.java +++ b/test/567-checker-builder-intrinsics/src/TestRotate.java @@ -16,76 +16,163 @@ public class TestRotate { - /// CHECK-START: int TestRotate.rotateLeftByte(byte, int) builder (after) + /// CHECK-START: int TestRotate.$inline$rotateLeftByte(byte, int) builder (after) /// CHECK: <<ArgVal:b\d+>> ParameterValue /// CHECK: <<ArgDist:i\d+>> ParameterValue - /// CHECK-DAG: <<NegDist:i\d+>> Neg [<<ArgDist>>] - /// CHECK-DAG: <<Result:i\d+>> Ror [<<ArgVal>>,<<NegDist>>] + /// CHECK-DAG: <<Result:i\d+>> Rol [<<ArgVal>>,<<ArgDist>>] /// CHECK-DAG: Return [<<Result>>] - /// CHECK-START: int TestRotate.rotateLeftByte(byte, int) builder (after) + /// CHECK-START: int TestRotate.$inline$rotateLeftByte(byte, int) builder (after) /// CHECK-NOT: InvokeStaticOrDirect - private static int rotateLeftByte(byte value, int distance) { + private static int $inline$rotateLeftByte(byte value, int distance) { return Integer.rotateLeft(value, distance); } - /// CHECK-START: int TestRotate.rotateLeftShort(short, int) builder (after) + private static int $noinline$rotateLeftByte(byte value, int distance) { + return Integer.rotateLeft(value, distance); + } + + /// CHECK-START: int TestRotate.$inline$rotateLeftShort(short, int) builder (after) /// CHECK: <<ArgVal:s\d+>> ParameterValue /// CHECK: <<ArgDist:i\d+>> ParameterValue - /// CHECK-DAG: <<NegDist:i\d+>> Neg [<<ArgDist>>] - /// CHECK-DAG: <<Result:i\d+>> Ror [<<ArgVal>>,<<NegDist>>] + /// CHECK-DAG: <<Result:i\d+>> Rol [<<ArgVal>>,<<ArgDist>>] /// CHECK-DAG: Return [<<Result>>] - /// CHECK-START: int TestRotate.rotateLeftShort(short, int) builder (after) + /// CHECK-START: int TestRotate.$inline$rotateLeftShort(short, int) builder (after) /// CHECK-NOT: InvokeStaticOrDirect - private static int rotateLeftShort(short value, int distance) { + private static int $inline$rotateLeftShort(short value, int distance) { return Integer.rotateLeft(value, distance); } - /// CHECK-START: int TestRotate.rotateLeftChar(char, int) builder (after) + private static int $noinline$rotateLeftShort(short value, int distance) { + return Integer.rotateLeft(value, distance); + } + + /// CHECK-START: int TestRotate.$inline$rotateLeftChar(char, int) builder (after) /// CHECK: <<ArgVal:c\d+>> ParameterValue /// CHECK: <<ArgDist:i\d+>> ParameterValue - /// CHECK-DAG: <<NegDist:i\d+>> Neg [<<ArgDist>>] - /// CHECK-DAG: <<Result:i\d+>> Ror [<<ArgVal>>,<<NegDist>>] + /// CHECK-DAG: <<Result:i\d+>> Rol [<<ArgVal>>,<<ArgDist>>] /// CHECK-DAG: Return [<<Result>>] - /// CHECK-START: int TestRotate.rotateLeftChar(char, int) builder (after) + /// CHECK-START: int TestRotate.$inline$rotateLeftChar(char, int) builder (after) /// CHECK-NOT: InvokeStaticOrDirect - private static int rotateLeftChar(char value, int distance) { + private static int $inline$rotateLeftChar(char value, int distance) { + return Integer.rotateLeft(value, distance); + } + + private static int $noinline$rotateLeftChar(char value, int distance) { return Integer.rotateLeft(value, distance); } - /// CHECK-START: int TestRotate.rotateLeftInt(int, int) builder (after) + /// CHECK-START: int TestRotate.$inline$rotateLeftInt(int, int) builder (after) /// CHECK: <<ArgVal:i\d+>> ParameterValue /// CHECK: <<ArgDist:i\d+>> ParameterValue - /// CHECK-DAG: <<NegDist:i\d+>> Neg [<<ArgDist>>] - /// CHECK-DAG: <<Result:i\d+>> Ror [<<ArgVal>>,<<NegDist>>] + /// CHECK-DAG: <<Result:i\d+>> Rol [<<ArgVal>>,<<ArgDist>>] /// CHECK-DAG: Return [<<Result>>] - /// CHECK-START: int TestRotate.rotateLeftInt(int, int) builder (after) + /// CHECK-START: int TestRotate.$inline$rotateLeftInt(int, int) builder (after) /// CHECK-NOT: InvokeStaticOrDirect - private static int rotateLeftInt(int value, int distance) { + private static int $inline$rotateLeftInt(int value, int distance) { + return Integer.rotateLeft(value, distance); + } + + /// CHECK-START-ARM64: int TestRotate.$noinline$rotateLeftInt(int, int) instruction_simplifier_arm64 (after) + /// CHECK: <<ArgVal:i\d+>> ParameterValue + /// CHECK: <<ArgDist:i\d+>> ParameterValue + /// CHECK-DAG: <<neg:i\d+>> Neg [<<ArgDist>>] + /// CHECK-DAG: <<Result:i\d+>> Ror [<<ArgVal>>,<<neg>>] + /// CHECK-DAG: Return [<<Result>>] + + /// CHECK-START-ARM64: int TestRotate.$noinline$rotateLeftInt(int, int) disassembly (after) + /// CHECK: neg {{w\d+}}, {{w\d+}} + /// CHECK: ror {{w\d+}}, {{w\d+}}, {{w\d+}} + + /// CHECK-START-RISCV64: int TestRotate.$noinline$rotateLeftInt(int, int) disassembly (after) + /// CHECK: rol {{a\d+}}, {{a\d+}}, {{a\d+}} + + private static int $noinline$rotateLeftInt(int value, int distance) { return Integer.rotateLeft(value, distance); } - /// CHECK-START: long TestRotate.rotateLeftLong(long, int) builder (after) + /// CHECK-START-ARM64: int TestRotate.$noinline$rotateLeftIntMulNegDistance(int, int) scheduler (before) + /// CHECK: <<ArgVal:i\d+>> ParameterValue + /// CHECK: <<ArgDist:i\d+>> ParameterValue + /// CHECK-DAG: <<neg:i\d+>> Neg [<<ArgDist>>] + /// CHECK-DAG: <<ror:i\d+>> Ror [<<ArgVal>>,<<neg>>] + /// CHECK-DAG: <<Result:i\d+>> Mul [<<neg>>,<<ror>>] + /// CHECK-DAG: Return [<<Result>>] + + private static int $noinline$rotateLeftIntMulNegDistance(int value, int distance) { + return Integer.rotateLeft(value, distance) * -distance; + } + + /// CHECK-START: int TestRotate.$noinline$rotateLeftIntConstant(int) builder (after) + /// CHECK: <<ArgVal:i\d+>> ParameterValue + /// CHECK: <<Constant:i\d+>> IntConstant 31 + /// CHECK-DAG: <<Result:i\d+>> Rol [<<ArgVal>>,<<Constant>>] + /// CHECK-DAG: Return [<<Result>>] + + /// CHECK-START-ARM64: int TestRotate.$noinline$rotateLeftIntConstant(int) disassembly (after) + /// CHECK: ror {{w\d+}}, {{w\d+}}, #1 + + /// CHECK-START-RISCV64: int TestRotate.$noinline$rotateLeftIntConstant(int) disassembly (after) + /// CHECK: roriw {{a\d+}}, {{a\d+}}, 1 + + private static int $noinline$rotateLeftIntConstant(int value) { + return Integer.rotateLeft(value, 31); + } + + /// CHECK-START: long TestRotate.$inline$rotateLeftLong(long, int) builder (after) /// CHECK: <<ArgVal:j\d+>> ParameterValue /// CHECK: <<ArgDist:i\d+>> ParameterValue - /// CHECK-DAG: <<NegDist:i\d+>> Neg [<<ArgDist>>] - /// CHECK-DAG: <<Result:j\d+>> Ror [<<ArgVal>>,<<NegDist>>] + /// CHECK-DAG: <<Result:j\d+>> Rol [<<ArgVal>>,<<ArgDist>>] /// CHECK-DAG: Return [<<Result>>] - /// CHECK-START: long TestRotate.rotateLeftLong(long, int) builder (after) + /// CHECK-START: long TestRotate.$inline$rotateLeftLong(long, int) builder (after) /// CHECK-NOT: InvokeStaticOrDirect - private static long rotateLeftLong(long value, int distance) { + private static long $inline$rotateLeftLong(long value, int distance) { return Long.rotateLeft(value, distance); } + /// CHECK-START-ARM64: long TestRotate.$noinline$rotateLeftLong(long, int) instruction_simplifier_arm64 (after) + /// CHECK: <<ArgVal:j\d+>> ParameterValue + /// CHECK: <<ArgDist:i\d+>> ParameterValue + /// CHECK-DAG: <<neg:i\d+>> Neg [<<ArgDist>>] + /// CHECK-DAG: <<Result:j\d+>> Ror [<<ArgVal>>,<<neg>>] + /// CHECK-DAG: Return [<<Result>>] + + /// CHECK-START-ARM64: long TestRotate.$noinline$rotateLeftLong(long, int) disassembly (after) + /// CHECK: neg {{w\d+}}, {{w\d+}} + /// CHECK: ror {{x\d+}}, {{x\d+}}, {{x\d+}} + + /// CHECK-START-RISCV64: long TestRotate.$noinline$rotateLeftLong(long, int) disassembly (after) + /// CHECK: rol {{a\d+}}, {{a\d+}}, {{a\d+}} + + private static long $noinline$rotateLeftLong(long value, int distance) { + return Long.rotateLeft(value, distance); + } + + /// CHECK-START: long TestRotate.$noinline$rotateLeftLongConstant(long) builder (after) + /// CHECK: <<ArgVal:j\d+>> ParameterValue + /// CHECK: <<Constant:i\d+>> IntConstant 63 + /// CHECK-DAG: <<Result:j\d+>> Rol [<<ArgVal>>,<<Constant>>] + /// CHECK-DAG: Return [<<Result>>] + + /// CHECK-START-ARM64: long TestRotate.$noinline$rotateLeftLongConstant(long) disassembly (after) + /// CHECK: ror {{x\d+}}, {{x\d+}}, #1 + + /// CHECK-START-RISCV64: long TestRotate.$noinline$rotateLeftLongConstant(long) disassembly (after) + /// CHECK: rori {{a\d+}}, {{a\d+}}, 1 + + private static long $noinline$rotateLeftLongConstant(long value) { + return Long.rotateLeft(value, 63); + } + /// CHECK-START: int TestRotate.rotateRightByte(byte, int) builder (after) /// CHECK: <<ArgVal:b\d+>> ParameterValue /// CHECK: <<ArgDist:i\d+>> ParameterValue @@ -152,17 +239,20 @@ public class TestRotate { } - /// CHECK-START: int TestRotate.rotateLeftIntWithByteDistance(int, byte) builder (after) + /// CHECK-START: int TestRotate.$inline$rotateLeftIntWithByteDistance(int, byte) builder (after) /// CHECK: <<ArgVal:i\d+>> ParameterValue /// CHECK: <<ArgDist:b\d+>> ParameterValue - /// CHECK-DAG: <<NegDist:i\d+>> Neg [<<ArgDist>>] - /// CHECK-DAG: <<Result:i\d+>> Ror [<<ArgVal>>,<<NegDist>>] + /// CHECK-DAG: <<Result:i\d+>> Rol [<<ArgVal>>,<<ArgDist>>] /// CHECK-DAG: Return [<<Result>>] - /// CHECK-START: int TestRotate.rotateLeftIntWithByteDistance(int, byte) builder (after) + /// CHECK-START: int TestRotate.$inline$rotateLeftIntWithByteDistance(int, byte) builder (after) /// CHECK-NOT: InvokeStaticOrDirect - private static int rotateLeftIntWithByteDistance(int value, byte distance) { + private static int $inline$rotateLeftIntWithByteDistance(int value, byte distance) { + return Integer.rotateLeft(value, distance); + } + + private static int $noinline$rotateLeftIntWithByteDistance(int value, byte distance) { return Integer.rotateLeft(value, distance); } @@ -179,43 +269,52 @@ public class TestRotate { return Integer.rotateRight(value, distance); } - /// CHECK-START: int TestRotate.rotateLeftBoolean(boolean, int) builder (after) + /// CHECK-START: int TestRotate.$inline$rotateLeftBoolean(boolean, int) builder (after) /// CHECK: <<ArgVal:z\d+>> ParameterValue /// CHECK: <<ArgDist:i\d+>> ParameterValue /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 /// CHECK-DAG: <<One:i\d+>> IntConstant 1 /// CHECK-DAG: <<Val:i\d+>> Phi [<<One>>,<<Zero>>] - /// CHECK-DAG: <<NegDist:i\d+>> Neg [<<ArgDist>>] - /// CHECK-DAG: <<Result:i\d+>> Ror [<<Val>>,<<NegDist>>] + /// CHECK-DAG: <<Result:i\d+>> Rol [<<Val>>,<<ArgDist>>] /// CHECK-DAG: Return [<<Result>>] - /// CHECK-START: int TestRotate.rotateLeftBoolean(boolean, int) builder (after) + /// CHECK-START: int TestRotate.$inline$rotateLeftBoolean(boolean, int) builder (after) /// CHECK-NOT: InvokeStaticOrDirect - /// CHECK-START: int TestRotate.rotateLeftBoolean(boolean, int) select_generator (after) + /// CHECK-START: int TestRotate.$inline$rotateLeftBoolean(boolean, int) select_generator (after) /// CHECK: <<ArgVal:z\d+>> ParameterValue /// CHECK: <<ArgDist:i\d+>> ParameterValue /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 /// CHECK-DAG: <<One:i\d+>> IntConstant 1 /// CHECK-DAG: <<SelVal:i\d+>> Select [<<Zero>>,<<One>>,<<ArgVal>>] - /// CHECK-DAG: <<NegDist:i\d+>> Neg [<<ArgDist>>] - /// CHECK-DAG: <<Result:i\d+>> Ror [<<SelVal>>,<<NegDist>>] + /// CHECK-DAG: <<Result:i\d+>> Rol [<<SelVal>>,<<ArgDist>>] /// CHECK-DAG: Return [<<Result>>] - /// CHECK-START: int TestRotate.rotateLeftBoolean(boolean, int) select_generator (after) + /// CHECK-START: int TestRotate.$inline$rotateLeftBoolean(boolean, int) select_generator (after) /// CHECK-NOT: Phi - /// CHECK-START: int TestRotate.rotateLeftBoolean(boolean, int) instruction_simplifier$before_codegen (after) + /// CHECK-START: int TestRotate.$inline$rotateLeftBoolean(boolean, int) instruction_simplifier$before_codegen (after) /// CHECK: <<ArgVal:z\d+>> ParameterValue /// CHECK: <<ArgDist:i\d+>> ParameterValue - /// CHECK-DAG: <<NegDist:i\d+>> Neg [<<ArgDist>>] - /// CHECK-DAG: <<Result:i\d+>> Ror [<<ArgVal>>,<<NegDist>>] + /// CHECK-DAG: <<Result:i\d+>> Rol [<<ArgVal>>,<<ArgDist>>] /// CHECK-DAG: Return [<<Result>>] - /// CHECK-START: int TestRotate.rotateLeftBoolean(boolean, int) instruction_simplifier$before_codegen (after) + /// CHECK-START: int TestRotate.$inline$rotateLeftBoolean(boolean, int) instruction_simplifier$before_codegen (after) /// CHECK-NOT: Select - private static int rotateLeftBoolean(boolean value, int distance) { + private static int $inline$rotateLeftBoolean(boolean value, int distance) { + // Note: D8 would replace the ternary expression `value ? 1 : 0` with `value` + // but explicit `if` is preserved. + int src; + if (value) { + src = 1; + } else { + src = 0; + } + return Integer.rotateLeft(src, distance); + } + + private static int $noinline$rotateLeftBoolean(boolean value, int distance) { // Note: D8 would replace the ternary expression `value ? 1 : 0` with `value` // but explicit `if` is preserved. int src; @@ -230,99 +329,184 @@ public class TestRotate { public static void testRotateLeftBoolean() { for (int i = 0; i < 40; i++) { // overshoot a bit int j = i & 31; - expectEqualsInt(0, rotateLeftBoolean(false, i)); - expectEqualsInt(1 << j, rotateLeftBoolean(true, i)); + expectEqualsInt(0, $inline$rotateLeftBoolean(false, i)); + expectEqualsInt(1 << j, $inline$rotateLeftBoolean(true, i)); + + expectEqualsInt(0, $noinline$rotateLeftBoolean(false, i)); + expectEqualsInt(1 << j, $noinline$rotateLeftBoolean(true, i)); } } public static void testRotateLeftByte() { - expectEqualsInt(0x00000001, rotateLeftByte((byte)0x01, 0)); - expectEqualsInt(0x00000002, rotateLeftByte((byte)0x01, 1)); - expectEqualsInt(0x80000000, rotateLeftByte((byte)0x01, 31)); - expectEqualsInt(0x00000001, rotateLeftByte((byte)0x01, 32)); // overshoot - expectEqualsInt(0xFFFFFF03, rotateLeftByte((byte)0x81, 1)); - expectEqualsInt(0xFFFFFE07, rotateLeftByte((byte)0x81, 2)); - expectEqualsInt(0x00000120, rotateLeftByte((byte)0x12, 4)); - expectEqualsInt(0xFFFF9AFF, rotateLeftByte((byte)0x9A, 8)); + expectEqualsInt(0x00000001, $inline$rotateLeftByte((byte)0x01, 0)); + expectEqualsInt(0x00000002, $inline$rotateLeftByte((byte)0x01, 1)); + expectEqualsInt(0x80000000, $inline$rotateLeftByte((byte)0x01, 31)); + expectEqualsInt(0x00000001, $inline$rotateLeftByte((byte)0x01, 32)); // overshoot + expectEqualsInt(0xFFFFFF03, $inline$rotateLeftByte((byte)0x81, 1)); + expectEqualsInt(0xFFFFFE07, $inline$rotateLeftByte((byte)0x81, 2)); + expectEqualsInt(0x00000120, $inline$rotateLeftByte((byte)0x12, 4)); + expectEqualsInt(0xFFFF9AFF, $inline$rotateLeftByte((byte)0x9A, 8)); + + expectEqualsInt(0x00000001, $noinline$rotateLeftByte((byte)0x01, 0)); + expectEqualsInt(0x00000002, $noinline$rotateLeftByte((byte)0x01, 1)); + expectEqualsInt(0x80000000, $noinline$rotateLeftByte((byte)0x01, 31)); + expectEqualsInt(0x00000001, $noinline$rotateLeftByte((byte)0x01, 32)); // overshoot + expectEqualsInt(0xFFFFFF03, $noinline$rotateLeftByte((byte)0x81, 1)); + expectEqualsInt(0xFFFFFE07, $noinline$rotateLeftByte((byte)0x81, 2)); + expectEqualsInt(0x00000120, $noinline$rotateLeftByte((byte)0x12, 4)); + expectEqualsInt(0xFFFF9AFF, $noinline$rotateLeftByte((byte)0x9A, 8)); + for (int i = 0; i < 40; i++) { // overshoot a bit int j = i & 31; - expectEqualsInt(0x00000000, rotateLeftByte((byte)0x0000, i)); - expectEqualsInt(0xFFFFFFFF, rotateLeftByte((byte)0xFFFF, i)); - expectEqualsInt((1 << j), rotateLeftByte((byte)0x0001, i)); - expectEqualsInt((0x12 << j) | (0x12 >>> -j), rotateLeftByte((byte)0x12, i)); + expectEqualsInt(0x00000000, $inline$rotateLeftByte((byte)0x0000, i)); + expectEqualsInt(0xFFFFFFFF, $inline$rotateLeftByte((byte)0xFFFF, i)); + expectEqualsInt((1 << j), $inline$rotateLeftByte((byte)0x0001, i)); + expectEqualsInt((0x12 << j) | (0x12 >>> -j), $inline$rotateLeftByte((byte)0x12, i)); + + expectEqualsInt(0x00000000, $noinline$rotateLeftByte((byte)0x0000, i)); + expectEqualsInt(0xFFFFFFFF, $noinline$rotateLeftByte((byte)0xFFFF, i)); + expectEqualsInt((1 << j), $noinline$rotateLeftByte((byte)0x0001, i)); + expectEqualsInt((0x12 << j) | (0x12 >>> -j), $noinline$rotateLeftByte((byte)0x12, i)); } } public static void testRotateLeftShort() { - expectEqualsInt(0x00000001, rotateLeftShort((short)0x0001, 0)); - expectEqualsInt(0x00000002, rotateLeftShort((short)0x0001, 1)); - expectEqualsInt(0x80000000, rotateLeftShort((short)0x0001, 31)); - expectEqualsInt(0x00000001, rotateLeftShort((short)0x0001, 32)); // overshoot - expectEqualsInt(0xFFFF0003, rotateLeftShort((short)0x8001, 1)); - expectEqualsInt(0xFFFE0007, rotateLeftShort((short)0x8001, 2)); - expectEqualsInt(0x00012340, rotateLeftShort((short)0x1234, 4)); - expectEqualsInt(0xFF9ABCFF, rotateLeftShort((short)0x9ABC, 8)); + expectEqualsInt(0x00000001, $inline$rotateLeftShort((short)0x0001, 0)); + expectEqualsInt(0x00000002, $inline$rotateLeftShort((short)0x0001, 1)); + expectEqualsInt(0x80000000, $inline$rotateLeftShort((short)0x0001, 31)); + expectEqualsInt(0x00000001, $inline$rotateLeftShort((short)0x0001, 32)); // overshoot + expectEqualsInt(0xFFFF0003, $inline$rotateLeftShort((short)0x8001, 1)); + expectEqualsInt(0xFFFE0007, $inline$rotateLeftShort((short)0x8001, 2)); + expectEqualsInt(0x00012340, $inline$rotateLeftShort((short)0x1234, 4)); + expectEqualsInt(0xFF9ABCFF, $inline$rotateLeftShort((short)0x9ABC, 8)); + + expectEqualsInt(0x00000001, $noinline$rotateLeftShort((short)0x0001, 0)); + expectEqualsInt(0x00000002, $noinline$rotateLeftShort((short)0x0001, 1)); + expectEqualsInt(0x80000000, $noinline$rotateLeftShort((short)0x0001, 31)); + expectEqualsInt(0x00000001, $noinline$rotateLeftShort((short)0x0001, 32)); // overshoot + expectEqualsInt(0xFFFF0003, $noinline$rotateLeftShort((short)0x8001, 1)); + expectEqualsInt(0xFFFE0007, $noinline$rotateLeftShort((short)0x8001, 2)); + expectEqualsInt(0x00012340, $noinline$rotateLeftShort((short)0x1234, 4)); + expectEqualsInt(0xFF9ABCFF, $noinline$rotateLeftShort((short)0x9ABC, 8)); + for (int i = 0; i < 40; i++) { // overshoot a bit int j = i & 31; - expectEqualsInt(0x00000000, rotateLeftShort((short)0x0000, i)); - expectEqualsInt(0xFFFFFFFF, rotateLeftShort((short)0xFFFF, i)); - expectEqualsInt((1 << j), rotateLeftShort((short)0x0001, i)); - expectEqualsInt((0x1234 << j) | (0x1234 >>> -j), rotateLeftShort((short)0x1234, i)); + expectEqualsInt(0x00000000, $inline$rotateLeftShort((short)0x0000, i)); + expectEqualsInt(0xFFFFFFFF, $inline$rotateLeftShort((short)0xFFFF, i)); + expectEqualsInt((1 << j), $inline$rotateLeftShort((short)0x0001, i)); + expectEqualsInt((0x1234 << j) | (0x1234 >>> -j), $inline$rotateLeftShort((short)0x1234, i)); + + expectEqualsInt(0x00000000, $noinline$rotateLeftShort((short)0x0000, i)); + expectEqualsInt(0xFFFFFFFF, $noinline$rotateLeftShort((short)0xFFFF, i)); + expectEqualsInt((1 << j), $noinline$rotateLeftShort((short)0x0001, i)); + expectEqualsInt((0x1234 << j) | (0x1234 >>> -j), $noinline$rotateLeftShort((short)0x1234, i)); } } public static void testRotateLeftChar() { - expectEqualsInt(0x00000001, rotateLeftChar((char)0x0001, 0)); - expectEqualsInt(0x00000002, rotateLeftChar((char)0x0001, 1)); - expectEqualsInt(0x80000000, rotateLeftChar((char)0x0001, 31)); - expectEqualsInt(0x00000001, rotateLeftChar((char)0x0001, 32)); // overshoot - expectEqualsInt(0x00010002, rotateLeftChar((char)0x8001, 1)); - expectEqualsInt(0x00020004, rotateLeftChar((char)0x8001, 2)); - expectEqualsInt(0x00012340, rotateLeftChar((char)0x1234, 4)); - expectEqualsInt(0x009ABC00, rotateLeftChar((char)0x9ABC, 8)); - expectEqualsInt(0x00FF0000, rotateLeftChar((char)0xFF00, 8)); + expectEqualsInt(0x00000001, $inline$rotateLeftChar((char)0x0001, 0)); + expectEqualsInt(0x00000002, $inline$rotateLeftChar((char)0x0001, 1)); + expectEqualsInt(0x80000000, $inline$rotateLeftChar((char)0x0001, 31)); + expectEqualsInt(0x00000001, $inline$rotateLeftChar((char)0x0001, 32)); // overshoot + expectEqualsInt(0x00010002, $inline$rotateLeftChar((char)0x8001, 1)); + expectEqualsInt(0x00020004, $inline$rotateLeftChar((char)0x8001, 2)); + expectEqualsInt(0x00012340, $inline$rotateLeftChar((char)0x1234, 4)); + expectEqualsInt(0x009ABC00, $inline$rotateLeftChar((char)0x9ABC, 8)); + expectEqualsInt(0x00FF0000, $inline$rotateLeftChar((char)0xFF00, 8)); + + expectEqualsInt(0x00000001, $noinline$rotateLeftChar((char)0x0001, 0)); + expectEqualsInt(0x00000002, $noinline$rotateLeftChar((char)0x0001, 1)); + expectEqualsInt(0x80000000, $noinline$rotateLeftChar((char)0x0001, 31)); + expectEqualsInt(0x00000001, $noinline$rotateLeftChar((char)0x0001, 32)); // overshoot + expectEqualsInt(0x00010002, $noinline$rotateLeftChar((char)0x8001, 1)); + expectEqualsInt(0x00020004, $noinline$rotateLeftChar((char)0x8001, 2)); + expectEqualsInt(0x00012340, $noinline$rotateLeftChar((char)0x1234, 4)); + expectEqualsInt(0x009ABC00, $noinline$rotateLeftChar((char)0x9ABC, 8)); + expectEqualsInt(0x00FF0000, $noinline$rotateLeftChar((char)0xFF00, 8)); + for (int i = 0; i < 40; i++) { // overshoot a bit int j = i & 31; - expectEqualsInt(0x00000000, rotateLeftChar((char)0x0000, i)); - expectEqualsInt((1 << j), rotateLeftChar((char)0x0001, i)); - expectEqualsInt((0x1234 << j) | (0x1234 >>> -j), rotateLeftChar((char)0x1234, i)); + expectEqualsInt(0x00000000, $inline$rotateLeftChar((char)0x0000, i)); + expectEqualsInt((1 << j), $inline$rotateLeftChar((char)0x0001, i)); + expectEqualsInt((0x1234 << j) | (0x1234 >>> -j), $inline$rotateLeftChar((char)0x1234, i)); + + expectEqualsInt(0x00000000, $noinline$rotateLeftChar((char)0x0000, i)); + expectEqualsInt((1 << j), $noinline$rotateLeftChar((char)0x0001, i)); + expectEqualsInt((0x1234 << j) | (0x1234 >>> -j), $noinline$rotateLeftChar((char)0x1234, i)); } } public static void testRotateLeftInt() { - expectEqualsInt(0x00000001, rotateLeftInt(0x00000001, 0)); - expectEqualsInt(0x00000002, rotateLeftInt(0x00000001, 1)); - expectEqualsInt(0x80000000, rotateLeftInt(0x00000001, 31)); - expectEqualsInt(0x00000001, rotateLeftInt(0x00000001, 32)); // overshoot - expectEqualsInt(0x00000003, rotateLeftInt(0x80000001, 1)); - expectEqualsInt(0x00000006, rotateLeftInt(0x80000001, 2)); - expectEqualsInt(0x23456781, rotateLeftInt(0x12345678, 4)); - expectEqualsInt(0xBCDEF09A, rotateLeftInt(0x9ABCDEF0, 8)); + expectEqualsInt(0x00000001, $inline$rotateLeftInt(0x00000001, 0)); + expectEqualsInt(0x00000002, $inline$rotateLeftInt(0x00000001, 1)); + expectEqualsInt(0x80000000, $inline$rotateLeftInt(0x00000001, 31)); + expectEqualsInt(0x00000001, $inline$rotateLeftInt(0x00000001, 32)); // overshoot + expectEqualsInt(0x00000003, $inline$rotateLeftInt(0x80000001, 1)); + expectEqualsInt(0x00000006, $inline$rotateLeftInt(0x80000001, 2)); + expectEqualsInt(0x23456781, $inline$rotateLeftInt(0x12345678, 4)); + expectEqualsInt(0xBCDEF09A, $inline$rotateLeftInt(0x9ABCDEF0, 8)); + + expectEqualsInt(0x00000001, $noinline$rotateLeftInt(0x00000001, 0)); + expectEqualsInt(0x00000002, $noinline$rotateLeftInt(0x00000001, 1)); + expectEqualsInt(0x80000000, $noinline$rotateLeftInt(0x00000001, 31)); + expectEqualsInt(0x00000001, $noinline$rotateLeftInt(0x00000001, 32)); // overshoot + expectEqualsInt(0x00000003, $noinline$rotateLeftInt(0x80000001, 1)); + expectEqualsInt(0x00000006, $noinline$rotateLeftInt(0x80000001, 2)); + expectEqualsInt(0x23456781, $noinline$rotateLeftInt(0x12345678, 4)); + expectEqualsInt(0xBCDEF09A, $noinline$rotateLeftInt(0x9ABCDEF0, 8)); + expectEqualsInt(0x80000000, $noinline$rotateLeftIntConstant(0x00000001)); + expectEqualsInt(Integer.MIN_VALUE, $noinline$rotateLeftIntMulNegDistance(1, -2)); + for (int i = 0; i < 40; i++) { // overshoot a bit int j = i & 31; - expectEqualsInt(0x00000000, rotateLeftInt(0x00000000, i)); - expectEqualsInt(0xFFFFFFFF, rotateLeftInt(0xFFFFFFFF, i)); - expectEqualsInt(1 << j, rotateLeftInt(0x00000001, i)); - expectEqualsInt((0x12345678 << j) | (0x12345678 >>> -j), rotateLeftInt(0x12345678, i)); + expectEqualsInt(0x00000000, $inline$rotateLeftInt(0x00000000, i)); + expectEqualsInt(0xFFFFFFFF, $inline$rotateLeftInt(0xFFFFFFFF, i)); + expectEqualsInt(1 << j, $inline$rotateLeftInt(0x00000001, i)); + expectEqualsInt((0x12345678 << j) | (0x12345678 >>> -j), + $inline$rotateLeftInt(0x12345678, i)); + + expectEqualsInt(0x00000000, $noinline$rotateLeftInt(0x00000000, i)); + expectEqualsInt(0xFFFFFFFF, $noinline$rotateLeftInt(0xFFFFFFFF, i)); + expectEqualsInt(1 << j, $noinline$rotateLeftInt(0x00000001, i)); + expectEqualsInt((0x12345678 << j) | (0x12345678 >>> -j), + $noinline$rotateLeftInt(0x12345678, i)); } } public static void testRotateLeftLong() { - expectEqualsLong(0x0000000000000001L, rotateLeftLong(0x0000000000000001L, 0)); - expectEqualsLong(0x0000000000000002L, rotateLeftLong(0x0000000000000001L, 1)); - expectEqualsLong(0x8000000000000000L, rotateLeftLong(0x0000000000000001L, 63)); - expectEqualsLong(0x0000000000000001L, rotateLeftLong(0x0000000000000001L, 64)); // overshoot - expectEqualsLong(0x0000000000000003L, rotateLeftLong(0x8000000000000001L, 1)); - expectEqualsLong(0x0000000000000006L, rotateLeftLong(0x8000000000000001L, 2)); - expectEqualsLong(0x23456789ABCDEF01L, rotateLeftLong(0x123456789ABCDEF0L, 4)); - expectEqualsLong(0x3456789ABCDEF012L, rotateLeftLong(0x123456789ABCDEF0L, 8)); + expectEqualsLong(0x0000000000000001L, $inline$rotateLeftLong(0x0000000000000001L, 0)); + expectEqualsLong(0x0000000000000002L, $inline$rotateLeftLong(0x0000000000000001L, 1)); + expectEqualsLong(0x8000000000000000L, $inline$rotateLeftLong(0x0000000000000001L, 63)); + expectEqualsLong(0x8000000000000000L, $noinline$rotateLeftLongConstant(0x0000000000000001L)); + expectEqualsLong(0x0000000000000001L, + $inline$rotateLeftLong(0x0000000000000001L, 64)); // overshoot + expectEqualsLong(0x0000000000000003L, $inline$rotateLeftLong(0x8000000000000001L, 1)); + expectEqualsLong(0x0000000000000006L, $inline$rotateLeftLong(0x8000000000000001L, 2)); + expectEqualsLong(0x23456789ABCDEF01L, $inline$rotateLeftLong(0x123456789ABCDEF0L, 4)); + expectEqualsLong(0x3456789ABCDEF012L, $inline$rotateLeftLong(0x123456789ABCDEF0L, 8)); + + expectEqualsLong(0x0000000000000001L, $noinline$rotateLeftLong(0x0000000000000001L, 0)); + expectEqualsLong(0x0000000000000002L, $noinline$rotateLeftLong(0x0000000000000001L, 1)); + expectEqualsLong(0x8000000000000000L, $noinline$rotateLeftLong(0x0000000000000001L, 63)); + expectEqualsLong(0x0000000000000001L, + $noinline$rotateLeftLong(0x0000000000000001L, 64)); // overshoot + expectEqualsLong(0x0000000000000003L, $noinline$rotateLeftLong(0x8000000000000001L, 1)); + expectEqualsLong(0x0000000000000006L, $noinline$rotateLeftLong(0x8000000000000001L, 2)); + expectEqualsLong(0x23456789ABCDEF01L, $noinline$rotateLeftLong(0x123456789ABCDEF0L, 4)); + expectEqualsLong(0x3456789ABCDEF012L, $noinline$rotateLeftLong(0x123456789ABCDEF0L, 8)); for (int i = 0; i < 70; i++) { // overshoot a bit int j = i & 63; - expectEqualsLong(0x0000000000000000L, rotateLeftLong(0x0000000000000000L, i)); - expectEqualsLong(0xFFFFFFFFFFFFFFFFL, rotateLeftLong(0xFFFFFFFFFFFFFFFFL, i)); - expectEqualsLong(1L << j, rotateLeftLong(0x0000000000000001, i)); + expectEqualsLong(0x0000000000000000L, $inline$rotateLeftLong(0x0000000000000000L, i)); + expectEqualsLong(0xFFFFFFFFFFFFFFFFL, $inline$rotateLeftLong(0xFFFFFFFFFFFFFFFFL, i)); + expectEqualsLong(1L << j, $inline$rotateLeftLong(0x0000000000000001, i)); + expectEqualsLong((0x123456789ABCDEF0L << j) | (0x123456789ABCDEF0L >>> -j), + $inline$rotateLeftLong(0x123456789ABCDEF0L, i)); + + expectEqualsLong(0x0000000000000000L, $noinline$rotateLeftLong(0x0000000000000000L, i)); + expectEqualsLong(0xFFFFFFFFFFFFFFFFL, $noinline$rotateLeftLong(0xFFFFFFFFFFFFFFFFL, i)); + expectEqualsLong(1L << j, $noinline$rotateLeftLong(0x0000000000000001, i)); expectEqualsLong((0x123456789ABCDEF0L << j) | (0x123456789ABCDEF0L >>> -j), - rotateLeftLong(0x123456789ABCDEF0L, i)); + $noinline$rotateLeftLong(0x123456789ABCDEF0L, i)); } } @@ -471,21 +655,39 @@ public class TestRotate { public static void testRotateLeftIntWithByteDistance() { - expectEqualsInt(0x00000001, rotateLeftIntWithByteDistance(0x00000001, (byte)0)); - expectEqualsInt(0x00000002, rotateLeftIntWithByteDistance(0x00000001, (byte)1)); - expectEqualsInt(0x80000000, rotateLeftIntWithByteDistance(0x00000001, (byte)31)); - expectEqualsInt(0x00000001, rotateLeftIntWithByteDistance(0x00000001, (byte)32)); // overshoot - expectEqualsInt(0x00000003, rotateLeftIntWithByteDistance(0x80000001, (byte)1)); - expectEqualsInt(0x00000006, rotateLeftIntWithByteDistance(0x80000001, (byte)2)); - expectEqualsInt(0x23456781, rotateLeftIntWithByteDistance(0x12345678, (byte)4)); - expectEqualsInt(0xBCDEF09A, rotateLeftIntWithByteDistance(0x9ABCDEF0, (byte)8)); + expectEqualsInt(0x00000001, $inline$rotateLeftIntWithByteDistance(0x00000001, (byte)0)); + expectEqualsInt(0x00000002, $inline$rotateLeftIntWithByteDistance(0x00000001, (byte)1)); + expectEqualsInt(0x80000000, $inline$rotateLeftIntWithByteDistance(0x00000001, (byte)31)); + expectEqualsInt(0x00000001, + $inline$rotateLeftIntWithByteDistance(0x00000001, (byte)32)); // overshoot + expectEqualsInt(0x00000003, $inline$rotateLeftIntWithByteDistance(0x80000001, (byte)1)); + expectEqualsInt(0x00000006, $inline$rotateLeftIntWithByteDistance(0x80000001, (byte)2)); + expectEqualsInt(0x23456781, $inline$rotateLeftIntWithByteDistance(0x12345678, (byte)4)); + expectEqualsInt(0xBCDEF09A, $inline$rotateLeftIntWithByteDistance(0x9ABCDEF0, (byte)8)); + + expectEqualsInt(0x00000001, $noinline$rotateLeftIntWithByteDistance(0x00000001, (byte)0)); + expectEqualsInt(0x00000002, $noinline$rotateLeftIntWithByteDistance(0x00000001, (byte)1)); + expectEqualsInt(0x80000000, $noinline$rotateLeftIntWithByteDistance(0x00000001, (byte)31)); + expectEqualsInt(0x00000001, + $noinline$rotateLeftIntWithByteDistance(0x00000001, (byte)32)); // overshoot + expectEqualsInt(0x00000003, $noinline$rotateLeftIntWithByteDistance(0x80000001, (byte)1)); + expectEqualsInt(0x00000006, $noinline$rotateLeftIntWithByteDistance(0x80000001, (byte)2)); + expectEqualsInt(0x23456781, $noinline$rotateLeftIntWithByteDistance(0x12345678, (byte)4)); + expectEqualsInt(0xBCDEF09A, $noinline$rotateLeftIntWithByteDistance(0x9ABCDEF0, (byte)8)); + for (byte i = 0; i < 40; i++) { // overshoot a bit byte j = (byte)(i & 31); - expectEqualsInt(0x00000000, rotateLeftIntWithByteDistance(0x00000000, i)); - expectEqualsInt(0xFFFFFFFF, rotateLeftIntWithByteDistance(0xFFFFFFFF, i)); - expectEqualsInt(1 << j, rotateLeftIntWithByteDistance(0x00000001, i)); + expectEqualsInt(0x00000000, $inline$rotateLeftIntWithByteDistance(0x00000000, i)); + expectEqualsInt(0xFFFFFFFF, $inline$rotateLeftIntWithByteDistance(0xFFFFFFFF, i)); + expectEqualsInt(1 << j, $inline$rotateLeftIntWithByteDistance(0x00000001, i)); + expectEqualsInt((0x12345678 << j) | (0x12345678 >>> -j), + $inline$rotateLeftIntWithByteDistance(0x12345678, i)); + + expectEqualsInt(0x00000000, $noinline$rotateLeftIntWithByteDistance(0x00000000, i)); + expectEqualsInt(0xFFFFFFFF, $noinline$rotateLeftIntWithByteDistance(0xFFFFFFFF, i)); + expectEqualsInt(1 << j, $noinline$rotateLeftIntWithByteDistance(0x00000001, i)); expectEqualsInt((0x12345678 << j) | (0x12345678 >>> -j), - rotateLeftIntWithByteDistance(0x12345678, i)); + $noinline$rotateLeftIntWithByteDistance(0x12345678, i)); } } @@ -493,7 +695,8 @@ public class TestRotate { expectEqualsInt(0x80000000, rotateRightIntWithByteDistance(0x80000000, (byte)0)); expectEqualsInt(0x40000000, rotateRightIntWithByteDistance(0x80000000, (byte)1)); expectEqualsInt(0x00000001, rotateRightIntWithByteDistance(0x80000000, (byte)31)); - expectEqualsInt(0x80000000, rotateRightIntWithByteDistance(0x80000000, (byte)32)); // overshoot + expectEqualsInt(0x80000000, + rotateRightIntWithByteDistance(0x80000000, (byte)32)); // overshoot expectEqualsInt(0xC0000000, rotateRightIntWithByteDistance(0x80000001, (byte)1)); expectEqualsInt(0x60000000, rotateRightIntWithByteDistance(0x80000001, (byte)2)); expectEqualsInt(0x81234567, rotateRightIntWithByteDistance(0x12345678, (byte)4)); |