commit c651b11ca099c4b878a023fbd61bde9f8e108eee
parent e301f7533bda6a2b139ad1771894919c12b87765
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Tue, 5 May 2026 17:19:04 -0700
hex2pp: single-pass + fixup list; cut wasted work; wire P1 lint
C reference (hex2pp.c) and the self-hosted P1 port (hex2pp.P1) both
move from a two-pass scanner to a single scan that emits zero
placeholders for forward refs and records fixups; an after-scan walk
resolves and patches them. Drops the duplicate-label dup-loop, replaces
the per-byte emit loops in .fill / .align with bulk fills, and avoids
copying name tokens by using pointer spans into the input buffer.
Discovered while porting: the P1 macro table only defines st/ld with
register a1 for offsets up to 64, and M0 silently passes unrecognized
tokens through as text. Two store/load sites in the new fixup record
needed offsets 72 and 80 and were producing misaligned binaries that
crashed at startup. scripts/lint.sh already catches this exact case
but was wired only as a Make dependency, never invoked. Run it from
the M1pp / hex2pp recipes on the host (the busybox build container has
no python3) before handing off to boot-build-p1.sh.
Diffstat:
| M | Makefile | | | 2 | ++ |
| M | hex2pp/hex2pp.P1 | | | 945 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------- |
| M | hex2pp/hex2pp.c | | | 240 | ++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------- |
3 files changed, 803 insertions(+), 384 deletions(-)
diff --git a/Makefile b/Makefile
@@ -256,9 +256,11 @@ ALPINE_GCC = podman run --rm --pull=never --platform $(PLATFORM_$(1)) \
-v $(CURDIR):/work -w /work boot2-alpine-gcc:$(1)
$(M1PP_BINS): build/%/M1pp/M1pp: M1pp/M1pp.P1 $(P1_BUILD_DEPS)
+ ARCH=$* sh scripts/lint.sh M1pp/M1pp.P1
$(call PODMAN,$*) sh scripts/boot-build-p1.sh M1pp/M1pp.P1 $@
$(HEX2PP_BINS): build/%/hex2pp/hex2pp: hex2pp/hex2pp.P1 $(P1_BUILD_DEPS)
+ ARCH=$* sh scripts/lint.sh hex2pp/hex2pp.P1
$(call PODMAN,$*) sh scripts/boot-build-p1.sh hex2pp/hex2pp.P1 $@
$(SCHEME1_BINS): build/%/scheme1/scheme1: $(SCHEME1_SRC) $(P1PP_BUILD_DEPS)
diff --git a/hex2pp/hex2pp.P1 b/hex2pp/hex2pp.P1
@@ -2,9 +2,10 @@
##
## Mirrors hex2pp/hex2pp.c. See docs/HEX2pp.md for the full spec; brief:
##
-## Single input file, scanned in two passes. Pass 1 records label
-## definitions while advancing a position counter (ip). Pass 2 emits
-## bytes, resolving label references against the table built in pass 1.
+## Input is scanned once. Label definitions are recorded into a table
+## on the fly; label references emit zero placeholders and append a
+## fixup record. After the scan, fixups are resolved against the
+## completed label table and patched into the output buffer.
##
## Active syntax:
## digits in current byte mode -> raw bytes (HEX or BINARY)
@@ -38,6 +39,8 @@ DEFINE H2_TEXT_CAP 0000800000000000
DEFINE H2_LABEL_CAP 0000100000000000
DEFINE H2_TOKEN_CAP 0010000000000000
DEFINE H2_SCOPE_CAP 2000000000000000
+DEFINE H2_FIXUP_CAP 0000100000000000
+DEFINE H2_SCOPE_HISTORY_CAP 0000400000000000
## openat / mode constants (Linux generic).
DEFINE H2_O_WRONLY_CREAT_TRUNC 4102000000000000
@@ -57,9 +60,9 @@ DEFINE ZERO4 '00000000'
## Region sizes (matching the C caps):
## scope_stack 32 * 8 = 256 B
## line_scratch = 64 B
-## name_buf = 4096 B
-## label_buf = 4096 B
-## other_buf = 4096 B
+## name_buf = 4096 B (used by read_directive_name)
+## label_buf = 4096 B (unused since single-pass refactor)
+## other_buf = 4096 B (unused since single-pass refactor)
## pat_buf = 4096 B
## ev_bytes = 8 B
## df_byte = 8 B
@@ -67,6 +70,8 @@ DEFINE ZERO4 '00000000'
## output_buf = 128 MiB
## text_buf = 8 MiB
## labels = 32 MiB (1<<20 * 32 B)
+## fixups = 96 MiB (1<<20 * 96 B)
+## scope_history = 32 MiB (1<<22 * 8 B)
##
## Compact cumulative offsets in 8-byte little-endian hex.
DEFINE H2_OFF_scope_stack 0000000000000000
@@ -77,14 +82,18 @@ DEFINE H2_OFF_other_buf 4021000000000000
DEFINE H2_OFF_pat_buf 4031000000000000
DEFINE H2_OFF_ev_bytes 4041000000000000
DEFINE H2_OFF_df_byte 5041000000000000
-## input_buf: 0x004200 + 16 MiB
-## output_buf: 0x1004200 + 128 MiB
-## text_buf: 0x9004200 + 8 MiB
-## labels: 0x9804200 + 32 MiB (end at ~184 MiB; ELF p_memsz is 512 MiB).
+## input_buf: 0x00004200 + 16 MiB
+## output_buf: 0x01004200 + 128 MiB
+## text_buf: 0x09004200 + 8 MiB
+## labels: 0x09804200 + 32 MiB
+## fixups: 0x0B804200 + 96 MiB
+## scope_history: 0x11804200 + 32 MiB (end at ~313 MiB; ELF p_memsz is 512 MiB).
DEFINE H2_OFF_input_buf 0042000000000000
DEFINE H2_OFF_output_buf 0042000100000000
DEFINE H2_OFF_text_buf 0042000900000000
DEFINE H2_OFF_labels 0042800900000000
+DEFINE H2_OFF_fixups 0042800B00000000
+DEFINE H2_OFF_scope_history 0042801100000000
## --- p1_main: argv parse -> load input -> two passes -> write -> exit ------
@@ -295,30 +304,21 @@ DEFINE H2_OFF_labels 0042800900000000
la_br &load_input
call
- # ---- Pass 1 ----------------------------------------------------------
- li_t0 %1 %0
- la_a0 &pass
+ # ---- Single scan: builds labels + fixup list -------------------------
+ li_t0 %0 %0
+ la_a0 &ip
+ st_t0,a0,0
+ la_a0 &output_used
st_t0,a0,0
- la_br &reset_pass_state
- call
- la_br &process_input
- call
la_a0 &scope_depth
- ld_t0,a0,0
- la_br &err_scope_unclosed
- bnez_t0
-
- # Clear cur_path so any post-pass error reports without file:line.
- li_t0 %0 %0
- la_a0 &cur_path
st_t0,a0,0
-
- # ---- Pass 2 ----------------------------------------------------------
- li_t0 %2 %0
- la_a0 &pass
+ la_a0 &scope_seq
+ st_t0,a0,0
+ la_a0 &ptrsize_used
+ st_t0,a0,0
+ li_t0 %4 %0
+ la_a0 &ptrsize
st_t0,a0,0
- la_br &reset_pass_state
- call
la_br &process_input
call
la_a0 &scope_depth
@@ -326,6 +326,11 @@ DEFINE H2_OFF_labels 0042800900000000
la_br &err_scope_unclosed
bnez_t0
+ # ---- Resolve fixups --------------------------------------------------
+ la_br &patch_fixups
+ call
+
+ # Clear cur_path so any post-fixup error reports without file:line.
li_t0 %0 %0
la_a0 &cur_path
st_t0,a0,0
@@ -336,26 +341,6 @@ DEFINE H2_OFF_labels 0042800900000000
li_a0 %0 %0
eret
-## reset_pass_state(): ip=0, output_used=0, scope_depth=0, scope_seq=0,
-## ptrsize=4, ptrsize_used=0.
-:reset_pass_state
- enter_0
- li_t0 %0 %0
- la_a0 &ip
- st_t0,a0,0
- la_a0 &output_used
- st_t0,a0,0
- la_a0 &scope_depth
- st_t0,a0,0
- la_a0 &scope_seq
- st_t0,a0,0
- la_a0 &ptrsize_used
- st_t0,a0,0
- li_t0 %4 %0
- la_a0 &ptrsize
- st_t0,a0,0
- eret
-
## --- Input loader ----------------------------------------------------------
## load_input(): openat(input_path), read into input_buf until EOF.
:load_input
@@ -479,16 +464,13 @@ DEFINE H2_OFF_labels 0042800900000000
ld_t0,a0,0
addi_t0,t0,1
st_t0,a0,0
- # name = read_name(name_buf, MAX_TOKEN)
- la_a0 &name_buf_ptr
- ld_a0,a0,0
- li_a1 H2_TOKEN_CAP
- la_br &read_name
+ # scan_name -> length in a0, pointer in sn_start (no copy).
+ la_br &scan_name
call
la_a1 &name_len
st_a0,a1,0
# dotted = (name[0] == '.') AND scope_depth > 0
- la_a0 &name_buf_ptr
+ la_a0 &sn_start
ld_a0,a0,0
lb_t0,a0,0
li_t1 %46 %0
@@ -514,13 +496,7 @@ DEFINE H2_OFF_labels 0042800900000000
la_a2 &name_scope
st_t0,a2,0
:handle_label_dispatch
- # Pass 1 only: define_label(name, len, scope)
- la_a0 &pass
- ld_t0,a0,0
- li_t1 %1 %0
- la_br &scan_loop
- bne_t0,t1
- la_a0 &name_buf_ptr
+ la_a0 &sn_start
ld_a0,a0,0
la_a1 &name_len
ld_a1,a1,0
@@ -937,8 +913,47 @@ DEFINE H2_OFF_labels 0042800900000000
li_a0 %8 %0
ret
+## scan_name() -> a0=length, sn_start=pointer into input buffer at name start.
+## Advances scan_pos past the name. Fatal on empty.
+:scan_name
+ enter_0
+ la_a0 &scan_pos
+ ld_t0,a0,0
+ la_a1 &sn_start
+ st_t0,a1,0
+:sn_loop
+ la_a0 &scan_pos
+ ld_t0,a0,0
+ la_a1 &scan_end
+ ld_t1,a1,0
+ la_br &sn_done
+ beq_t0,t1
+ la_br &sn_done
+ blt_t1,t0
+ lb_a0,t0,0
+ la_br &is_name_terminator
+ call
+ la_br &sn_done
+ bnez_a0
+ la_a0 &scan_pos
+ ld_t0,a0,0
+ addi_t0,t0,1
+ st_t0,a0,0
+ la_br &sn_loop
+ b
+:sn_done
+ la_a0 &scan_pos
+ ld_t0,a0,0
+ la_a1 &sn_start
+ ld_t1,a1,0
+ sub_a0,t0,t1
+ la_br &err_empty_name
+ beqz_a0
+ eret
+
## read_name(a0=out_buf, a1=max) -> a0=length. Reads scan_pos into out_buf
## until is_name_terminator or scan_end. Fatal on overflow / empty.
+## (Kept for reference; superseded by scan_name in single-pass refactor.)
:read_name
enter_0
la_a2 &rn_out
@@ -1453,7 +1468,7 @@ DEFINE H2_OFF_labels 0042800900000000
li_a0 %0 %0
eret
-## define_label(a0=src, a1=len, a2=scope_id).
+## define_label(a0=src, a1=len, a2=scope_id). First definition wins.
:define_label
enter_0
la_a3 &dl_src
@@ -1462,48 +1477,6 @@ DEFINE H2_OFF_labels 0042800900000000
st_a1,a3,0
la_a3 &dl_scope
st_a2,a3,0
- li_t0 %0 %0
- la_a0 &dl_i
- st_t0,a0,0
-:dl_dup_loop
- la_a0 &dl_i
- ld_t0,a0,0
- la_a1 &label_count
- ld_t1,a1,0
- la_br &dl_dup_done
- beq_t0,t1
- mov_a0,t0
- la_br &label_addr
- call
- la_a3 &dl_label
- st_a0,a3,0
- # scope match?
- ld_t0,a0,24
- la_a1 &dl_scope
- ld_t1,a1,0
- la_br &dl_dup_next
- bne_t0,t1
- # name match?
- la_a0 &dl_label
- ld_a0,a0,0
- la_a1 &dl_src
- ld_a1,a1,0
- la_a2 &dl_len
- ld_a2,a2,0
- la_br &name_eq
- call
- la_br &dl_dup_next
- beqz_a0
- la_br &err_duplicate_label
- b
-:dl_dup_next
- la_a0 &dl_i
- ld_t0,a0,0
- addi_t0,t0,1
- st_t0,a0,0
- la_br &dl_dup_loop
- b
-:dl_dup_done
la_a0 &label_count
ld_t0,a0,0
li_t1 H2_LABEL_CAP
@@ -1550,23 +1523,25 @@ DEFINE H2_OFF_labels 0042800900000000
st_t0,a0,0
eret
-## lookup_label(a0=src, a1=len) -> a0=target_ip.
-:lookup_label
+## lookup_label_in(a0=src, a1=len, a2=stack_ptr, a3=depth) -> a0=target_ip.
+:lookup_label_in
enter_0
- la_a2 &ll_src
- st_a0,a2,0
- la_a2 &ll_len
- st_a1,a2,0
- # dotted? (first byte == '.' AND scope_depth > 0)
+ la_t1 &ll_src
+ st_a0,t1,0
+ la_t1 &ll_len
+ st_a1,t1,0
+ la_t1 &ll_stack
+ st_a2,t1,0
+ la_t1 &ll_depth
+ st_a3,t1,0
+ # dotted? (first byte == '.' AND depth > 0)
lb_t0,a0,0
li_t1 %46 %0
la_br &ll_undotted
bne_t0,t1
- la_a3 &scope_depth
- ld_t0,a3,0
la_br &ll_undotted
- beqz_t0
- addi_t0,t0,neg1
+ beqz_a3
+ addi_t0,a3,neg1
la_a1 &ll_d
st_t0,a1,0
:ll_dot_outer
@@ -1574,7 +1549,7 @@ DEFINE H2_OFF_labels 0042800900000000
ld_t0,a0,0
la_br &err_undefined_local
bltz_t0
- la_a1 &scope_stack_ptr
+ la_a1 &ll_stack
ld_a1,a1,0
shli_t2,t0,3
add_a1,a1,t2
@@ -1673,7 +1648,8 @@ DEFINE H2_OFF_labels 0042800900000000
## --- Reference processor ---------------------------------------------------
## process_reference(): cur_sigil already set; scan_pos already past sigil.
-## Reads label and (optional) -other, advances ip on pass 1, emits on pass 2.
+## Captures label name span (and optional -OTHER), reserves placeholder bytes,
+## and appends a Fixup record. Resolution happens later in patch_fixups.
:process_reference
enter_0
la_br &set_sigil_info
@@ -1692,36 +1668,40 @@ DEFINE H2_OFF_labels 0042800900000000
call
la_br &err_sigil_no_label
bnez_a0
- # llen = read_name(label_buf, MAX_TOKEN)
- la_a0 &label_buf_ptr
- ld_a0,a0,0
- li_a1 H2_TOKEN_CAP
- la_br &read_name
+ # scan_name -> length in a0, pointer in sn_start (no copy).
+ la_br &scan_name
call
- la_a1 &pr_llen
+ la_a1 &pref_name_len
st_a0,a1,0
+ la_a0 &sn_start
+ ld_t0,a0,0
+ la_a1 &pref_name
+ st_t0,a1,0
+ # default: other = NULL, other_len = 0
li_t0 %0 %0
- la_a0 &pr_has_other
- st_t0,a0,0
- # optional '-' or '>' OTHER
+ la_a1 &pref_other
+ st_t0,a1,0
+ la_a1 &pref_other_len
+ st_t0,a1,0
+ # optional '-' or '>' separator (tight, no whitespace)
la_a0 &scan_pos
ld_t0,a0,0
la_a1 &scan_end
ld_t1,a1,0
- la_br &pr_after_other
+ la_br &pref_after_other
beq_t0,t1
- la_br &pr_after_other
+ la_br &pref_after_other
blt_t1,t0
lb_a0,t0,0
li_t1 %45 %0
- la_br &pr_consume_sep
+ la_br &pref_consume_sep
beq_a0,t1
li_t1 %62 %0
- la_br &pr_consume_sep
+ la_br &pref_consume_sep
beq_a0,t1
- la_br &pr_after_other
+ la_br &pref_after_other
b
-:pr_consume_sep
+:pref_consume_sep
la_a0 &scan_pos
ld_t0,a0,0
addi_t0,t0,1
@@ -1737,96 +1717,155 @@ DEFINE H2_OFF_labels 0042800900000000
call
la_br &err_minus_no_label
bnez_a0
- la_a0 &other_buf_ptr
- ld_a0,a0,0
- li_a1 H2_TOKEN_CAP
- la_br &read_name
+ la_br &scan_name
call
- la_a1 &pr_olen
+ la_a1 &pref_other_len
st_a0,a1,0
- li_t0 %1 %0
- la_a0 &pr_has_other
- st_t0,a0,0
-:pr_after_other
- la_a0 &pass
+ la_a0 &sn_start
ld_t0,a0,0
- li_t1 %1 %0
- la_br &pr_pass2
- bne_t0,t1
- # ip += pr_width
- la_a0 &ip
- ld_t1,a0,0
- la_a1 &pr_width
- ld_t2,a1,0
- add_t1,t1,t2
- st_t1,a0,0
- eret
-:pr_pass2
- la_a0 &label_buf_ptr
+ la_a1 &pref_other
+ st_t0,a1,0
+:pref_after_other
+ # Reserve placeholder bytes; emit_zeros returns starting offset.
+ la_a0 &pr_width
ld_a0,a0,0
- la_a1 &pr_llen
- ld_a1,a1,0
- la_br &lookup_label
+ la_br &emit_zeros
call
- la_a1 &pr_t_label
+ la_a1 &rf_out_off
st_a0,a1,0
- la_a0 &pr_has_other
+ # ip_after = ip (already advanced by emit_zeros).
+ la_a0 &ip
ld_t0,a0,0
- la_br &pr_no_other
- beqz_t0
- la_a0 &other_buf_ptr
- ld_a0,a0,0
- la_a1 &pr_olen
- ld_a1,a1,0
- la_br &lookup_label
+ la_a1 &rf_ip_after
+ st_t0,a1,0
+ la_br &record_fixup
call
- la_a1 &pr_t_label
- ld_a1,a1,0
- sub_a0,a1,a0
- la_a1 &pr_value
- st_a0,a1,0
- la_br &pr_emit
- b
-:pr_no_other
- la_a0 &pr_is_rel
+ eret
+
+## record_fixup(): pref_name/pref_name_len/pref_other/pref_other_len already set
+## by caller. rf_out_off and rf_ip_after also set. Snapshots cur_sigil/cur_path/
+## cur_line and the current scope stack (if depth > 0).
+:record_fixup
+ enter_0
+ la_a0 &fixup_count
ld_t0,a0,0
- la_br &pr_abs
- beqz_t0
- # rel: value = t_label - (ip + width)
- la_a0 &ip
- ld_a1,a0,0
- la_a0 &pr_width
+ li_t1 H2_FIXUP_CAP
+ la_br &err_too_many_fixups
+ beq_t0,t1
+ la_br &err_too_many_fixups
+ blt_t1,t0
+ # fp = fixups_ptr + count * 96
+ li_t1 %96 %0
+ mul_a1,t0,t1
+ la_a2 &fixups_ptr
+ ld_a2,a2,0
+ add_a1,a1,a2
+ la_a3 &rf_fp
+ st_a1,a3,0
+ # offset 0: out_off
+ la_a0 &rf_out_off
ld_t0,a0,0
- add_a1,a1,t0
- la_a0 &pr_t_label
- ld_a0,a0,0
- sub_a0,a0,a1
- la_a1 &pr_value
- st_a0,a1,0
- la_br &pr_emit
- b
-:pr_abs
- # value = t_label + base_address
- la_a0 &pr_t_label
- ld_a1,a0,0
- la_a0 &base_address
+ st_t0,a1,0
+ # offset 8: ip_at_ref
+ la_a0 &rf_ip_after
+ ld_t0,a0,0
+ st_t0,a1,8
+ # offset 16: name
+ la_a0 &pref_name
+ ld_t0,a0,0
+ st_t0,a1,16
+ # offset 24: other (0 if none)
+ la_a0 &pref_other
+ ld_t0,a0,0
+ st_t0,a1,24
+ # offset 32: src_path = cur_path
+ la_a0 &cur_path
+ ld_t0,a0,0
+ st_t0,a1,32
+ # offset 40: name_len
+ la_a0 &pref_name_len
+ ld_t0,a0,0
+ st_t0,a1,40
+ # offset 48: other_len
+ la_a0 &pref_other_len
+ ld_t0,a0,0
+ st_t0,a1,48
+ # offset 56: scope_hist_off (filled below if depth > 0)
+ li_t0 %0 %0
+ st_t0,a1,56
+ # offset 64: scope_depth (snapshot)
+ la_a0 &scope_depth
+ ld_t0,a0,0
+ st_t0,a1,64
+ # offset 72: src_line = cur_line (offset > 64: indirect via a3)
+ la_a0 &cur_line
+ ld_t0,a0,0
+ li_t1 %72 %0
+ add_a3,a1,t1
+ st_t0,a3,0
+ # offset 80: sigil
+ la_a0 &cur_sigil
+ ld_t0,a0,0
+ li_t1 %80 %0
+ add_a3,a1,t1
+ st_t0,a3,0
+ # If depth > 0, snapshot scope_stack into scope_history.
+ la_a0 &scope_depth
+ ld_t0,a0,0
+ la_br &rf_no_scope
+ beqz_t0
+ la_a1 &scope_history_used
+ ld_t1,a1,0
+ add_t2,t1,t0
+ li_a3 H2_SCOPE_HISTORY_CAP
+ la_br &err_scope_history_overflow
+ blt_a3,t2
+ # Set scope_hist_off (offset 56) on the fixup.
+ la_a0 &rf_fp
ld_a0,a0,0
- add_a1,a1,a0
- la_a3 &pr_value
- st_a1,a3,0
-:pr_emit
- la_a1 &pr_width
+ st_t1,a0,56
+ li_t2 %0 %0
+ la_a0 &rf_i
+ st_t2,a0,0
+:rf_copy_loop
+ la_a0 &rf_i
+ ld_t0,a0,0
+ la_a1 &scope_depth
+ ld_t1,a1,0
+ la_br &rf_copy_done
+ beq_t0,t1
+ # src = scope_stack_ptr + i*8
+ la_a1 &scope_stack_ptr
ld_a1,a1,0
- la_a2 &pr_lo
+ shli_t2,t0,3
+ add_a1,a1,t2
+ ld_a3,a1,0
+ # dst = scope_history_ptr + (scope_history_used + i)*8
+ la_a2 &scope_history_used
ld_a2,a2,0
- la_a3 &pr_hi
- ld_a3,a3,0
- la_a0 &pr_range_check
+ add_a2,a2,t0
+ shli_a2,a2,3
+ la_a1 &scope_history_ptr
+ ld_a1,a1,0
+ add_a1,a1,a2
+ st_a3,a1,0
+ addi_t0,t0,1
+ la_a0 &rf_i
+ st_t0,a0,0
+ la_br &rf_copy_loop
+ b
+:rf_copy_done
+ la_a1 &scope_history_used
+ ld_t0,a1,0
+ la_a2 &scope_depth
+ ld_t1,a2,0
+ add_t0,t0,t1
+ st_t0,a1,0
+:rf_no_scope
+ la_a0 &fixup_count
ld_t0,a0,0
- la_a0 &pr_value
- ld_a0,a0,0
- la_br &emit_value
- call
+ addi_t0,t0,1
+ st_t0,a0,0
eret
## set_sigil_info(): reads cur_sigil; populates pr_width / pr_is_rel /
@@ -2067,44 +2106,47 @@ DEFINE H2_OFF_labels 0042800900000000
la_a1 &da_pad
st_a3,a1,0
:da_emit
+ la_a0 &da_pad
+ ld_t0,a0,0
+ la_br &da_emit_done
+ beqz_t0
+ # No-pattern fast path: emit_zeros(pad).
+ la_a1 &da_has_pat
+ ld_t1,a1,0
+ la_br &da_emit_pattern
+ bnez_t1
+ la_a0 &da_pad
+ ld_a0,a0,0
+ la_br &emit_zeros
+ call
+ la_br &da_emit_done
+ b
+:da_emit_pattern
li_t0 %0 %0
la_a0 &da_i
st_t0,a0,0
-:da_emit_loop
+:da_pattern_loop
la_a0 &da_i
ld_t0,a0,0
la_a1 &da_pad
ld_t1,a1,0
la_br &da_emit_done
beq_t0,t1
- la_a0 &da_has_pat
- ld_t1,a0,0
- la_br &da_emit_zero
- beqz_t1
# b = pat[i % patlen]
- la_a0 &da_i
- ld_a0,a0,0
la_a1 &da_patlen
ld_a1,a1,0
- rem_a2,a0,a1
+ rem_a2,t0,a1
la_a0 &pat_buf_ptr
ld_a0,a0,0
add_a0,a0,a2
lb_a0,a0,0
la_br &emit_byte
call
- la_br &da_emit_inc
- b
-:da_emit_zero
- li_a0 %0 %0
- la_br &emit_byte
- call
-:da_emit_inc
la_a0 &da_i
ld_t0,a0,0
addi_t0,t0,1
st_t0,a0,0
- la_br &da_emit_loop
+ la_br &da_pattern_loop
b
:da_emit_done
eret
@@ -2126,27 +2168,15 @@ DEFINE H2_OFF_labels 0042800900000000
ld_a0,a0,0
la_br &parse_one_byte
call
- li_t0 %0 %0
- la_a0 &df_i
- st_t0,a0,0
-:df_loop
- la_a0 &df_i
- ld_t0,a0,0
- la_a1 &df_n
- ld_t1,a1,0
- la_br &df_done
- beq_t0,t1
- la_a0 &df_byte_ptr
+ la_a0 &df_n
ld_a0,a0,0
- lb_a0,a0,0
- la_br &emit_byte
+ la_br &df_done
+ beqz_a0
+ la_a1 &df_byte_ptr
+ ld_a1,a1,0
+ lb_a1,a1,0
+ la_br &emit_fill
call
- la_a0 &df_i
- ld_t0,a0,0
- addi_t0,t0,1
- st_t0,a0,0
- la_br &df_loop
- b
:df_done
eret
@@ -2223,13 +2253,8 @@ DEFINE H2_OFF_labels 0042800900000000
## --- Emit ------------------------------------------------------------------
-## emit_byte(a0=byte): pass 1 only bumps ip; pass 2 also writes to output_buf.
+## emit_byte(a0=byte): write one byte to output_buf and bump ip.
:emit_byte
- la_a1 &pass
- ld_t0,a1,0
- li_t1 %2 %0
- la_br &eb_after_write
- bne_t0,t1
la_a1 &output_used
ld_t0,a1,0
li_t1 H2_OUTPUT_CAP
@@ -2243,31 +2268,109 @@ DEFINE H2_OFF_labels 0042800900000000
sb_a0,a2,0
addi_t0,t0,1
st_t0,a1,0
-:eb_after_write
la_a0 &ip
ld_t0,a0,0
addi_t0,t0,1
st_t0,a0,0
ret
-## emit_value(a0=value, a1=width, a2=lo, a3=hi, t0=range_check). Range-checks,
-## packs little-endian into ev_bytes[0..width-1], emits in LE/BE order.
-:emit_value
+## emit_zeros(a0=n) -> a0 = starting offset (output_used before the call).
+## Writes n zero bytes to output_buf and advances output_used + ip by n.
+:emit_zeros
enter_0
- la_t1 &ev_value
+ la_a3 &emz_n
+ st_a0,a3,0
+ # Capacity check: output_used + n must fit.
+ la_a1 &output_used
+ ld_t0,a1,0
+ add_t1,t0,a0
+ li_t2 H2_OUTPUT_CAP
+ la_br &err_output_overflow
+ blt_t2,t1
+ la_a2 &emz_off
+ st_t0,a2,0
+ la_a2 &output_buf_ptr
+ ld_a2,a2,0
+ add_a2,a2,t0
+ li_t1 %0 %0
+:emz_loop
+ la_a3 &emz_n
+ ld_t2,a3,0
+ la_br &emz_done
+ beq_t1,t2
+ add_a3,a2,t1
+ li_t0 %0 %0
+ sb_t0,a3,0
+ addi_t1,t1,1
+ la_br &emz_loop
+ b
+:emz_done
+ la_a3 &emz_n
+ ld_t0,a3,0
+ la_a1 &output_used
+ ld_t1,a1,0
+ add_t1,t1,t0
+ st_t1,a1,0
+ la_a1 &ip
+ ld_t1,a1,0
+ add_t1,t1,t0
+ st_t1,a1,0
+ la_a0 &emz_off
+ ld_a0,a0,0
+ eret
+
+## emit_fill(a0=n, a1=byte): write n copies of byte; advances output_used + ip.
+:emit_fill
+ enter_0
+ la_a3 &emf_n
+ st_a0,a3,0
+ la_a3 &emf_b
+ st_a1,a3,0
+ la_a2 &output_used
+ ld_t0,a2,0
+ add_t1,t0,a0
+ li_t2 H2_OUTPUT_CAP
+ la_br &err_output_overflow
+ blt_t2,t1
+ la_a2 &output_buf_ptr
+ ld_a2,a2,0
+ add_a2,a2,t0
+ li_t1 %0 %0
+:emf_loop
+ la_a3 &emf_n
+ ld_t2,a3,0
+ la_br &emf_done
+ beq_t1,t2
+ la_a3 &emf_b
+ ld_t0,a3,0
+ add_a3,a2,t1
+ sb_t0,a3,0
+ addi_t1,t1,1
+ la_br &emf_loop
+ b
+:emf_done
+ la_a3 &emf_n
+ ld_t0,a3,0
+ la_a1 &output_used
+ ld_t1,a1,0
+ add_t1,t1,t0
+ st_t1,a1,0
+ la_a1 &ip
+ ld_t1,a1,0
+ add_t1,t1,t0
+ st_t1,a1,0
+ eret
+
+## write_value(a0=out_off): patch a value into output_buf at out_off.
+## Caller pre-populates ev_value/ev_width/ev_lo/ev_hi/ev_range_check.
+:write_value
+ enter_0
+ la_t1 &ev_out_off
st_a0,t1,0
- la_t1 &ev_width
- st_a1,t1,0
- la_t1 &ev_lo
- st_a2,t1,0
- la_t1 &ev_hi
- st_a3,t1,0
- la_t1 &ev_range_check
- st_t0,t1,0
la_a0 &ev_range_check
ld_t0,a0,0
- la_br &ev_no_range
+ la_br &wv_no_range
beqz_t0
la_a0 &ev_value
ld_a0,a0,0
@@ -2281,7 +2384,7 @@ DEFINE H2_OFF_labels 0042800900000000
ld_a1,a1,0
la_br &err_ref_out_of_range
blt_a1,a0
-:ev_no_range
+:wv_no_range
la_a0 &ev_value
ld_a0,a0,0
la_a1 &ev_pack_v
@@ -2289,12 +2392,12 @@ DEFINE H2_OFF_labels 0042800900000000
li_t0 %0 %0
la_a0 &ev_i
st_t0,a0,0
-:ev_pack_loop
+:wv_pack_loop
la_a0 &ev_i
ld_t0,a0,0
la_a1 &ev_width
ld_t1,a1,0
- la_br &ev_emit_dispatch
+ la_br &wv_emit_dispatch
beq_t0,t1
la_a1 &ev_pack_v
ld_a3,a1,0
@@ -2310,59 +2413,227 @@ DEFINE H2_OFF_labels 0042800900000000
addi_t0,t0,1
la_a0 &ev_i
st_t0,a0,0
- la_br &ev_pack_loop
+ la_br &wv_pack_loop
b
-:ev_emit_dispatch
+:wv_emit_dispatch
la_a0 &big_endian
ld_t0,a0,0
- la_br &ev_emit_be
+ la_br &wv_emit_be
bnez_t0
li_t0 %0 %0
la_a0 &ev_i
st_t0,a0,0
-:ev_emit_le_loop
+:wv_emit_le_loop
la_a0 &ev_i
ld_t0,a0,0
la_a1 &ev_width
ld_t1,a1,0
- la_br &ev_done
+ la_br &wv_done
beq_t0,t1
la_a2 &ev_bytes_ptr
ld_a2,a2,0
add_a2,a2,t0
lb_a0,a2,0
- la_br &emit_byte
- call
- la_a0 &ev_i
- ld_t0,a0,0
+ la_a3 &output_buf_ptr
+ ld_a3,a3,0
+ la_a2 &ev_out_off
+ ld_a2,a2,0
+ add_a3,a3,a2
+ add_a3,a3,t0
+ sb_a0,a3,0
addi_t0,t0,1
+ la_a0 &ev_i
st_t0,a0,0
- la_br &ev_emit_le_loop
+ la_br &wv_emit_le_loop
b
-:ev_emit_be
- la_a0 &ev_width
- ld_t0,a0,0
- addi_t0,t0,neg1
- la_a1 &ev_i
- st_t0,a1,0
-:ev_emit_be_loop
+:wv_emit_be
+ li_t0 %0 %0
+ la_a0 &ev_i
+ st_t0,a0,0
+:wv_emit_be_loop
la_a0 &ev_i
ld_t0,a0,0
- la_br &ev_done
- bltz_t0
+ la_a1 &ev_width
+ ld_t1,a1,0
+ la_br &wv_done
+ beq_t0,t1
+ # src_idx = (width - 1) - i
+ addi_t1,t1,neg1
+ sub_t2,t1,t0
la_a2 &ev_bytes_ptr
ld_a2,a2,0
- add_a2,a2,t0
+ add_a2,a2,t2
lb_a0,a2,0
- la_br &emit_byte
- call
+ la_a3 &output_buf_ptr
+ ld_a3,a3,0
+ la_a2 &ev_out_off
+ ld_a2,a2,0
+ add_a3,a3,a2
+ add_a3,a3,t0
+ sb_a0,a3,0
+ addi_t0,t0,1
la_a0 &ev_i
+ st_t0,a0,0
+ la_br &wv_emit_be_loop
+ b
+:wv_done
+ eret
+
+## --- Fixup resolution ------------------------------------------------------
+## patch_fixups(): walk fixups[0..fixup_count), resolve labels, write values
+## into output_buf. Per-fixup error reports use the snapshotted src_path /
+## src_line via the cur_path / cur_line globals.
+:patch_fixups
+ enter_0
+ li_t0 %0 %0
+ la_a0 &pf_i
+ st_t0,a0,0
+:pf_loop
+ la_a0 &pf_i
ld_t0,a0,0
- addi_t0,t0,neg1
+ la_a1 &fixup_count
+ ld_t1,a1,0
+ la_br &pf_done
+ beq_t0,t1
+ # fp = fixups + i*96
+ li_t1 %96 %0
+ mul_a1,t0,t1
+ la_a2 &fixups_ptr
+ ld_a2,a2,0
+ add_a1,a1,a2
+ la_a3 &pf_fp
+ st_a1,a3,0
+ # cur_path = fp[32]; cur_line = fp[72]; cur_sigil = fp[80]
+ ld_t0,a1,32
+ la_a2 &cur_path
+ st_t0,a2,0
+ li_t1 %72 %0
+ add_a3,a1,t1
+ ld_t0,a3,0
+ la_a2 &cur_line
+ st_t0,a2,0
+ li_t1 %80 %0
+ add_a3,a1,t1
+ ld_t0,a3,0
+ la_a2 &cur_sigil
+ st_t0,a2,0
+ # pr_width / pr_is_rel / pr_lo / pr_hi / pr_range_check
+ la_br &set_sigil_info
+ call
+ # Determine stack pointer for the lookup (NULL if depth == 0).
+ la_a0 &pf_fp
+ ld_a1,a0,0
+ ld_t0,a1,64
+ la_a2 &pf_depth
+ st_t0,a2,0
+ li_t1 %0 %0
+ la_a2 &pf_stack
+ st_t1,a2,0
+ la_br &pf_lookup_label
+ beqz_t0
+ # stack = scope_history_ptr + scope_hist_off * 8
+ ld_t1,a1,56
+ shli_t1,t1,3
+ la_a2 &scope_history_ptr
+ ld_a2,a2,0
+ add_a2,a2,t1
+ la_a3 &pf_stack
+ st_a2,a3,0
+:pf_lookup_label
+ la_a0 &pf_fp
+ ld_a1,a0,0
+ ld_a0,a1,16
+ ld_t0,a1,40
+ mov_a1,t0
+ la_a2 &pf_stack
+ ld_a2,a2,0
+ la_a3 &pf_depth
+ ld_a3,a3,0
+ la_br &lookup_label_in
+ call
+ la_a1 &pf_t_label
+ st_a0,a1,0
+ # If other != 0: value = t_label - t_other; emit.
+ la_a0 &pf_fp
+ ld_a1,a0,0
+ ld_t0,a1,24
+ la_br &pf_no_other
+ beqz_t0
+ mov_a0,t0
+ ld_t0,a1,48
+ mov_a1,t0
+ la_a2 &pf_stack
+ ld_a2,a2,0
+ la_a3 &pf_depth
+ ld_a3,a3,0
+ la_br &lookup_label_in
+ call
+ la_a1 &pf_t_label
+ ld_a1,a1,0
+ sub_a0,a1,a0
+ la_a1 &pf_value
+ st_a0,a1,0
+ la_br &pf_emit
+ b
+:pf_no_other
+ la_a0 &pr_is_rel
+ ld_t0,a0,0
+ la_br &pf_abs
+ beqz_t0
+ # rel: value = t_label - ip_at_ref
+ la_a0 &pf_fp
+ ld_a1,a0,0
+ ld_t1,a1,8
+ la_a0 &pf_t_label
+ ld_a0,a0,0
+ sub_a0,a0,t1
+ la_a1 &pf_value
+ st_a0,a1,0
+ la_br &pf_emit
+ b
+:pf_abs
+ la_a0 &pf_t_label
+ ld_a1,a0,0
+ la_a0 &base_address
+ ld_a0,a0,0
+ add_a1,a1,a0
+ la_a3 &pf_value
+ st_a1,a3,0
+:pf_emit
+ # Populate ev_* state, then write_value(out_off).
+ la_a0 &pf_value
+ ld_t0,a0,0
+ la_a1 &ev_value
+ st_t0,a1,0
+ la_a0 &pr_width
+ ld_t0,a0,0
+ la_a1 &ev_width
+ st_t0,a1,0
+ la_a0 &pr_lo
+ ld_t0,a0,0
+ la_a1 &ev_lo
+ st_t0,a1,0
+ la_a0 &pr_hi
+ ld_t0,a0,0
+ la_a1 &ev_hi
+ st_t0,a1,0
+ la_a0 &pr_range_check
+ ld_t0,a0,0
+ la_a1 &ev_range_check
+ st_t0,a1,0
+ la_a0 &pf_fp
+ ld_a1,a0,0
+ ld_a0,a1,0
+ la_br &write_value
+ call
+ # i++
+ la_a0 &pf_i
+ ld_t0,a0,0
+ addi_t0,t0,1
st_t0,a0,0
- la_br &ev_emit_be_loop
+ la_br &pf_loop
b
-:ev_done
+:pf_done
eret
## --- Output writer ---------------------------------------------------------
@@ -2910,6 +3181,14 @@ DEFINE H2_OFF_labels 0042800900000000
la_a0 &msg_bad_long
la_br &fatal
b
+:err_too_many_fixups
+ la_a0 &msg_too_many_fixups
+ la_br &fatal
+ b
+:err_scope_history_overflow
+ la_a0 &msg_scope_history_overflow
+ la_br &fatal
+ b
:err_ptrsize_bad
la_a0 &msg_ptrsize_bad
la_br &fatal
@@ -2979,6 +3258,8 @@ DEFINE H2_OFF_labels 0042800900000000
:msg_bad_long "invalid integer argument" '00'
:msg_ptrsize_bad ".ptrsize: N must be 4 or 8" '00'
:msg_ptrsize_conflict ".ptrsize conflicts with already-used width" '00'
+:msg_too_many_fixups "too many references" '00000000'
+:msg_scope_history_overflow "scope history overflow" '00'
## BSS pointer-slot init table.
:bss_init_tbl
@@ -2994,6 +3275,8 @@ DEFINE H2_OFF_labels 0042800900000000
&output_buf_ptr ZERO4 H2_OFF_output_buf
&text_buf_ptr ZERO4 H2_OFF_text_buf
&labels_ptr ZERO4 H2_OFF_labels
+&fixups_ptr ZERO4 H2_OFF_fixups
+&scope_history_ptr ZERO4 H2_OFF_scope_history
:bss_init_tbl_end
## --- BSS scalars ----------------------------------------------------------
@@ -3049,6 +3332,10 @@ ZERO8
ZERO8
:label_count
ZERO8
+:fixup_count
+ZERO8
+:scope_history_used
+ZERO8
:scope_depth
ZERO8
:scope_seq
@@ -3068,6 +3355,54 @@ ZERO8
:rn_n
ZERO8
+## scan_name (returns ptr+len; pointer goes here, length in a0).
+:sn_start
+ZERO8
+
+## record_fixup scratch
+:rf_name
+ZERO8
+:rf_name_len
+ZERO8
+:rf_other
+ZERO8
+:rf_other_len
+ZERO8
+:rf_out_off
+ZERO8
+:rf_ip_after
+ZERO8
+:rf_idx
+ZERO8
+:rf_fp
+ZERO8
+:rf_i
+ZERO8
+
+## patch_fixups scratch
+:pf_i
+ZERO8
+:pf_fp
+ZERO8
+:pf_t_label
+ZERO8
+:pf_value
+ZERO8
+:pf_stack
+ZERO8
+:pf_depth
+ZERO8
+
+## process_reference scratch (single-pass record path)
+:pref_name
+ZERO8
+:pref_name_len
+ZERO8
+:pref_other
+ZERO8
+:pref_other_len
+ZERO8
+
## decimal read
:rd_val
ZERO8
@@ -3126,11 +3461,15 @@ ZERO8
:dl_name_off
ZERO8
-## lookup_label
+## lookup_label_in
:ll_src
ZERO8
:ll_len
ZERO8
+:ll_stack
+ZERO8
+:ll_depth
+ZERO8
:ll_d
ZERO8
:ll_sid
@@ -3164,7 +3503,7 @@ ZERO8
:pr_value
ZERO8
-## emit_value
+## emit_value / write_value
:ev_value
ZERO8
:ev_width
@@ -3179,6 +3518,18 @@ ZERO8
ZERO8
:ev_i
ZERO8
+:ev_out_off
+ZERO8
+
+## emit_zeros / emit_fill scratch
+:emz_n
+ZERO8
+:emz_off
+ZERO8
+:emf_n
+ZERO8
+:emf_b
+ZERO8
## directive scratch
:da_n
@@ -3257,5 +3608,9 @@ ZERO8
ZERO8
:labels_ptr
ZERO8
+:fixups_ptr
+ZERO8
+:scope_history_ptr
+ZERO8
:ELF_end
diff --git a/hex2pp/hex2pp.c b/hex2pp/hex2pp.c
@@ -3,9 +3,10 @@
*
* See docs/HEX2pp.md for the spec. Brief summary:
*
- * Inputs are concatenated, scanned in two passes. Pass 1 records label
- * definitions while advancing a position counter (ip). Pass 2 emits
- * bytes, resolving label references against the table built in pass 1.
+ * Input is scanned once. Label definitions are recorded into a table
+ * on the fly; label references emit zero placeholders and append a
+ * fixup record. After the scan, fixups are resolved against the
+ * completed label table and patched into the output buffer.
*
* Active syntax:
* digits in current byte mode -> raw bytes (HEX or BINARY)
@@ -27,12 +28,14 @@
#include <string.h>
#include <sys/stat.h>
-#define MAX_INPUT_BYTES (16 * 1024 * 1024)
-#define MAX_OUTPUT_BYTES (128 * 1024 * 1024)
-#define MAX_LABELS (1 << 20)
-#define MAX_TEXT (8 * 1024 * 1024)
-#define MAX_TOKEN 4096
-#define MAX_SCOPE_DEPTH 32
+#define MAX_INPUT_BYTES (16 * 1024 * 1024)
+#define MAX_OUTPUT_BYTES (128 * 1024 * 1024)
+#define MAX_LABELS (1 << 20)
+#define MAX_FIXUPS (1 << 20)
+#define MAX_TEXT (8 * 1024 * 1024)
+#define MAX_TOKEN 4096
+#define MAX_SCOPE_DEPTH 32
+#define MAX_SCOPE_HISTORY (1 << 22)
enum { HEX_MODE, BINARY_MODE };
@@ -49,6 +52,20 @@ struct Label {
int scope_id; /* 0 = global */
};
+struct Fixup {
+ long long out_off; /* offset in output_buf to patch */
+ long long ip_at_ref; /* ip immediately after the reference's bytes */
+ const char *name; /* points into input buffer */
+ const char *other; /* NULL if no -OTHER */
+ const char *src_path;
+ int name_len;
+ int other_len;
+ int scope_hist_off; /* index into scope_history (depth>0 only) */
+ int scope_depth;
+ int src_line;
+ int sigil;
+};
+
static struct InFile input_file;
static char text_buf[MAX_TEXT];
@@ -57,6 +74,12 @@ static int text_used;
static struct Label labels[MAX_LABELS];
static int label_count;
+static struct Fixup fixups[MAX_FIXUPS];
+static int fixup_count;
+
+static int scope_history[MAX_SCOPE_HISTORY];
+static int scope_history_used;
+
static unsigned char output_buf[MAX_OUTPUT_BYTES];
static long long output_used;
@@ -73,7 +96,6 @@ static int scope_stack[MAX_SCOPE_DEPTH];
static int scope_depth;
static int scope_seq;
-static int pass; /* 1 or 2 */
static const char *cur_path;
static int cur_line;
@@ -116,13 +138,6 @@ static int name_eq(const struct Label *L, const char *s, int len)
static void define_label(const char *s, int len, int scope_id)
{
- int i;
- /* Duplicate-definition check (within the same scope namespace). */
- for (i = 0; i < label_count; i++) {
- if (labels[i].scope_id == scope_id && name_eq(&labels[i], s, len)) {
- die("duplicate label '%.*s'", len, s);
- }
- }
if (label_count >= MAX_LABELS) {
die("too many labels");
}
@@ -133,17 +148,17 @@ static void define_label(const char *s, int len, int scope_id)
label_count++;
}
-static long long lookup_label(const char *s, int len)
+static long long lookup_label_in(const char *s, int len, const int *stack, int depth)
{
int i;
int d;
- int dotted = (len > 0 && s[0] == '.' && scope_depth > 0);
+ int dotted = (len > 0 && s[0] == '.' && depth > 0);
if (dotted) {
/* Inside a scope, walk the scope stack innermost-out. A dotted
* name resolves to the nearest enclosing definition, so an inner
* scope can shadow an outer one with the same local name. */
- for (d = scope_depth - 1; d >= 0; d--) {
- int sid = scope_stack[d];
+ for (d = depth - 1; d >= 0; d--) {
+ int sid = stack[d];
for (i = 0; i < label_count; i++) {
if (labels[i].scope_id == sid && name_eq(&labels[i], s, len)) {
return labels[i].target_ip;
@@ -157,8 +172,6 @@ static long long lookup_label(const char *s, int len)
return labels[i].target_ip;
}
}
- /* Also accept dotted labels referenced without sigil context?
- * No -- non-dotted lookup only sees global. */
die("undefined label '%.*s'", len, s);
}
return 0; /* unreachable */
@@ -168,10 +181,6 @@ static long long lookup_label(const char *s, int len)
static void emit_byte(unsigned b)
{
- if (pass != 2) {
- ip++;
- return;
- }
if (output_used >= MAX_OUTPUT_BYTES) {
die("output overflow");
}
@@ -179,7 +188,29 @@ static void emit_byte(unsigned b)
ip++;
}
-static void emit_value(long long v, int width, long long lo, long long hi, int range_check)
+static long long emit_zeros(long long n)
+{
+ long long off = output_used;
+ if (output_used + n > MAX_OUTPUT_BYTES) {
+ die("output overflow");
+ }
+ memset(output_buf + output_used, 0, (size_t)n);
+ output_used += n;
+ ip += n;
+ return off;
+}
+
+static void emit_fill(long long n, unsigned char b)
+{
+ if (output_used + n > MAX_OUTPUT_BYTES) {
+ die("output overflow");
+ }
+ memset(output_buf + output_used, b, (size_t)n);
+ output_used += n;
+ ip += n;
+}
+
+static void write_value(long long out_off, long long v, int width, long long lo, long long hi, int range_check)
{
int i;
unsigned char bytes[8];
@@ -191,18 +222,16 @@ static void emit_value(long long v, int width, long long lo, long long hi, int r
die("internal: bad reference width %d", width);
}
- /* Pack as little-endian into bytes[0..width-1]. */
for (i = 0; i < width; i++) {
bytes[i] = (unsigned char)((unsigned long long)v >> (8 * i)) & 0xff;
}
if (big_endian) {
- /* Reverse for big-endian output. */
for (i = 0; i < width; i++) {
- emit_byte(bytes[width - 1 - i]);
+ output_buf[out_off + i] = bytes[width - 1 - i];
}
} else {
for (i = 0; i < width; i++) {
- emit_byte(bytes[i]);
+ output_buf[out_off + i] = bytes[i];
}
}
}
@@ -347,18 +376,18 @@ static int is_name_terminator(int c)
return 0;
}
-static int read_name(struct Scanner *s, char *out, int max)
+/* Scan a label name; return pointer span into the input buffer (no copy). */
+static int scan_name(struct Scanner *s, const char **out_start)
{
- int n = 0;
+ int start = s->pos;
while (s->pos < s->len) {
int c = (unsigned char)s->buf[s->pos];
if (is_name_terminator(c)) break;
- if (n >= max) die("name too long");
- out[n++] = (char)c;
s->pos++;
}
- if (n == 0) die("expected label name");
- return n;
+ if (s->pos == start) die("expected label name");
+ *out_start = s->buf + start;
+ return s->pos - start;
}
/* Decimal integer (for directive arity arguments). */
@@ -403,14 +432,44 @@ static struct SigilInfo sigil_info(int c)
return si;
}
+static void record_fixup(const char *name, int name_len,
+ const char *other, int other_len,
+ int sigil, long long out_off, long long ip_after)
+{
+ struct Fixup *f;
+ if (fixup_count >= MAX_FIXUPS) die("too many references");
+ f = &fixups[fixup_count++];
+ f->out_off = out_off;
+ f->ip_at_ref = ip_after;
+ f->name = name;
+ f->name_len = name_len;
+ f->other = other;
+ f->other_len = other_len;
+ f->sigil = sigil;
+ f->src_path = cur_path;
+ f->src_line = cur_line;
+ f->scope_depth = scope_depth;
+ if (scope_depth > 0) {
+ if (scope_history_used + scope_depth > MAX_SCOPE_HISTORY) {
+ die("scope history overflow");
+ }
+ f->scope_hist_off = scope_history_used;
+ memcpy(&scope_history[scope_history_used], scope_stack,
+ (size_t)scope_depth * sizeof(int));
+ scope_history_used += scope_depth;
+ } else {
+ f->scope_hist_off = 0;
+ }
+}
+
static void process_reference(struct Scanner *s, int sigil)
{
- char label[MAX_TOKEN];
- char other[MAX_TOKEN];
- int llen, olen = 0;
- int has_other = 0;
+ const char *name_start;
+ const char *other_start = NULL;
+ int name_len;
+ int other_len = 0;
struct SigilInfo si = sigil_info(sigil);
- long long value = 0;
+ long long out_off;
if (sigil == '&' || sigil == '%') ptrsize_used = 1;
@@ -418,7 +477,7 @@ static void process_reference(struct Scanner *s, int sigil)
if (s->pos >= s->len || is_name_terminator((unsigned char)s->buf[s->pos])) {
die("sigil '%c' not followed by label name", sigil);
}
- llen = read_name(s, label, sizeof(label));
+ name_len = scan_name(s, &name_start);
/* Optional '-' OTHER or '>' OTHER (tight, no whitespace).
* '>' is a synonym for '-', accepted for hex2 compatibility. */
@@ -427,30 +486,12 @@ static void process_reference(struct Scanner *s, int sigil)
if (s->pos >= s->len || is_name_terminator((unsigned char)s->buf[s->pos])) {
die("'-' must be followed by label name");
}
- olen = read_name(s, other, sizeof(other));
- has_other = 1;
- }
-
- if (pass == 1) {
- ip += si.width;
- return;
+ other_len = scan_name(s, &other_start);
}
- /* Pass 2: compute value. */
- {
- long long t_label = lookup_label(label, llen);
- if (has_other) {
- long long t_other = lookup_label(other, olen);
- value = t_label - t_other;
- } else if (si.is_rel) {
- /* base = ip immediately after the reference's bytes are accounted for */
- value = t_label - (ip + si.width);
- } else {
- /* abs: target's absolute address (which includes Base_Address) */
- value = t_label + base_address;
- }
- emit_value(value, si.width, si.lo, si.hi, si.range_check);
- }
+ out_off = emit_zeros(si.width);
+ record_fixup(name_start, name_len, other_start, other_len,
+ sigil, out_off, ip);
}
/* --- directives ------------------------------------------------------- */
@@ -510,10 +551,6 @@ static void do_align(struct Scanner *s)
if (s->pos < s->len) {
c = (unsigned char)s->buf[s->pos];
if (is_byte_digit(c)) {
- int dummy;
- unsigned char dummyb;
- (void)dummyb;
- (void)dummy;
parse_one_byte_literal(s, NULL, 1, patbuf, (int)sizeof(patbuf), &patlen);
has_pattern = 1;
}
@@ -521,25 +558,30 @@ static void do_align(struct Scanner *s)
target = (ip + N - 1) & ~(N - 1);
pad = target - ip;
+ if (pad <= 0) return;
+ if (output_used + pad > MAX_OUTPUT_BYTES) die("output overflow");
if (!has_pattern) {
- for (i = 0; i < pad; i++) emit_byte(0);
+ memset(output_buf + output_used, 0, (size_t)pad);
} else {
- for (i = 0; i < pad; i++) emit_byte(patbuf[i % patlen]);
+ for (i = 0; i < pad; i++) {
+ output_buf[output_used + i] = patbuf[i % patlen];
+ }
}
+ output_used += pad;
+ ip += pad;
}
static void do_fill(struct Scanner *s)
{
long long N;
unsigned char b;
- long long i;
skip_inline_ws(s);
N = read_decimal(s);
if (N < 0) die(".fill: N must be non-negative (got %lld)", N);
skip_inline_ws(s);
parse_one_byte_literal(s, &b, 0, NULL, 0, NULL);
- for (i = 0; i < N; i++) emit_byte(b);
+ if (N > 0) emit_fill(N, b);
}
static void do_ptrsize(struct Scanner *s)
@@ -584,17 +626,17 @@ static void process_file(struct InFile *f)
c = (unsigned char)s.buf[s.pos];
if (c == ':') {
- char name[MAX_TOKEN];
+ const char *name;
int n;
int dotted;
int scope;
s.pos++;
- n = read_name(&s, name, sizeof(name));
+ n = scan_name(&s, &name);
/* A dot-prefixed name is scope-local only inside a .scope;
* outside, it is an ordinary global name. */
dotted = (n > 0 && name[0] == '.' && scope_depth > 0);
scope = dotted ? scope_stack[scope_depth - 1] : 0;
- if (pass == 1) define_label(name, n, scope);
+ define_label(name, n, scope);
continue;
}
@@ -627,6 +669,37 @@ static void process_file(struct InFile *f)
}
}
+/* --- fixup resolution ------------------------------------------------- */
+
+static void patch_fixups(void)
+{
+ int i;
+ for (i = 0; i < fixup_count; i++) {
+ struct Fixup *f = &fixups[i];
+ struct SigilInfo si = sigil_info(f->sigil);
+ const int *stack = (f->scope_depth > 0)
+ ? &scope_history[f->scope_hist_off]
+ : NULL;
+ long long t_label;
+ long long value;
+
+ cur_path = f->src_path;
+ cur_line = f->src_line;
+
+ t_label = lookup_label_in(f->name, f->name_len, stack, f->scope_depth);
+ if (f->other != NULL) {
+ long long t_other = lookup_label_in(f->other, f->other_len,
+ stack, f->scope_depth);
+ value = t_label - t_other;
+ } else if (si.is_rel) {
+ value = t_label - f->ip_at_ref;
+ } else {
+ value = t_label + base_address;
+ }
+ write_value(f->out_off, value, si.width, si.lo, si.hi, si.range_check);
+ }
+}
+
/* --- argument parsing & top-level ------------------------------------- */
static long long parse_long(const char *s, const char *what)
@@ -717,18 +790,6 @@ int main(int argc, char **argv)
}
load_input(in_path);
- /* Pass 1: collect labels. */
- pass = 1;
- ip = 0;
- scope_depth = 0;
- scope_seq = 0;
- ptrsize = 4;
- ptrsize_used = 0;
- process_file(&input_file);
- if (scope_depth != 0) die(".scope not closed at end of input");
-
- /* Pass 2: emit. */
- pass = 2;
ip = 0;
output_used = 0;
scope_depth = 0;
@@ -737,6 +798,7 @@ int main(int argc, char **argv)
ptrsize_used = 0;
process_file(&input_file);
if (scope_depth != 0) die(".scope not closed at end of input");
+ patch_fixups();
/* Write output. */
{