ARM64: revised generation of ADD/SUB/CMP immediate

- Support a wider range of immediate values for ADD and SUB,
  using two instructions when needed (add/sub middle 12 bits then low 12 bits).

- Do not rely on the assembler to convert CMP immediate negative to
  CMN immediate.
master
Xavier Leroy 2020-08-26 14:55:38 +00:00
parent 65544ffd1f
commit cc25ceaaec
2 changed files with 39 additions and 4 deletions

View File

@ -321,6 +321,37 @@ let output_epilogue f =
(* reset CFA back because function body may continue *)
if n > 0 then cfi_adjust_cfa_offset n
(* Output add-immediate / sub-immediate / cmp-immediate instructions *)
let rec emit_addimm rd rs n =
if n < 0 then emit_subimm rd rs (-n)
else if n <= 0xFFF then
` add {emit_reg rd}, {emit_reg rs}, #{emit_int n}\n`
else begin
assert (n <= 0xFFF_FFF);
let nl = n land 0xFFF and nh = n land 0xFFF_000 in
` add {emit_reg rd}, {emit_reg rs}, #{emit_int nh}\n`;
if nl <> 0 then
` add {emit_reg rd}, {emit_reg rd}, #{emit_int nl}\n`
end
and emit_subimm rd rs n =
if n < 0 then emit_addimm rd rs (-n)
else if n <= 0xFFF then
` sub {emit_reg rd}, {emit_reg rs}, #{emit_int n}\n`
else begin
assert (n <= 0xFFF_FFF);
let nl = n land 0xFFF and nh = n land 0xFFF_000 in
` sub {emit_reg rd}, {emit_reg rs}, #{emit_int nh}\n`;
if nl <> 0 then
` sub {emit_reg rd}, {emit_reg rd}, #{emit_int nl}\n`
end
let emit_cmpimm rs n =
if n >= 0
then ` cmp {emit_reg rs}, #{emit_int n}\n`
else ` cmn {emit_reg rs}, #{emit_int (-n)}\n`
(* Name of current function *)
let function_name = ref ""
(* Entry point for tail recursive calls *)
@ -746,11 +777,15 @@ let emit_instr i =
assembly_code_for_allocation i ~n ~far:false ~label_after_call_gc ~dbginfo
| Lop(Ispecific (Ifar_alloc { bytes = n; label_after_call_gc; dbginfo })) ->
assembly_code_for_allocation i ~n ~far:true ~label_after_call_gc ~dbginfo
| Lop(Iintop_imm(Iadd, n)) ->
emit_addimm i.res.(0) i.arg.(0) n
| Lop(Iintop_imm(Isub, n)) ->
emit_subimm i.res.(0) i.arg.(0) n
| Lop(Iintop(Icomp cmp)) ->
` cmp {emit_reg i.arg.(0)}, {emit_reg i.arg.(1)}\n`;
` cset {emit_reg i.res.(0)}, {emit_string (name_for_comparison cmp)}\n`
| Lop(Iintop_imm(Icomp cmp, n)) ->
` cmp {emit_reg i.arg.(0)}, #{emit_int n}\n`;
emit_cmpimm i.arg.(0) n;
` cset {emit_reg i.res.(0)}, {emit_string (name_for_comparison cmp)}\n`
| Lop(Iintop (Icheckbound { label_after_error; })) ->
let lbl = bound_error_label i.dbg ?label:label_after_error in
@ -765,7 +800,7 @@ let emit_instr i =
`{emit_label lbl2}:\n`;
| Lop(Iintop_imm(Icheckbound { label_after_error; }, n)) ->
let lbl = bound_error_label i.dbg ?label:label_after_error in
` cmp {emit_reg i.arg.(0)}, #{emit_int n}\n`;
emit_cmpimm i.arg.(0) n;
` b.ls {emit_label lbl}\n`
| Lop(Ispecific(
Ifar_intop_imm_checkbound { bound; label_after_error; })) ->
@ -869,7 +904,7 @@ let emit_instr i =
let comp = name_for_comparison cmp in
` b.{emit_string comp} {emit_label lbl}\n`
| Iinttest_imm(cmp, n) ->
` cmp {emit_reg i.arg.(0)}, #{emit_int n}\n`;
emit_cmpimm i.arg.(0) n;
let comp = name_for_comparison cmp in
` b.{emit_string comp} {emit_label lbl}\n`
| Ifloattest cmp ->

View File

@ -108,7 +108,7 @@ method is_immediate_test _cmp n =
method is_immediate op n =
match op with
| Iadd | Isub -> is_immediate n
| Iadd | Isub -> n <= 0xFFF_FFF && n >= -0xFFF_FFF
| Imul | Imulh -> false (* no mul immediate instruction *)
| Iand | Ior | Ixor -> is_logical_immediate n
| Icomp _ | Icheckbound _ -> is_immediate n