[Git][ghc/ghc][wip/supersven/ghc-9.10-riscv-ncg] 2 commits: Better comment

Sven Tennie (@supersven) gitlab at gitlab.haskell.org
Fri Jun 28 06:28:11 UTC 2024



Sven Tennie pushed to branch wip/supersven/ghc-9.10-riscv-ncg at Glasgow Haskell Compiler / GHC


Commits:
8d323497 by Sven Tennie at 2024-06-27T05:39:29+00:00
Better comment

- - - - -
cf5c4908 by Sven Tennie at 2024-06-28T06:25:29+00:00
Cleanup relocation code

- - - - -


2 changed files:

- rts/linker/Elf.c
- rts/linker/elf_reloc_riscv64.c


Changes:

=====================================
rts/linker/Elf.c
=====================================
@@ -1128,8 +1128,9 @@ end:
    return result;
 }
 
-// the aarch64 and riscv64 linkers use relocacteObjectCodeAarch64,
-// see elf_reloc_aarch64.{h,c}, elf_reloc_riscv64.{h,c}
+// the aarch64 and riscv64 linkers use relocateObjectCodeAarch64() and
+// relocateObjectCodeRISCV64() (respectively), see elf_reloc_aarch64.{h,c} and
+// elf_reloc_riscv64.{h,c}
 #if !defined(aarch64_HOST_ARCH) && !defined(riscv64_HOST_ARCH)
 
 /* Do ELF relocations which lack an explicit addend.  All x86-linux
@@ -2013,6 +2014,7 @@ ocResolve_ELF ( ObjectCode* oc )
 #if defined(powerpc_HOST_ARCH)
     ocFlushInstructionCache( oc );
 #elif defined(riscv64_HOST_ARCH)
+    /* New-style pseudo-polymorph (by architecture) call */
     flushInstructionCache( oc );
 #endif
 


=====================================
rts/linker/elf_reloc_riscv64.c
=====================================
@@ -29,7 +29,6 @@ typedef uint16_t cinst_t;
 char *relocationTypeToString(Elf64_Xword type);
 int32_t decodeAddendRISCV64(Section *section, Elf_Rel *rel);
 bool encodeAddendRISCV64(Section *section, Elf_Rel *rel, int32_t addend);
-int32_t SignExtend32(uint32_t X, unsigned B);
 void write8le(uint8_t *p, uint8_t v);
 uint8_t read8le(const uint8_t *P);
 void write16le(cinst_t *p, uint16_t v);
