ART: Refactor GenSelect, refactor gen_common accordingly
This adds a GenSelect method meant for selection of constants. The
general-purpose GenInstanceof code is refactored to take advantage of
this. This cleans up code and squashes a branch-over on ARM64 to a
cset.
Also add a slow-path for type initialization in GenInstanceof.
Change-Id: Ie4494858bb8c26d386cf2e628172b81bba911ae5
diff --git a/compiler/dex/quick/arm64/codegen_arm64.h b/compiler/dex/quick/arm64/codegen_arm64.h
index de97653..da586b1 100644
--- a/compiler/dex/quick/arm64/codegen_arm64.h
+++ b/compiler/dex/quick/arm64/codegen_arm64.h
@@ -206,7 +206,14 @@
void GenFillArrayData(DexOffset table_offset, RegLocation rl_src);
void GenFusedFPCmpBranch(BasicBlock* bb, MIR* mir, bool gt_bias, bool is_double);
void GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir);
- void GenSelect(BasicBlock* bb, MIR* mir);
+ void GenSelect(BasicBlock* bb, MIR* mir) OVERRIDE;
+ void GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+ int32_t true_val, int32_t false_val, RegStorage rs_dest,
+ int dest_reg_class) OVERRIDE;
+ // Helper used in the above two.
+ void GenSelect(int32_t left, int32_t right, ConditionCode code, RegStorage rs_dest,
+ int result_reg_class);
+
bool GenMemBarrier(MemBarrierKind barrier_kind);
void GenMonitorEnter(int opt_flags, RegLocation rl_src);
void GenMonitorExit(int opt_flags, RegLocation rl_src);
diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc
index 6dc4a7a..a76d275 100644
--- a/compiler/dex/quick/arm64/int_arm64.cc
+++ b/compiler/dex/quick/arm64/int_arm64.cc
@@ -85,141 +85,129 @@
StoreValueWide(rl_dest, rl_result);
}
-void Arm64Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
- RegLocation rl_result;
- RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
- RegLocation rl_dest = mir_graph_->GetDest(mir);
- RegisterClass src_reg_class = rl_src.ref ? kRefReg : kCoreReg;
- RegisterClass result_reg_class = rl_dest.ref ? kRefReg : kCoreReg;
+static constexpr bool kUseDeltaEncodingInGenSelect = false;
- rl_src = LoadValue(rl_src, src_reg_class);
+void Arm64Mir2Lir::GenSelect(int32_t true_val, int32_t false_val, ConditionCode ccode,
+ RegStorage rs_dest, int result_reg_class) {
+ if (false_val == 0 || // 0 is better as first operand.
+ true_val == 1 || // Potentially Csinc.
+ true_val == -1 || // Potentially Csinv.
+ true_val == false_val + 1) { // Potentially Csinc.
+ ccode = NegateComparison(ccode);
+ std::swap(true_val, false_val);
+ }
+
+ ArmConditionCode code = ArmConditionEncoding(ccode);
+
+ int opcode; // The opcode.
+ RegStorage left_op = RegStorage::InvalidReg(); // The operands.
+ RegStorage right_op = RegStorage::InvalidReg(); // The operands.
+
+ bool is_wide = rs_dest.Is64Bit();
+
+ RegStorage zero_reg = is_wide ? rs_xzr : rs_wzr;
+
+ if (true_val == 0) {
+ left_op = zero_reg;
+ } else {
+ left_op = rs_dest;
+ LoadConstantNoClobber(rs_dest, true_val);
+ }
+ if (false_val == 1) {
+ right_op = zero_reg;
+ opcode = kA64Csinc4rrrc;
+ } else if (false_val == -1) {
+ right_op = zero_reg;
+ opcode = kA64Csinv4rrrc;
+ } else if (false_val == true_val + 1) {
+ right_op = left_op;
+ opcode = kA64Csinc4rrrc;
+ } else if (false_val == -true_val) {
+ right_op = left_op;
+ opcode = kA64Csneg4rrrc;
+ } else if (false_val == ~true_val) {
+ right_op = left_op;
+ opcode = kA64Csinv4rrrc;
+ } else if (true_val == 0) {
+ // left_op is zero_reg.
+ right_op = rs_dest;
+ LoadConstantNoClobber(rs_dest, false_val);
+ opcode = kA64Csel4rrrc;
+ } else {
+ // Generic case.
+ RegStorage t_reg2 = AllocTypedTemp(false, result_reg_class);
+ if (is_wide) {
+ if (t_reg2.Is32Bit()) {
+ t_reg2 = As64BitReg(t_reg2);
+ }
+ } else {
+ if (t_reg2.Is64Bit()) {
+ t_reg2 = As32BitReg(t_reg2);
+ }
+ }
+
+ if (kUseDeltaEncodingInGenSelect) {
+ int32_t delta = false_val - true_val;
+ uint32_t abs_val = delta < 0 ? -delta : delta;
+
+ if (abs_val < 0x1000) { // TODO: Replace with InexpensiveConstant with opcode.
+ // Can encode as immediate to an add.
+ right_op = t_reg2;
+ OpRegRegImm(kOpAdd, t_reg2, left_op, delta);
+ }
+ }
+
+ // Load as constant.
+ if (!right_op.Valid()) {
+ LoadConstantNoClobber(t_reg2, false_val);
+ right_op = t_reg2;
+ }
+
+ opcode = kA64Csel4rrrc;
+ }
+
+ DCHECK(left_op.Valid() && right_op.Valid());
+ NewLIR4(is_wide ? WIDE(opcode) : opcode, rs_dest.GetReg(), left_op.GetReg(), right_op.GetReg(),
+ code);
+}
+
+void Arm64Mir2Lir::GenSelectConst32(RegStorage left_op, RegStorage right_op, ConditionCode code,
+ int32_t true_val, int32_t false_val, RegStorage rs_dest,
+ int dest_reg_class) {
+ DCHECK(rs_dest.Valid());
+ OpRegReg(kOpCmp, left_op, right_op);
+ GenSelect(true_val, false_val, code, rs_dest, dest_reg_class);
+}
+
+void Arm64Mir2Lir::GenSelect(BasicBlock* bb, MIR* mir) {
+ RegLocation rl_src = mir_graph_->GetSrc(mir, 0);
+ rl_src = LoadValue(rl_src, rl_src.ref ? kRefReg : kCoreReg);
// rl_src may be aliased with rl_result/rl_dest, so do compare early.
OpRegImm(kOpCmp, rl_src.reg, 0);
- ArmConditionCode code = ArmConditionEncoding(mir->meta.ccode);
+ RegLocation rl_dest = mir_graph_->GetDest(mir);
// The kMirOpSelect has two variants, one for constants and one for moves.
- bool is_wide = rl_dest.ref || rl_dest.wide;
-
if (mir->ssa_rep->num_uses == 1) {
- uint32_t true_val = mir->dalvikInsn.vB;
- uint32_t false_val = mir->dalvikInsn.vC;
-
- int opcode; // The opcode.
- int left_op, right_op; // The operands.
- bool rl_result_evaled = false;
-
- // Check some simple cases.
- // TODO: Improve this.
- int zero_reg = (is_wide ? rs_xzr : rs_wzr).GetReg();
-
- if ((true_val == 0 && false_val == 1) || (true_val == 1 && false_val == 0)) {
- // CSInc cheap based on wzr.
- if (true_val == 1) {
- // Negate.
- code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
- }
-
- left_op = right_op = zero_reg;
- opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc;
- } else if ((true_val == 0 && false_val == 0xFFFFFFFF) ||
- (true_val == 0xFFFFFFFF && false_val == 0)) {
- // CSneg cheap based on wzr.
- if (true_val == 0xFFFFFFFF) {
- // Negate.
- code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
- }
-
- left_op = right_op = zero_reg;
- opcode = is_wide ? WIDE(kA64Csinv4rrrc) : kA64Csinv4rrrc;
- } else if (true_val == 0 || false_val == 0) {
- // Csel half cheap based on wzr.
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
- rl_result_evaled = true;
- if (false_val == 0) {
- // Negate.
- code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
- }
- LoadConstantNoClobber(rl_result.reg, true_val == 0 ? false_val : true_val);
- left_op = zero_reg;
- right_op = rl_result.reg.GetReg();
- opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
- } else if (true_val == 1 || false_val == 1) {
- // CSInc half cheap based on wzr.
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
- rl_result_evaled = true;
- if (true_val == 1) {
- // Negate.
- code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
- }
- LoadConstantNoClobber(rl_result.reg, true_val == 1 ? false_val : true_val);
- left_op = rl_result.reg.GetReg();
- right_op = zero_reg;
- opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc;
- } else if (true_val == 0xFFFFFFFF || false_val == 0xFFFFFFFF) {
- // CSneg half cheap based on wzr.
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
- rl_result_evaled = true;
- if (true_val == 0xFFFFFFFF) {
- // Negate.
- code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
- }
- LoadConstantNoClobber(rl_result.reg, true_val == 0xFFFFFFFF ? false_val : true_val);
- left_op = rl_result.reg.GetReg();
- right_op = zero_reg;
- opcode = is_wide ? WIDE(kA64Csinv4rrrc) : kA64Csinv4rrrc;
- } else if ((true_val + 1 == false_val) || (false_val + 1 == true_val)) {
- // Load a constant and use CSinc. Use rl_result.
- if (false_val + 1 == true_val) {
- // Negate.
- code = ArmConditionEncoding(NegateComparison(mir->meta.ccode));
- true_val = false_val;
- }
-
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
- rl_result_evaled = true;
- LoadConstantNoClobber(rl_result.reg, true_val);
- left_op = right_op = rl_result.reg.GetReg();
- opcode = is_wide ? WIDE(kA64Csinc4rrrc) : kA64Csinc4rrrc;
- } else {
- // Csel. The rest. Use rl_result and a temp.
- // TODO: To minimize the constants being loaded, check whether one can be inexpensively
- // loaded as n - 1 or ~n.
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
- rl_result_evaled = true;
- LoadConstantNoClobber(rl_result.reg, true_val);
- RegStorage t_reg2 = AllocTypedTemp(false, result_reg_class);
- if (rl_dest.wide) {
- if (t_reg2.Is32Bit()) {
- t_reg2 = As64BitReg(t_reg2);
- }
- }
- LoadConstantNoClobber(t_reg2, false_val);
-
- // Use csel.
- left_op = rl_result.reg.GetReg();
- right_op = t_reg2.GetReg();
- opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
- }
-
- if (!rl_result_evaled) {
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
- }
-
- NewLIR4(opcode, rl_result.reg.GetReg(), left_op, right_op, code);
+ RegLocation rl_result = EvalLoc(rl_dest, rl_dest.ref ? kRefReg : kCoreReg, true);
+ GenSelect(mir->dalvikInsn.vB, mir->dalvikInsn.vC, mir->meta.ccode, rl_result.reg,
+ rl_dest.ref ? kRefReg : kCoreReg);
+ StoreValue(rl_dest, rl_result);
} else {
RegLocation rl_true = mir_graph_->reg_location_[mir->ssa_rep->uses[1]];
RegLocation rl_false = mir_graph_->reg_location_[mir->ssa_rep->uses[2]];
+ RegisterClass result_reg_class = rl_dest.ref ? kRefReg : kCoreReg;
rl_true = LoadValue(rl_true, result_reg_class);
rl_false = LoadValue(rl_false, result_reg_class);
- rl_result = EvalLoc(rl_dest, result_reg_class, true);
+ RegLocation rl_result = EvalLoc(rl_dest, result_reg_class, true);
+ bool is_wide = rl_dest.ref || rl_dest.wide;
int opcode = is_wide ? WIDE(kA64Csel4rrrc) : kA64Csel4rrrc;
NewLIR4(opcode, rl_result.reg.GetReg(),
- rl_true.reg.GetReg(), rl_false.reg.GetReg(), code);
+ rl_true.reg.GetReg(), rl_false.reg.GetReg(), ArmConditionEncoding(mir->meta.ccode));
+ StoreValue(rl_dest, rl_result);
}
- StoreValue(rl_dest, rl_result);
}
void Arm64Mir2Lir::GenFusedLongCmpBranch(BasicBlock* bb, MIR* mir) {