Skip to content

Commit 4a7420a

Browse files
committed
[assembler] bugfix: allow forward references to origin names.
1 parent 8b6b8de commit 4a7420a

File tree

4 files changed

+247
-40
lines changed

4 files changed

+247
-40
lines changed

assembler/src/asmlib/ast.rs

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1281,27 +1281,6 @@ impl Origin {
12811281
Address::new(u18!(0o200_000))
12821282
}
12831283

1284-
pub(crate) fn has_same_specification(&self, that: &Origin) -> bool {
1285-
use Origin::*;
1286-
if self.span() == that.span() {
1287-
match (self, that) {
1288-
(Literal(_, a1), Literal(_, a2)) if a1 == a2 => true,
1289-
(Symbolic(_, n1), Symbolic(_, n2)) if n1 == n2 => true,
1290-
(Deduced(_, n1, a1), Deduced(_, n2, a2)) if n1 == n2 && a1 == a2 => true,
1291-
(Deduced(_, n1, _), Symbolic(_, n2)) | (Symbolic(_, n1), Deduced(_, n2, _))
1292-
if n1 == n2 =>
1293-
{
1294-
// Only one of the two origins has an address, so
1295-
// that does not feature in the comparison.
1296-
true
1297-
}
1298-
_ => false,
1299-
}
1300-
} else {
1301-
false
1302-
}
1303-
}
1304-
13051284
pub(super) fn symbol_uses(
13061285
&self,
13071286
block_id: BlockIdentifier,

assembler/src/asmlib/eval.rs

Lines changed: 114 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use base::{
99
prelude::{Address, IndexBy, Unsigned18Bit, Unsigned36Bit},
1010
subword,
1111
};
12+
#[cfg(test)]
13+
use base::{u18, u36};
1214

1315
use super::ast::*;
1416
use super::collections::OneOrMore;
@@ -17,6 +19,8 @@ use super::memorymap::{
1719
};
1820
use super::source::Source;
1921
use super::span::*;
22+
#[cfg(test)]
23+
use super::symbol::AddressUse;
2024
use super::symbol::{ConfigUse, IndexUse, OriginUse, SymbolName};
2125

2226
use super::types::{AssemblerFailure, BlockIdentifier, MachineLimitExceededFailure, ProgramError};
@@ -212,6 +216,7 @@ impl<R: RcUpdater> EvaluationContext<'_, R> {
212216
match existing_def {
213217
ImplicitDefinition::DefaultAssigned(value, _) => Ok(*value),
214218
ImplicitDefinition::Undefined(_) => {
219+
let saved_def = existing_def.clone();
215220
let context: SymbolContext = existing_def.context().clone();
216221
let span: Span = *context.any_span();
217222

@@ -243,8 +248,9 @@ impl<R: RcUpdater> EvaluationContext<'_, R> {
243248
) {
244249
Ok(()) => Ok(value),
245250
Err(e) => {
251+
dbg!(saved_def);
246252
panic!(
247-
"got a bad symbol definition error ({e:?}) on a previously undefined origin symbol"
253+
"got a bad symbol definition error ({e:?}) on a previously undefined origin symbol {name}"
248254
);
249255
}
250256
}
@@ -274,6 +280,113 @@ impl<R: RcUpdater> EvaluationContext<'_, R> {
274280
}
275281
}
276282

