Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ jobs:
make tools/agbcc_sdk_cc/installed
/usr/bin/python3 tools/scripts/test_sdk_cc_reuse.py
/usr/bin/python3 tools/scripts/test_sdk_loop_cc_reuse.py
/usr/bin/python3 tools/scripts/test_sdk_late_pool.py
/usr/bin/python3 tools/scripts/test_sdk_lsr_flags.py --compiler tools/agbcc_sdk_flags/bin/agbcc --reference-compiler tools/agbcc_tst/bin/agbcc
/usr/bin/python3 tools/scripts/test_sdk_halfword_alignment.py --compiler tools/agbcc_sdk_flags/bin/agbcc --reference-compiler tools/agbcc_tst/bin/agbcc
/usr/bin/python3 tools/scripts/test_sdk_trampoline_alignment.py --compiler tools/agbcc_sdk_flags/bin/agbcc --reference-compiler tools/agbcc_tst/bin/agbcc
Expand Down
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ tools/agbcc_tst/installed: tools/install_agbcc_tst.sh tools/patches/agbcc-tst-eq

$(BUILD)/src/m4a_1.o: Makefile tools/agbcc_tst/installed

tools/agbcc_sdk_cc/installed: tools/install_agbcc_sdk_cc.sh tools/patches/agbcc-tst-eq-ne-only.patch tools/patches/agbcc-sdk-cc-reuse.patch tools/patches/agbcc-sdk-label-cc-reuse.patch tools/patches/agbcc-sdk-sign-cc-reuse.patch tools/patches/agbcc-sdk-loop-cc-reuse.patch
tools/agbcc_sdk_cc/installed: tools/install_agbcc_sdk_cc.sh tools/patches/agbcc-tst-eq-ne-only.patch tools/patches/agbcc-sdk-cc-reuse.patch tools/patches/agbcc-sdk-label-cc-reuse.patch tools/patches/agbcc-sdk-sign-cc-reuse.patch tools/patches/agbcc-sdk-loop-cc-reuse.patch tools/patches/agbcc-sdk-late-pool.patch
sh tools/install_agbcc_sdk_cc.sh

$(BUILD)/src/m4a_1_engine.o: Makefile tools/agbcc_sdk_cc/installed
Expand Down
6 changes: 5 additions & 1 deletion tools/install_agbcc_sdk_cc.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ SIGN_PATCH="$ROOT/tools/patches/agbcc-sdk-sign-cc-reuse.patch"
SIGN_SHA=f434c8999569964798b40adfb9d29a5dde6bfbb04395d697feebe4b2820217e0
LOOP_PATCH="$ROOT/tools/patches/agbcc-sdk-loop-cc-reuse.patch"
LOOP_SHA=4de75458ad2a2472c5b83e7254abd9cacae5ac53b951385d30b4b4f194fc5beb
LATE_PATCH="$ROOT/tools/patches/agbcc-sdk-late-pool.patch"
LATE_SHA=5d03b24a211750423a708b5fdc207afe26e73216b49babeba2a769b8db9be408
TST_PATCH="$ROOT/tools/patches/agbcc-tst-eq-ne-only.patch"
TST_SHA=6e4c7ada5e1dd97a0c6a7c9c105e02ac68e1948bfdd945b892b111302b311ade
mkdir -p "$DEST/bin" "$DEST/source"
Expand All @@ -26,6 +28,7 @@ printf '%s %s\n' "$PATCH_SHA" "$PATCH" | sha256sum -c -
printf '%s %s\n' "$LABEL_SHA" "$LABEL_PATCH" | sha256sum -c -
printf '%s %s\n' "$SIGN_SHA" "$SIGN_PATCH" | sha256sum -c -
printf '%s %s\n' "$LOOP_SHA" "$LOOP_PATCH" | sha256sum -c -
printf '%s %s\n' "$LATE_SHA" "$LATE_PATCH" | sha256sum -c -
printf '%s %s\n' "$TST_SHA" "$TST_PATCH" | sha256sum -c -
python3 - "$ARCHIVE" "$DEST/source" <<'PY'
import os, sys, zipfile
Expand All @@ -38,7 +41,7 @@ with zipfile.ZipFile(sys.argv[1]) as archive:
os.chmod(os.path.join(sys.argv[2], entry.filename), mode)
PY
SOURCE="$DEST/source/agbcc-$REV"
(cd "$SOURCE" && patch --fuzz=0 -p1 < "$TST_PATCH" && patch --fuzz=0 -p1 < "$PATCH" && patch --fuzz=0 -p1 < "$LABEL_PATCH" && patch --fuzz=0 -p1 < "$SIGN_PATCH" && patch --fuzz=0 -p1 < "$LOOP_PATCH")
(cd "$SOURCE" && patch --fuzz=0 -p1 < "$TST_PATCH" && patch --fuzz=0 -p1 < "$PATCH" && patch --fuzz=0 -p1 < "$LABEL_PATCH" && patch --fuzz=0 -p1 < "$SIGN_PATCH" && patch --fuzz=0 -p1 < "$LOOP_PATCH" && patch --fuzz=0 -p1 < "$LATE_PATCH")
make -C "$SOURCE/gcc" -j1 normal
EXE=
[ ! -f "$SOURCE/gcc/agbcc.exe" ] || EXE=.exe
Expand All @@ -51,6 +54,7 @@ cp "$SOURCE/gcc/COPYING" "$DEST/COPYING"
printf 'label_patch_sha256=%s\n' "$LABEL_SHA"
printf 'sign_patch_sha256=%s\n' "$SIGN_SHA"
printf 'loop_patch_sha256=%s\n' "$LOOP_SHA"
printf 'late_pool_patch_sha256=%s\n' "$LATE_SHA"
sha256sum "$DEST/bin/agbcc$EXE"
} > "$DEST/installed"
echo "SDK comparison-flags compiler ready: $DEST/bin/agbcc$EXE"
33 changes: 33 additions & 0 deletions tools/patches/README.sdk-cc-reuse.md
Original file line number Diff line number Diff line change
Expand Up @@ -158,3 +158,36 @@ Only the admitted byte loop loses its comparison; all excluded loops retain
it. A non-returning loop with the expected r0 already present must fail the
return check. Off and loop-without-base text are reference byte-identical.
CI runs this ROM-free suite. No SDK function is activated or credited here.