@@ -48,8 +47,6 @@ int32_t computeAddend(ElfRelocationATable * relaTab, unsigned relNo, Elf_Rel *re
 void setJType(inst_t *loc, uint32_t val);
 void setIType(inst_t *loc, int32_t val);
 void checkInt(inst_t *loc, int32_t v, int n);
-uint32_t setLO12_I(uint32_t insn, uint32_t imm);
-uint32_t setLO12_S(uint32_t insn, uint32_t imm);
 void setUType(inst_t *loc, int32_t val);
 
 
@@ -122,91 +119,97 @@ int32_t decodeAddendRISCV64(Section *section STG_UNUSED,
   abort(/* we don't support Rel locations yet. */);
 }
 
-// Sign-extend the number in the bottom B bits of X to a 32-bit integer.
-// Requires 0 < B <= 32. (32 bit is sufficient as we can only encode 20 + 12 =
-// 32 bit in a relocation pair.)
-int32_t SignExtend32(uint32_t X, unsigned B) {
-  assert(B > 0 && "Bit width can't be 0.");
-  assert(B <= 32 && "Bit width out of range.");
-  return (int32_t)(X << (32 - B)) >> (32 - B);
-}
-
 // Make sure that V can be represented as an N bit signed integer.
 void checkInt(inst_t *loc, int32_t v, int n) {
-  if (v != SignExtend32(v, n)) {
+  if (v != signExtend32(v, n)) {
     debugBelch("Relocation at 0x%x is out of range. value: 0x%x (%d), "
                "sign-extended value: 0x%x (%d), max bits 0x%x (%d)\n",
-               *loc, v, v, SignExtend32(v, n), SignExtend32(v, n), n, n);
+               *loc, v, v, signExtend32(v, n), signExtend32(v, n), n, n);
   }
 }
 
-// RISCV is little-endian by definition.
+// RISCV is little-endian by definition: We can rely on (implicit) casts.
 void write8le(uint8_t *p, uint8_t v) { *p = v; }
 
-// RISCV is little-endian by definition.
-uint8_t read8le(const uint8_t *P) { return *P; }
+// RISCV is little-endian by definition: We can rely on (implicit) casts.
+uint8_t read8le(const uint8_t *p) { return *p; }
 
-// RISCV is little-endian by definition.
+// RISCV is little-endian by definition: We can rely on (implicit) casts.
 void write16le(cinst_t *p, uint16_t v) { *p = v; }
 
-// RISCV is little-endian by definition.
-uint16_t read16le(const cinst_t *P) { return *P; }
+// RISCV is little-endian by definition: We can rely on (implicit) casts.
+uint16_t read16le(const cinst_t *p) { return *p; }
 
-// RISCV is little-endian by definition.
-uint32_t read32le(const inst_t *P) { return *P; }
+// RISCV is little-endian by definition: We can rely on (implicit) casts.
+uint32_t read32le(const inst_t *p) { return *p; }
 
-// RISCV is little-endian by definition.
+// RISCV is little-endian by definition: We can rely on (implicit) casts.
 void write32le(inst_t *p, uint32_t v) { *p = v; }
 
-// RISCV is little-endian by definition.
-uint64_t read64le(const uint64_t *P) { return *P; }
+// RISCV is little-endian by definition: We can rely on (implicit) casts.
+uint64_t read64le(const uint64_t *p) { return *p; }
 
-// RISCV is little-endian by definition.
+// RISCV is little-endian by definition: We can rely on (implicit) casts.
 void write64le(uint64_t *p, uint64_t v) { *p = v; }
 
 uint32_t extractBits(uint64_t v, uint32_t begin, uint32_t end) {
   return (v & ((1ULL << (begin + 1)) - 1)) >> end;
 }
 
-uint32_t setLO12_I(uint32_t insn, uint32_t imm) {
-  IF_DEBUG(linker, debugBelch("setLO12_I: insn 0x%x imm 0x%x (insn & 0xfffff) "
-                              "0x%x (imm << 20) 0x%x \n",
-                              insn, imm, (insn & 0xfffff), (imm << 20)));
-  return (insn & 0xfffff) | (imm << 20);
-}
-
-uint32_t setLO12_S(uint32_t insn, uint32_t imm) {
-  return (insn & 0x1fff07f) | (extractBits(imm, 11, 5) << 25) |
-         (extractBits(imm, 4, 0) << 7);
-}
-
+// Set immediate val in the instruction at *loc. In U-type instructions the
+// upper 20bits carry the upper 20bits of the immediate.
 void setUType(inst_t *loc, int32_t val) {
   const unsigned bits = 32;
   uint32_t hi = val + 0x800;
-  checkInt(loc, SignExtend32(hi, bits) >> 12, 20);
+  checkInt(loc, signExtend32(hi, bits) >> 12, 20);
   IF_DEBUG(linker, debugBelch("setUType: hi 0x%x val 0x%x\n", hi, val));
-  write32le(loc, (read32le(loc) & 0xFFF) | (hi & 0xFFFFF000));
+
+  uint32_t imm = hi & 0xFFFFF000;
+  write32le(loc, (read32le(loc) & 0xFFF) | imm);
 }
 
+// Set immediate val in the instruction at *loc. In I-type instructions the
+// upper 12bits carry the lower 12bit of the immediate.
 void setIType(inst_t *loc, int32_t val) {
   uint64_t hi = (val + 0x800) >> 12;
   uint64_t lo = val - (hi << 12);
+
   IF_DEBUG(linker, debugBelch("setIType: hi 0x%lx lo 0x%lx\n", hi, lo));
   IF_DEBUG(linker, debugBelch("setIType: loc %p  *loc 0x%x  val 0x%x\n", loc,
                               *loc, val));
-  uint32_t insn = setLO12_I(read32le(loc), lo & 0xfff);
-  IF_DEBUG(linker, debugBelch("setIType: insn 0x%x\n", insn));
-  write32le(loc, insn);
+
+  uint32_t imm = lo & 0xfff;
+  uint32_t instr = (read32le(loc) & 0xfffff) | (imm << 20);
+
+  IF_DEBUG(linker, debugBelch("setIType: insn 0x%x\n", instr));
+  write32le(loc, instr);
   IF_DEBUG(linker, debugBelch("setIType: loc %p  *loc' 0x%x  val 0x%x\n", loc,
                               *loc, val));
 }
 
+// Set immediate val in the instruction at *loc. In S-type instructions the
+// lower 12 bits of the immediate are at bits 7 to 11 ([0:4]) and 25 to 31
+// ([5:11]).
 void setSType(inst_t *loc, uint32_t val) {
   uint64_t hi = (val + 0x800) >> 12;
   uint64_t lo = val - (hi << 12);
-  write32le(loc, setLO12_S(read32le(loc), lo));
+
+  uint32_t imm = lo;
+  uint32_t instr = (read32le(loc) & 0x1fff07f) | (extractBits(imm, 11, 5) << 25) |
+         (extractBits(imm, 4, 0) << 7);
+
+  write32le(loc, instr);
 }
 
+// Set immediate val in the instruction at *loc. In J-type instructions the
+// immediate has 20bits which are pretty scattered:
+// instr bit -> imm bit
+// 31 -> 20
+// [30:21] -> [10:1]
+// 20 -> 11
+// [19:12] -> [19:12]
+//
+// N.B. bit 0 of the immediate is missing!
 void setJType(inst_t *loc, uint32_t val) {
   checkInt(loc, val, 21);
 
@@ -220,6 +223,15 @@ void setJType(inst_t *loc, uint32_t val) {
   write32le(loc, insn);
 }
 
+// Set immediate val in the instruction at *loc. In B-type instructions the
+// immediate has 12bits which are pretty scattered:
+// instr bit -> imm bit
+// 31 -> 12
+// [30:25] -> [10:5]
+// [11:8] -> [4:1]
+// 7 -> 11
+//
+// N.B. bit 0 of the immediate is missing!
 void setBType(inst_t *loc, uint32_t val) {
   checkInt(loc, val, 13);
 
@@ -233,6 +245,18 @@ void setBType(inst_t *loc, uint32_t val) {
   write32le(loc, insn);
 }
 
+
+// Set immediate val in the instruction at *loc. CB-type instructions have a
+// lenght of 16 bits (half-word, compared to the usual 32bit/word instructions.)
+// The immediate has 8bits which are pretty scattered:
+// instr bit -> imm bit
+// 12 -> 8
+// [11:10] -> [4:3]
+// [6:5] -> [7:6]
+// [4:3] -> [2:1]
+// 2 -> 5
+//
+// N.B. bit 0 of the immediate is missing!
 void setCBType(cinst_t *loc, uint32_t val) {
   checkInt((inst_t *)loc, val, 9);
   uint16_t insn = read16le(loc) & 0xE383;
@@ -246,6 +270,20 @@ void setCBType(cinst_t *loc, uint32_t val) {
   write16le(loc, insn);
 }
 
+// Set immediate val in the instruction at *loc. CJ-type instructions have a
+// lenght of 16 bits (half-word, compared to the usual 32bit/word instructions.)
+// The immediate has 11bits which are pretty scattered:
+// instr bit -> imm bit
+// 12 -> 11
+// 11 -> 4
+// [10:9] ->[9:8]
+// 8 -> 10
+// 7 -> 6
+// 6 -> 7
+// [5:3] -> [3:1]
+// 2 -> 5
+//
+// N.B. bit 0 of the immediate is missing!
 void setCJType(cinst_t *loc, uint32_t val) {
   checkInt((inst_t *)loc, val, 12);
   uint16_t insn = read16le(loc) & 0xE003;
@@ -428,9 +466,9 @@ int32_t computeAddend(ElfRelocationATable * relaTab, unsigned relNo, Elf_Rel *re
     FALLTHROUGH;
   case R_RISCV_PCREL_LO12_I: {
     // Lookup related HI20 relocation and use that value. I'm still confused why
-    // relocations aren't pure, but this is how LLVM does it. And, calculating
-    // the lower 12 bit without any relation ship to the GOT entry's address
-    // makes no sense either.
+    // relocations aren't self-contained, but this is how LLVM does it. And,
+    // calculating the lower 12 bit without any relationship to the GOT entry's
+    // address makes no sense either.
       for (int64_t i = relNo; i >= 0 ; i--) {
         Elf_Rela *rel_prime = &relaTab->relocations[i];
 
@@ -630,19 +668,16 @@ bool relocateObjectCodeRISCV64(ObjectCode *oc) {
 }
 
 void flushInstructionCacheRISCV64(ObjectCode *oc) {
-  // Synchronize the memory and instruction cache to prevent illegal
-  // instruction exceptions. On Linux the parameters of
-  // __builtin___clear_cache are currently unused. Add them anyways for future
-  // compatibility. (I.e. the parameters couldn't be checked during
-  // development.)
+  // Synchronize the memory and instruction cache to prevent illegal instruction
+  // exceptions. On Linux the parameters of __builtin___clear_cache are
+  // currently unused. Add them anyways for future compatibility. (I.e. the
+  // parameters couldn't be checked during development.)
 
   /* The main object code */
   void *codeBegin = oc->image + oc->misalignment;
-  // TODO: Check the upper boundary e.g. with a debugger.
   __builtin___clear_cache(codeBegin, (void*) ((uint64_t*) codeBegin + oc->fileSize));
 
   /* Jump Islands */
-  // TODO: Check the upper boundary e.g. with a debugger.
   __builtin___clear_cache((void *)oc->symbol_extras,
                           (void *)(oc->symbol_extras + oc->n_symbol_extras));
 



View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/bbf81c3ff30eb7ef53212bc6e3ae7ac567e93b3e...cf5c49087f25f3c7ae08da0dec34504e7aa8d3c7

-- 
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/bbf81c3ff30eb7ef53212bc6e3ae7ac567e93b3e...cf5c49087f25f3c7ae08da0dec34504e7aa8d3c7
You're receiving this email because of your account on gitlab.haskell.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20240628/65dd2b8c/attachment-0001.html>


More information about the ghc-commits mailing list