Skip to content

Commit

Permalink
fix #115: allow labels in asm blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
hlorenzi committed Feb 1, 2025
1 parent a48d22b commit 95bc0ec
Show file tree
Hide file tree
Showing 9 changed files with 315 additions and 27 deletions.
226 changes: 201 additions & 25 deletions src/asm/resolver/eval_asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,195 @@ pub fn eval_asm(

// Keep the current position to advance
// between instructions
let mut cur_position = ctx.bank_data.cur_position;
let position_at_start = ctx.bank_data.cur_position;


let mut labels = std::collections::HashMap::<String, expr::Value>::new();
for node in &query.ast.nodes
{
if let asm::AstAny::Symbol(ast_symbol) = node
{
if !matches!(ast_symbol.kind, asm::AstSymbolKind::Label)
{
query.report.error_span(
"only labels are permitted in `asm` blocks",
node.span());

return Err(());
}

if ast_symbol.hierarchy_level != 0
{
query.report.error_span(
"only top-level labels are permitted in `asm` blocks",
node.span());

return Err(());
}

labels.insert(
ast_symbol.name.clone(),
expr::Value::Unknown);
}

else if let asm::AstAny::Instruction(_) = node
{
continue;
}

else
{
query.report.error_span(
"invalid content for `asm` block",
node.span());

return Err(());
}
}

resolve_iteratively(
opts,
fileserver,
decls,
defs,
ctx,
query,
position_at_start,
&mut labels)
}


fn resolve_iteratively(
opts: &asm::AssemblyOptions,
fileserver: &mut dyn util::FileServer,
decls: &asm::ItemDecls,
defs: &asm::ItemDefs,
ctx: &asm::ResolverContext,
query: &mut expr::EvalAsmBlockQuery,
position_at_start: usize,
labels: &mut std::collections::HashMap::<String, expr::Value>)
-> Result<expr::Value, ()>
{
let mut iter_count = 0;
let max_iterations = opts.max_iterations;

while iter_count < max_iterations
{
iter_count += 1;

let is_first_iteration = iter_count == 1;
let is_last_iteration = iter_count == max_iterations;

let result = resolve_once(
opts,
fileserver,
decls,
defs,
ctx,
query,
position_at_start,
labels,
is_first_iteration,
is_last_iteration)?;

if !result.unstable
{
break;
}
}

let result = resolve_once(
opts,
fileserver,
decls,
defs,
ctx,
query,
position_at_start,
labels,
false,
true)?;

if !result.unstable
{
return Ok(result.value);
}

if ctx.can_guess()
{
return Ok(expr::Value::Unknown);
}

query.report.error_span(
"`asm` block did not converge",
query.span);

return Err(());
}


struct AsmBlockResult
{
value: expr::Value,
unstable: bool,
}


