diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c index 3fe2f0f..5b3e3c4 100644 --- a/gcc/config/aarch64/aarch64.c +++ b/gcc/config/aarch64/aarch64.c @@ -4757,13 +4757,65 @@ aarch64_legitimize_address (rtx x, rtx /* orig_x */, machine_mode mode) We try to pick as large a range for the offset as possible to maximize the chance of a CSE. However, for aligned addresses we limit the range to 4k so that structures with different sized - elements are likely to use the same base. */ + elements are likely to use the same base. We need to be careful + not split CONST for some forms address expressions, otherwise it + will generate sub-optimal code. */ if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1))) { HOST_WIDE_INT offset = INTVAL (XEXP (x, 1)); HOST_WIDE_INT base_offset; + if (GET_CODE (XEXP (x, 0)) == PLUS) + { + rtx op0 = XEXP (XEXP (x, 0), 0); + rtx op1 = XEXP (XEXP (x, 0), 1); + + /* For addr expression in the form like "r1 + r2 + 0x3ffc". + Since the offset is within range supported by addressing + mode "reg+offset", we don't split the const and legalize + it into below insn and expr sequence: + r3 = r1 + r2; + "r3 + 0x3ffc". */ + if (REG_P (op0) && REG_P (op1)) + { + machine_mode addr_mode = GET_MODE (x); + rtx base = gen_reg_rtx (addr_mode); + rtx addr = plus_constant (addr_mode, base, offset); + + if (aarch64_legitimate_address_hook_p (mode, addr, false)) + { + emit_insn (gen_adddi3 (base, op0, op1)); + return addr; + } + } + /* For addr expression in the form like "r1 + r2<<2 + 0x3ffc". + Live above, we don't split the const and legalize it into + below insn and expr sequence: + r3 = 0x3ffc; + r4 = r1 + r3; + "r4 + r2<<2". */ + else if (REG_P (op0) || REG_P (op1)) + { + machine_mode addr_mode = GET_MODE (x); + rtx base = gen_reg_rtx (addr_mode); + + /* Switch to make sure that register is in op0. */ + if (REG_P (op1)) + std::swap (op0, op1); + + rtx addr = gen_rtx_fmt_ee (PLUS, addr_mode, op1, base); + + if (aarch64_legitimate_address_hook_p (mode, addr, false)) + { + base = force_operand (plus_constant (addr_mode, + op0, offset), + NULL_RTX); + return gen_rtx_fmt_ee (PLUS, addr_mode, op1, base); + } + } + } + /* Does it look like we'll need a load/store-pair operation? */ if (GET_MODE_SIZE (mode) > 16 || mode == TImode)