@@ -39,6 +39,7 @@ class LoongArch final : public TargetInfo {
3939 void relocate (uint8_t *loc, const Relocation &rel,
4040 uint64_t val) const override ;
4141 bool relaxOnce (int pass) const override ;
42+ void relocateAlloc (InputSectionBase &sec, uint8_t *buf) const override ;
4243 void finalizeRelax (int passes) const override ;
4344};
4445} // end anonymous namespace
@@ -53,6 +54,8 @@ enum Op {
5354 ADDI_W = 0x02800000 ,
5455 ADDI_D = 0x02c00000 ,
5556 ANDI = 0x03400000 ,
57+ ORI = 0x03800000 ,
58+ LU12I_W = 0x14000000 ,
5659 PCADDI = 0x18000000 ,
5760 PCADDU12I = 0x1c000000 ,
5861 LD_W = 0x28800000 ,
@@ -1002,6 +1005,88 @@ static bool relax(Ctx &ctx, InputSection &sec) {
10021005 return changed;
10031006}
10041007
1008+ // Convert TLS IE to LE in the normal or medium code model.
1009+ // Original code sequence:
1010+ // * pcalau12i $a0, %ie_pc_hi20(sym)
1011+ // * ld.d $a0, $a0, %ie_pc_lo12(sym)
1012+ //
1013+ // The code sequence converted is as follows:
1014+ // * lu12i.w $a0, %le_hi20(sym) # le_hi20 != 0, otherwise NOP
1015+ // * ori $a0, src, %le_lo12(sym) # le_hi20 != 0, src = $a0,
1016+ // # otherwise, src = $zero
1017+ //
1018+ // When relaxation enables, redundant NOPs can be removed.
1019+ static void tlsIeToLe (uint8_t *loc, const Relocation &rel, uint64_t val) {
1020+ assert (isInt<32 >(val) &&
1021+ " val exceeds the range of medium code model in tlsIeToLe" );
1022+
1023+ bool isUInt12 = isUInt<12 >(val);
1024+ const uint32_t currInsn = read32le (loc);
1025+ switch (rel.type ) {
1026+ case R_LARCH_TLS_IE_PC_HI20:
1027+ if (isUInt12)
1028+ write32le (loc, insn (ANDI, R_ZERO, R_ZERO, 0 )); // nop
1029+ else
1030+ write32le (loc, insn (LU12I_W, getD5 (currInsn), extractBits (val, 31 , 12 ),
1031+ 0 )); // lu12i.w $a0, %le_hi20
1032+ break ;
1033+ case R_LARCH_TLS_IE_PC_LO12:
1034+ if (isUInt12)
1035+ write32le (loc, insn (ORI, getD5 (currInsn), R_ZERO,
1036+ val)); // ori $a0, $zero, %le_lo12
1037+ else
1038+ write32le (loc, insn (ORI, getD5 (currInsn), getJ5 (currInsn),
1039+ lo12 (val))); // ori $a0, $a0, %le_lo12
1040+ break ;
1041+ }
1042+ }
1043+
1044+ void LoongArch::relocateAlloc (InputSectionBase &sec, uint8_t *buf) const {
1045+ const unsigned bits = ctx.arg .is64 ? 64 : 32 ;
1046+ uint64_t secAddr = sec.getOutputSection ()->addr ;
1047+ if (auto *s = dyn_cast<InputSection>(&sec))
1048+ secAddr += s->outSecOff ;
1049+ else if (auto *ehIn = dyn_cast<EhInputSection>(&sec))
1050+ secAddr += ehIn->getParent ()->outSecOff ;
1051+ bool isExtreme = false ;
1052+ const MutableArrayRef<Relocation> relocs = sec.relocs ();
1053+ for (size_t i = 0 , size = relocs.size (); i != size; ++i) {
1054+ Relocation &rel = relocs[i];
1055+ uint8_t *loc = buf + rel.offset ;
1056+ uint64_t val = SignExtend64 (
1057+ sec.getRelocTargetVA (ctx, rel, secAddr + rel.offset ), bits);
1058+
1059+ switch (rel.expr ) {
1060+ case R_RELAX_HINT:
1061+ continue ;
1062+ case R_RELAX_TLS_IE_TO_LE:
1063+ if (rel.type == R_LARCH_TLS_IE_PC_HI20) {
1064+ // LoongArch does not support IE to LE optimization in the extreme code
1065+ // model. In this case, the relocs are as follows:
1066+ //
1067+ // * i -- R_LARCH_TLS_IE_PC_HI20
1068+ // * i+1 -- R_LARCH_TLS_IE_PC_LO12
1069+ // * i+2 -- R_LARCH_TLS_IE64_PC_LO20
1070+ // * i+3 -- R_LARCH_TLS_IE64_PC_HI12
1071+ isExtreme =
1072+ (i + 2 < size && relocs[i + 2 ].type == R_LARCH_TLS_IE64_PC_LO20);
1073+ }
1074+ if (isExtreme) {
1075+ rel.expr = getRelExpr (rel.type , *rel.sym , loc);
1076+ val = SignExtend64 (sec.getRelocTargetVA (ctx, rel, secAddr + rel.offset ),
1077+ bits);
1078+ relocateNoSym (loc, rel.type , val);
1079+ } else {
1080+ tlsIeToLe (loc, rel, val);
1081+ }
1082+ continue ;
1083+ default :
1084+ break ;
1085+ }
1086+ relocate (loc, rel, val);
1087+ }
1088+ }
1089+
10051090// When relaxing just R_LARCH_ALIGN, relocDeltas is usually changed only once in
10061091// the absence of a linker script. For call and load/store R_LARCH_RELAX, code
10071092// shrinkage may reduce displacement and make more relocations eligible for
0 commit comments