## Later existing literal pools

The separate `agbcc-sdk-late-pool.patch` has SHA256
`5d03b24a211750423a708b5fdc207afe26e73216b49babeba2a769b8db9be408`.
Its independent `-fthumb-late-pool` option defaults off. Production module
flags do not enable it. Legacy pool placement returns the first barrier;
the option may choose a later existing barrier within a conservative bound.

The bounded 4096-node scan counts instruction lengths and reserves space for
every pending literal, intentionally overcounting duplicates. Halfword
constants reserve a word, matching reorg's promotion, and doubleword constants
reserve eight bytes. Prospective literal-load lengths are also bounded below.
Only a barrier with code plus reserved literals at most 1000 bytes qualifies,
leaving margin for alignment and Thumb PC bias within its 1020-byte reach.
Unsupported sizes stop the search. Without an admitted later barrier, the
compiler falls back to unchanged legacy placement. Existing pool/relocation emission is reused.
There are no forced placements, ROM/function exceptions or opcode rewrites.

Run `python3 tools/scripts/test_sdk_late_pool.py`, accepting the same compiler,
reference, dependency and output arguments as the CC suite. Five profiles
execute 140,000 cases: reference, off, late placement, all options combined,
and late placement without TST. The off text is byte-identical to reference;
the positive fixture must move its word pool after the return.

Tests include 120 distinct constants and multiple barriers, a long function
with 520 volatile updates beyond the reach budget, 32/64-bit values and linked
read-only data references. They execute boundaries and seeded random values
with all incoming NZCV combinations and check memory, 32/64-bit results,
return PC, SP and r4-r11. Linked ELF sections are loaded at actual addresses.
A spinning negative control with the expected r0 already present must fail
return validation. CI runs these ROM-free tests. This capability does not
activate production flags or award a matching function.
71 changes: 71 additions & 0 deletions tools/patches/agbcc-sdk-late-pool.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -597,6 +597,7 @@
int flag_thumb_label_cc_reuse = 0;
int flag_thumb_sign_cc_reuse = 0;
int flag_thumb_loop_cc_reuse = 0;
+int flag_thumb_late_pool = 0;