283+
#[test]
284+
fn can_deduce_address_of_origin_with_previous_forward_reference() {
285+
// GIVEN a program containing two blocks, in whicht the second block has a
286+
// symbolic origin and where there is a forward reference within the first
287+
// block to the address of the second block
288+
let origin_name = SymbolName::from("INPUT");
289+
let origin_def = Origin::Symbolic(span(2455..2461), origin_name.clone());
290+
const BLOCK0_SIZE: Unsigned18Bit = u18!(4);
291+
const BLOCK1_SIZE: Unsigned18Bit = u18!(3);
292+
let block0_pos = BlockPosition {
293+
block_identifier: BlockIdentifier::from(0),
294+
// The first reference to block 1 is inside block 1, so the
295+
// span of block1 includes the location of the first reference
296+
// to origin_name. This is not a necessary setup for our test
297+
// scenario, but it is realistic.
298+
span: span(0..2400),
299+
origin: None,
300+
block_address: None,
301+
block_size: BLOCK0_SIZE,
302+
};
303+
let block1_pos = BlockPosition {
304+
block_identifier: BlockIdentifier::from(1),
305+
span: span(2455..2800),
306+
origin: None,
307+
block_address: None,
308+
block_size: BLOCK1_SIZE,
309+
};
310+
311+
let mut implicit_symtab = ImplicitSymbolTable::default();
312+
implicit_symtab.load_test_definitions([(
313+
origin_name.clone(),
314+
ImplicitDefinition::Undefined(SymbolContext {
315+
address: AddressUse::IncludesAddress,
316+
origin: OriginUse::IncludesOrigin(block1_pos.block_identifier, origin_def.clone()),
317+
uses: [
318+
// The first reference is inside block 0.
319+
OrderableSpan(span(1188..1193)),
320+
// The second reference is in the origin definition of
321+
// block 1 (which is the thing we're trying to assign
322+
// a default value to).
323+
OrderableSpan(span(block1_pos.span.start..(block1_pos.span.start + 6))),
324+
// There is a third reference (but this is just from
325+
// our motivating example, it is not necessary to the
326+
// test).
327+
OrderableSpan(span(3059..3064)),
328+
]
329+
.into_iter()
330+
.collect(),
331+
}),
332+
)]);
333+
334+
let explicit_symtab = ExplicitSymbolTable::default();
335+
let mut register_assigner = IndexRegisterAssigner::default();
336+
let mut memory_map = MemoryMap::new([
337+
(span(0..10), None, BLOCK0_SIZE),
338+
(
339+
span(2455..2461),
340+
Some(Origin::Symbolic(span(2455..2461), origin_name.clone())),
341+
BLOCK1_SIZE,
342+
),
343+
]);
344+
let mut rc_block = RcBlock::default();
345+
let mut context = EvaluationContext {
346+
here: HereValue::Address(Address::from(u18!(0o100))),
347+
explicit_symtab: &explicit_symtab,
348+
implicit_symtab: &mut implicit_symtab,
349+
index_register_assigner: &mut register_assigner,
350+
memory_map: &memory_map,
351+
rc_updater: &mut rc_block,
352+
lookup_operation: LookupOperation::default(),
353+
};
354+
355+
// Assign an address for block 0 as part of the scenario setup.
356+
let block0_addr = block0_pos
357+
.evaluate(&mut context)
358+
.expect("should be able to assign an address to the first block");
359+
drop(context); // no longer want an immutable borrow on memory_map.
360+
memory_map.set_block_position(
361+
block0_pos.block_identifier,
362+
subword::right_half(block0_addr).into(),
363+
);
364+
365+
// WHEN we try to assign an address to the second block (block 1).
366+
let mut context = EvaluationContext {
367+
here: HereValue::Address(Address::from(u18!(0o100))),
368+
explicit_symtab: &explicit_symtab,
369+
implicit_symtab: &mut implicit_symtab,
370+
index_register_assigner: &mut register_assigner,
371+
memory_map: &memory_map,
372+
rc_updater: &mut rc_block,
373+
lookup_operation: LookupOperation::default(),
374+
};
375+
let eval_result = context.fetch_or_assign_default(&origin_name);
376+
377+
// THEN we should assign an address without failing.
378+
match eval_result {
379+
Err(e) => {
380+
panic!("should not fail to assign a start address to a block: {e}");
381+
}
382+
Ok(default_value) => {
383+
let expected: Unsigned36Bit =
384+
u36!(0o200000).wrapping_add(Unsigned36Bit::from(BLOCK0_SIZE));
385+
assert_eq!(default_value, expected);
386+
}
387+
}
388+
}
389+
277390
impl<R: RcUpdater> EvaluationContext<'_, R> {
278391
pub(super) fn for_target_address<F, T>(&mut self, new_here: HereValue, mut closure: F) -> T
279392
where

