nirlineinfos.nim 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879
  1. #
  2. #
  3. # The Nim Compiler
  4. # (c) Copyright 2023 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # For the line information we use 32 bits. They are used as follows:
  10. # Bit 0 (AsideBit): If we have inline line information or not. If not, the
  11. # remaining 31 bits are used as an index into a seq[(LitId, int, int)].
  12. #
  13. # We use 10 bits for the "file ID", this means a program can consist of as much
  14. # as 1024 different files. (If it uses more files than that, the overflow bit
  15. # would be set.)
  16. # This means we have 21 bits left to encode the (line, col) pair. We use 7 bits for the column
  17. # so 128 is the limit and 14 bits for the line number.
  18. # The packed representation supports files with up to 16384 lines.
  19. # Keep in mind that whenever any limit is reached the AsideBit is set and the real line
  20. # information is kept in a side channel.
  21. import std / assertions
  22. const
  23. AsideBit = 1
  24. FileBits = 10
  25. LineBits = 14
  26. ColBits = 7
  27. FileMax = (1 shl FileBits) - 1
  28. LineMax = (1 shl LineBits) - 1
  29. ColMax = (1 shl ColBits) - 1
  30. static:
  31. assert AsideBit + FileBits + LineBits + ColBits == 32
  32. import .. / ic / bitabs # for LitId
  33. type
  34. PackedLineInfo* = distinct uint32
  35. LineInfoManager* = object
  36. aside*: seq[(LitId, int32, int32)]
  37. proc pack*(m: var LineInfoManager; file: LitId; line, col: int32): PackedLineInfo =
  38. if file.uint32 <= FileMax.uint32 and line <= LineMax and col <= ColMax:
  39. let col = if col < 0'i32: 0'u32 else: col.uint32
  40. let line = if line < 0'i32: 0'u32 else: line.uint32
  41. # use inline representation:
  42. result = PackedLineInfo((file.uint32 shl 1'u32) or (line shl uint32(AsideBit + FileBits)) or
  43. (col shl uint32(AsideBit + FileBits + LineBits)))
  44. else:
  45. result = PackedLineInfo((m.aside.len shl 1) or AsideBit)
  46. m.aside.add (file, line, col)
  47. proc unpack*(m: LineInfoManager; i: PackedLineInfo): (LitId, int32, int32) =
  48. let i = i.uint32
  49. if (i and 1'u32) == 0'u32:
  50. # inline representation:
  51. result = (LitId((i shr 1'u32) and FileMax.uint32),
  52. int32((i shr uint32(AsideBit + FileBits)) and LineMax.uint32),
  53. int32((i shr uint32(AsideBit + FileBits + LineBits)) and ColMax.uint32))
  54. else:
  55. result = m.aside[int(i shr 1'u32)]
  56. proc getFileId*(m: LineInfoManager; i: PackedLineInfo): LitId =
  57. result = unpack(m, i)[0]
  58. when isMainModule:
  59. var m = LineInfoManager(aside: @[])
  60. for i in 0'i32..<16388'i32:
  61. for col in 0'i32..<100'i32:
  62. let packed = pack(m, LitId(1023), i, col)
  63. let u = unpack(m, packed)
  64. assert u[0] == LitId(1023)
  65. assert u[1] == i
  66. assert u[2] == col
  67. echo m.aside.len