[v1] LoongArch: Add new code model 'emdium'.

Message ID 20220820090425.525292-1-chenglulu@loongson.cn
State New
Headers
Series [v1] LoongArch: Add new code model 'emdium'. |

Commit Message

Lulu Cheng Aug. 20, 2022, 9:04 a.m. UTC
  The function jump instruction in normal mode is 'bl',
so the scope of the function jump is +-128MB.

Now we've added support for 'medium' mode, this mode is
to complete the function jump through two instructions:
	pcalau12i + jirl
So in this mode the function jump range is increased to +-2GB.

Compared with 'normal' mode, 'medium' mode only affects the
jump range of functions.

gcc/ChangeLog:

	* config/loongarch/genopts/loongarch-strings: Support code model medium.
	* config/loongarch/genopts/loongarch.opt.in: Likewise.
	* config/loongarch/loongarch-def.c: Likewise.
	* config/loongarch/loongarch-def.h (CMODEL_LARGE): Likewise.
	(CMODEL_EXTREME): Likewise.
	(N_CMODEL_TYPES): Likewise.
	(CMODEL_MEDIUM): Likewise.
	* config/loongarch/loongarch-opts.cc: Likewise.
	* config/loongarch/loongarch-opts.h (TARGET_CMODEL_MEDIUM): Likewise.
	* config/loongarch/loongarch-str.h (STR_CMODEL_MEDIUM): Likewise.
	* config/loongarch/loongarch.cc (loongarch_call_tls_get_addr):
	Tls symbol Loading support medium mode.
	(loongarch_legitimize_call_address): When medium mode, make a symbolic
	jump with two instructions.
	(loongarch_option_override_internal): Support medium.
	* config/loongarch/loongarch.md (@pcalau12i<mode>): New template.
	(@sibcall_internal_1<mode>): New function call templates added to support
	medium mode.
	(@sibcall_value_internal_1<mode>): Likewise.
	(@sibcall_value_multiple_internal_1<mode>): Likewise.
	(@call_internal_1<mode>): Likewise.
	(@call_value_internal_1<mode>): Likewise.
	(@call_value_multiple_internal_1<mode>): Likewise.
	* config/loongarch/loongarch.opt: Support medium.
	* config/loongarch/predicates.md: Add processing about medium mode.
	* doc/invoke.texi: Document for '-mcmodel=medium'.

gcc/testsuite/ChangeLog:

	* gcc.target/loongarch/func-call-medium-1.c: New test.
	* gcc.target/loongarch/func-call-medium-2.c: New test.
	* gcc.target/loongarch/func-call-medium-3.c: New test.
	* gcc.target/loongarch/func-call-medium-4.c: New test.
	* gcc.target/loongarch/func-call-medium-5.c: New test.
	* gcc.target/loongarch/func-call-medium-6.c: New test.
	* gcc.target/loongarch/func-call-medium-7.c: New test.
	* gcc.target/loongarch/func-call-medium-8.c: New test.
	* gcc.target/loongarch/tls-gd-noplt.c: Add compile parameter '-mexplicit-relocs'.
---
 .../loongarch/genopts/loongarch-strings       |   1 +
 gcc/config/loongarch/genopts/loongarch.opt.in |   3 +
 gcc/config/loongarch/loongarch-def.c          |   1 +
 gcc/config/loongarch/loongarch-def.h          |   7 +-
 gcc/config/loongarch/loongarch-opts.cc        |  15 ++-
 gcc/config/loongarch/loongarch-opts.h         |   1 +
 gcc/config/loongarch/loongarch-str.h          |   1 +
 gcc/config/loongarch/loongarch.cc             | 123 +++++++++++++----
 gcc/config/loongarch/loongarch.md             | 125 +++++++++++++++++-
 gcc/config/loongarch/loongarch.opt            |   3 +
 gcc/config/loongarch/predicates.md            |  15 ++-
 gcc/doc/invoke.texi                           |   3 +
 .../gcc.target/loongarch/func-call-medium-1.c |  41 ++++++
 .../gcc.target/loongarch/func-call-medium-2.c |  41 ++++++
 .../gcc.target/loongarch/func-call-medium-3.c |  41 ++++++
 .../gcc.target/loongarch/func-call-medium-4.c |  41 ++++++
 .../gcc.target/loongarch/func-call-medium-5.c |  42 ++++++
 .../gcc.target/loongarch/func-call-medium-6.c |  42 ++++++
 .../gcc.target/loongarch/func-call-medium-7.c |  43 ++++++
 .../gcc.target/loongarch/func-call-medium-8.c |  42 ++++++
 .../gcc.target/loongarch/tls-gd-noplt.c       |   4 +-
 21 files changed, 595 insertions(+), 40 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c
 create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c
  

Comments

Huacai Chen Aug. 20, 2022, 9:18 a.m. UTC | #1
Hi, Lulu,

I think there is a typo in your subject line.

Huacai