typedef struct
{
@@ -746,6 +747,8 @@
{"prologue-bugfix", &flag_prologue_bugfix, 1,
"Prevent unnecessary saving of the lr register to the stack"},
#endif
+ {"thumb-late-pool", &flag_thumb_late_pool, 1,
+ "Prefer later existing barriers within a conservative literal reach budget"},
{"thumb-loop-cc-reuse", &flag_thumb_loop_cc_reuse, 1,
"Reuse signed decrement flags in proven byte-seeded loops"},
{"thumb-sign-cc-reuse", &flag_thumb_sign_cc_reuse, 1,
--- a/gcc/thumb.c
+++ b/gcc/thumb.c
@@ -248,12 +248,49 @@

#define MAX_COUNT_SI 1000

+
+extern int flag_thumb_late_pool;
+
+/* Prefer a later existing dead-code boundary only while code plus a
+ conservative reservation for every literal remains in reach. Duplicate
+ constants are intentionally overcounted; HI loads become SI in reorg. */
+static rtx
+thumb_find_late_pool_barrier(rtx scan)
+{
+ rtx best = 0;
+ int code_bytes = 0, pool_bytes = 0, budget = 4096;
+ for (; scan && budget-- > 0; scan = NEXT_INSN(scan)) {
+ int length = get_attr_length(scan);
+ if (GET_CODE(scan) == INSN && GET_CODE(PATTERN(scan)) == SET) {
+ rtx p = PATTERN(scan), source = SET_SRC(p), dest = SET_DEST(p);
+ enum machine_mode mode = GET_MODE(dest);
+ if (dest != pc_rtx && fixit(source, mode)) {
+ int size = GET_MODE_SIZE(mode);
+ if (size < 4) size = 4;
+ if (size != 4 && size != 8) break;
+ pool_bytes += size;
+ if (length < size / 2) length = size / 2;
+ }
+ }
+ code_bytes += length;
+ /* Preserve margin for word alignment and Thumb's PC bias. */
+ if (code_bytes + pool_bytes > MAX_COUNT_SI) break;
+ if (GET_CODE(scan) == BARRIER) best = scan;
+ }
+ return best;
+}
+
static rtx
find_barrier(rtx from)
{
int count = 0;
rtx found_barrier = 0;
rtx label;
+
+ if (flag_thumb_late_pool) {
+ rtx late = thumb_find_late_pool_barrier(from);
+ if (late) return late;
+ }

while (from && count < MAX_COUNT_SI)
{
81 changes: 81 additions & 0 deletions tools/scripts/test_sdk_late_pool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env python3
from pathlib import Path
import sys,struct,json,itertools,random,argparse,subprocess,re
ROOT=Path(__file__).resolve().parents[2]
parser=argparse.ArgumentParser(description='Execute generic Thumb literal-pool layout fixtures; no ROM required.')
parser.add_argument('--compiler',type=Path,default=ROOT/'tools/agbcc_sdk_cc/bin/agbcc')
parser.add_argument('--reference-compiler',type=Path,default=ROOT/'tools/agbcc_tst/bin/agbcc')
parser.add_argument('--deps',type=Path)
parser.add_argument('--out',type=Path,default=ROOT/'build/sdk_late_pool_tests')
args=parser.parse_args()
if args.deps:sys.path.insert(0,str(args.deps.resolve()))
def executable(p):
p=p.resolve()
return p.with_suffix('.exe') if not p.is_file() and p.with_suffix('.exe').is_file() else p
from unicorn import Uc,UC_ARCH_ARM,UC_MODE_THUMB
from unicorn.arm_const import *
from elftools.elf.elffile import ELFFile
out=args.out.resolve();out.mkdir(parents=True,exist_ok=True);mask=0xffffffff
values=[0,1,2,3,0x7fffffff,0x80000000,0x80000001,0xfffffffe,mask]
rng=random.Random(5361020);pairs=list(itertools.product(values,repeat=2))+[(rng.getrandbits(32),rng.getrandbits(32)) for _ in range(64)]
constants=[(0x13579bdf+i*0x01020409)&mask for i in range(120)];sentinel=0xa5a5a5a5

def expected(name,a,b):
if name=='pool_paths':
v=((0x13579bdf+b) if a&1 else (0x2468ace0-b))&mask
return ((v^0xa5c3e179) if a&2 else (v+0x10203040)&mask),v
if name=='pool_pointer':
v=([0x12345678,0x87654321,0x80000001,mask][a&3]+b)&mask
return v^0x55aa13c7,v
if name=='pool_wide':return (0x12345678fffffff8 if a&1 else 0xfedcba9887654321),sentinel
if name=='pool_many':
v=(constants[a]+b)&mask if a<len(constants) else (a-b)&mask
return (v^0x55aa13c7 if a<len(constants) else v),v
if name=='pool_far':
v=0x78563412 if a&1 else (0x78563412+520*b)&mask
return ((v+b)&mask if a&1 else (v+0x13579bdf)&mask),v
raise AssertionError(name)
def cases(name):
if name=='pool_many':return [(a,b) for a in range(125) for b in values]+pairs
if name=='pool_far':return list(itertools.product([0,1,2,3,mask],values))
return pairs
base=0x1000000;data=0x2000000;stack=0x3000000;stop=base+0xf000;reports=[]
for profile,opts in [('reference',[]),('off',[]),('on',['-fthumb-late-pool']),('combined',['-fthumb-late-pool','-fthumb-cc-reuse','-fthumb-label-cc-reuse','-fthumb-sign-cc-reuse','-fthumb-loop-cc-reuse']),('without_tst',['-fthumb-late-pool'])]:
compiler=executable(args.reference_compiler if profile=='reference' else args.compiler)
stem=out/profile;tst=[] if profile=='without_tst' else ['-ftst']
for cmd in [['arm-none-eabi-cpp','-undef','-nostdinc',ROOT/'tools/tests/fixtures/sdk_late_pool.c','-o',stem.with_suffix('.i')],[str(compiler),'-O2','-mthumb-interwork','-fprologue-bugfix',*tst,*opts,stem.with_suffix('.i'),'-o',stem.with_suffix('.s')],['arm-none-eabi-as','-mcpu=arm7tdmi','-o',stem.with_suffix('.o'),stem.with_suffix('.s')],['arm-none-eabi-ld','-Ttext=0x1000000','-e','pool_paths','-o',stem.with_suffix('.elf'),stem.with_suffix('.o')]]:
subprocess.run(cmd,cwd=ROOT,check=True)
assembly=stem.with_suffix('.s').read_text()
if profile in ['on','combined','without_tst']:
body=assembly.split('\npool_paths:\n',1)[1].split('\n.Lfe',1)[0]
assert body.index('\t.word')>body.rindex('\tbx\t'),'positive fixture pool must follow its return'
with (out/(profile+'.elf')).open('rb') as f:
elf=ELFFile(f);code=elf.get_section_by_name('.text').data();sections=[(s['sh_addr'],s.data()) for s in elf.iter_sections() if s['sh_flags']&2 and s['sh_size']>0 and s['sh_type']!='SHT_NOBITS'];syms={s.name:s['st_value']&~1 for s in elf.get_section_by_name('.symtab').iter_symbols() if s.name.startswith('pool_') and s['st_info']['type']=='STT_FUNC'}
if profile=='reference':reference=code
if profile=='off':assert code==reference,'default output changed'
if profile=='on':assert code!=reference,'pool fixture did not exercise a layout change'
mu=Uc(UC_ARCH_ARM,UC_MODE_THUMB);mu.mem_map(base,0x10000);mu.mem_map(data,0x1000);mu.mem_map(stack,0x1000)
for address,content in sections:
assert base<=address and address+len(content)<stop,(profile,hex(address),len(content))
mu.mem_write(address,content)
preserved=[UC_ARM_REG_R4,UC_ARM_REG_R5,UC_ARM_REG_R6,UC_ARM_REG_R7,UC_ARM_REG_R8,UC_ARM_REG_R9,UC_ARM_REG_R10,UC_ARM_REG_R11];count=0
for name,address in syms.items():
if name=='pool_negative_spin':continue
for a,b in cases(name):
want,memory=expected(name,a,b)
for flags in range(16):
mu.reg_write(UC_ARM_REG_CPSR,(flags<<28)|0x30)
for reg,value in [(UC_ARM_REG_R0,a),(UC_ARM_REG_R1,b),(UC_ARM_REG_R2,data),(UC_ARM_REG_R3,a^b),(UC_ARM_REG_R12,0x12345678),(UC_ARM_REG_SP,stack+4080),(UC_ARM_REG_LR,stop|1)]:mu.reg_write(reg,value)
saved={reg:(0x44440000+i*4369^a^b)&mask for i,reg in enumerate(preserved)}
for reg,value in saved.items():mu.reg_write(reg,value)
mu.mem_write(data,struct.pack('<II',sentinel,0x5a5a5a5a));mu.emu_start(address|1,stop,count=20000)
ok=mu.reg_read(UC_ARM_REG_PC)==stop and mu.reg_read(UC_ARM_REG_SP)==stack+4080 and all(mu.reg_read(k)==v for k,v in saved.items())
result=mu.reg_read(UC_ARM_REG_R0)
if name=='pool_wide':result|=mu.reg_read(UC_ARM_REG_R1)<<32
actual_memory=struct.unpack('<II',bytes(mu.mem_read(data,8)))
assert ok and result==want and actual_memory==(memory,0x5a5a5a5a),(profile,name,hex(a),hex(b),flags,ok,hex(result),hex(want),actual_memory,hex(memory))
count+=1
mu.reg_write(UC_ARM_REG_R0,0);mu.reg_write(UC_ARM_REG_SP,stack+4080);mu.reg_write(UC_ARM_REG_LR,stop|1);mu.emu_start(syms['pool_negative_spin']|1,stop,count=1000)
assert mu.reg_read(UC_ARM_REG_PC)!=stop and mu.reg_read(UC_ARM_REG_R0)==0
report=dict(profile=profile,cases=count,bytes=len(code),negative_control_rejected=True);reports.append(report);print(report,flush=True)
(out/'runtime-report.json').write_text(json.dumps(reports,indent=2)+'\n')
Loading
Loading