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 @@ -24,6 +24,7 @@ jobs:
/usr/bin/python3 tools/scripts/test_sdk_tst_matches.py
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_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/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
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 @@ -11,6 +11,8 @@ LABEL_PATCH="$ROOT/tools/patches/agbcc-sdk-label-cc-reuse.patch"
LABEL_SHA=6492f5ca23c68a471f4cb4c92e32ed4344984ea73b1c4d12cd52f1ffbf805d5f
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
TST_PATCH="$ROOT/tools/patches/agbcc-tst-eq-ne-only.patch"
TST_SHA=6e4c7ada5e1dd97a0c6a7c9c105e02ac68e1948bfdd945b892b111302b311ade
mkdir -p "$DEST/bin" "$DEST/source"
Expand All @@ -23,6 +25,7 @@ printf '%s %s\n' "$ARCHIVE_SHA" "$ARCHIVE" | sha256sum -c -
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' "$TST_SHA" "$TST_PATCH" | sha256sum -c -
python3 - "$ARCHIVE" "$DEST/source" <<'PY'
import os, sys, zipfile
Expand All @@ -35,7 +38,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")
(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")
make -C "$SOURCE/gcc" -j1 normal
EXE=
[ ! -f "$SOURCE/gcc/agbcc.exe" ] || EXE=.exe
Expand All @@ -47,6 +50,7 @@ cp "$SOURCE/gcc/COPYING" "$DEST/COPYING"
printf 'base_tst_patch_sha256=%s\n' "$TST_SHA"
printf 'label_patch_sha256=%s\n' "$LABEL_SHA"
printf 'sign_patch_sha256=%s\n' "$SIGN_SHA"
printf 'loop_patch_sha256=%s\n' "$LOOP_SHA"
sha256sum "$DEST/bin/agbcc$EXE"
} > "$DEST/installed"
echo "SDK comparison-flags compiler ready: $DEST/bin/agbcc$EXE"
35 changes: 35 additions & 0 deletions tools/patches/README.sdk-cc-reuse.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,3 +123,38 @@ and on the taken path. Assembly requires PL/MI for the positive fixtures,
retained CMP for GT and opaque controls, and a fresh CMP for the subsequent
ordered consumer. Option-off and sign-without-base output remain reference
byte-identical. This compiler capability adds no matching-function credit.

## Byte-seeded loop decrement extension

The separate `agbcc-sdk-loop-cc-reuse.patch` has SHA256
`4de75458ad2a2472c5b83e7254abd9cacae5ac53b951385d30b4b4f194fc5beb`.
Default-off `-fthumb-loop-cc-reuse` requires base comparison reuse; production
module flags do not enable it.

The proof accepts only a low SI counter loaded from a nonvolatile unsigned
byte before a single-entry loop, with no other counter definitions, calls or
opaque ASM in the body. The sole decrement is immediately followed by a zero
comparison and a signed GT backedge. Every region label's outside incoming
edges are checked, and unknown indirect/switch jumps are rejected. The byte
seed scan is bounded to eight instructions, region traversal to 256 nodes
and the whole-function entry audit to 4096 nodes. Budget exhaustion rejects
the optimization. Full INSN register-set checks include writeback notes.

Inductively, the first input lies in 0..255; repeated iterations receive a
positive previous result. Only SUB #1 changes it, so its result is -1..254
and overflow is zero. Its NZV therefore matches CMP(result,0) for signed GT.
A shared bounded proof additionally rejects any later observer of the
different carry bit on either path. The fact is cleared after the consumer.
This reasoning does not apply to arbitrary 32-bit counters at INT_MIN.

