uninit.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. """Insert checks for uninitialized values."""
  2. from __future__ import annotations
  3. from mypyc.analysis.dataflow import AnalysisDict, analyze_must_defined_regs, cleanup_cfg, get_cfg
  4. from mypyc.common import BITMAP_BITS
  5. from mypyc.ir.func_ir import FuncIR, all_values
  6. from mypyc.ir.ops import (
  7. Assign,
  8. BasicBlock,
  9. Branch,
  10. ComparisonOp,
  11. Integer,
  12. IntOp,
  13. LoadAddress,
  14. LoadErrorValue,
  15. Op,
  16. RaiseStandardError,
  17. Register,
  18. Unreachable,
  19. Value,
  20. )
  21. from mypyc.ir.rtypes import bitmap_rprimitive
  22. def insert_uninit_checks(ir: FuncIR) -> None:
  23. # Remove dead blocks from the CFG, which helps avoid spurious
  24. # checks due to unused error handling blocks.
  25. cleanup_cfg(ir.blocks)
  26. cfg = get_cfg(ir.blocks)
  27. must_defined = analyze_must_defined_regs(
  28. ir.blocks, cfg, set(ir.arg_regs), all_values(ir.arg_regs, ir.blocks)
  29. )
  30. ir.blocks = split_blocks_at_uninits(ir.blocks, must_defined.before)
  31. def split_blocks_at_uninits(
  32. blocks: list[BasicBlock], pre_must_defined: AnalysisDict[Value]
  33. ) -> list[BasicBlock]:
  34. new_blocks: list[BasicBlock] = []
  35. init_registers = []
  36. init_registers_set = set()
  37. bitmap_registers: list[Register] = [] # Init status bitmaps
  38. bitmap_backed: list[Register] = [] # These use bitmaps to track init status
  39. # First split blocks on ops that may raise.
  40. for block in blocks:
  41. ops = block.ops
  42. block.ops = []
  43. cur_block = block
  44. new_blocks.append(cur_block)
  45. for i, op in enumerate(ops):
  46. defined = pre_must_defined[block, i]
  47. for src in op.unique_sources():
  48. # If a register operand is not guaranteed to be
  49. # initialized is an operand to something other than a
  50. # check that it is defined, insert a check.
  51. # Note that for register operand in a LoadAddress op,
  52. # we should be able to use it without initialization
  53. # as we may need to use its address to update itself
  54. if (
  55. isinstance(src, Register)
  56. and src not in defined
  57. and not (isinstance(op, Branch) and op.op == Branch.IS_ERROR)
  58. and not isinstance(op, LoadAddress)
  59. ):
  60. new_block, error_block = BasicBlock(), BasicBlock()
  61. new_block.error_handler = error_block.error_handler = cur_block.error_handler
  62. new_blocks += [error_block, new_block]
  63. if src not in init_registers_set:
  64. init_registers.append(src)
  65. init_registers_set.add(src)
  66. if not src.type.error_overlap:
  67. cur_block.ops.append(
  68. Branch(
  69. src,
  70. true_label=error_block,
  71. false_label=new_block,
  72. op=Branch.IS_ERROR,
  73. line=op.line,
  74. )
  75. )
  76. else:
  77. # We need to use bitmap for this one.
  78. check_for_uninit_using_bitmap(
  79. cur_block.ops,
  80. src,
  81. bitmap_registers,
  82. bitmap_backed,
  83. error_block,
  84. new_block,
  85. op.line,
  86. )
  87. raise_std = RaiseStandardError(
  88. RaiseStandardError.UNBOUND_LOCAL_ERROR,
  89. f'local variable "{src.name}" referenced before assignment',
  90. op.line,
  91. )
  92. error_block.ops.append(raise_std)
  93. error_block.ops.append(Unreachable())
  94. cur_block = new_block
  95. cur_block.ops.append(op)
  96. if bitmap_backed:
  97. update_register_assignments_to_set_bitmap(new_blocks, bitmap_registers, bitmap_backed)
  98. if init_registers:
  99. new_ops: list[Op] = []
  100. for reg in init_registers:
  101. err = LoadErrorValue(reg.type, undefines=True)
  102. new_ops.append(err)
  103. new_ops.append(Assign(reg, err))
  104. for reg in bitmap_registers:
  105. new_ops.append(Assign(reg, Integer(0, bitmap_rprimitive)))
  106. new_blocks[0].ops[0:0] = new_ops
  107. return new_blocks
  108. def check_for_uninit_using_bitmap(
  109. ops: list[Op],
  110. src: Register,
  111. bitmap_registers: list[Register],
  112. bitmap_backed: list[Register],
  113. error_block: BasicBlock,
  114. ok_block: BasicBlock,
  115. line: int,
  116. ) -> None:
  117. """Check if src is defined using a bitmap.
  118. Modifies ops, bitmap_registers and bitmap_backed.
  119. """
  120. if src not in bitmap_backed:
  121. # Set up a new bitmap backed register.
  122. bitmap_backed.append(src)
  123. n = (len(bitmap_backed) - 1) // BITMAP_BITS
  124. if len(bitmap_registers) <= n:
  125. bitmap_registers.append(Register(bitmap_rprimitive, f"__locals_bitmap{n}"))
  126. index = bitmap_backed.index(src)
  127. masked = IntOp(
  128. bitmap_rprimitive,
  129. bitmap_registers[index // BITMAP_BITS],
  130. Integer(1 << (index & (BITMAP_BITS - 1)), bitmap_rprimitive),
  131. IntOp.AND,
  132. line,
  133. )
  134. ops.append(masked)
  135. chk = ComparisonOp(masked, Integer(0, bitmap_rprimitive), ComparisonOp.EQ)
  136. ops.append(chk)
  137. ops.append(Branch(chk, error_block, ok_block, Branch.BOOL))
  138. def update_register_assignments_to_set_bitmap(
  139. blocks: list[BasicBlock], bitmap_registers: list[Register], bitmap_backed: list[Register]
  140. ) -> None:
  141. """Update some assignments to registers to also set a bit in a bitmap.
  142. The bitmaps are used to track if a local variable has been assigned to.
  143. Modifies blocks.
  144. """
  145. for block in blocks:
  146. if any(isinstance(op, Assign) and op.dest in bitmap_backed for op in block.ops):
  147. new_ops: list[Op] = []
  148. for op in block.ops:
  149. if isinstance(op, Assign) and op.dest in bitmap_backed:
  150. index = bitmap_backed.index(op.dest)
  151. new_ops.append(op)
  152. reg = bitmap_registers[index // BITMAP_BITS]
  153. new = IntOp(
  154. bitmap_rprimitive,
  155. reg,
  156. Integer(1 << (index & (BITMAP_BITS - 1)), bitmap_rprimitive),
  157. IntOp.OR,
  158. op.line,
  159. )
  160. new_ops.append(new)
  161. new_ops.append(Assign(reg, new))
  162. else:
  163. new_ops.append(op)
  164. block.ops = new_ops