fn resolve_once(
opts: &asm::AssemblyOptions,
fileserver: &mut dyn util::FileServer,
decls: &asm::ItemDecls,
defs: &asm::ItemDefs,
ctx: &asm::ResolverContext,
query: &mut expr::EvalAsmBlockQuery,
position_at_start: usize,
labels: &mut std::collections::HashMap::<String, expr::Value>,
is_first_iteration: bool,
is_last_iteration: bool)
-> Result<AsmBlockResult, ()>
{
let mut result = util::BigInt::new(0, Some(0));
let mut cur_position = position_at_start;
let mut unstable = false;

for node in &query.ast.nodes
{
if let asm::AstAny::Instruction(ast_instr) = node
// Clone the context to use our own position
let new_bank_datum = asm::resolver::BankData {
cur_position,
};

let mut inner_ctx = ctx.clone();
inner_ctx.bank_data = &new_bank_datum;
inner_ctx.is_first_iteration = is_first_iteration;
inner_ctx.is_last_iteration = is_last_iteration;


if let asm::AstAny::Symbol(ast_symbol) = node
{
let cur_address = inner_ctx.eval_address(
query.report,
query.span,
defs,
true)?;

let new_value = expr::Value::make_integer(cur_address);

let maybe_prev_value = labels.get(&ast_symbol.name);
if let Some(prev_value) = maybe_prev_value
{
if prev_value != &new_value
{
unstable = true;
}
}

labels.insert(
ast_symbol.name.clone(),
new_value);
}

else if let asm::AstAny::Instruction(ast_instr) = node
{
let substs = parse_substitutions(
query.report,
Expand Down Expand Up @@ -77,27 +258,23 @@ pub fn eval_asm(

maybe_no_matches?;


// Clone the context to use our own position
let new_bank_datum = asm::resolver::BankData {
cur_position,
};

let mut inner_ctx = ctx.clone();
inner_ctx.bank_data = &new_bank_datum;


// Try to resolve the encoding
let mut new_eval_ctx = query.eval_ctx
.hygienize_locals_for_asm_subst();

for (label_name, label_value) in labels.iter()
{
new_eval_ctx.set_local(label_name, label_value.clone());
}

if let Some(ref s) = attempted_match_excerpt
{
query.report.push_parent_short_note(
s,
ast_instr.span);
}

let maybe_encodings = asm::resolver::instruction::resolve_encoding(
query.report,
opts,
Expand All @@ -108,7 +285,7 @@ pub fn eval_asm(
defs,
&mut inner_ctx,
&mut new_eval_ctx);

if let Some(_) = attempted_match_excerpt
{
query.report.pop_parent();
Expand All @@ -127,23 +304,22 @@ pub fn eval_asm(
&encodings[0].1,
(size, 0));
}
else if !ctx.can_guess()
else
{
return Err(());
}
}
unstable = true;

else
{
query.report.error_span(
"invalid content for `asm` block",
node.span());

return Err(());
if !inner_ctx.can_guess()
{
return Err(());
}
}
}
}

Ok(expr::Value::make_integer(result))
Ok(AsmBlockResult {
value: expr::Value::make_integer(result),
unstable,
})
}


Expand Down
4 changes: 2 additions & 2 deletions src/asm/resolver/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ pub fn resolve_encoding<'encoding>(

if num_encodings_resolved == 0
{
if ctx.is_last_iteration
if !ctx.can_guess()
{
let mut msgs = Vec::new();

Expand Down Expand Up @@ -195,7 +195,7 @@ pub fn resolve_encoding<'encoding>(

// Expect only a single remaining encoding
// on the last iteration
if ctx.is_last_iteration &&
if !ctx.can_guess() &&
smallest_encodings.len() > 1
{
let mut notes = Vec::new();
Expand Down
23 changes: 23 additions & 0 deletions tests/expr_asm/err_label_convergence.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ruledef test
{
ld {x} =>
{
assert(x <= 0x8)
0x11 @ x`16
}

ld {x} =>
{
assert(x > 0x8)
0x22 @ x`8
}
test => asm {
ld label
ld label
ld label
label:
}
}

test ; error: failed / note:_:15: within / error:_:15: did not converge
13 changes: 13 additions & 0 deletions tests/expr_asm/err_pc_misaligned.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ruledef
{
ld {addr: u8} => 0xee @ addr
test => asm {
ld 0x11
ld 0x22
ld $
ld 0x33
}
}

#d 0xf
test ; error: failed / note:_:4: within / error:_:7: failed / note:_:3: within / error:_:7: not aligned / note: 4 more bits
22 changes: 22 additions & 0 deletions tests/expr_asm/ok_label_convergence.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ruledef test
{
ld {x} =>
{
assert(x <= 0x8)
0x11 @ x`16
}

ld {x} =>
{
assert(x > 0x8)
0x22 @ x`8
}
test => asm {
ld label
ld label
label:
}
}

test ; = 0x110006_110006
13 changes: 13 additions & 0 deletions tests/expr_asm/ok_label_forwardref.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ruledef
{
jmp {addr: u8} => 0xee @ addr
loop => asm {
jmp 0x11
jmp label
label:
jmp 0x22
jmp 0x33
}
}

loop ; = 0xee11_ee04_ee22_ee33
14 changes: 14 additions & 0 deletions tests/expr_asm/ok_label_shadowing.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#ruledef
{
jmp {addr: u8} => 0xee @ addr
loop => asm {
jmp 0x11
label:
jmp 0x22
jmp label
jmp 0x33
}
}

label:
loop ; = 0xee11_ee22_ee02_ee33
Loading

0 comments on commit 95bc0ec

Please sign in to comment.