Run `python3 tools/scripts/test_sdk_loop_cc_reuse.py` (the same compiler,
reference, dependency and output arguments as the main CC suite). Six
profiles cover reference, off, base only, loop plus base, all reuse options
combined and loop without base. They test every byte seed with all incoming NZCV combinations and
ABI/memory/return checks. Negative controls cover arbitrary word counters
including INT_MIN, changed counters, outside entry to an inner label, body
ASM, volatile seeds, real C calls, opaque exit paths and the traversal bound.
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.
166 changes: 166 additions & 0 deletions tools/patches/agbcc-sdk-loop-cc-reuse.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -596,6 +596,7 @@
int flag_thumb_cc_reuse = 0;
int flag_thumb_label_cc_reuse = 0;
int flag_thumb_sign_cc_reuse = 0;
+int flag_thumb_loop_cc_reuse = 0;

typedef struct
{
@@ -745,6 +746,8 @@
{"prologue-bugfix", &flag_prologue_bugfix, 1,
"Prevent unnecessary saving of the lr register to the stack"},
#endif
+ {"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,
"Reuse Thumb arithmetic sign flags for sign-bit tests"},
{"thumb-label-cc-reuse", &flag_thumb_label_cc_reuse, 1,
--- a/gcc/thumb.h
+++ b/gcc/thumb.h
@@ -1115,3 +1115,5 @@

extern int flag_thumb_sign_cc_reuse;
#define CC_THUMB_SIGN_ONLY 0400
+
+extern int flag_thumb_loop_cc_reuse;
--- a/gcc/thumb.c
+++ b/gcc/thumb.c
@@ -1685,6 +1685,116 @@
return 0;
}

+
+/* Known direct CFG edges only. Return epilogues have no local successor. */
+static int
+thumb_loop_cc_return(rtx p)
+{
+ return GET_CODE(p) == RETURN
+ || (GET_CODE(p) == UNSPEC_VOLATILE && XINT(p, 1) == 1
+ && XVECLEN(p, 0) == 1
+ && GET_CODE(XVECEXP(p, 0, 0)) == CONST_INT
+ && INTVAL(XVECEXP(p, 0, 0)) == 0);
+}
+
+static rtx
+thumb_loop_cc_target(rtx jump)
+{
+ rtx p = PATTERN(jump), source, yes, no;
+ if (GET_CODE(p) != SET || SET_DEST(p) != pc_rtx) return 0;
+ source = SET_SRC(p);
+ if (GET_CODE(source) == LABEL_REF) return XEXP(source, 0);
+ if (GET_CODE(source) != IF_THEN_ELSE) return 0;
+ yes = XEXP(source, 1); no = XEXP(source, 2);
+ if (GET_CODE(yes) == LABEL_REF && no == pc_rtx) return XEXP(yes, 0);
+ if (yes == pc_rtx && GET_CODE(no) == LABEL_REF) return XEXP(no, 0);
+ return 0;
+}
+
+/* A unique entry seeds 0..255. Only SUB #1 changes the counter, and
+ the sole repeat edge requires its signed result >0. Inductively every
+ SUB input is 0..255, hence result -1..254 and V=0. This is not valid
+ for an arbitrary SI decrement at INT_MIN. */
+static int
+thumb_loop_cc_range(rtx sub, rtx latch, rtx header, rtx value)
+{
+ rtx scan, p, source, labels[256], target;
+ int count = 0, budget = 256, i, inside = 0, found_sub = 0, found_latch = 0;
+ if (GET_CODE(header) != CODE_LABEL || LABEL_NUSES(header) != 1
+ || JUMP_LABEL(latch) != header) return 0;
+ for (scan = header; scan && budget-- > 0; scan = NEXT_INSN(scan)) {
+ if (GET_CODE(scan) == CODE_LABEL) labels[count++] = scan;
+ else if (GET_CODE(scan) == NOTE || GET_CODE(scan) == BARRIER) ;
+ else {
+ if (GET_RTX_CLASS(GET_CODE(scan)) != 'i'
+ || GET_CODE(scan) == CALL_INSN) return 0;
+ p = PATTERN(scan);
+ if (GET_CODE(p) == ASM_INPUT || asm_noperands(p) >= 0) return 0;
+ if (scan == sub) found_sub = 1;
+ else if (reg_set_p(value, scan)) return 0;
+ if (GET_CODE(scan) == JUMP_INSN
+ && !thumb_loop_cc_return(p) && !thumb_loop_cc_target(scan)) return 0;
+ }
+ if (scan == latch) { found_latch = 1; break; }
+ }
+ if (!found_sub || !found_latch) return 0;
+ /* The only outside entry is fallthrough through the seed. */
+ budget = 4096;
+ for (scan = get_insns(); scan && budget-- > 0; scan = NEXT_INSN(scan)) {
+ if (scan == header) inside = 1;
+ if (GET_CODE(scan) == JUMP_INSN && !thumb_loop_cc_return(PATTERN(scan))) {
+ target = thumb_loop_cc_target(scan);
+ if (!target) return 0;
+ if (!inside) for (i = 0; i < count; ++i)
+ if (target == labels[i]) return 0;
+ }
+ if (scan == latch) inside = 0;
+ }
+ if (scan) return 0;
+ budget = 8;
+ for (scan = prev_nonnote_insn(header); scan && budget-- > 0;
+ scan = prev_nonnote_insn(scan)) {
+ if (GET_CODE(scan) != INSN) return 0;
+ p = PATTERN(scan);
+ if (GET_CODE(p) != SET || asm_noperands(p) >= 0) return 0;
+ if (reg_set_p(value, scan)) {
+ if (!rtx_equal_p(SET_DEST(p), value)) return 0;
+ source = SET_SRC(p);
+ return GET_CODE(source) == ZERO_EXTEND && GET_MODE(source) == SImode
+ && GET_CODE(XEXP(source, 0)) == MEM
+ && GET_MODE(XEXP(source, 0)) == QImode
+ && !side_effects_p(XEXP(source, 0));
+ }
+ if (side_effects_p(SET_DEST(p)) || side_effects_p(SET_SRC(p))) return 0;
+ }
+ return 0;
+}
+
+static int
+thumb_loop_cc_consumer(rtx sub, rtx value)
+{
+ rtx compare = next_nonnote_insn(sub), latch, p, source, condition, yes, no, header;
+ int budget = 256;
+ if (!compare || GET_CODE(compare) != INSN) return 0;
+ p = PATTERN(compare);
+ if (GET_CODE(p) != SET || SET_DEST(p) != cc0_rtx
+ || !rtx_equal_p(SET_SRC(p), value)) return 0;
+ latch = next_nonnote_insn(compare);
+ if (!latch || GET_CODE(latch) != JUMP_INSN) return 0;
+ p = PATTERN(latch);
+ if (GET_CODE(p) != SET || SET_DEST(p) != pc_rtx) return 0;
+ source = SET_SRC(p);
+ if (GET_CODE(source) != IF_THEN_ELSE) return 0;
+ condition = XEXP(source, 0); yes = XEXP(source, 1); no = XEXP(source, 2);
+ if (GET_CODE(condition) != GT || XEXP(condition, 0) != cc0_rtx
+ || XEXP(condition, 1) != const0_rtx
+ || GET_CODE(yes) != LABEL_REF || no != pc_rtx) return 0;
+ header = XEXP(yes, 0);
+ return thumb_loop_cc_range(sub, latch, header, value)
+ && thumb_cc_reuse_flags_dead(header, &budget)
+ && thumb_cc_reuse_flags_dead(next_nonnote_insn(latch), &budget);
+}
+
void
thumb_sdk_notice_update_cc(rtx exp, rtx insn)
{
@@ -1713,6 +1823,19 @@
return;
}
if (thumb_cc_reuse_store(insn)) return;
+ if (flag_thumb_loop_cc_reuse && GET_CODE(dest) == REG
+ && GET_MODE(dest) == SImode && REGNO(dest) < 8
+ && GET_CODE(source) == PLUS
+ && rtx_equal_p(XEXP(source, 0), dest)
+ && GET_CODE(XEXP(source, 1)) == CONST_INT
+ && INTVAL(XEXP(source, 1)) == -1
+ && thumb_loop_cc_consumer(insn, dest)) {
+ CC_STATUS_INIT;
+ cc_status.value1 = dest;
+ thumb_cc_reuse_zero_only = 1;
+ return;
+ }
+
if (flag_thumb_sign_cc_reuse && GET_CODE(dest) == REG
&& GET_MODE(dest) == SImode && REGNO(dest) < 8
&& GET_CODE(source) == PLUS
83 changes: 83 additions & 0 deletions tools/scripts/test_sdk_loop_cc_reuse.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/usr/bin/env python3
from pathlib import Path
import sys,struct,json,re,argparse,subprocess
ROOT=Path(__file__).resolve().parents[2]
parser=argparse.ArgumentParser(description='Execute generic bounded byte-loop CC 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_loop_cc_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
vals=[0,1,2,3,0x7fffffff,0x80000000,0x80000001,0xffffffff,0xfffffffe]
def signed(n):return n if n<0x80000000 else n-0x100000000
def expected(name,a,b,initial):
n=initial if name=='loop_word' else b if name=='loop_inner_entry' and a&1 else initial&255
total=0
for iteration in range(300):
if name=='loop_inner_entry':
if not(iteration==0 and a&1):total+=2
if total>4:return total,n
elif name=='loop_changed':
n=(n+b)&mask;total+=1
if total==3:return total,n
else:
total=(total+a)&mask
if name=='loop_word' and total==(3*a)&mask:return total,n
n=(n-1)&mask
if signed(n)<=0:return total,n
raise AssertionError('oracle did not terminate')
def cases(name):
if name in ['loop_byte','loop_opaque','loop_volatile','loop_call','loop_opaque_exit']:
return [(a,0,n) for n in range(256) for a in [0,1,mask]]
if name=='loop_bound':return [(a,0,n) for n in [0,1,2] for a in [0,1,mask]]
if name=='loop_word':return [(a,0,n) for n in vals for a in vals]
if name=='loop_changed':return [(0,b,n) for n in [0,1,255] for b in vals]
if name=='loop_inner_entry':return [(a,b,n) for n in [0,1,255] for a in [0,1] for b in vals]
raise AssertionError(name)
base=0x1000000;data=0x2000000;stack=0x3000000;stop=base+0xf000;reports=[]
for profile,opts in [('reference',[]),('off',[]),('base',['-fthumb-cc-reuse']),('on',['-fthumb-cc-reuse','-fthumb-loop-cc-reuse']),('combined',['-fthumb-cc-reuse','-fthumb-loop-cc-reuse','-fthumb-label-cc-reuse','-fthumb-sign-cc-reuse']),('without_base',['-fthumb-loop-cc-reuse'])]:
compiler=executable(args.reference_compiler if profile=='reference' else args.compiler)
stem=out/profile
for cmd in [['arm-none-eabi-cpp','-undef','-nostdinc',ROOT/'tools/tests/fixtures/sdk_loop_cc_reuse.c','-o',stem.with_suffix('.i')],[str(compiler),'-O2','-mthumb-interwork','-fprologue-bugfix','-ftst',*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','loop_byte','-o',stem.with_suffix('.elf'),stem.with_suffix('.o')]]:
subprocess.run(cmd,cwd=ROOT,check=True)
with (out/(profile+'.elf')).open('rb') as f:
elf=ELFFile(f);code=elf.get_section_by_name('.text').data();syms={s.name:s['st_value']&~1 for s in elf.get_section_by_name('.symtab').iter_symbols() if s.name.startswith('loop_') and s.name!='loop_identity'}
if profile=='reference':reference=code
if profile in ['off','without_base']:assert code==reference
assembly=(out/(profile+'.s')).read_text()
assert re.search(r'^\s+bl\s+loop_identity\b',assembly,re.M),'call fixture was inlined'
for name in syms:
if name=='loop_negative_spin':continue
body=assembly.split('\n'+name+':\n')[1].split('\n.Lfe')[0]
count=len(re.findall(r'^\s+cmp\s',body,re.M));want=(0 if profile in ['on','combined'] else 1) if name=='loop_byte' else 2 if name in ['loop_word','loop_changed','loop_inner_entry'] else 1
assert count==want,(profile,name,count,want)
mu=Uc(UC_ARCH_ARM,UC_MODE_THUMB);mu.mem_map(base,0x10000);mu.mem_write(base,code);mu.mem_map(data,0x1000);mu.mem_map(stack,0x1000)
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=='loop_negative_spin':continue
for a,b,initial in cases(name):
want,wantn=expected(name,a,b,initial)
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',initial,0xa5a5a5a5));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);memory=struct.unpack('<II',bytes(mu.mem_read(data,8)))
assert ok and result==want and memory==(initial,wantn),(profile,name,hex(a),hex(b),hex(initial),flags,ok,result,want,memory,wantn)
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['loop_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));reports.append(report);print(report,flush=True)
(out/'runtime-report.json').write_text(json.dumps(reports,indent=2)+'\n')
47 changes: 47 additions & 0 deletions tools/tests/fixtures/sdk_loop_cc_reuse.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
typedef unsigned int u32;
typedef int s32;
typedef unsigned char u8;
u32 loop_byte(u32 a,u32 b,u32*out){u32 n=*(u8*)out;u32 sum=0;again:sum+=a;if((s32)--n>0)goto again;out[1]=n;return sum;}
u32 loop_word(u32 a,u32 b,u32*out){u32 n=*out;u32 sum=0;again:sum+=a;if(sum==3*a)goto done;if((s32)--n>0)goto again;done:out[1]=n;return sum;}
u32 loop_changed(u32 a,u32 b,u32*out){u32 n=*(u8*)out;u32 sum=0;again:n+=b;sum++;if(sum==3)goto done;if((s32)--n>0)goto again;done:out[1]=n;return sum;}
u32 loop_inner_entry(u32 a,u32 b,u32*out){u32 n=b;u32 sum=0;if(a&1)goto middle;n=*(u8*)out;again:sum+=2;middle:if(sum>4)goto done;if((s32)--n>0)goto again;done:out[1]=n;return sum;}
u32 loop_opaque(u32 a,u32 b,u32*out){u32 n=*(u8*)out;u32 sum=0;again:sum+=a;__asm__ volatile("" ::: "cc");if((s32)--n>0)goto again;out[1]=n;return sum;}
u32 loop_volatile(u32 a,u32 b,u32*out){u32 n=*(volatile u8*)out;u32 sum=0;again:sum+=a;if((s32)--n>0)goto again;out[1]=n;return sum;}

u32 loop_identity(u32 value){return value;}
u32 loop_call(u32 a,u32 b,u32*out){u32 n=*(u8*)out;u32 sum=0;again:sum=loop_identity(sum+a);if((s32)--n>0)goto again;out[1]=n;return sum;}
u32 loop_opaque_exit(u32 a,u32 b,u32*out){u32 n=*(u8*)out;u32 sum=0;again:sum+=a;if((s32)--n>0)goto again;out[1]=n;__asm__ volatile("" ::: "cc");return sum;}
#define STORES10 *target=sum; *target=sum; *target=sum; *target=sum; *target=sum; *target=sum; *target=sum; *target=sum; *target=sum; *target=sum;
u32 loop_bound(u32 a,u32 b,u32*out){u32 n=*(u8*)out;u32 sum=0;volatile u32*target=out+1;again:sum+=a;
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
STORES10
if((s32)--n>0)goto again;out[1]=n;return sum;}
u32 loop_negative_spin(u32 a,u32 b,u32*out){for(;;){}}
Loading