assembler/src/asmlib/symbol.rs

Lines changed: 121 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -161,10 +161,8 @@ pub(crate) enum AddressUse {
161161
impl AddressUse {
162162
fn or(&self, other: &AddressUse) -> AddressUse {
163163
match (self, other) {
164-
(AddressUse::IncludesAddress, _) | (_, AddressUse::IncludesAddress) => {
165-
AddressUse::IncludesAddress
166-
}
167-
_ => AddressUse::NotAddress,
164+
(AddressUse::NotAddress, AddressUse::NotAddress) => AddressUse::NotAddress,
165+
_ => AddressUse::IncludesAddress,
168166
}
169167
}
170168
}
@@ -249,7 +247,9 @@ impl SymbolContext {
249247
pub(crate) fn origin(block_id: BlockIdentifier, origin: Origin) -> SymbolContext {
250248
let span = origin.span();
251249
SymbolContext {
252-
address: AddressUse::NotAddress,
250+
// All origins are (implicitly) uses of the symbol as an
251+
// address.
252+
address: AddressUse::IncludesAddress,
253253
origin: OriginUse::IncludesOrigin(block_id, origin),
254254
uses: SymbolContext::uses(span),
255255
}
@@ -301,7 +301,7 @@ impl SymbolContext {
301301
OriginUse::IncludesOrigin(my_block, my_origin),
302302
OriginUse::IncludesOrigin(their_block, their_origin),
303303
) => {
304-
if my_block == their_block && my_origin.has_same_specification(their_origin) {
304+
if my_block == their_block {
305305
// If one of the origins is a deduced origin, that
306306
// has more information, so retain that one.
307307
//
@@ -366,7 +366,24 @@ impl SymbolContext {
366366
}
367367

368368
pub(super) fn requires_rc_word_allocation(&self) -> bool {
369-
self.address == AddressUse::IncludesAddress
369+
if self.address == AddressUse::IncludesAddress {
370+
if matches!(&self.origin, &OriginUse::IncludesOrigin(_, _)) {
371+
// This symbol is used in address contexts, but it is
372+
// the name used for an origin. Therefore we do not
373+
// need to allocate an RC-word for it.
374+
false
375+
} else {
376+
// This name is used in an address context but it is
377+
// not the name of an origin, so when there is no tag
378+
// definition for it, we will need to allocate an
379+
// RC-word for it.
380+
true
381+
}
382+
} else {
383+
// Since nothing expects this symbol to refer to an
384+
// address, there is no need to allocate an RC-word.
385+
false
386+
}
370387
}
371388

372389
pub(super) fn any_span(&self) -> &Span {
@@ -511,17 +528,103 @@ fn test_origin_cannot_be_used_as_a_configuration_value() {
511528

512529
#[test]
513530
fn test_deduced_origin_merge() {
514-
use super::span::span;
515-
use base::prelude::Address;
516-
use base::u18;
517-
let span = span(0..4);
518-
let block = BlockIdentifier::from(0);
519-
let name = SymbolName::from("OGN");
520-
let address = Address::from(u18!(0o200_000));
521-
let mut current = SymbolContext::origin(block, Origin::Symbolic(span, name.clone()));
522-
let new_use = SymbolContext::origin(block, Origin::Deduced(span, name.clone(), address));
523-
assert_eq!(current.merge(&name, new_use.clone()), Ok(()));
524-
assert_eq!(current, new_use);
531+
struct Contexts {
532+
reference: SymbolContext,
533+
origin_definition: SymbolContext,
534+
expected_merge: SymbolContext,
535+
}
536+
// Convenience function for creating the test input and expected output.
537+
fn make_symbolic_and_deduced_origin_contexts(
538+
name: SymbolName,
539+
is_forward_reference: bool,
540+
) -> Contexts {
541+
let block = BlockIdentifier::from(0);
542+
543+
let deduced_origin_span: Span = span(1000..1010);
544+
let symbol_span = if is_forward_reference {
545+
// The reference appears before the origin specification
546+
span(10..20)
547+
} else {
548+
// The reference appears after the origin specification
549+
span(2000..2020)
550+
};
551+
552+
// In our examples, the deduced origin value is a
553+
// symbolically-defined origin to which we would deduced
554+
// address (from the locations and sizes of the blocks
555+
// preceding it).
556+
let deduced_origin_context = SymbolContext {
557+
address: AddressUse::IncludesAddress,
558+
origin: OriginUse::IncludesOrigin(
559+
block,
560+
Origin::Symbolic(deduced_origin_span, name.clone()),
561+
),
562+
uses: SymbolContext::uses(deduced_origin_span),
563+
};
564+
565+
// The symbolic context is is simply a reference to the
566+
// origin, either preceding (is_forward_reference) or
567+
// following (!is_forward_reference) the definition of the
568+
// origin.
569+
let reference_context = SymbolContext {
570+
address: AddressUse::IncludesAddress,
571+
// Although this use of the symbol will turn out to be a
572+
// reference to an origin, we cannot tell that at the
573+
// reference site, and so the context in which this symbol
574+
// is used at that point is not an origin context.
575+
origin: OriginUse::NotOrigin {
576+
config: ConfigUse::NotConfig,
577+
index: IndexUse::NotIndex,
578+
},
579+
uses: SymbolContext::uses(symbol_span),
580+
};
581+
582+
Contexts {
583+
reference: reference_context,
584+
origin_definition: deduced_origin_context,
585+
expected_merge: SymbolContext {
586+
address: AddressUse::IncludesAddress,
587+
origin: OriginUse::IncludesOrigin(
588+
BlockIdentifier::from(0),
589+
Origin::Symbolic(deduced_origin_span, name.clone()),
590+
),
591+
uses: [
592+
OrderableSpan(deduced_origin_span),
593+
OrderableSpan(symbol_span),
594+
]
595+
.into_iter()
596+
.collect(),
597+
},
598+
}
599+
}
600+
let name = SymbolName::from("OGNX");
601+
// Set up contexts for the forward-reference and the backward-reference cases.
602+
let contexts_backward_ref = make_symbolic_and_deduced_origin_contexts(name.clone(), false);
603+
let contexts_forward_ref = make_symbolic_and_deduced_origin_contexts(name.clone(), true);
604+
// The value in expected_merge is not the same in each case, since
605+
// although the span of the origin definition is fixed, the span
606+
// of the reference to it is different for the forward-reference
607+
// and backward-reference cases.
608+
609+
// Merge for the defined-then-used direction (where we encouter
610+
// the origin defintiion and later a reference to it).
611+
let mut current = contexts_backward_ref.origin_definition.clone();
612+
assert_eq!(
613+
current.merge(&name, contexts_backward_ref.reference.clone()),
614+
Ok(())
615+
);
616+
assert_eq!(current, contexts_backward_ref.expected_merge);
617+
618+
// Merge in forward-reference direction (where we find a reference
619+
// to the origin address of a block before we have seen the origin
620+
// definition of that block).
621+
//
622+
let mut current = contexts_forward_ref.reference.clone(); // we find the fwd ref first
623+
assert_eq!(
624+
current.merge(&name, contexts_forward_ref.origin_definition.clone()),
625+
Ok(())
626+
);
627+
assert_eq!(current, contexts_forward_ref.expected_merge);
525628
}
526629

527630
impl From<(&Script, Span)> for SymbolContext {

assembler/src/asmlib/symtab.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,18 @@ impl ImplicitSymbolTable {
8585
.or_insert_with(|| ImplicitDefinition::DefaultAssigned(value.into(), context.clone()))
8686
.merge_context(&name, context)
8787
}
88+
89+
#[cfg(test)]
90+
pub(super) fn load_test_definitions<
91+
I: IntoIterator<Item = (SymbolName, ImplicitDefinition)>,
92+
>(
93+
&mut self,
94+
defs: I,
95+
) {
96+
for (name, definition) in defs.into_iter() {
97+
self.definitions.insert(name, definition);
98+
}
99+
}
88100
}
89101

90102
#[derive(Debug, Default, Clone, PartialEq, Eq)]

0 commit comments

Comments
 (0)