On Sat, Aug 20, 2022 at 5:05 PM Lulu Cheng <chenglulu@loongson.cn> wrote:
>
> The function jump instruction in normal mode is 'bl',
> so the scope of the function jump is +-128MB.
>
> Now we've added support for 'medium' mode, this mode is
> to complete the function jump through two instructions:
>         pcalau12i + jirl
> So in this mode the function jump range is increased to +-2GB.
>
> Compared with 'normal' mode, 'medium' mode only affects the
> jump range of functions.
>
> gcc/ChangeLog:
>
>         * config/loongarch/genopts/loongarch-strings: Support code model medium.
>         * config/loongarch/genopts/loongarch.opt.in: Likewise.
>         * config/loongarch/loongarch-def.c: Likewise.
>         * config/loongarch/loongarch-def.h (CMODEL_LARGE): Likewise.
>         (CMODEL_EXTREME): Likewise.
>         (N_CMODEL_TYPES): Likewise.
>         (CMODEL_MEDIUM): Likewise.
>         * config/loongarch/loongarch-opts.cc: Likewise.
>         * config/loongarch/loongarch-opts.h (TARGET_CMODEL_MEDIUM): Likewise.
>         * config/loongarch/loongarch-str.h (STR_CMODEL_MEDIUM): Likewise.
>         * config/loongarch/loongarch.cc (loongarch_call_tls_get_addr):
>         Tls symbol Loading support medium mode.
>         (loongarch_legitimize_call_address): When medium mode, make a symbolic
>         jump with two instructions.
>         (loongarch_option_override_internal): Support medium.
>         * config/loongarch/loongarch.md (@pcalau12i<mode>): New template.
>         (@sibcall_internal_1<mode>): New function call templates added to support
>         medium mode.
>         (@sibcall_value_internal_1<mode>): Likewise.
>         (@sibcall_value_multiple_internal_1<mode>): Likewise.
>         (@call_internal_1<mode>): Likewise.
>         (@call_value_internal_1<mode>): Likewise.
>         (@call_value_multiple_internal_1<mode>): Likewise.
>         * config/loongarch/loongarch.opt: Support medium.
>         * config/loongarch/predicates.md: Add processing about medium mode.
>         * doc/invoke.texi: Document for '-mcmodel=medium'.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.target/loongarch/func-call-medium-1.c: New test.
>         * gcc.target/loongarch/func-call-medium-2.c: New test.
>         * gcc.target/loongarch/func-call-medium-3.c: New test.
>         * gcc.target/loongarch/func-call-medium-4.c: New test.
>         * gcc.target/loongarch/func-call-medium-5.c: New test.
>         * gcc.target/loongarch/func-call-medium-6.c: New test.
>         * gcc.target/loongarch/func-call-medium-7.c: New test.
>         * gcc.target/loongarch/func-call-medium-8.c: New test.
>         * gcc.target/loongarch/tls-gd-noplt.c: Add compile parameter '-mexplicit-relocs'.
> ---
>  .../loongarch/genopts/loongarch-strings       |   1 +
>  gcc/config/loongarch/genopts/loongarch.opt.in |   3 +
>  gcc/config/loongarch/loongarch-def.c          |   1 +
>  gcc/config/loongarch/loongarch-def.h          |   7 +-
>  gcc/config/loongarch/loongarch-opts.cc        |  15 ++-
>  gcc/config/loongarch/loongarch-opts.h         |   1 +
>  gcc/config/loongarch/loongarch-str.h          |   1 +
>  gcc/config/loongarch/loongarch.cc             | 123 +++++++++++++----
>  gcc/config/loongarch/loongarch.md             | 125 +++++++++++++++++-
>  gcc/config/loongarch/loongarch.opt            |   3 +
>  gcc/config/loongarch/predicates.md            |  15 ++-
>  gcc/doc/invoke.texi                           |   3 +
>  .../gcc.target/loongarch/func-call-medium-1.c |  41 ++++++
>  .../gcc.target/loongarch/func-call-medium-2.c |  41 ++++++
>  .../gcc.target/loongarch/func-call-medium-3.c |  41 ++++++
>  .../gcc.target/loongarch/func-call-medium-4.c |  41 ++++++
>  .../gcc.target/loongarch/func-call-medium-5.c |  42 ++++++
>  .../gcc.target/loongarch/func-call-medium-6.c |  42 ++++++
>  .../gcc.target/loongarch/func-call-medium-7.c |  43 ++++++
>  .../gcc.target/loongarch/func-call-medium-8.c |  42 ++++++
>  .../gcc.target/loongarch/tls-gd-noplt.c       |   4 +-
>  21 files changed, 595 insertions(+), 40 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c
>  create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c
>  create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c
>  create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c
>  create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c
>  create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c
>  create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c
>  create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c
>
> diff --git a/gcc/config/loongarch/genopts/loongarch-strings b/gcc/config/loongarch/genopts/loongarch-strings
> index cb88ed56b68..44ebb7ab10b 100644
> --- a/gcc/config/loongarch/genopts/loongarch-strings
> +++ b/gcc/config/loongarch/genopts/loongarch-strings
> @@ -54,5 +54,6 @@ OPTSTR_CMODEL       cmodel
>  STR_CMODEL_NORMAL     normal
>  STR_CMODEL_TINY              tiny
>  STR_CMODEL_TS        tiny-static
> +STR_CMODEL_MEDIUM     medium
>  STR_CMODEL_LARGE      large
>  STR_CMODEL_EXTREME    extreme
> diff --git a/gcc/config/loongarch/genopts/loongarch.opt.in b/gcc/config/loongarch/genopts/loongarch.opt.in
> index a571b6b7524..ebdd9538d48 100644
> --- a/gcc/config/loongarch/genopts/loongarch.opt.in
> +++ b/gcc/config/loongarch/genopts/loongarch.opt.in
> @@ -172,6 +172,9 @@ Enum(cmodel) String(@@STR_CMODEL_TINY@@) Value(CMODEL_TINY)
>  EnumValue
>  Enum(cmodel) String(@@STR_CMODEL_TS@@) Value(CMODEL_TINY_STATIC)
>
> +EnumValue
> +Enum(cmodel) String(@@STR_CMODEL_MEDIUM@@) Value(CMODEL_MEDIUM)
> +
>  EnumValue
>  Enum(cmodel) String(@@STR_CMODEL_LARGE@@) Value(CMODEL_LARGE)
>
> diff --git a/gcc/config/loongarch/loongarch-def.c b/gcc/config/loongarch/loongarch-def.c
> index c8769b7d65e..cbf995d81b5 100644
> --- a/gcc/config/loongarch/loongarch-def.c
> +++ b/gcc/config/loongarch/loongarch-def.c
> @@ -152,6 +152,7 @@ loongarch_cmodel_strings[] = {
>    [CMODEL_NORMAL]        = STR_CMODEL_NORMAL,
>    [CMODEL_TINY]                  = STR_CMODEL_TINY,
>    [CMODEL_TINY_STATIC]   = STR_CMODEL_TS,
> +  [CMODEL_MEDIUM]        = STR_CMODEL_MEDIUM,
>    [CMODEL_LARGE]         = STR_CMODEL_LARGE,
>    [CMODEL_EXTREME]       = STR_CMODEL_EXTREME,
>  };
> diff --git a/gcc/config/loongarch/loongarch-def.h b/gcc/config/loongarch/loongarch-def.h
> index c2c35b6ba5c..b5985f07052 100644
> --- a/gcc/config/loongarch/loongarch-def.h
> +++ b/gcc/config/loongarch/loongarch-def.h
> @@ -82,9 +82,10 @@ extern const char* loongarch_cmodel_strings[];
>  #define CMODEL_NORMAL        0
>  #define CMODEL_TINY          1
>  #define CMODEL_TINY_STATIC    2
> -#define CMODEL_LARGE         3
> -#define CMODEL_EXTREME       4
> -#define N_CMODEL_TYPES       5
> +#define CMODEL_MEDIUM        3
> +#define CMODEL_LARGE         4
> +#define CMODEL_EXTREME       5
> +#define N_CMODEL_TYPES       6
>
>  /* enum switches */
>  /* The "SW_" codes represent command-line switches (options that
> diff --git a/gcc/config/loongarch/loongarch-opts.cc b/gcc/config/loongarch/loongarch-opts.cc
> index 2ae89f23443..e13eafb5882 100644
> --- a/gcc/config/loongarch/loongarch-opts.cc
> +++ b/gcc/config/loongarch/loongarch-opts.cc
> @@ -376,11 +376,24 @@ fallback:
>
>    /* 5.  Target code model */
>    t.cmodel = constrained.cmodel ? opt_cmodel : CMODEL_NORMAL;
> -  if (t.cmodel != CMODEL_NORMAL && t.cmodel != CMODEL_EXTREME)
> +
> +  switch (t.cmodel)
>      {
> +    case CMODEL_TINY:
> +    case CMODEL_TINY_STATIC:
> +    case CMODEL_LARGE:
>        warning (0, "%qs is not supported, now cmodel is set to %qs",
>                loongarch_cmodel_strings[t.cmodel], "normal");
>        t.cmodel = CMODEL_NORMAL;
> +      break;
> +
> +    case CMODEL_NORMAL:
> +    case CMODEL_MEDIUM:
> +    case CMODEL_EXTREME:
> +      break;
> +
> +    default:
> +      gcc_unreachable ();
>      }
>
>    /* Cleanup and return.  */
> diff --git a/gcc/config/loongarch/loongarch-opts.h b/gcc/config/loongarch/loongarch-opts.h
> index da24ecd2b50..3523a4cf78d 100644
> --- a/gcc/config/loongarch/loongarch-opts.h
> +++ b/gcc/config/loongarch/loongarch-opts.h
> @@ -46,6 +46,7 @@ loongarch_config_target (struct loongarch_target *target,
>  #define TARGET_CMODEL_NORMAL       (la_target.cmodel == CMODEL_NORMAL)
>  #define TARGET_CMODEL_TINY         (la_target.cmodel == CMODEL_TINY)
>  #define TARGET_CMODEL_TINY_STATIC   (la_target.cmodel == CMODEL_TINY_STATIC)
> +#define TARGET_CMODEL_MEDIUM       (la_target.cmodel == CMODEL_MEDIUM)
>  #define TARGET_CMODEL_LARGE        (la_target.cmodel == CMODEL_LARGE)
>  #define TARGET_CMODEL_EXTREME      (la_target.cmodel == CMODEL_EXTREME)
>
> diff --git a/gcc/config/loongarch/loongarch-str.h b/gcc/config/loongarch/loongarch-str.h
> index 0e8889b8c96..9f1b0989c82 100644
> --- a/gcc/config/loongarch/loongarch-str.h
> +++ b/gcc/config/loongarch/loongarch-str.h
> @@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.  If not see
>  #define STR_CMODEL_NORMAL "normal"
>  #define STR_CMODEL_TINY "tiny"
>  #define STR_CMODEL_TS "tiny-static"
> +#define STR_CMODEL_MEDIUM "medium"
>  #define STR_CMODEL_LARGE "large"
>  #define STR_CMODEL_EXTREME "extreme"
>
> diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc
> index 207ac2762c6..16fd4acc970 100644
> --- a/gcc/config/loongarch/loongarch.cc
> +++ b/gcc/config/loongarch/loongarch.cc
> @@ -2461,44 +2461,96 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0)
>      }
>
>    if (flag_plt)
> -    insn = emit_call_insn (gen_call_value_internal (v0,
> -                                                   loongarch_tls_symbol,
> -                                                   const0_rtx));
> -  else
>      {
> -      rtx dest = gen_reg_rtx (Pmode);
> -
> -      if (TARGET_CMODEL_EXTREME)
> +      switch (la_opt_cmodel)
>         {
> -         gcc_assert (TARGET_EXPLICIT_RELOCS);
> +       case CMODEL_NORMAL:
> +         insn = emit_call_insn (gen_call_value_internal (v0,
> +                                                         loongarch_tls_symbol,
> +                                                         const0_rtx));
> +         break;
>
> -         rtx tmp1 = gen_reg_rtx (Pmode);
> -         rtx high = gen_reg_rtx (Pmode);
> +       case CMODEL_MEDIUM:
> +           {
> +             rtx reg = gen_reg_rtx (Pmode);
> +             if (TARGET_EXPLICIT_RELOCS)
> +               {
> +                 emit_insn (gen_pcalau12i (Pmode, reg, loongarch_tls_symbol));
> +                 rtx call = gen_call_value_internal_1 (Pmode, v0, reg,
> +                                                       loongarch_tls_symbol,
> +                                                       const0_rtx);
> +                 insn = emit_call_insn (call);
> +               }
> +             else
> +               {
> +                 emit_move_insn (reg, loongarch_tls_symbol);
> +                 insn = emit_call_insn (gen_call_value_internal (v0,
> +                                                                 reg,
> +                                                                 const0_rtx));
> +               }
> +             break;
> +           }
>
> -         loongarch_emit_move (high,
> -                              gen_rtx_HIGH (Pmode, loongarch_tls_symbol));
> -         loongarch_emit_move (tmp1, gen_rtx_LO_SUM (Pmode,
> -                                                    gen_rtx_REG (Pmode, 0),
> -                                                    loongarch_tls_symbol));
> -         emit_insn (gen_lui_h_lo20 (tmp1, tmp1, loongarch_tls_symbol));
> -         emit_insn (gen_lui_h_hi12 (tmp1, tmp1, loongarch_tls_symbol));
> -         loongarch_emit_move (dest,
> -                              gen_rtx_MEM (Pmode,
> -                                           gen_rtx_PLUS (Pmode, high, tmp1)));
> +       /* code model extreme not support plt.  */
> +       case CMODEL_EXTREME:
> +       case CMODEL_LARGE:
> +       case CMODEL_TINY:
> +       case CMODEL_TINY_STATIC:
> +       default:
> +         gcc_unreachable ();
>         }
> -      else
> +    }
> +  else
> +    {
> +      rtx dest = gen_reg_rtx (Pmode);
> +
> +      switch (la_opt_cmodel)
>         {
> -         if (TARGET_EXPLICIT_RELOCS)
> +       case CMODEL_NORMAL:
> +       case CMODEL_MEDIUM:
> +           {
> +             if (TARGET_EXPLICIT_RELOCS)
> +               {
> +                 rtx high = gen_reg_rtx (Pmode);
> +                 loongarch_emit_move (high,
> +                                      gen_rtx_HIGH (Pmode,
> +                                                    loongarch_tls_symbol));
> +                 emit_insn (gen_ld_from_got (Pmode, dest, high,
> +                                             loongarch_tls_symbol));
> +               }
> +             else
> +               loongarch_emit_move (dest, loongarch_tls_symbol);
> +             break;
> +           }
> +
> +       case CMODEL_EXTREME:
>             {
> +             gcc_assert (TARGET_EXPLICIT_RELOCS);
> +
> +             rtx tmp1 = gen_reg_rtx (Pmode);
>               rtx high = gen_reg_rtx (Pmode);
> +
>               loongarch_emit_move (high,
>                                    gen_rtx_HIGH (Pmode, loongarch_tls_symbol));
> -             emit_insn (gen_ld_from_got (Pmode, dest, high,
> -                                         loongarch_tls_symbol));
> +             loongarch_emit_move (tmp1, gen_rtx_LO_SUM (Pmode,
> +                                                        gen_rtx_REG (Pmode, 0),
> +                                                        loongarch_tls_symbol));
> +             emit_insn (gen_lui_h_lo20 (tmp1, tmp1, loongarch_tls_symbol));
> +             emit_insn (gen_lui_h_hi12 (tmp1, tmp1, loongarch_tls_symbol));
> +             loongarch_emit_move (dest,
> +                                  gen_rtx_MEM (Pmode,
> +                                               gen_rtx_PLUS (Pmode,
> +                                                             high, tmp1)));
>             }
> -         else
> -           loongarch_emit_move (dest, loongarch_tls_symbol);
> +         break;
> +
> +       case CMODEL_LARGE:
> +       case CMODEL_TINY:
> +       case CMODEL_TINY_STATIC:
> +       default:
> +         gcc_unreachable ();
>         }
> +
>        insn = emit_call_insn (gen_call_value_internal (v0, dest, const0_rtx));
>      }
>
> @@ -2618,6 +2670,24 @@ loongarch_legitimize_call_address (rtx addr)
>        loongarch_emit_move (reg, addr);
>        return reg;
>      }
> +
> +  enum loongarch_symbol_type symbol_type = loongarch_classify_symbol (addr);
> +
> +  /* Split function call insn 'bl sym' or 'bl %plt(sym)' to :
> +     pcalau12i $rd, %pc_hi20(sym)
> +     jr $rd, %pc_lo12(sym).  */
> +
> +  if (TARGET_CMODEL_MEDIUM
> +      && TARGET_EXPLICIT_RELOCS
> +      && (SYMBOL_REF_P (addr) || LABEL_REF_P (addr))
> +      && (symbol_type == SYMBOL_PCREL
> +         || (symbol_type == SYMBOL_GOT_DISP && flag_plt)))
> +    {
> +      rtx reg = gen_reg_rtx (Pmode);
> +      emit_insn (gen_pcalau12i (Pmode, reg, addr));
> +      return gen_rtx_LO_SUM (Pmode, reg, addr);
> +    }
> +
>    return addr;
>  }
>
> @@ -5996,6 +6066,7 @@ loongarch_option_override_internal (struct gcc_options *opts)
>         break;
>
>        case CMODEL_TINY_STATIC:
> +      case CMODEL_MEDIUM:
>        case CMODEL_NORMAL:
>        case CMODEL_TINY:
>        case CMODEL_LARGE:
> diff --git a/gcc/config/loongarch/loongarch.md b/gcc/config/loongarch/loongarch.md
> index 8fc10444c2a..3787fd8230f 100644
> --- a/gcc/config/loongarch/loongarch.md
> +++ b/gcc/config/loongarch/loongarch.md
> @@ -59,11 +59,15 @@ (define_c_enum "unspec" [
>    UNSPEC_CRCC
>
>    UNSPEC_LOAD_FROM_GOT
> +  UNSPEC_PCALAU12I
>    UNSPEC_ORI_L_LO12
>    UNSPEC_LUI_L_HI20
>    UNSPEC_LUI_H_LO20
>    UNSPEC_LUI_H_HI12
>    UNSPEC_TLS_LOW
> +
> +  UNSPEC_SIBCALL_VALUE_MULTIPLE_INTERNAL_1
> +  UNSPEC_CALL_VALUE_MULTIPLE_INTERNAL_1
>  ])
>
>  (define_c_enum "unspecv" [
> @@ -1946,6 +1950,14 @@ (define_insn "@lui_l_hi20<mode>"
>    [(set_attr "type" "move")]
>  )
>
> +(define_insn "@pcalau12i<mode>"
> +  [(set (match_operand:P 0 "register_operand" "=j")
> +       (unspec:P [(match_operand:P 1 "symbolic_operand" "")]
> +       UNSPEC_PCALAU12I))]
> +  ""
> +  "pcalau12i\t%0,%%pc_hi20(%1)"
> +  [(set_attr "type" "move")])
> +
>  (define_insn "@ori_l_lo12<mode>"
>    [(set (match_operand:P 0 "register_operand" "=r")
>         (unspec:P [(match_operand:P 1 "register_operand" "r")
> @@ -2877,7 +2889,12 @@ (define_expand "sibcall"
>  {
>    rtx target = loongarch_legitimize_call_address (XEXP (operands[0], 0));
>
> -  emit_call_insn (gen_sibcall_internal (target, operands[1]));
> +  if (GET_CODE (target) == LO_SUM)
> +    emit_call_insn (gen_sibcall_internal_1 (Pmode, XEXP (target, 0),
> +                                           XEXP (target, 1),
> +                                           operands[1]));
> +  else
> +    emit_call_insn (gen_sibcall_internal (target, operands[1]));
>    DONE;
>  })
>
> @@ -2891,6 +2908,14 @@ (define_insn "sibcall_internal"
>     b\t%%plt(%0)"
>    [(set_attr "jirl" "indirect,direct,direct")])
>
> +(define_insn "@sibcall_internal_1<mode>"
> +  [(call (mem:P (lo_sum:P (match_operand:P 0 "register_operand" "j")
> +                         (match_operand:P 1 "symbolic_operand" "")))
> +        (match_operand 2 "" ""))]
> +  "SIBLING_CALL_P (insn) && TARGET_CMODEL_MEDIUM"
> +  "jirl\t$r0,%0,%%pc_lo12(%1)"
> +  [(set_attr "jirl" "indirect")])
> +
>  (define_expand "sibcall_value"
>    [(parallel [(set (match_operand 0 "")
>                    (call (match_operand 1 "")
> @@ -2906,7 +2931,14 @@ (define_expand "sibcall_value"
>        rtx arg1 = XEXP (XVECEXP (operands[0],0, 0), 0);
>        rtx arg2 = XEXP (XVECEXP (operands[0],0, 1), 0);
>
> -      emit_call_insn (gen_sibcall_value_multiple_internal (arg1, target,
> +      if (GET_CODE (target) == LO_SUM)
> +       emit_call_insn (gen_sibcall_value_multiple_internal_1 (Pmode, arg1,
> +                                                          XEXP (target, 0),
> +                                                          XEXP (target, 1),
> +                                                          operands[2],
> +                                                          arg2));
> +      else
> +       emit_call_insn (gen_sibcall_value_multiple_internal (arg1, target,
>                                                            operands[2],
>                                                            arg2));
>      }
> @@ -2916,7 +2948,13 @@ (define_expand "sibcall_value"
>        if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) == 1)
>         operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0);
>
> -      emit_call_insn (gen_sibcall_value_internal (operands[0], target,
> +      if (GET_CODE (target) == LO_SUM)
> +       emit_call_insn (gen_sibcall_value_internal_1 (Pmode, operands[0],
> +                                                 XEXP (target, 0),
> +                                                 XEXP (target, 1),
> +                                                 operands[2]));
> +      else
> +       emit_call_insn (gen_sibcall_value_internal (operands[0], target,
>                                                   operands[2]));
>      }
>    DONE;
> @@ -2933,6 +2971,15 @@ (define_insn "sibcall_value_internal"
>     b\t%%plt(%1)"
>    [(set_attr "jirl" "indirect,direct,direct")])
>
> +(define_insn "@sibcall_value_internal_1<mode>"
> +  [(set (match_operand 0 "register_operand" "")
> +       (call (mem:P (lo_sum:P (match_operand:P 1 "register_operand" "j")
> +                              (match_operand:P 2 "symbolic_operand" "")))
> +             (match_operand 3 "" "")))]
> +  "SIBLING_CALL_P (insn) && TARGET_CMODEL_MEDIUM"
> +  "jirl\t$r0,%1,%%pc_lo12(%2)"
> +  [(set_attr "jirl" "indirect")])
> +
>  (define_insn "sibcall_value_multiple_internal"
>    [(set (match_operand 0 "register_operand" "")
>         (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,b"))
> @@ -2947,6 +2994,21 @@ (define_insn "sibcall_value_multiple_internal"
>     b\t%%plt(%1)"
>    [(set_attr "jirl" "indirect,direct,direct")])
>
> +(define_insn "@sibcall_value_multiple_internal_1<mode>"
> +  [(set (match_operand 0 "register_operand" "")
> +       (call (mem:P (unspec:P [(match_operand:P 1 "register_operand" "j")
> +                               (match_operand:P 2 "symbolic_operand" "")]
> +                     UNSPEC_SIBCALL_VALUE_MULTIPLE_INTERNAL_1))
> +             (match_operand 3 "" "")))
> +   (set (match_operand 4 "register_operand" "")
> +       (call (mem:P (unspec:P [(match_dup 1)
> +                               (match_dup 2)]
> +                     UNSPEC_SIBCALL_VALUE_MULTIPLE_INTERNAL_1))
> +             (match_dup 3)))]
> +  "SIBLING_CALL_P (insn) && TARGET_CMODEL_MEDIUM"
> +  "jirl\t$r0,%1,%%pc_lo12(%2)"
> +  [(set_attr "jirl" "indirect")])
> +
>  (define_expand "call"
>    [(parallel [(call (match_operand 0 "")
>                     (match_operand 1 ""))
> @@ -2956,7 +3018,11 @@ (define_expand "call"
>  {
>    rtx target = loongarch_legitimize_call_address (XEXP (operands[0], 0));
>
> -  emit_call_insn (gen_call_internal (target, operands[1]));
> +  if (GET_CODE (target) == LO_SUM)
> +    emit_call_insn (gen_call_internal_1 (Pmode, XEXP (target, 0),
> +                                        XEXP (target, 1), operands[1]));
> +  else
> +    emit_call_insn (gen_call_internal (target, operands[1]));
>    DONE;
>  })
>
> @@ -2971,6 +3037,15 @@ (define_insn "call_internal"
>     bl\t%%plt(%0)"
>    [(set_attr "jirl" "indirect,direct,direct")])
>
> +(define_insn "@call_internal_1<mode>"
> +  [(call (mem:P (lo_sum:P (match_operand:P 0 "register_operand" "j")
> +                         (match_operand:P 1 "symbolic_operand" "")))
> +        (match_operand 2 "" ""))
> +   (clobber (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_CMODEL_MEDIUM"
> +  "jirl\t$r1,%0,%%pc_lo12(%1)"
> +  [(set_attr "jirl" "indirect")])
> +
>  (define_expand "call_value"
>    [(parallel [(set (match_operand 0 "")
>                    (call (match_operand 1 "")
> @@ -2985,7 +3060,13 @@ (define_expand "call_value"
>        rtx arg1 = XEXP (XVECEXP (operands[0], 0, 0), 0);
>        rtx arg2 = XEXP (XVECEXP (operands[0], 0, 1), 0);
>
> -      emit_call_insn (gen_call_value_multiple_internal (arg1, target,
> +      if (GET_CODE (target) == LO_SUM)
> +       emit_call_insn (gen_call_value_multiple_internal_1 (Pmode, arg1,
> +                                                           XEXP (target, 0),
> +                                                           XEXP (target, 1),
> +                                                           operands[2], arg2));
> +      else
> +       emit_call_insn (gen_call_value_multiple_internal (arg1, target,
>                                                         operands[2], arg2));
>      }
>     else
> @@ -2994,7 +3075,13 @@ (define_expand "call_value"
>        if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) == 1)
>             operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0);
>
> -      emit_call_insn (gen_call_value_internal (operands[0], target,
> +      if (GET_CODE (target) == LO_SUM)
> +       emit_call_insn (gen_call_value_internal_1 (Pmode, operands[0],
> +                                                  XEXP (target, 0),
> +                                                  XEXP (target, 1),
> +                                                  operands[2]));
> +      else
> +       emit_call_insn (gen_call_value_internal (operands[0], target,
>                                                operands[2]));
>      }
>    DONE;
> @@ -3012,6 +3099,16 @@ (define_insn "call_value_internal"
>     bl\t%%plt(%1)"
>    [(set_attr "jirl" "indirect,direct,direct")])
>
> +(define_insn "@call_value_internal_1<mode>"
> +  [(set (match_operand 0 "register_operand" "")
> +       (call (mem:P (lo_sum:P (match_operand:P 1 "register_operand" "j")
> +                              (match_operand:P 2 "symbolic_operand" "")))
> +             (match_operand 3 "" "")))
> +   (clobber (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_CMODEL_MEDIUM"
> +  "jirl\t$r1,%1,%%pc_lo12(%2)"
> +  [(set_attr "jirl" "indirect")])
> +
>  (define_insn "call_value_multiple_internal"
>    [(set (match_operand 0 "register_operand" "")
>         (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,b"))
> @@ -3027,6 +3124,22 @@ (define_insn "call_value_multiple_internal"
>     bl\t%%plt(%1)"
>    [(set_attr "jirl" "indirect,direct,direct")])
>
> +(define_insn "@call_value_multiple_internal_1<mode>"
> +  [(set (match_operand 0 "register_operand" "")
> +       (call (mem:P (unspec:P [(match_operand:P 1 "register_operand" "j")
> +                               (match_operand:P 2 "symbolic_operand" "")]
> +                     UNSPEC_CALL_VALUE_MULTIPLE_INTERNAL_1))
> +             (match_operand 3 "" "")))
> +   (set (match_operand 4 "register_operand" "")
> +       (call (mem:P (unspec:P [(match_dup 1)
> +                               (match_dup 2)]
> +                     UNSPEC_CALL_VALUE_MULTIPLE_INTERNAL_1))
> +             (match_dup 3)))
> +   (clobber (reg:SI RETURN_ADDR_REGNUM))]
> +  "TARGET_CMODEL_MEDIUM"
> +  "jirl\t$r1,%1,%%pc_lo12(%2)"
> +  [(set_attr "jirl" "indirect")])
> +
>
>  ;; Call subroutine returning any type.
>  (define_expand "untyped_call"
> diff --git a/gcc/config/loongarch/loongarch.opt b/gcc/config/loongarch/loongarch.opt
> index 9df7e187283..6395234218b 100644
> --- a/gcc/config/loongarch/loongarch.opt
> +++ b/gcc/config/loongarch/loongarch.opt
> @@ -179,6 +179,9 @@ Enum(cmodel) String(tiny) Value(CMODEL_TINY)
>  EnumValue
>  Enum(cmodel) String(tiny-static) Value(CMODEL_TINY_STATIC)
>
> +EnumValue
> +Enum(cmodel) String(medium) Value(CMODEL_MEDIUM)
> +
>  EnumValue
>  Enum(cmodel) String(large) Value(CMODEL_LARGE)
>
> diff --git a/gcc/config/loongarch/predicates.md b/gcc/config/loongarch/predicates.md
> index e38c6fbdd5f..8bd0c1376c9 100644
> --- a/gcc/config/loongarch/predicates.md
> +++ b/gcc/config/loongarch/predicates.md
> @@ -123,16 +123,27 @@ (define_predicate "const_call_insn_operand"
>    if (offset != const0_rtx)
>      return false;
>
> +  /* When compiling with '-mcmodel=medium -mexplicit-relocs'
> +     symbols are splited in loongarch_legitimize_call_address.
> +
> +     When compiling with '-mcmodel=medium -mno-explicit-relocs',
> +     first obtain the symbolic address or the address of the
> +     plt entry, and then perform an indirect jump, so return false.  */
> +
>    switch (symbol_type)
>      {
>      case SYMBOL_PCREL:
> -      if (TARGET_CMODEL_EXTREME)
> +      if (TARGET_CMODEL_EXTREME
> +         || (TARGET_CMODEL_MEDIUM && !TARGET_EXPLICIT_RELOCS))
>         return false;
>        else
>         return 1;
>
>      case SYMBOL_GOT_DISP:
> -      if (TARGET_CMODEL_EXTREME || !flag_plt)
> +      if (TARGET_CMODEL_EXTREME
> +         || !flag_plt
> +         || (flag_plt && TARGET_CMODEL_MEDIUM
> +             && !TARGET_EXPLICIT_RELOCS))
>         return false;
>        else
>         return 1;
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 8712bc25e3c..60702a52c8f 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -25084,6 +25084,9 @@ Set the code model to one of:
>  The text segment must be within 128MB addressing space.  The data segment must
>  be within 2GB addressing space.
>
> +@item medium
> +The text segment and data segment must be within 2GB addressing space.
> +
>  @item large (Not implemented yet)
>
>  @item extreme
> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c
> new file mode 100644
> index 00000000000..276d73e5ee8
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c
> @@ -0,0 +1,41 @@
> +/* { dg-do compile } */
> +/* { dg-options "-mabi=lp64d -O0 -fpic -fplt -mno-explicit-relocs -mcmodel=medium" } */
> +/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test1:.*la\.global\t.*f\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" } } */
> +
> +extern void g (void);
> +void
> +f (void)
> +{}
> +
> +static void
> +l (void)
> +{}
> +
> +void
> +test (void)
> +{
> +  g ();
> +}
> +
> +void
> +test1 (void)
> +{
> +  f ();
> +}
> +
> +void
> +test2 (void)
> +{
> +  l ();
> +}
> +
> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
> +
> +void
> +test3 (void)
> +{
> +  a = 10;
> +}
> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c
> new file mode 100644
> index 00000000000..237821c066b
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c
> @@ -0,0 +1,41 @@
> +/* { dg-do compile } */
> +/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt -mno-explicit-relocs -mcmodel=medium" } */
> +/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test1:.*la\.local\t.*f\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" } } */
> +
> +extern void g (void);
> +void
> +f (void)
> +{}
> +
> +static void
> +l (void)
> +{}
> +
> +void
> +test (void)
> +{
> +  g ();
> +}
> +
> +void
> +test1 (void)
> +{
> +  f ();
> +}
> +
> +void
> +test2 (void)
> +{
> +  l ();
> +}
> +
> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
> +
> +void
> +test3 (void)
> +{
> +  a = 10;
> +}
> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c
> new file mode 100644
> index 00000000000..9a6e16103bc
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c
> @@ -0,0 +1,41 @@
> +/* { dg-do compile } */
> +/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mno-explicit-relocs -mcmodel=medium" } */
> +/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test1:.*la\.global\t.*f\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" } } */
> +
> +extern void g (void);
> +void
> +f (void)
> +{}
> +
> +static void
> +l (void)
> +{}
> +
> +void
> +test (void)
> +{
> +  g ();
> +}
> +
> +void
> +test1 (void)
> +{
> +  f ();
> +}
> +
> +void
> +test2 (void)
> +{
> +  l ();
> +}
> +
> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
> +
> +void
> +test3 (void)
> +{
> +  a = 10;
> +}
> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c
> new file mode 100644
> index 00000000000..2577e345239
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c
> @@ -0,0 +1,41 @@
> +/* { dg-do compile } */
> +/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mno-explicit-relocs -mcmodel=medium" } */
> +/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test1:.*la\.local\t.*f\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" } } */
> +
> +extern void g (void);
> +void
> +f (void)
> +{}
> +
> +static void
> +l (void)
> +{}
> +
> +void
> +test (void)
> +{
> +  g ();
> +}
> +
> +void
> +test1 (void)
> +{
> +  f ();
> +}
> +
> +void
> +test2 (void)
> +{
> +  l ();
> +}
> +
> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
> +
> +void
> +test3 (void)
> +{
> +  a = 10;
> +}
> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c
> new file mode 100644
> index 00000000000..d70b6ea4663
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c
> @@ -0,0 +1,42 @@
> +/* { dg-do compile } */
> +/* { dg-options "-mabi=lp64d -O0 -fpic -fplt -mexplicit-relocs -mcmodel=medium" } */
> +/* { dg-final { scan-assembler "test:.*pcalau12i.*%pc_hi20\\(g\\)\n\tjirl.*pc_lo12\\(g\\)" } } */
> +/* { dg-final { scan-assembler "test1:.*pcalau12i.*%pc_hi20\\(f\\)\n\tjirl.*%pc_lo12\\(f\\)" } } */
> +/* { dg-final { scan-assembler "test2:.*pcalau12i.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */
> +/* { dg-final { scan-assembler "test3:.*pcalau12i.*%pc_hi20\\(__tls_get_addr\\)\n\t.*\n\tjirl.*%pc_lo12\\(__tls_get_addr\\)" } } */
> +
> +extern void g (void);
> +
> +void
> +f (void)
> +{}
> +
> +static void
> +l (void)
> +{}
> +
> +void
> +test (void)
> +{
> +  g ();
> +}
> +
> +void
> +test1 (void)
> +{
> +  f ();
> +}
> +
> +void
> +test2 (void)
> +{
> +  l ();
> +}
> +
> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
> +
> +void
> +test3 (void)
> +{
> +  a = 10;
> +}
> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c
> new file mode 100644
> index 00000000000..f963a99441a
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c
> @@ -0,0 +1,42 @@
> +/* { dg-do compile } */
> +/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt -mexplicit-relocs -mcmodel=medium" } */
> +/* { dg-final { scan-assembler "test:.*pcalau12i.*%pc_hi20\\(g\\)\n\tjirl.*pc_lo12\\(g\\)" } } */
> +/* { dg-final { scan-assembler "test1:.*pcalau12i.*%pc_hi20\\(f\\)\n\tjirl.*%pc_lo12\\(f\\)" } } */
> +/* { dg-final { scan-assembler "test2:.*pcalau12i.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */
> +/* { dg-final { scan-assembler "test3:.*pcalau12i.*%pc_hi20\\(__tls_get_addr\\)\n\t.*\n\tjirl.*%pc_lo12\\(__tls_get_addr\\)" } } */
> +
> +extern void g (void);
> +
> +void
> +f (void)
> +{}
> +
> +static void
> +l (void)
> +{}
> +
> +void
> +test (void)
> +{
> +  g ();
> +}
> +
> +void
> +test1 (void)
> +{
> +  f ();
> +}
> +
> +void
> +test2 (void)
> +{
> +  l ();
> +}
> +
> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
> +
> +void
> +test3 (void)
> +{
> +  a = 10;
> +}
> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c
> new file mode 100644
> index 00000000000..f2818b2da76
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c
> @@ -0,0 +1,43 @@
> +/* { dg-do compile } */
> +/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mexplicit-relocs -mcmodel=medium" } */
> +/* { dg-final { scan-assembler "test:.*pcalau12i\t.*%got_pc_hi20\\(g\\)\n\tld\.d\t.*%got_pc_lo12\\(g\\)\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test1:.*pcalau12i\t.*%got_pc_hi20\\(f\\)\n\tld\.d\t.*%got_pc_lo12\\(f\\)\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test2:.*pcalau12i\t.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */
> +/* { dg-final { scan-assembler "test3:.*pcalau12i.*%got_pc_hi20\\(__tls_get_addr\\)\n\tld\.d.*%got_pc_lo12\\(__tls_get_addr\\)" } } */
> +
> +
> +extern void g (void);
> +
> +void
> +f (void)
> +{}
> +
> +static void
> +l (void)
> +{}
> +
> +void
> +test (void)
> +{
> +  g ();
> +}
> +
> +void
> +test1 (void)
> +{
> +  f ();
> +}
> +
> +void
> +test2 (void)
> +{
> +  l ();
> +}
> +
> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
> +
> +void
> +test3 (void)
> +{
> +  a = 10;
> +}
> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c
> new file mode 100644
> index 00000000000..7fa873d84bb
> --- /dev/null
> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c
> @@ -0,0 +1,42 @@
> +/* { dg-do compile } */
> +/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mexplicit-relocs -mcmodel=medium" } */
> +/* { dg-final { scan-assembler "test:.*pcalau12i\t.*%got_pc_hi20\\(g\\)\n\tld\.d\t.*%got_pc_lo12\\(g\\)\n\tjirl" } } */
> +/* { dg-final { scan-assembler "test1:.*pcalau12i\t.*%pc_hi20\\(f\\)\n\tjirl.*%pc_lo12\\(f\\)" } } */
> +/* { dg-final { scan-assembler "test2:.*pcalau12i\t.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */
> +/* { dg-final { scan-assembler "test3:.*pcalau12i.*%got_pc_hi20\\(__tls_get_addr\\)\n\tld\.d.*%got_pc_lo12\\(__tls_get_addr\\)" } } */
> +
> +extern void g (void);
> +
> +void
> +f (void)
> +{}
> +
> +static void
> +l (void)
> +{}
> +
> +void
> +test (void)
> +{
> +  g ();
> +}
> +
> +void
> +test1 (void)
> +{
> +  f ();
> +}
> +
> +void
> +test2 (void)
> +{
> +  l ();
> +}
> +
> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
> +
> +void
> +test3 (void)
> +{
> +  a = 10;
> +}
> diff --git a/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c b/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c
> index 32a0acf9b18..375663286f0 100644
> --- a/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c
> +++ b/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c
> @@ -1,6 +1,6 @@
>  /* { dg-do compile } */
> -/* { dg-options "-O2 -fno-plt -mcmodel=normal" } */
> -/* { dg-final { scan-assembler "pcalau12i\t.*%got_pc_hi20\\(__tls_get_addr\\)" } } */
> +/* { dg-options "-O0 -fno-plt -mcmodel=normal -mexplicit-relocs" } */
> +/* { dg-final { scan-assembler "pcalau12i\t.*%got_pc_hi20\\(__tls_get_addr\\)\n\tld\.d.*%got_pc_lo12\\(__tls_get_addr\\)" } } */
>
>  __attribute__ ((tls_model ("global-dynamic"))) __thread int a;
>
> --
> 2.31.1
>
  
Lulu Cheng Aug. 24, 2022, 6:58 a.m. UTC | #2
Pushed to r13-2165.

在 2022/8/20 下午5:18, Huacai Chen 写道:
> Hi, Lulu,
>
> I think there is a typo in your subject line.
>
> Huacai
>
> On Sat, Aug 20, 2022 at 5:05 PM Lulu Cheng <chenglulu@loongson.cn> wrote:
>> The function jump instruction in normal mode is 'bl',
>> so the scope of the function jump is +-128MB.
>>
>> Now we've added support for 'medium' mode, this mode is
>> to complete the function jump through two instructions:
>>          pcalau12i + jirl
>> So in this mode the function jump range is increased to +-2GB.
>>
>> Compared with 'normal' mode, 'medium' mode only affects the
>> jump range of functions.
>>
>> gcc/ChangeLog:
>>
>>          * config/loongarch/genopts/loongarch-strings: Support code model medium.
>>          * config/loongarch/genopts/loongarch.opt.in: Likewise.
>>          * config/loongarch/loongarch-def.c: Likewise.
>>          * config/loongarch/loongarch-def.h (CMODEL_LARGE): Likewise.
>>          (CMODEL_EXTREME): Likewise.
>>          (N_CMODEL_TYPES): Likewise.
>>          (CMODEL_MEDIUM): Likewise.
>>          * config/loongarch/loongarch-opts.cc: Likewise.
>>          * config/loongarch/loongarch-opts.h (TARGET_CMODEL_MEDIUM): Likewise.
>>          * config/loongarch/loongarch-str.h (STR_CMODEL_MEDIUM): Likewise.
>>          * config/loongarch/loongarch.cc (loongarch_call_tls_get_addr):
>>          Tls symbol Loading support medium mode.
>>          (loongarch_legitimize_call_address): When medium mode, make a symbolic
>>          jump with two instructions.
>>          (loongarch_option_override_internal): Support medium.
>>          * config/loongarch/loongarch.md (@pcalau12i<mode>): New template.
>>          (@sibcall_internal_1<mode>): New function call templates added to support
>>          medium mode.
>>          (@sibcall_value_internal_1<mode>): Likewise.
>>          (@sibcall_value_multiple_internal_1<mode>): Likewise.
>>          (@call_internal_1<mode>): Likewise.
>>          (@call_value_internal_1<mode>): Likewise.
>>          (@call_value_multiple_internal_1<mode>): Likewise.
>>          * config/loongarch/loongarch.opt: Support medium.
>>          * config/loongarch/predicates.md: Add processing about medium mode.
>>          * doc/invoke.texi: Document for '-mcmodel=medium'.
>>
>> gcc/testsuite/ChangeLog:
>>
>>          * gcc.target/loongarch/func-call-medium-1.c: New test.
>>          * gcc.target/loongarch/func-call-medium-2.c: New test.
>>          * gcc.target/loongarch/func-call-medium-3.c: New test.
>>          * gcc.target/loongarch/func-call-medium-4.c: New test.
>>          * gcc.target/loongarch/func-call-medium-5.c: New test.
>>          * gcc.target/loongarch/func-call-medium-6.c: New test.
>>          * gcc.target/loongarch/func-call-medium-7.c: New test.
>>          * gcc.target/loongarch/func-call-medium-8.c: New test.
>>          * gcc.target/loongarch/tls-gd-noplt.c: Add compile parameter '-mexplicit-relocs'.
>> ---
>>   .../loongarch/genopts/loongarch-strings       |   1 +
>>   gcc/config/loongarch/genopts/loongarch.opt.in |   3 +
>>   gcc/config/loongarch/loongarch-def.c          |   1 +
>>   gcc/config/loongarch/loongarch-def.h          |   7 +-
>>   gcc/config/loongarch/loongarch-opts.cc        |  15 ++-
>>   gcc/config/loongarch/loongarch-opts.h         |   1 +
>>   gcc/config/loongarch/loongarch-str.h          |   1 +
>>   gcc/config/loongarch/loongarch.cc             | 123 +++++++++++++----
>>   gcc/config/loongarch/loongarch.md             | 125 +++++++++++++++++-
>>   gcc/config/loongarch/loongarch.opt            |   3 +
>>   gcc/config/loongarch/predicates.md            |  15 ++-
>>   gcc/doc/invoke.texi                           |   3 +
>>   .../gcc.target/loongarch/func-call-medium-1.c |  41 ++++++
>>   .../gcc.target/loongarch/func-call-medium-2.c |  41 ++++++
>>   .../gcc.target/loongarch/func-call-medium-3.c |  41 ++++++
>>   .../gcc.target/loongarch/func-call-medium-4.c |  41 ++++++
>>   .../gcc.target/loongarch/func-call-medium-5.c |  42 ++++++
>>   .../gcc.target/loongarch/func-call-medium-6.c |  42 ++++++
>>   .../gcc.target/loongarch/func-call-medium-7.c |  43 ++++++
>>   .../gcc.target/loongarch/func-call-medium-8.c |  42 ++++++
>>   .../gcc.target/loongarch/tls-gd-noplt.c       |   4 +-
>>   21 files changed, 595 insertions(+), 40 deletions(-)
>>   create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c
>>   create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c
>>   create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c
>>   create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c
>>   create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c
>>   create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c
>>   create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c
>>   create mode 100644 gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c
>>
>> diff --git a/gcc/config/loongarch/genopts/loongarch-strings b/gcc/config/loongarch/genopts/loongarch-strings
>> index cb88ed56b68..44ebb7ab10b 100644
>> --- a/gcc/config/loongarch/genopts/loongarch-strings
>> +++ b/gcc/config/loongarch/genopts/loongarch-strings
>> @@ -54,5 +54,6 @@ OPTSTR_CMODEL       cmodel
>>   STR_CMODEL_NORMAL     normal
>>   STR_CMODEL_TINY              tiny
>>   STR_CMODEL_TS        tiny-static
>> +STR_CMODEL_MEDIUM     medium
>>   STR_CMODEL_LARGE      large
>>   STR_CMODEL_EXTREME    extreme
>> diff --git a/gcc/config/loongarch/genopts/loongarch.opt.in b/gcc/config/loongarch/genopts/loongarch.opt.in
>> index a571b6b7524..ebdd9538d48 100644
>> --- a/gcc/config/loongarch/genopts/loongarch.opt.in
>> +++ b/gcc/config/loongarch/genopts/loongarch.opt.in
>> @@ -172,6 +172,9 @@ Enum(cmodel) String(@@STR_CMODEL_TINY@@) Value(CMODEL_TINY)
>>   EnumValue
>>   Enum(cmodel) String(@@STR_CMODEL_TS@@) Value(CMODEL_TINY_STATIC)
>>
>> +EnumValue
>> +Enum(cmodel) String(@@STR_CMODEL_MEDIUM@@) Value(CMODEL_MEDIUM)
>> +
>>   EnumValue
>>   Enum(cmodel) String(@@STR_CMODEL_LARGE@@) Value(CMODEL_LARGE)
>>
>> diff --git a/gcc/config/loongarch/loongarch-def.c b/gcc/config/loongarch/loongarch-def.c
>> index c8769b7d65e..cbf995d81b5 100644
>> --- a/gcc/config/loongarch/loongarch-def.c
>> +++ b/gcc/config/loongarch/loongarch-def.c
>> @@ -152,6 +152,7 @@ loongarch_cmodel_strings[] = {
>>     [CMODEL_NORMAL]        = STR_CMODEL_NORMAL,
>>     [CMODEL_TINY]                  = STR_CMODEL_TINY,
>>     [CMODEL_TINY_STATIC]   = STR_CMODEL_TS,
>> +  [CMODEL_MEDIUM]        = STR_CMODEL_MEDIUM,
>>     [CMODEL_LARGE]         = STR_CMODEL_LARGE,
>>     [CMODEL_EXTREME]       = STR_CMODEL_EXTREME,
>>   };
>> diff --git a/gcc/config/loongarch/loongarch-def.h b/gcc/config/loongarch/loongarch-def.h
>> index c2c35b6ba5c..b5985f07052 100644
>> --- a/gcc/config/loongarch/loongarch-def.h
>> +++ b/gcc/config/loongarch/loongarch-def.h
>> @@ -82,9 +82,10 @@ extern const char* loongarch_cmodel_strings[];
>>   #define CMODEL_NORMAL        0
>>   #define CMODEL_TINY          1
>>   #define CMODEL_TINY_STATIC    2
>> -#define CMODEL_LARGE         3
>> -#define CMODEL_EXTREME       4
>> -#define N_CMODEL_TYPES       5
>> +#define CMODEL_MEDIUM        3
>> +#define CMODEL_LARGE         4
>> +#define CMODEL_EXTREME       5
>> +#define N_CMODEL_TYPES       6
>>
>>   /* enum switches */
>>   /* The "SW_" codes represent command-line switches (options that
>> diff --git a/gcc/config/loongarch/loongarch-opts.cc b/gcc/config/loongarch/loongarch-opts.cc
>> index 2ae89f23443..e13eafb5882 100644
>> --- a/gcc/config/loongarch/loongarch-opts.cc
>> +++ b/gcc/config/loongarch/loongarch-opts.cc
>> @@ -376,11 +376,24 @@ fallback:
>>
>>     /* 5.  Target code model */
>>     t.cmodel = constrained.cmodel ? opt_cmodel : CMODEL_NORMAL;
>> -  if (t.cmodel != CMODEL_NORMAL && t.cmodel != CMODEL_EXTREME)
>> +
>> +  switch (t.cmodel)
>>       {
>> +    case CMODEL_TINY:
>> +    case CMODEL_TINY_STATIC:
>> +    case CMODEL_LARGE:
>>         warning (0, "%qs is not supported, now cmodel is set to %qs",
>>                 loongarch_cmodel_strings[t.cmodel], "normal");
>>         t.cmodel = CMODEL_NORMAL;
>> +      break;
>> +
>> +    case CMODEL_NORMAL:
>> +    case CMODEL_MEDIUM:
>> +    case CMODEL_EXTREME:
>> +      break;
>> +
>> +    default:
>> +      gcc_unreachable ();
>>       }
>>
>>     /* Cleanup and return.  */
>> diff --git a/gcc/config/loongarch/loongarch-opts.h b/gcc/config/loongarch/loongarch-opts.h
>> index da24ecd2b50..3523a4cf78d 100644
>> --- a/gcc/config/loongarch/loongarch-opts.h
>> +++ b/gcc/config/loongarch/loongarch-opts.h
>> @@ -46,6 +46,7 @@ loongarch_config_target (struct loongarch_target *target,
>>   #define TARGET_CMODEL_NORMAL       (la_target.cmodel == CMODEL_NORMAL)
>>   #define TARGET_CMODEL_TINY         (la_target.cmodel == CMODEL_TINY)
>>   #define TARGET_CMODEL_TINY_STATIC   (la_target.cmodel == CMODEL_TINY_STATIC)
>> +#define TARGET_CMODEL_MEDIUM       (la_target.cmodel == CMODEL_MEDIUM)
>>   #define TARGET_CMODEL_LARGE        (la_target.cmodel == CMODEL_LARGE)
>>   #define TARGET_CMODEL_EXTREME      (la_target.cmodel == CMODEL_EXTREME)
>>
>> diff --git a/gcc/config/loongarch/loongarch-str.h b/gcc/config/loongarch/loongarch-str.h
>> index 0e8889b8c96..9f1b0989c82 100644
>> --- a/gcc/config/loongarch/loongarch-str.h
>> +++ b/gcc/config/loongarch/loongarch-str.h
>> @@ -53,6 +53,7 @@ along with GCC; see the file COPYING3.  If not see
>>   #define STR_CMODEL_NORMAL "normal"
>>   #define STR_CMODEL_TINY "tiny"
>>   #define STR_CMODEL_TS "tiny-static"
>> +#define STR_CMODEL_MEDIUM "medium"
>>   #define STR_CMODEL_LARGE "large"
>>   #define STR_CMODEL_EXTREME "extreme"
>>
>> diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc
>> index 207ac2762c6..16fd4acc970 100644
>> --- a/gcc/config/loongarch/loongarch.cc
>> +++ b/gcc/config/loongarch/loongarch.cc
>> @@ -2461,44 +2461,96 @@ loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0)
>>       }
>>
>>     if (flag_plt)
>> -    insn = emit_call_insn (gen_call_value_internal (v0,
>> -                                                   loongarch_tls_symbol,
>> -                                                   const0_rtx));
>> -  else
>>       {
>> -      rtx dest = gen_reg_rtx (Pmode);
>> -
>> -      if (TARGET_CMODEL_EXTREME)
>> +      switch (la_opt_cmodel)
>>          {
>> -         gcc_assert (TARGET_EXPLICIT_RELOCS);
>> +       case CMODEL_NORMAL:
>> +         insn = emit_call_insn (gen_call_value_internal (v0,
>> +                                                         loongarch_tls_symbol,
>> +                                                         const0_rtx));
>> +         break;
>>
>> -         rtx tmp1 = gen_reg_rtx (Pmode);
>> -         rtx high = gen_reg_rtx (Pmode);
>> +       case CMODEL_MEDIUM:
>> +           {
>> +             rtx reg = gen_reg_rtx (Pmode);
>> +             if (TARGET_EXPLICIT_RELOCS)
>> +               {
>> +                 emit_insn (gen_pcalau12i (Pmode, reg, loongarch_tls_symbol));
>> +                 rtx call = gen_call_value_internal_1 (Pmode, v0, reg,
>> +                                                       loongarch_tls_symbol,
>> +                                                       const0_rtx);
>> +                 insn = emit_call_insn (call);
>> +               }
>> +             else
>> +               {
>> +                 emit_move_insn (reg, loongarch_tls_symbol);
>> +                 insn = emit_call_insn (gen_call_value_internal (v0,
>> +                                                                 reg,
>> +                                                                 const0_rtx));
>> +               }
>> +             break;
>> +           }
>>
>> -         loongarch_emit_move (high,
>> -                              gen_rtx_HIGH (Pmode, loongarch_tls_symbol));
>> -         loongarch_emit_move (tmp1, gen_rtx_LO_SUM (Pmode,
>> -                                                    gen_rtx_REG (Pmode, 0),
>> -                                                    loongarch_tls_symbol));
>> -         emit_insn (gen_lui_h_lo20 (tmp1, tmp1, loongarch_tls_symbol));
>> -         emit_insn (gen_lui_h_hi12 (tmp1, tmp1, loongarch_tls_symbol));
>> -         loongarch_emit_move (dest,
>> -                              gen_rtx_MEM (Pmode,
>> -                                           gen_rtx_PLUS (Pmode, high, tmp1)));
>> +       /* code model extreme not support plt.  */
>> +       case CMODEL_EXTREME:
>> +       case CMODEL_LARGE:
>> +       case CMODEL_TINY:
>> +       case CMODEL_TINY_STATIC:
>> +       default:
>> +         gcc_unreachable ();
>>          }
>> -      else
>> +    }
>> +  else
>> +    {
>> +      rtx dest = gen_reg_rtx (Pmode);
>> +
>> +      switch (la_opt_cmodel)
>>          {
>> -         if (TARGET_EXPLICIT_RELOCS)
>> +       case CMODEL_NORMAL:
>> +       case CMODEL_MEDIUM:
>> +           {
>> +             if (TARGET_EXPLICIT_RELOCS)
>> +               {
>> +                 rtx high = gen_reg_rtx (Pmode);
>> +                 loongarch_emit_move (high,
>> +                                      gen_rtx_HIGH (Pmode,
>> +                                                    loongarch_tls_symbol));
>> +                 emit_insn (gen_ld_from_got (Pmode, dest, high,
>> +                                             loongarch_tls_symbol));
>> +               }
>> +             else
>> +               loongarch_emit_move (dest, loongarch_tls_symbol);
>> +             break;
>> +           }
>> +
>> +       case CMODEL_EXTREME:
>>              {
>> +             gcc_assert (TARGET_EXPLICIT_RELOCS);
>> +
>> +             rtx tmp1 = gen_reg_rtx (Pmode);
>>                rtx high = gen_reg_rtx (Pmode);
>> +
>>                loongarch_emit_move (high,
>>                                     gen_rtx_HIGH (Pmode, loongarch_tls_symbol));
>> -             emit_insn (gen_ld_from_got (Pmode, dest, high,
>> -                                         loongarch_tls_symbol));
>> +             loongarch_emit_move (tmp1, gen_rtx_LO_SUM (Pmode,
>> +                                                        gen_rtx_REG (Pmode, 0),
>> +                                                        loongarch_tls_symbol));
>> +             emit_insn (gen_lui_h_lo20 (tmp1, tmp1, loongarch_tls_symbol));
>> +             emit_insn (gen_lui_h_hi12 (tmp1, tmp1, loongarch_tls_symbol));
>> +             loongarch_emit_move (dest,
>> +                                  gen_rtx_MEM (Pmode,
>> +                                               gen_rtx_PLUS (Pmode,
>> +                                                             high, tmp1)));
>>              }
>> -         else
>> -           loongarch_emit_move (dest, loongarch_tls_symbol);
>> +         break;
>> +
>> +       case CMODEL_LARGE:
>> +       case CMODEL_TINY:
>> +       case CMODEL_TINY_STATIC:
>> +       default:
>> +         gcc_unreachable ();
>>          }
>> +
>>         insn = emit_call_insn (gen_call_value_internal (v0, dest, const0_rtx));
>>       }
>>
>> @@ -2618,6 +2670,24 @@ loongarch_legitimize_call_address (rtx addr)
>>         loongarch_emit_move (reg, addr);
>>         return reg;
>>       }
>> +
>> +  enum loongarch_symbol_type symbol_type = loongarch_classify_symbol (addr);
>> +
>> +  /* Split function call insn 'bl sym' or 'bl %plt(sym)' to :
>> +     pcalau12i $rd, %pc_hi20(sym)
>> +     jr $rd, %pc_lo12(sym).  */
>> +
>> +  if (TARGET_CMODEL_MEDIUM
>> +      && TARGET_EXPLICIT_RELOCS
>> +      && (SYMBOL_REF_P (addr) || LABEL_REF_P (addr))
>> +      && (symbol_type == SYMBOL_PCREL
>> +         || (symbol_type == SYMBOL_GOT_DISP && flag_plt)))
>> +    {
>> +      rtx reg = gen_reg_rtx (Pmode);
>> +      emit_insn (gen_pcalau12i (Pmode, reg, addr));
>> +      return gen_rtx_LO_SUM (Pmode, reg, addr);
>> +    }
>> +
>>     return addr;
>>   }
>>
>> @@ -5996,6 +6066,7 @@ loongarch_option_override_internal (struct gcc_options *opts)
>>          break;
>>
>>         case CMODEL_TINY_STATIC:
>> +      case CMODEL_MEDIUM:
>>         case CMODEL_NORMAL:
>>         case CMODEL_TINY:
>>         case CMODEL_LARGE:
>> diff --git a/gcc/config/loongarch/loongarch.md b/gcc/config/loongarch/loongarch.md
>> index 8fc10444c2a..3787fd8230f 100644
>> --- a/gcc/config/loongarch/loongarch.md
>> +++ b/gcc/config/loongarch/loongarch.md
>> @@ -59,11 +59,15 @@ (define_c_enum "unspec" [
>>     UNSPEC_CRCC
>>
>>     UNSPEC_LOAD_FROM_GOT
>> +  UNSPEC_PCALAU12I
>>     UNSPEC_ORI_L_LO12
>>     UNSPEC_LUI_L_HI20
>>     UNSPEC_LUI_H_LO20
>>     UNSPEC_LUI_H_HI12
>>     UNSPEC_TLS_LOW
>> +
>> +  UNSPEC_SIBCALL_VALUE_MULTIPLE_INTERNAL_1
>> +  UNSPEC_CALL_VALUE_MULTIPLE_INTERNAL_1
>>   ])
>>
>>   (define_c_enum "unspecv" [
>> @@ -1946,6 +1950,14 @@ (define_insn "@lui_l_hi20<mode>"
>>     [(set_attr "type" "move")]
>>   )
>>
>> +(define_insn "@pcalau12i<mode>"
>> +  [(set (match_operand:P 0 "register_operand" "=j")
>> +       (unspec:P [(match_operand:P 1 "symbolic_operand" "")]
>> +       UNSPEC_PCALAU12I))]
>> +  ""
>> +  "pcalau12i\t%0,%%pc_hi20(%1)"
>> +  [(set_attr "type" "move")])
>> +
>>   (define_insn "@ori_l_lo12<mode>"
>>     [(set (match_operand:P 0 "register_operand" "=r")
>>          (unspec:P [(match_operand:P 1 "register_operand" "r")
>> @@ -2877,7 +2889,12 @@ (define_expand "sibcall"
>>   {
>>     rtx target = loongarch_legitimize_call_address (XEXP (operands[0], 0));
>>
>> -  emit_call_insn (gen_sibcall_internal (target, operands[1]));
>> +  if (GET_CODE (target) == LO_SUM)
>> +    emit_call_insn (gen_sibcall_internal_1 (Pmode, XEXP (target, 0),
>> +                                           XEXP (target, 1),
>> +                                           operands[1]));
>> +  else
>> +    emit_call_insn (gen_sibcall_internal (target, operands[1]));
>>     DONE;
>>   })
>>
>> @@ -2891,6 +2908,14 @@ (define_insn "sibcall_internal"
>>      b\t%%plt(%0)"
>>     [(set_attr "jirl" "indirect,direct,direct")])
>>
>> +(define_insn "@sibcall_internal_1<mode>"
>> +  [(call (mem:P (lo_sum:P (match_operand:P 0 "register_operand" "j")
>> +                         (match_operand:P 1 "symbolic_operand" "")))
>> +        (match_operand 2 "" ""))]
>> +  "SIBLING_CALL_P (insn) && TARGET_CMODEL_MEDIUM"
>> +  "jirl\t$r0,%0,%%pc_lo12(%1)"
>> +  [(set_attr "jirl" "indirect")])
>> +
>>   (define_expand "sibcall_value"
>>     [(parallel [(set (match_operand 0 "")
>>                     (call (match_operand 1 "")
>> @@ -2906,7 +2931,14 @@ (define_expand "sibcall_value"
>>         rtx arg1 = XEXP (XVECEXP (operands[0],0, 0), 0);
>>         rtx arg2 = XEXP (XVECEXP (operands[0],0, 1), 0);
>>
>> -      emit_call_insn (gen_sibcall_value_multiple_internal (arg1, target,
>> +      if (GET_CODE (target) == LO_SUM)
>> +       emit_call_insn (gen_sibcall_value_multiple_internal_1 (Pmode, arg1,
>> +                                                          XEXP (target, 0),
>> +                                                          XEXP (target, 1),
>> +                                                          operands[2],
>> +                                                          arg2));
>> +      else
>> +       emit_call_insn (gen_sibcall_value_multiple_internal (arg1, target,
>>                                                             operands[2],
>>                                                             arg2));
>>       }
>> @@ -2916,7 +2948,13 @@ (define_expand "sibcall_value"
>>         if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) == 1)
>>          operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0);
>>
>> -      emit_call_insn (gen_sibcall_value_internal (operands[0], target,
>> +      if (GET_CODE (target) == LO_SUM)
>> +       emit_call_insn (gen_sibcall_value_internal_1 (Pmode, operands[0],
>> +                                                 XEXP (target, 0),
>> +                                                 XEXP (target, 1),
>> +                                                 operands[2]));
>> +      else
>> +       emit_call_insn (gen_sibcall_value_internal (operands[0], target,
>>                                                    operands[2]));
>>       }
>>     DONE;
>> @@ -2933,6 +2971,15 @@ (define_insn "sibcall_value_internal"
>>      b\t%%plt(%1)"
>>     [(set_attr "jirl" "indirect,direct,direct")])
>>
>> +(define_insn "@sibcall_value_internal_1<mode>"
>> +  [(set (match_operand 0 "register_operand" "")
>> +       (call (mem:P (lo_sum:P (match_operand:P 1 "register_operand" "j")
>> +                              (match_operand:P 2 "symbolic_operand" "")))
>> +             (match_operand 3 "" "")))]
>> +  "SIBLING_CALL_P (insn) && TARGET_CMODEL_MEDIUM"
>> +  "jirl\t$r0,%1,%%pc_lo12(%2)"
>> +  [(set_attr "jirl" "indirect")])
>> +
>>   (define_insn "sibcall_value_multiple_internal"
>>     [(set (match_operand 0 "register_operand" "")
>>          (call (mem:SI (match_operand 1 "call_insn_operand" "j,c,b"))
>> @@ -2947,6 +2994,21 @@ (define_insn "sibcall_value_multiple_internal"
>>      b\t%%plt(%1)"
>>     [(set_attr "jirl" "indirect,direct,direct")])
>>
>> +(define_insn "@sibcall_value_multiple_internal_1<mode>"
>> +  [(set (match_operand 0 "register_operand" "")
>> +       (call (mem:P (unspec:P [(match_operand:P 1 "register_operand" "j")
>> +                               (match_operand:P 2 "symbolic_operand" "")]
>> +                     UNSPEC_SIBCALL_VALUE_MULTIPLE_INTERNAL_1))
>> +             (match_operand 3 "" "")))
>> +   (set (match_operand 4 "register_operand" "")
>> +       (call (mem:P (unspec:P [(match_dup 1)
>> +                               (match_dup 2)]
>> +                     UNSPEC_SIBCALL_VALUE_MULTIPLE_INTERNAL_1))
>> +             (match_dup 3)))]
>> +  "SIBLING_CALL_P (insn) && TARGET_CMODEL_MEDIUM"
>> +  "jirl\t$r0,%1,%%pc_lo12(%2)"
>> +  [(set_attr "jirl" "indirect")])
>> +
>>   (define_expand "call"
>>     [(parallel [(call (match_operand 0 "")
>>                      (match_operand 1 ""))
>> @@ -2956,7 +3018,11 @@ (define_expand "call"
>>   {
>>     rtx target = loongarch_legitimize_call_address (XEXP (operands[0], 0));
>>
>> -  emit_call_insn (gen_call_internal (target, operands[1]));
>> +  if (GET_CODE (target) == LO_SUM)
>> +    emit_call_insn (gen_call_internal_1 (Pmode, XEXP (target, 0),
>> +                                        XEXP (target, 1), operands[1]));
>> +  else
>> +    emit_call_insn (gen_call_internal (target, operands[1]));
>>     DONE;
>>   })
>>
>> @@ -2971,6 +3037,15 @@ (define_insn "call_internal"
>>      bl\t%%plt(%0)"
>>     [(set_attr "jirl" "indirect,direct,direct")])
>>
>> +(define_insn "@call_internal_1<mode>"
>> +  [(call (mem:P (lo_sum:P (match_operand:P 0 "register_operand" "j")
>> +                         (match_operand:P 1 "symbolic_operand" "")))
>> +        (match_operand 2 "" ""))
>> +   (clobber (reg:SI RETURN_ADDR_REGNUM))]
>> +  "TARGET_CMODEL_MEDIUM"
>> +  "jirl\t$r1,%0,%%pc_lo12(%1)"
>> +  [(set_attr "jirl" "indirect")])
>> +
>>   (define_expand "call_value"
>>     [(parallel [(set (match_operand 0 "")
>>                     (call (match_operand 1 "")
>> @@ -2985,7 +3060,13 @@ (define_expand "call_value"
>>         rtx arg1 = XEXP (XVECEXP (operands[0], 0, 0), 0);
>>         rtx arg2 = XEXP (XVECEXP (operands[0], 0, 1), 0);
>>
>> -      emit_call_insn (gen_call_value_multiple_internal (arg1, target,
>> +      if (GET_CODE (target) == LO_SUM)
>> +       emit_call_insn (gen_call_value_multiple_internal_1 (Pmode, arg1,
>> +                                                           XEXP (target, 0),
>> +                                                           XEXP (target, 1),
>> +                                                           operands[2], arg2));
>> +      else
>> +       emit_call_insn (gen_call_value_multiple_internal (arg1, target,
>>                                                          operands[2], arg2));
>>       }
>>      else
>> @@ -2994,7 +3075,13 @@ (define_expand "call_value"
>>         if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) == 1)
>>              operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0);
>>
>> -      emit_call_insn (gen_call_value_internal (operands[0], target,
>> +      if (GET_CODE (target) == LO_SUM)
>> +       emit_call_insn (gen_call_value_internal_1 (Pmode, operands[0],
>> +                                                  XEXP (target, 0),
>> +                                                  XEXP (target, 1),
>> +                                                  operands[2]));
>> +      else
>> +       emit_call_insn (gen_call_value_internal (operands[0], target,
>>                                                 operands[2]));
>>       }
>>     DONE;
>> @@ -3012,6 +3099,16 @@ (define_insn "call_value_internal"
>>      bl\t%%plt(%1)"
>>     [(set_attr "jirl" "indirect,direct,direct")])
>>
>> +(define_insn "@call_value_internal_1<mode>"
>> +  [(set (match_operand 0 "register_operand" "")
>> +       (call (mem:P (lo_sum:P (match_operand:P 1 "register_operand" "j")
>> +                              (match_operand:P 2 "symbolic_operand" "")))
>> +             (match_operand 3 "" "")))
>> +   (clobber (reg:SI RETURN_ADDR_REGNUM))]
>> +  "TARGET_CMODEL_MEDIUM"
>> +  "jirl\t$r1,%1,%%pc_lo12(%2)"
>> +  [(set_attr "jirl" "indirect")])
>> +
>>   (define_insn "call_value_multiple_internal"
>>     [(set (match_operand 0 "register_operand" "")
>>          (call (mem:SI (match_operand 1 "call_insn_operand" "e,c,b"))
>> @@ -3027,6 +3124,22 @@ (define_insn "call_value_multiple_internal"
>>      bl\t%%plt(%1)"
>>     [(set_attr "jirl" "indirect,direct,direct")])
>>
>> +(define_insn "@call_value_multiple_internal_1<mode>"
>> +  [(set (match_operand 0 "register_operand" "")
>> +       (call (mem:P (unspec:P [(match_operand:P 1 "register_operand" "j")
>> +                               (match_operand:P 2 "symbolic_operand" "")]
>> +                     UNSPEC_CALL_VALUE_MULTIPLE_INTERNAL_1))
>> +             (match_operand 3 "" "")))
>> +   (set (match_operand 4 "register_operand" "")
>> +       (call (mem:P (unspec:P [(match_dup 1)
>> +                               (match_dup 2)]
>> +                     UNSPEC_CALL_VALUE_MULTIPLE_INTERNAL_1))
>> +             (match_dup 3)))
>> +   (clobber (reg:SI RETURN_ADDR_REGNUM))]
>> +  "TARGET_CMODEL_MEDIUM"
>> +  "jirl\t$r1,%1,%%pc_lo12(%2)"
>> +  [(set_attr "jirl" "indirect")])
>> +
>>
>>   ;; Call subroutine returning any type.
>>   (define_expand "untyped_call"
>> diff --git a/gcc/config/loongarch/loongarch.opt b/gcc/config/loongarch/loongarch.opt
>> index 9df7e187283..6395234218b 100644
>> --- a/gcc/config/loongarch/loongarch.opt
>> +++ b/gcc/config/loongarch/loongarch.opt
>> @@ -179,6 +179,9 @@ Enum(cmodel) String(tiny) Value(CMODEL_TINY)
>>   EnumValue
>>   Enum(cmodel) String(tiny-static) Value(CMODEL_TINY_STATIC)
>>
>> +EnumValue
>> +Enum(cmodel) String(medium) Value(CMODEL_MEDIUM)
>> +
>>   EnumValue
>>   Enum(cmodel) String(large) Value(CMODEL_LARGE)
>>
>> diff --git a/gcc/config/loongarch/predicates.md b/gcc/config/loongarch/predicates.md
>> index e38c6fbdd5f..8bd0c1376c9 100644
>> --- a/gcc/config/loongarch/predicates.md
>> +++ b/gcc/config/loongarch/predicates.md
>> @@ -123,16 +123,27 @@ (define_predicate "const_call_insn_operand"
>>     if (offset != const0_rtx)
>>       return false;
>>
>> +  /* When compiling with '-mcmodel=medium -mexplicit-relocs'
>> +     symbols are splited in loongarch_legitimize_call_address.
>> +
>> +     When compiling with '-mcmodel=medium -mno-explicit-relocs',
>> +     first obtain the symbolic address or the address of the
>> +     plt entry, and then perform an indirect jump, so return false.  */
>> +
>>     switch (symbol_type)
>>       {
>>       case SYMBOL_PCREL:
>> -      if (TARGET_CMODEL_EXTREME)
>> +      if (TARGET_CMODEL_EXTREME
>> +         || (TARGET_CMODEL_MEDIUM && !TARGET_EXPLICIT_RELOCS))
>>          return false;
>>         else
>>          return 1;
>>
>>       case SYMBOL_GOT_DISP:
>> -      if (TARGET_CMODEL_EXTREME || !flag_plt)
>> +      if (TARGET_CMODEL_EXTREME
>> +         || !flag_plt
>> +         || (flag_plt && TARGET_CMODEL_MEDIUM
>> +             && !TARGET_EXPLICIT_RELOCS))
>>          return false;
>>         else
>>          return 1;
>> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
>> index 8712bc25e3c..60702a52c8f 100644
>> --- a/gcc/doc/invoke.texi
>> +++ b/gcc/doc/invoke.texi
>> @@ -25084,6 +25084,9 @@ Set the code model to one of:
>>   The text segment must be within 128MB addressing space.  The data segment must
>>   be within 2GB addressing space.
>>
>> +@item medium
>> +The text segment and data segment must be within 2GB addressing space.
>> +
>>   @item large (Not implemented yet)
>>
>>   @item extreme
>> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c
>> new file mode 100644
>> index 00000000000..276d73e5ee8
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c
>> @@ -0,0 +1,41 @@
>> +/* { dg-do compile } */
>> +/* { dg-options "-mabi=lp64d -O0 -fpic -fplt -mno-explicit-relocs -mcmodel=medium" } */
>> +/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test1:.*la\.global\t.*f\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" } } */
>> +
>> +extern void g (void);
>> +void
>> +f (void)
>> +{}
>> +
>> +static void
>> +l (void)
>> +{}
>> +
>> +void
>> +test (void)
>> +{
>> +  g ();
>> +}
>> +
>> +void
>> +test1 (void)
>> +{
>> +  f ();
>> +}
>> +
>> +void
>> +test2 (void)
>> +{
>> +  l ();
>> +}
>> +
>> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
>> +
>> +void
>> +test3 (void)
>> +{
>> +  a = 10;
>> +}
>> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c
>> new file mode 100644
>> index 00000000000..237821c066b
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c
>> @@ -0,0 +1,41 @@
>> +/* { dg-do compile } */
>> +/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt -mno-explicit-relocs -mcmodel=medium" } */
>> +/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test1:.*la\.local\t.*f\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" } } */
>> +
>> +extern void g (void);
>> +void
>> +f (void)
>> +{}
>> +
>> +static void
>> +l (void)
>> +{}
>> +
>> +void
>> +test (void)
>> +{
>> +  g ();
>> +}
>> +
>> +void
>> +test1 (void)
>> +{
>> +  f ();
>> +}
>> +
>> +void
>> +test2 (void)
>> +{
>> +  l ();
>> +}
>> +
>> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
>> +
>> +void
>> +test3 (void)
>> +{
>> +  a = 10;
>> +}
>> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c
>> new file mode 100644
>> index 00000000000..9a6e16103bc
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c
>> @@ -0,0 +1,41 @@
>> +/* { dg-do compile } */
>> +/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mno-explicit-relocs -mcmodel=medium" } */
>> +/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test1:.*la\.global\t.*f\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" } } */
>> +
>> +extern void g (void);
>> +void
>> +f (void)
>> +{}
>> +
>> +static void
>> +l (void)
>> +{}
>> +
>> +void
>> +test (void)
>> +{
>> +  g ();
>> +}
>> +
>> +void
>> +test1 (void)
>> +{
>> +  f ();
>> +}
>> +
>> +void
>> +test2 (void)
>> +{
>> +  l ();
>> +}
>> +
>> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
>> +
>> +void
>> +test3 (void)
>> +{
>> +  a = 10;
>> +}
>> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c
>> new file mode 100644
>> index 00000000000..2577e345239
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c
>> @@ -0,0 +1,41 @@
>> +/* { dg-do compile } */
>> +/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mno-explicit-relocs -mcmodel=medium" } */
>> +/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test1:.*la\.local\t.*f\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" } } */
>> +
>> +extern void g (void);
>> +void
>> +f (void)
>> +{}
>> +
>> +static void
>> +l (void)
>> +{}
>> +
>> +void
>> +test (void)
>> +{
>> +  g ();
>> +}
>> +
>> +void
>> +test1 (void)
>> +{
>> +  f ();
>> +}
>> +
>> +void
>> +test2 (void)
>> +{
>> +  l ();
>> +}
>> +
>> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
>> +
>> +void
>> +test3 (void)
>> +{
>> +  a = 10;
>> +}
>> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c
>> new file mode 100644
>> index 00000000000..d70b6ea4663
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c
>> @@ -0,0 +1,42 @@
>> +/* { dg-do compile } */
>> +/* { dg-options "-mabi=lp64d -O0 -fpic -fplt -mexplicit-relocs -mcmodel=medium" } */
>> +/* { dg-final { scan-assembler "test:.*pcalau12i.*%pc_hi20\\(g\\)\n\tjirl.*pc_lo12\\(g\\)" } } */
>> +/* { dg-final { scan-assembler "test1:.*pcalau12i.*%pc_hi20\\(f\\)\n\tjirl.*%pc_lo12\\(f\\)" } } */
>> +/* { dg-final { scan-assembler "test2:.*pcalau12i.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */
>> +/* { dg-final { scan-assembler "test3:.*pcalau12i.*%pc_hi20\\(__tls_get_addr\\)\n\t.*\n\tjirl.*%pc_lo12\\(__tls_get_addr\\)" } } */
>> +
>> +extern void g (void);
>> +
>> +void
>> +f (void)
>> +{}
>> +
>> +static void
>> +l (void)
>> +{}
>> +
>> +void
>> +test (void)
>> +{
>> +  g ();
>> +}
>> +
>> +void
>> +test1 (void)
>> +{
>> +  f ();
>> +}
>> +
>> +void
>> +test2 (void)
>> +{
>> +  l ();
>> +}
>> +
>> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
>> +
>> +void
>> +test3 (void)
>> +{
>> +  a = 10;
>> +}
>> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c
>> new file mode 100644
>> index 00000000000..f963a99441a
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c
>> @@ -0,0 +1,42 @@
>> +/* { dg-do compile } */
>> +/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt -mexplicit-relocs -mcmodel=medium" } */
>> +/* { dg-final { scan-assembler "test:.*pcalau12i.*%pc_hi20\\(g\\)\n\tjirl.*pc_lo12\\(g\\)" } } */
>> +/* { dg-final { scan-assembler "test1:.*pcalau12i.*%pc_hi20\\(f\\)\n\tjirl.*%pc_lo12\\(f\\)" } } */
>> +/* { dg-final { scan-assembler "test2:.*pcalau12i.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */
>> +/* { dg-final { scan-assembler "test3:.*pcalau12i.*%pc_hi20\\(__tls_get_addr\\)\n\t.*\n\tjirl.*%pc_lo12\\(__tls_get_addr\\)" } } */
>> +
>> +extern void g (void);
>> +
>> +void
>> +f (void)
>> +{}
>> +
>> +static void
>> +l (void)
>> +{}
>> +
>> +void
>> +test (void)
>> +{
>> +  g ();
>> +}
>> +
>> +void
>> +test1 (void)
>> +{
>> +  f ();
>> +}
>> +
>> +void
>> +test2 (void)
>> +{
>> +  l ();
>> +}
>> +
>> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
>> +
>> +void
>> +test3 (void)
>> +{
>> +  a = 10;
>> +}
>> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c
>> new file mode 100644
>> index 00000000000..f2818b2da76
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c
>> @@ -0,0 +1,43 @@
>> +/* { dg-do compile } */
>> +/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mexplicit-relocs -mcmodel=medium" } */
>> +/* { dg-final { scan-assembler "test:.*pcalau12i\t.*%got_pc_hi20\\(g\\)\n\tld\.d\t.*%got_pc_lo12\\(g\\)\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test1:.*pcalau12i\t.*%got_pc_hi20\\(f\\)\n\tld\.d\t.*%got_pc_lo12\\(f\\)\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test2:.*pcalau12i\t.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */
>> +/* { dg-final { scan-assembler "test3:.*pcalau12i.*%got_pc_hi20\\(__tls_get_addr\\)\n\tld\.d.*%got_pc_lo12\\(__tls_get_addr\\)" } } */
>> +
>> +
>> +extern void g (void);
>> +
>> +void
>> +f (void)
>> +{}
>> +
>> +static void
>> +l (void)
>> +{}
>> +
>> +void
>> +test (void)
>> +{
>> +  g ();
>> +}
>> +
>> +void
>> +test1 (void)
>> +{
>> +  f ();
>> +}
>> +
>> +void
>> +test2 (void)
>> +{
>> +  l ();
>> +}
>> +
>> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
>> +
>> +void
>> +test3 (void)
>> +{
>> +  a = 10;
>> +}
>> diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c
>> new file mode 100644
>> index 00000000000..7fa873d84bb
>> --- /dev/null
>> +++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c
>> @@ -0,0 +1,42 @@
>> +/* { dg-do compile } */
>> +/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mexplicit-relocs -mcmodel=medium" } */
>> +/* { dg-final { scan-assembler "test:.*pcalau12i\t.*%got_pc_hi20\\(g\\)\n\tld\.d\t.*%got_pc_lo12\\(g\\)\n\tjirl" } } */
>> +/* { dg-final { scan-assembler "test1:.*pcalau12i\t.*%pc_hi20\\(f\\)\n\tjirl.*%pc_lo12\\(f\\)" } } */
>> +/* { dg-final { scan-assembler "test2:.*pcalau12i\t.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */
>> +/* { dg-final { scan-assembler "test3:.*pcalau12i.*%got_pc_hi20\\(__tls_get_addr\\)\n\tld\.d.*%got_pc_lo12\\(__tls_get_addr\\)" } } */
>> +
>> +extern void g (void);
>> +
>> +void
>> +f (void)
>> +{}
>> +
>> +static void
>> +l (void)
>> +{}
>> +
>> +void
>> +test (void)
>> +{
>> +  g ();
>> +}
>> +
>> +void
>> +test1 (void)
>> +{
>> +  f ();
>> +}
>> +
>> +void
>> +test2 (void)
>> +{
>> +  l ();
>> +}
>> +
>> +__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
>> +
>> +void
>> +test3 (void)
>> +{
>> +  a = 10;
>> +}
>> diff --git a/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c b/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c
>> index 32a0acf9b18..375663286f0 100644
>> --- a/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c
>> +++ b/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c
>> @@ -1,6 +1,6 @@
>>   /* { dg-do compile } */
>> -/* { dg-options "-O2 -fno-plt -mcmodel=normal" } */
>> -/* { dg-final { scan-assembler "pcalau12i\t.*%got_pc_hi20\\(__tls_get_addr\\)" } } */
>> +/* { dg-options "-O0 -fno-plt -mcmodel=normal -mexplicit-relocs" } */
>> +/* { dg-final { scan-assembler "pcalau12i\t.*%got_pc_hi20\\(__tls_get_addr\\)\n\tld\.d.*%got_pc_lo12\\(__tls_get_addr\\)" } } */
>>
>>   __attribute__ ((tls_model ("global-dynamic"))) __thread int a;
>>
>> --
>> 2.31.1
>>
  

Patch

diff --git a/gcc/config/loongarch/genopts/loongarch-strings b/gcc/config/loongarch/genopts/loongarch-strings
index cb88ed56b68..44ebb7ab10b 100644
--- a/gcc/config/loongarch/genopts/loongarch-strings
+++ b/gcc/config/loongarch/genopts/loongarch-strings
@@ -54,5 +54,6 @@  OPTSTR_CMODEL	      cmodel
 STR_CMODEL_NORMAL     normal
 STR_CMODEL_TINY	      tiny
 STR_CMODEL_TS	      tiny-static
+STR_CMODEL_MEDIUM     medium
 STR_CMODEL_LARGE      large
 STR_CMODEL_EXTREME    extreme
diff --git a/gcc/config/loongarch/genopts/loongarch.opt.in b/gcc/config/loongarch/genopts/loongarch.opt.in
index a571b6b7524..ebdd9538d48 100644
--- a/gcc/config/loongarch/genopts/loongarch.opt.in
+++ b/gcc/config/loongarch/genopts/loongarch.opt.in
@@ -172,6 +172,9 @@  Enum(cmodel) String(@@STR_CMODEL_TINY@@) Value(CMODEL_TINY)
 EnumValue
 Enum(cmodel) String(@@STR_CMODEL_TS@@) Value(CMODEL_TINY_STATIC)
 
+EnumValue
+Enum(cmodel) String(@@STR_CMODEL_MEDIUM@@) Value(CMODEL_MEDIUM)
+
 EnumValue
 Enum(cmodel) String(@@STR_CMODEL_LARGE@@) Value(CMODEL_LARGE)
 
diff --git a/gcc/config/loongarch/loongarch-def.c b/gcc/config/loongarch/loongarch-def.c
index c8769b7d65e..cbf995d81b5 100644
--- a/gcc/config/loongarch/loongarch-def.c
+++ b/gcc/config/loongarch/loongarch-def.c
@@ -152,6 +152,7 @@  loongarch_cmodel_strings[] = {
   [CMODEL_NORMAL]	  = STR_CMODEL_NORMAL,
   [CMODEL_TINY]		  = STR_CMODEL_TINY,
   [CMODEL_TINY_STATIC]	  = STR_CMODEL_TS,
+  [CMODEL_MEDIUM]	  = STR_CMODEL_MEDIUM,
   [CMODEL_LARGE]	  = STR_CMODEL_LARGE,
   [CMODEL_EXTREME]	  = STR_CMODEL_EXTREME,
 };
diff --git a/gcc/config/loongarch/loongarch-def.h b/gcc/config/loongarch/loongarch-def.h
index c2c35b6ba5c..b5985f07052 100644
--- a/gcc/config/loongarch/loongarch-def.h
+++ b/gcc/config/loongarch/loongarch-def.h
@@ -82,9 +82,10 @@  extern const char* loongarch_cmodel_strings[];
 #define CMODEL_NORMAL	      0
 #define CMODEL_TINY	      1
 #define CMODEL_TINY_STATIC    2
-#define CMODEL_LARGE	      3
-#define CMODEL_EXTREME	      4
-#define N_CMODEL_TYPES	      5
+#define CMODEL_MEDIUM	      3
+#define CMODEL_LARGE	      4
+#define CMODEL_EXTREME	      5
+#define N_CMODEL_TYPES	      6
 
 /* enum switches */
 /* The "SW_" codes represent command-line switches (options that
diff --git a/gcc/config/loongarch/loongarch-opts.cc b/gcc/config/loongarch/loongarch-opts.cc
index 2ae89f23443..e13eafb5882 100644
--- a/gcc/config/loongarch/loongarch-opts.cc
+++ b/gcc/config/loongarch/loongarch-opts.cc
@@ -376,11 +376,24 @@  fallback:
 
   /* 5.  Target code model */
   t.cmodel = constrained.cmodel ? opt_cmodel : CMODEL_NORMAL;
-  if (t.cmodel != CMODEL_NORMAL && t.cmodel != CMODEL_EXTREME)
+
+  switch (t.cmodel)
     {
+    case CMODEL_TINY:
+    case CMODEL_TINY_STATIC:
+    case CMODEL_LARGE:
       warning (0, "%qs is not supported, now cmodel is set to %qs",
 	       loongarch_cmodel_strings[t.cmodel], "normal");
       t.cmodel = CMODEL_NORMAL;
+      break;
+
+    case CMODEL_NORMAL:
+    case CMODEL_MEDIUM:
+    case CMODEL_EXTREME:
+      break;
+
+    default:
+      gcc_unreachable ();
     }
 
   /* Cleanup and return.  */
diff --git a/gcc/config/loongarch/loongarch-opts.h b/gcc/config/loongarch/loongarch-opts.h
index da24ecd2b50..3523a4cf78d 100644
--- a/gcc/config/loongarch/loongarch-opts.h
+++ b/gcc/config/loongarch/loongarch-opts.h
@@ -46,6 +46,7 @@  loongarch_config_target (struct loongarch_target *target,
 #define TARGET_CMODEL_NORMAL	    (la_target.cmodel == CMODEL_NORMAL)
 #define TARGET_CMODEL_TINY	    (la_target.cmodel == CMODEL_TINY)
 #define TARGET_CMODEL_TINY_STATIC   (la_target.cmodel == CMODEL_TINY_STATIC)
+#define TARGET_CMODEL_MEDIUM	    (la_target.cmodel == CMODEL_MEDIUM)
 #define TARGET_CMODEL_LARGE	    (la_target.cmodel == CMODEL_LARGE)
 #define TARGET_CMODEL_EXTREME	    (la_target.cmodel == CMODEL_EXTREME)
 
diff --git a/gcc/config/loongarch/loongarch-str.h b/gcc/config/loongarch/loongarch-str.h
index 0e8889b8c96..9f1b0989c82 100644
--- a/gcc/config/loongarch/loongarch-str.h
+++ b/gcc/config/loongarch/loongarch-str.h
@@ -53,6 +53,7 @@  along with GCC; see the file COPYING3.  If not see
 #define STR_CMODEL_NORMAL "normal"
 #define STR_CMODEL_TINY "tiny"
 #define STR_CMODEL_TS "tiny-static"
+#define STR_CMODEL_MEDIUM "medium"
 #define STR_CMODEL_LARGE "large"
 #define STR_CMODEL_EXTREME "extreme"
 
diff --git a/gcc/config/loongarch/loongarch.cc b/gcc/config/loongarch/loongarch.cc
index 207ac2762c6..16fd4acc970 100644
--- a/gcc/config/loongarch/loongarch.cc
+++ b/gcc/config/loongarch/loongarch.cc
@@ -2461,44 +2461,96 @@  loongarch_call_tls_get_addr (rtx sym, enum loongarch_symbol_type type, rtx v0)
     }
 
   if (flag_plt)
-    insn = emit_call_insn (gen_call_value_internal (v0,
-						    loongarch_tls_symbol,
-						    const0_rtx));
-  else
     {
-      rtx dest = gen_reg_rtx (Pmode);
-
-      if (TARGET_CMODEL_EXTREME)
+      switch (la_opt_cmodel)
 	{
-	  gcc_assert (TARGET_EXPLICIT_RELOCS);
+	case CMODEL_NORMAL:
+	  insn = emit_call_insn (gen_call_value_internal (v0,
+							  loongarch_tls_symbol,
+							  const0_rtx));
+	  break;
 
-	  rtx tmp1 = gen_reg_rtx (Pmode);
-	  rtx high = gen_reg_rtx (Pmode);
+	case CMODEL_MEDIUM:
+	    {
+	      rtx reg = gen_reg_rtx (Pmode);
+	      if (TARGET_EXPLICIT_RELOCS)
+		{
+		  emit_insn (gen_pcalau12i (Pmode, reg, loongarch_tls_symbol));
+		  rtx call = gen_call_value_internal_1 (Pmode, v0, reg,
+							loongarch_tls_symbol,
+							const0_rtx);
+		  insn = emit_call_insn (call);
+		}
+	      else
+		{
+		  emit_move_insn (reg, loongarch_tls_symbol);
+		  insn = emit_call_insn (gen_call_value_internal (v0,
+								  reg,
+								  const0_rtx));
+		}
+	      break;
+	    }
 
-	  loongarch_emit_move (high,
-			       gen_rtx_HIGH (Pmode, loongarch_tls_symbol));
-	  loongarch_emit_move (tmp1, gen_rtx_LO_SUM (Pmode,
-						     gen_rtx_REG (Pmode, 0),
-						     loongarch_tls_symbol));
-	  emit_insn (gen_lui_h_lo20 (tmp1, tmp1, loongarch_tls_symbol));
-	  emit_insn (gen_lui_h_hi12 (tmp1, tmp1, loongarch_tls_symbol));
-	  loongarch_emit_move (dest,
-			       gen_rtx_MEM (Pmode,
-					    gen_rtx_PLUS (Pmode, high, tmp1)));
+	/* code model extreme not support plt.  */
+	case CMODEL_EXTREME:
+	case CMODEL_LARGE:
+	case CMODEL_TINY:
+	case CMODEL_TINY_STATIC:
+	default:
+	  gcc_unreachable ();
 	}
-      else
+    }
+  else
+    {
+      rtx dest = gen_reg_rtx (Pmode);
+
+      switch (la_opt_cmodel)
 	{
-	  if (TARGET_EXPLICIT_RELOCS)
+	case CMODEL_NORMAL:
+	case CMODEL_MEDIUM:
+	    {
+	      if (TARGET_EXPLICIT_RELOCS)
+		{
+		  rtx high = gen_reg_rtx (Pmode);
+		  loongarch_emit_move (high,
+				       gen_rtx_HIGH (Pmode,
+						     loongarch_tls_symbol));
+		  emit_insn (gen_ld_from_got (Pmode, dest, high,
+					      loongarch_tls_symbol));
+		}
+	      else
+		loongarch_emit_move (dest, loongarch_tls_symbol);
+	      break;
+	    }
+
+	case CMODEL_EXTREME:
 	    {
+	      gcc_assert (TARGET_EXPLICIT_RELOCS);
+
+	      rtx tmp1 = gen_reg_rtx (Pmode);
 	      rtx high = gen_reg_rtx (Pmode);
+
 	      loongarch_emit_move (high,
 				   gen_rtx_HIGH (Pmode, loongarch_tls_symbol));
-	      emit_insn (gen_ld_from_got (Pmode, dest, high,
-					  loongarch_tls_symbol));
+	      loongarch_emit_move (tmp1, gen_rtx_LO_SUM (Pmode,
+							 gen_rtx_REG (Pmode, 0),
+							 loongarch_tls_symbol));
+	      emit_insn (gen_lui_h_lo20 (tmp1, tmp1, loongarch_tls_symbol));
+	      emit_insn (gen_lui_h_hi12 (tmp1, tmp1, loongarch_tls_symbol));
+	      loongarch_emit_move (dest,
+				   gen_rtx_MEM (Pmode,
+						gen_rtx_PLUS (Pmode,
+							      high, tmp1)));
 	    }
-	  else
-	    loongarch_emit_move (dest, loongarch_tls_symbol);
+	  break;
+
+	case CMODEL_LARGE:
+	case CMODEL_TINY:
+	case CMODEL_TINY_STATIC:
+	default:
+	  gcc_unreachable ();
 	}
+
       insn = emit_call_insn (gen_call_value_internal (v0, dest, const0_rtx));
     }
 
@@ -2618,6 +2670,24 @@  loongarch_legitimize_call_address (rtx addr)
       loongarch_emit_move (reg, addr);
       return reg;
     }
+
+  enum loongarch_symbol_type symbol_type = loongarch_classify_symbol (addr);
+
+  /* Split function call insn 'bl sym' or 'bl %plt(sym)' to :
+     pcalau12i $rd, %pc_hi20(sym)
+     jr $rd, %pc_lo12(sym).  */
+
+  if (TARGET_CMODEL_MEDIUM
+      && TARGET_EXPLICIT_RELOCS
+      && (SYMBOL_REF_P (addr) || LABEL_REF_P (addr))
+      && (symbol_type == SYMBOL_PCREL
+	  || (symbol_type == SYMBOL_GOT_DISP && flag_plt)))
+    {
+      rtx reg = gen_reg_rtx (Pmode);
+      emit_insn (gen_pcalau12i (Pmode, reg, addr));
+      return gen_rtx_LO_SUM (Pmode, reg, addr);
+    }
+
   return addr;
 }
 
@@ -5996,6 +6066,7 @@  loongarch_option_override_internal (struct gcc_options *opts)
 	break;
 
       case CMODEL_TINY_STATIC:
+      case CMODEL_MEDIUM:
       case CMODEL_NORMAL:
       case CMODEL_TINY:
       case CMODEL_LARGE:
diff --git a/gcc/config/loongarch/loongarch.md b/gcc/config/loongarch/loongarch.md
index 8fc10444c2a..3787fd8230f 100644
--- a/gcc/config/loongarch/loongarch.md
+++ b/gcc/config/loongarch/loongarch.md
@@ -59,11 +59,15 @@  (define_c_enum "unspec" [
   UNSPEC_CRCC
 
   UNSPEC_LOAD_FROM_GOT
+  UNSPEC_PCALAU12I
   UNSPEC_ORI_L_LO12
   UNSPEC_LUI_L_HI20
   UNSPEC_LUI_H_LO20
   UNSPEC_LUI_H_HI12
   UNSPEC_TLS_LOW
+
+  UNSPEC_SIBCALL_VALUE_MULTIPLE_INTERNAL_1
+  UNSPEC_CALL_VALUE_MULTIPLE_INTERNAL_1
 ])
 
 (define_c_enum "unspecv" [
@@ -1946,6 +1950,14 @@  (define_insn "@lui_l_hi20<mode>"
   [(set_attr "type" "move")]
 )
 
+(define_insn "@pcalau12i<mode>"
+  [(set (match_operand:P 0 "register_operand" "=j")
+	(unspec:P [(match_operand:P 1 "symbolic_operand" "")]
+	UNSPEC_PCALAU12I))]
+  ""
+  "pcalau12i\t%0,%%pc_hi20(%1)"
+  [(set_attr "type" "move")])
+
 (define_insn "@ori_l_lo12<mode>"
   [(set (match_operand:P 0 "register_operand" "=r")
 	(unspec:P [(match_operand:P 1 "register_operand" "r")
@@ -2877,7 +2889,12 @@  (define_expand "sibcall"
 {
   rtx target = loongarch_legitimize_call_address (XEXP (operands[0], 0));
 
-  emit_call_insn (gen_sibcall_internal (target, operands[1]));
+  if (GET_CODE (target) == LO_SUM)
+    emit_call_insn (gen_sibcall_internal_1 (Pmode, XEXP (target, 0),
+					    XEXP (target, 1),
+					    operands[1]));
+  else
+    emit_call_insn (gen_sibcall_internal (target, operands[1]));
   DONE;
 })
 
@@ -2891,6 +2908,14 @@  (define_insn "sibcall_internal"
    b\t%%plt(%0)"
   [(set_attr "jirl" "indirect,direct,direct")])
 
+(define_insn "@sibcall_internal_1<mode>"
+  [(call (mem:P (lo_sum:P (match_operand:P 0 "register_operand" "j")
+			  (match_operand:P 1 "symbolic_operand" "")))
+	 (match_operand 2 "" ""))]
+  "SIBLING_CALL_P (insn) && TARGET_CMODEL_MEDIUM"
+  "jirl\t$r0,%0,%%pc_lo12(%1)"
+  [(set_attr "jirl" "indirect")])
+
 (define_expand "sibcall_value"
   [(parallel [(set (match_operand 0 "")
 		   (call (match_operand 1 "")
@@ -2906,7 +2931,14 @@  (define_expand "sibcall_value"
       rtx arg1 = XEXP (XVECEXP (operands[0],0, 0), 0);
       rtx arg2 = XEXP (XVECEXP (operands[0],0, 1), 0);
 
-      emit_call_insn (gen_sibcall_value_multiple_internal (arg1, target,
+      if (GET_CODE (target) == LO_SUM)
+	emit_call_insn (gen_sibcall_value_multiple_internal_1 (Pmode, arg1,
+							   XEXP (target, 0),
+							   XEXP (target, 1),
+							   operands[2],
+							   arg2));
+      else
+	emit_call_insn (gen_sibcall_value_multiple_internal (arg1, target,
 							   operands[2],
 							   arg2));
     }
@@ -2916,7 +2948,13 @@  (define_expand "sibcall_value"
       if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) == 1)
 	operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0);
 
-      emit_call_insn (gen_sibcall_value_internal (operands[0], target,
+      if (GET_CODE (target) == LO_SUM)
+	emit_call_insn (gen_sibcall_value_internal_1 (Pmode, operands[0],
+						  XEXP (target, 0),
+						  XEXP (target, 1),
+						  operands[2]));
+      else
+	emit_call_insn (gen_sibcall_value_internal (operands[0], target,
 						  operands[2]));
     }
   DONE;
@@ -2933,6 +2971,15 @@  (define_insn "sibcall_value_internal"
    b\t%%plt(%1)"
   [(set_attr "jirl" "indirect,direct,direct")])
 
+(define_insn "@sibcall_value_internal_1<mode>"
+  [(set (match_operand 0 "register_operand" "")
+	(call (mem:P (lo_sum:P (match_operand:P 1 "register_operand" "j")
+			       (match_operand:P 2 "symbolic_operand" "")))
+	      (match_operand 3 "" "")))]
+  "SIBLING_CALL_P (insn) && TARGET_CMODEL_MEDIUM"
+  "jirl\t$r0,%1,%%pc_lo12(%2)"
+  [(set_attr "jirl" "indirect")])
+
 (define_insn "sibcall_value_multiple_internal"
   [(set (match_operand 0 "register_operand" "")
 	(call (mem:SI (match_operand 1 "call_insn_operand" "j,c,b"))
@@ -2947,6 +2994,21 @@  (define_insn "sibcall_value_multiple_internal"
    b\t%%plt(%1)"
   [(set_attr "jirl" "indirect,direct,direct")])
 
+(define_insn "@sibcall_value_multiple_internal_1<mode>"
+  [(set (match_operand 0 "register_operand" "")
+	(call (mem:P (unspec:P [(match_operand:P 1 "register_operand" "j")
+			        (match_operand:P 2 "symbolic_operand" "")]
+		      UNSPEC_SIBCALL_VALUE_MULTIPLE_INTERNAL_1))
+	      (match_operand 3 "" "")))
+   (set (match_operand 4 "register_operand" "")
+	(call (mem:P (unspec:P [(match_dup 1)
+			        (match_dup 2)]
+		      UNSPEC_SIBCALL_VALUE_MULTIPLE_INTERNAL_1))
+	      (match_dup 3)))]
+  "SIBLING_CALL_P (insn) && TARGET_CMODEL_MEDIUM"
+  "jirl\t$r0,%1,%%pc_lo12(%2)"
+  [(set_attr "jirl" "indirect")])
+
 (define_expand "call"
   [(parallel [(call (match_operand 0 "")
 		    (match_operand 1 ""))
@@ -2956,7 +3018,11 @@  (define_expand "call"
 {
   rtx target = loongarch_legitimize_call_address (XEXP (operands[0], 0));
 
-  emit_call_insn (gen_call_internal (target, operands[1]));
+  if (GET_CODE (target) == LO_SUM)
+    emit_call_insn (gen_call_internal_1 (Pmode, XEXP (target, 0),
+					 XEXP (target, 1), operands[1]));
+  else
+    emit_call_insn (gen_call_internal (target, operands[1]));
   DONE;
 })
 
@@ -2971,6 +3037,15 @@  (define_insn "call_internal"
    bl\t%%plt(%0)"
   [(set_attr "jirl" "indirect,direct,direct")])
 
+(define_insn "@call_internal_1<mode>"
+  [(call (mem:P (lo_sum:P (match_operand:P 0 "register_operand" "j")
+			  (match_operand:P 1 "symbolic_operand" "")))
+	 (match_operand 2 "" ""))
+   (clobber (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_CMODEL_MEDIUM"
+  "jirl\t$r1,%0,%%pc_lo12(%1)"
+  [(set_attr "jirl" "indirect")])
+
 (define_expand "call_value"
   [(parallel [(set (match_operand 0 "")
 		   (call (match_operand 1 "")
@@ -2985,7 +3060,13 @@  (define_expand "call_value"
       rtx arg1 = XEXP (XVECEXP (operands[0], 0, 0), 0);
       rtx arg2 = XEXP (XVECEXP (operands[0], 0, 1), 0);
 
-      emit_call_insn (gen_call_value_multiple_internal (arg1, target,
+      if (GET_CODE (target) == LO_SUM)
+	emit_call_insn (gen_call_value_multiple_internal_1 (Pmode, arg1,
+							    XEXP (target, 0),
+							    XEXP (target, 1),
+							    operands[2], arg2));
+      else
+	emit_call_insn (gen_call_value_multiple_internal (arg1, target,
 							operands[2], arg2));
     }
    else
@@ -2994,7 +3075,13 @@  (define_expand "call_value"
       if (GET_CODE (operands[0]) == PARALLEL && XVECLEN (operands[0], 0) == 1)
 	    operands[0] = XEXP (XVECEXP (operands[0], 0, 0), 0);
 
-      emit_call_insn (gen_call_value_internal (operands[0], target,
+      if (GET_CODE (target) == LO_SUM)
+	emit_call_insn (gen_call_value_internal_1 (Pmode, operands[0],
+						   XEXP (target, 0),
+						   XEXP (target, 1),
+						   operands[2]));
+      else
+	emit_call_insn (gen_call_value_internal (operands[0], target,
 					       operands[2]));
     }
   DONE;
@@ -3012,6 +3099,16 @@  (define_insn "call_value_internal"
    bl\t%%plt(%1)"
   [(set_attr "jirl" "indirect,direct,direct")])
 
+(define_insn "@call_value_internal_1<mode>"
+  [(set (match_operand 0 "register_operand" "")
+	(call (mem:P (lo_sum:P (match_operand:P 1 "register_operand" "j")
+			       (match_operand:P 2 "symbolic_operand" "")))
+	      (match_operand 3 "" "")))
+   (clobber (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_CMODEL_MEDIUM"
+  "jirl\t$r1,%1,%%pc_lo12(%2)"
+  [(set_attr "jirl" "indirect")])
+
 (define_insn "call_value_multiple_internal"
   [(set (match_operand 0 "register_operand" "")
 	(call (mem:SI (match_operand 1 "call_insn_operand" "e,c,b"))
@@ -3027,6 +3124,22 @@  (define_insn "call_value_multiple_internal"
    bl\t%%plt(%1)"
   [(set_attr "jirl" "indirect,direct,direct")])
 
+(define_insn "@call_value_multiple_internal_1<mode>"
+  [(set (match_operand 0 "register_operand" "")
+	(call (mem:P (unspec:P [(match_operand:P 1 "register_operand" "j")
+			        (match_operand:P 2 "symbolic_operand" "")]
+		      UNSPEC_CALL_VALUE_MULTIPLE_INTERNAL_1))
+	      (match_operand 3 "" "")))
+   (set (match_operand 4 "register_operand" "")
+	(call (mem:P (unspec:P [(match_dup 1)
+			        (match_dup 2)]
+		      UNSPEC_CALL_VALUE_MULTIPLE_INTERNAL_1))
+	      (match_dup 3)))
+   (clobber (reg:SI RETURN_ADDR_REGNUM))]
+  "TARGET_CMODEL_MEDIUM"
+  "jirl\t$r1,%1,%%pc_lo12(%2)"
+  [(set_attr "jirl" "indirect")])
+
 
 ;; Call subroutine returning any type.
 (define_expand "untyped_call"
diff --git a/gcc/config/loongarch/loongarch.opt b/gcc/config/loongarch/loongarch.opt
index 9df7e187283..6395234218b 100644
--- a/gcc/config/loongarch/loongarch.opt
+++ b/gcc/config/loongarch/loongarch.opt
@@ -179,6 +179,9 @@  Enum(cmodel) String(tiny) Value(CMODEL_TINY)
 EnumValue
 Enum(cmodel) String(tiny-static) Value(CMODEL_TINY_STATIC)
 
+EnumValue
+Enum(cmodel) String(medium) Value(CMODEL_MEDIUM)
+
 EnumValue
 Enum(cmodel) String(large) Value(CMODEL_LARGE)
 
diff --git a/gcc/config/loongarch/predicates.md b/gcc/config/loongarch/predicates.md
index e38c6fbdd5f..8bd0c1376c9 100644
--- a/gcc/config/loongarch/predicates.md
+++ b/gcc/config/loongarch/predicates.md
@@ -123,16 +123,27 @@  (define_predicate "const_call_insn_operand"
   if (offset != const0_rtx)
     return false;
 
+  /* When compiling with '-mcmodel=medium -mexplicit-relocs'
+     symbols are splited in loongarch_legitimize_call_address.
+
+     When compiling with '-mcmodel=medium -mno-explicit-relocs',
+     first obtain the symbolic address or the address of the
+     plt entry, and then perform an indirect jump, so return false.  */
+
   switch (symbol_type)
     {
     case SYMBOL_PCREL:
-      if (TARGET_CMODEL_EXTREME)
+      if (TARGET_CMODEL_EXTREME
+	  || (TARGET_CMODEL_MEDIUM && !TARGET_EXPLICIT_RELOCS))
 	return false;
       else
 	return 1;
 
     case SYMBOL_GOT_DISP:
-      if (TARGET_CMODEL_EXTREME || !flag_plt)
+      if (TARGET_CMODEL_EXTREME
+	  || !flag_plt
+	  || (flag_plt && TARGET_CMODEL_MEDIUM
+	      && !TARGET_EXPLICIT_RELOCS))
 	return false;
       else
 	return 1;
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 8712bc25e3c..60702a52c8f 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -25084,6 +25084,9 @@  Set the code model to one of:
 The text segment must be within 128MB addressing space.  The data segment must
 be within 2GB addressing space.
 
+@item medium
+The text segment and data segment must be within 2GB addressing space.
+
 @item large (Not implemented yet)
 
 @item extreme
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c
new file mode 100644
index 00000000000..276d73e5ee8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-1.c
@@ -0,0 +1,41 @@ 
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fpic -fplt -mno-explicit-relocs -mcmodel=medium" } */
+/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */
+/* { dg-final { scan-assembler "test1:.*la\.global\t.*f\n\tjirl" } } */
+/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */
+/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" } } */
+
+extern void g (void);
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+  g ();
+}
+
+void
+test1 (void)
+{
+  f ();
+}
+
+void
+test2 (void)
+{
+  l ();
+}
+
+__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
+
+void
+test3 (void)
+{
+  a = 10;
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c
new file mode 100644
index 00000000000..237821c066b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-2.c
@@ -0,0 +1,41 @@ 
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt -mno-explicit-relocs -mcmodel=medium" } */
+/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */
+/* { dg-final { scan-assembler "test1:.*la\.local\t.*f\n\tjirl" } } */
+/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */
+/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" } } */
+
+extern void g (void);
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+  g ();
+}
+
+void
+test1 (void)
+{
+  f ();
+}
+
+void
+test2 (void)
+{
+  l ();
+}
+
+__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
+
+void
+test3 (void)
+{
+  a = 10;
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c
new file mode 100644
index 00000000000..9a6e16103bc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-3.c
@@ -0,0 +1,41 @@ 
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mno-explicit-relocs -mcmodel=medium" } */
+/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */
+/* { dg-final { scan-assembler "test1:.*la\.global\t.*f\n\tjirl" } } */
+/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */
+/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" } } */
+
+extern void g (void);
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+  g ();
+}
+
+void
+test1 (void)
+{
+  f ();
+}
+
+void
+test2 (void)
+{
+  l ();
+}
+
+__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
+
+void
+test3 (void)
+{
+  a = 10;
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c
new file mode 100644
index 00000000000..2577e345239
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-4.c
@@ -0,0 +1,41 @@ 
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mno-explicit-relocs -mcmodel=medium" } */
+/* { dg-final { scan-assembler "test:.*la\.global\t.*g\n\tjirl" } } */
+/* { dg-final { scan-assembler "test1:.*la\.local\t.*f\n\tjirl" } } */
+/* { dg-final { scan-assembler "test2:.*la\.local\t.*l\n\tjirl" } } */
+/* { dg-final { scan-assembler "test3:.*la\.global\t.*\_\_tls\_get\_addr" } } */
+
+extern void g (void);
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+  g ();
+}
+
+void
+test1 (void)
+{
+  f ();
+}
+
+void
+test2 (void)
+{
+  l ();
+}
+
+__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
+
+void
+test3 (void)
+{
+  a = 10;
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c
new file mode 100644
index 00000000000..d70b6ea4663
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-5.c
@@ -0,0 +1,42 @@ 
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fpic -fplt -mexplicit-relocs -mcmodel=medium" } */
+/* { dg-final { scan-assembler "test:.*pcalau12i.*%pc_hi20\\(g\\)\n\tjirl.*pc_lo12\\(g\\)" } } */
+/* { dg-final { scan-assembler "test1:.*pcalau12i.*%pc_hi20\\(f\\)\n\tjirl.*%pc_lo12\\(f\\)" } } */
+/* { dg-final { scan-assembler "test2:.*pcalau12i.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */
+/* { dg-final { scan-assembler "test3:.*pcalau12i.*%pc_hi20\\(__tls_get_addr\\)\n\t.*\n\tjirl.*%pc_lo12\\(__tls_get_addr\\)" } } */
+
+extern void g (void);
+
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+  g ();
+}
+
+void
+test1 (void)
+{
+  f ();
+}
+
+void
+test2 (void)
+{
+  l ();
+}
+
+__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
+
+void
+test3 (void)
+{
+  a = 10;
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c
new file mode 100644
index 00000000000..f963a99441a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-6.c
@@ -0,0 +1,42 @@ 
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fno-pic -fplt -mexplicit-relocs -mcmodel=medium" } */
+/* { dg-final { scan-assembler "test:.*pcalau12i.*%pc_hi20\\(g\\)\n\tjirl.*pc_lo12\\(g\\)" } } */
+/* { dg-final { scan-assembler "test1:.*pcalau12i.*%pc_hi20\\(f\\)\n\tjirl.*%pc_lo12\\(f\\)" } } */
+/* { dg-final { scan-assembler "test2:.*pcalau12i.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */
+/* { dg-final { scan-assembler "test3:.*pcalau12i.*%pc_hi20\\(__tls_get_addr\\)\n\t.*\n\tjirl.*%pc_lo12\\(__tls_get_addr\\)" } } */
+
+extern void g (void);
+
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+  g ();
+}
+
+void
+test1 (void)
+{
+  f ();
+}
+
+void
+test2 (void)
+{
+  l ();
+}
+
+__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
+
+void
+test3 (void)
+{
+  a = 10;
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c
new file mode 100644
index 00000000000..f2818b2da76
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-7.c
@@ -0,0 +1,43 @@ 
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fpic -fno-plt -mexplicit-relocs -mcmodel=medium" } */
+/* { dg-final { scan-assembler "test:.*pcalau12i\t.*%got_pc_hi20\\(g\\)\n\tld\.d\t.*%got_pc_lo12\\(g\\)\n\tjirl" } } */
+/* { dg-final { scan-assembler "test1:.*pcalau12i\t.*%got_pc_hi20\\(f\\)\n\tld\.d\t.*%got_pc_lo12\\(f\\)\n\tjirl" } } */
+/* { dg-final { scan-assembler "test2:.*pcalau12i\t.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */
+/* { dg-final { scan-assembler "test3:.*pcalau12i.*%got_pc_hi20\\(__tls_get_addr\\)\n\tld\.d.*%got_pc_lo12\\(__tls_get_addr\\)" } } */
+
+
+extern void g (void);
+
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+  g ();
+}
+
+void
+test1 (void)
+{
+  f ();
+}
+
+void
+test2 (void)
+{
+  l ();
+}
+
+__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
+
+void
+test3 (void)
+{
+  a = 10;
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c b/gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c
new file mode 100644
index 00000000000..7fa873d84bb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/loongarch/func-call-medium-8.c
@@ -0,0 +1,42 @@ 
+/* { dg-do compile } */
+/* { dg-options "-mabi=lp64d -O0 -fno-pic -fno-plt -mexplicit-relocs -mcmodel=medium" } */
+/* { dg-final { scan-assembler "test:.*pcalau12i\t.*%got_pc_hi20\\(g\\)\n\tld\.d\t.*%got_pc_lo12\\(g\\)\n\tjirl" } } */
+/* { dg-final { scan-assembler "test1:.*pcalau12i\t.*%pc_hi20\\(f\\)\n\tjirl.*%pc_lo12\\(f\\)" } } */
+/* { dg-final { scan-assembler "test2:.*pcalau12i\t.*%pc_hi20\\(l\\)\n\tjirl.*%pc_lo12\\(l\\)" } } */
+/* { dg-final { scan-assembler "test3:.*pcalau12i.*%got_pc_hi20\\(__tls_get_addr\\)\n\tld\.d.*%got_pc_lo12\\(__tls_get_addr\\)" } } */
+
+extern void g (void);
+
+void
+f (void)
+{}
+
+static void
+l (void)
+{}
+
+void
+test (void)
+{
+  g ();
+}
+
+void
+test1 (void)
+{
+  f ();
+}
+
+void
+test2 (void)
+{
+  l ();
+}
+
+__attribute__ ((tls_model ("global-dynamic"))) __thread int a;
+
+void
+test3 (void)
+{
+  a = 10;
+}
diff --git a/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c b/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c
index 32a0acf9b18..375663286f0 100644
--- a/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c
+++ b/gcc/testsuite/gcc.target/loongarch/tls-gd-noplt.c
@@ -1,6 +1,6 @@ 
 /* { dg-do compile } */
-/* { dg-options "-O2 -fno-plt -mcmodel=normal" } */
-/* { dg-final { scan-assembler "pcalau12i\t.*%got_pc_hi20\\(__tls_get_addr\\)" } } */
+/* { dg-options "-O0 -fno-plt -mcmodel=normal -mexplicit-relocs" } */
+/* { dg-final { scan-assembler "pcalau12i\t.*%got_pc_hi20\\(__tls_get_addr\\)\n\tld\.d.*%got_pc_lo12\\(__tls_get_addr\\)" } } */
 
 __attribute__ ((tls_model ("global-dynamic"))) __thread int a;