From f3332dbf23e895974d345207b914e317038e740f Mon Sep 17 00:00:00 2001 From: Ben Wiederhake Date: Sat, 24 Jun 2023 01:04:12 +0200 Subject: [PATCH] Assembler: Error on unused labels --- assembler/asm.py | 25 +++++++++++++++++++++++-- assembler/asm_test.py | 12 ++++++++++-- src/connect4.rs | 4 ++-- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/assembler/asm.py b/assembler/asm.py index a96da40..c571a21 100755 --- a/assembler/asm.py +++ b/assembler/asm.py @@ -109,8 +109,9 @@ def __init__(self): self.segment_words = [None] * (SEGMENT_LENGTH // 2) self.current_lineno = None self.current_pointer = 0x0000 - self.known_labels = dict() - self.forward_references = dict() + self.unused_labels = set() + self.known_labels = dict() # Values are (destination_offset, destination_lineno) + self.forward_references = dict() # Values are lists of ForwardReference instances self.error_log = [] self.expect_hash = None # Or tuple (line, SHA256 hex) @@ -158,6 +159,7 @@ def forward(self, by_words, label_name, bound_method, data): fwd_ref = ForwardReference(self, by_words, bound_method, data) if label_name in self.known_labels: # Immediate resolution + self.unused_labels.discard(label_name) return fwd_ref.apply() else: # Must be skipped for now, to be patched when 'label_name' is defined @@ -720,6 +722,7 @@ def emit_b_by_value(self, command, condition_reg, offset_value): def emit_b_to_label(self, command, condition_reg, label_name): destination_offset, destination_lineno = self.known_labels[label_name] + self.unused_labels.discard(label_name) offset_value = mod_s16(destination_offset - self.current_pointer) return self.emit_b_by_value( f"{command} (to label {label_name}=0x{destination_offset:04X}, defined in line {destination_lineno})", @@ -801,6 +804,7 @@ def command_j_immediate(self, command, offset): def emit_j_to_label(self, label_name, extra_offset): destination = self.known_labels[label_name][0] + extra_offset + self.unused_labels.discard(label_name) delta = mod_s16(destination - self.current_pointer) pseudo_command = f"j (to {label_name} {extra_offset:+} = by {delta:+})" return self.command_j_immediate(pseudo_command, delta) @@ -889,6 +893,7 @@ def parse_directive_offset(self, command, args): f"The already-defined labels are: {sorted(list(self.known_labels.keys()))}" ) self.current_pointer = self.known_labels[label_name][0] + self.unused_labels.discard(label_name) # No codegen return True @@ -929,6 +934,7 @@ def parse_directive_label(self, command, args): return self.error( f"Label '{label_name}' previously defined in line {old_line} (old offset 0x{old_offset:04X}, new offset 0x{self.current_pointer:04X})" ) + assert label_name not in self.unused_labels self.known_labels[label_name] = (self.current_pointer, self.current_lineno) old_references = self.forward_references.get(label_name) if old_references is not None: @@ -939,6 +945,8 @@ def parse_directive_label(self, command, args): any_reference_failed = True if any_reference_failed: return self.error(f"When label {label_name} was defined.") + else: + self.unused_labels.add(label_name) # No codegen return True @@ -982,6 +990,7 @@ def parse_line(self, line, lineno): def segment_bytes(self): assert len(self.segment_words) == 65536 + has_problem = False if self.forward_references: error_text = ", ".join( f"line {fwd_ref.orig_lineno} at offset {fwd_ref.orig_pointer} references label {label_name}" @@ -994,6 +1003,18 @@ def segment_bytes(self): self.error( f"Did you mean any of these defined labels? {list(self.known_labels.keys())}" ) + has_problem = True + if self.unused_labels: + error_text = ", ".join( + f"'{label_name}' (line {label_lineno}, offset {label_offset})" + for label_name in sorted(self.unused_labels) + for label_offset, label_lineno in [self.known_labels[label_name]] + ) + # FIXME write test + self.error( + f"Unused label(s), try using them in dead code, or commenting them out: {error_text}" + ) + if has_problem: return None segment = bytearray(SEGMENT_LENGTH) for i, word in enumerate(self.segment_words): diff --git a/assembler/asm_test.py b/assembler/asm_test.py index c6add70..08240fb 100755 --- a/assembler/asm_test.py +++ b/assembler/asm_test.py @@ -770,6 +770,8 @@ def test_wrapping(self): """\ .label _hello_world ret + # Cannot have an unreferenced label, sadly :( + .offset _hello_world """, "102A", [], @@ -782,6 +784,10 @@ def test_wrapping(self): ret .label _hello_more_world lw r4, 0x56 + # Cannot have an unreferenced label, sadly :( + .offset _hello_world + .offset _hello_world_again + .offset _hello_more_world """, "102A 3456", [], @@ -2175,6 +2181,7 @@ def test_wrapping(self): [ "line 5: Found end of asm text, but some forward references are unresolved: line 4 at offset 2 references label _wrong_label", "line 5: Did you mean any of these defined labels? ['_some_label']", + "line 5: Unused label(s), try using them in dead code, or commenting them out: '_some_label' (line 2, offset 1)", ], ), ( @@ -2279,6 +2286,7 @@ def test_wrapping(self): [ "line 5: Found end of asm text, but some forward references are unresolved: line 4 at offset 2 references label _wrong_label", "line 5: Did you mean any of these defined labels? ['_some_label']", + "line 5: Unused label(s), try using them in dead code, or commenting them out: '_some_label' (line 2, offset 1)", ], ), ( @@ -2893,14 +2901,14 @@ def test_wrapping(self): lw r1, 0xFF89 lw r1, r1 b r1 _move_nonzero # (offset is +0x3) - .label _move_zero # On move 0, play in column 3. + # .label _move_zero # On move 0, play in column 3. lw r0, 3 ret .label _move_nonzero lw r0, 18 ge r1 r0 b r0 _move_late # (offset is +0x2) - .label _move_early # On moves 1-17, play in column (n - 1) % 7. + # .label _move_early # On moves 1-17, play in column (n - 1) % 7. decr r1 # j _move_late # Surprise optimization: This is a noop, this time! .label _move_late # On moves 18-20, play in column n % 7. diff --git a/src/connect4.rs b/src/connect4.rs index 879c416..21dcedc 100644 --- a/src/connect4.rs +++ b/src/connect4.rs @@ -982,14 +982,14 @@ mod test_game { instructions_two[0] = 0x3189; // lw r1, 0xFF89 instructions_two[1] = 0x2111; // lw r1, r1 instructions_two[2] = 0x9101; // b r1 _move_nonzero # (offset is +0x3) - // .label _move_zero # On move 0, play in column 3. + // # .label _move_zero # On move 0, play in column 3. instructions_two[3] = 0x3003; // lw r0, 3 instructions_two[4] = 0x102A; // ret // .label _move_nonzero instructions_two[5] = 0x3012; // lw r0, 18 instructions_two[6] = 0x8610; // ge r1 r0 instructions_two[7] = 0x9000; // b r0 _move_late # (offset is +0x2) - // .label _move_early # On moves 1-17, play in column (n - 1) % 7. + // # .label _move_early # On moves 1-17, play in column (n - 1) % 7. instructions_two[8] = 0x5811; // decr r1 // # j _move_late # Surprise optimization: This is a noop, this time! // .label _move_late # On moves 18-20, play in column n % 7.