Native iPhone port of Factorio 2.0.77
Run the original macOS ARM64 game code on iOS: - tools/retarget.py: header-only Mach-O retargeting to an iOS dylib - platform/native: UIKit host and macOS API shims (AppKit for SDL's Cocoa backend, CoreGraphics displays, CoreAudio device, Metal device properties, Wuffs PNG decoding, Steam and peripheral stubs) - touch, overlay, iOS keyboard and hardware keyboard input - tools/build_native.py: build, sign, install, data copy, launch - docs: build guide, controls, architecture Also includes the earlier static-translation research (ARM64 -> WASM -> native) under docs/research, runtime/, tests/ and tools/. No game files are included; the build reads them from a local install.
This commit is contained in:
commit
51d085e865
89 files changed
+7999
No files matched your search
@@ -0,0 +1,137 @@
|
||||
"""Compare direct guest calls, nested LR saves, recursion, tails and memory with ARM64."""
|
||||
import json
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
sys.path.insert(0,str(ROOT/'tools'))
|
||||
from a64_program import ProgramCompiler
|
||||
from a64_lift import UnsupportedInstruction
|
||||
|
||||
OUT = ROOT/'build/program'
|
||||
OUT.mkdir(parents=True,exist_ok=True)
|
||||
|
||||
|
||||
def bl(site,target): return 0x94000000 | (((target-site)//4)&0x3ffffff)
|
||||
def body(words): return struct.pack('<'+'I'*len(words),*words)
|
||||
def run(*args): subprocess.run(list(map(str,args)),cwd=ROOT,check=True)
|
||||
|
||||
|
||||
fixtures = {
|
||||
'nested': (0x1000, {
|
||||
0x1000:[0xa9bf7bf3,0xaa0003f3,bl(0x1008,0x1100),bl(0x100c,0x1200),0x8b130000,0xa8c17bf3,0xd65f03c0],
|
||||
0x1100:[0x91000400,0xd65f03c0],
|
||||
0x1200:[0xa9bf7bf4,bl(0x1204,0x1100),0xa8c17bf4,0x91000800,0xd65f03c0]}),
|
||||
'recursive': (0x2000, {0x2000:[0xa9bf7bf3,0xb4000080,0xd1000400,
|
||||
bl(0x200c,0x2000),0x91000400,0xa8c17bf3,0xd65f03c0]}),
|
||||
'tail': (0x3000, {0x3000:[0x14000040],0x3100:[0x91001c00,0xd65f03c0]}),
|
||||
'memory': (0x4000, {
|
||||
0x4000:[0xa9bf7bf3,0xaa0003f3,bl(0x4008,0x4100),0xaa1303e0,bl(0x4010,0x4100),0xa8c17bf3,0xd65f03c0],
|
||||
0x4100:[0xf9400008,0x91000508,0xf9000008,0xaa0803e0,0xd65f03c0]})}
|
||||
|
||||
assembly = ['.text','.p2align 2']
|
||||
cursor = 0x1000
|
||||
for name,(entry,functions) in fixtures.items():
|
||||
compiler = ProgramCompiler()
|
||||
wat = compiler.compile({address:body(words) for address,words in functions.items()},entry)
|
||||
(OUT/f'{name}.wat').write_text(wat)
|
||||
run(ROOT/'build/wabt/wat2wasm',OUT/f'{name}.wat','-o',OUT/f'{name}.wasm')
|
||||
run(ROOT/'build/wabt/wasm2c',OUT/f'{name}.wasm','-o',OUT/f'{name}.c','--module-name='+name)
|
||||
for address,words in sorted(functions.items()):
|
||||
assert address>=cursor
|
||||
assembly.append(f'.space {address-cursor}')
|
||||
if address==entry: assembly += [f'.globl _native_{name}',f'_native_{name}:']
|
||||
assembly += [f'.long 0x{word:08x}' for word in words]
|
||||
cursor=address+4*len(words)
|
||||
(OUT/'oracle.S').write_text('\n'.join(assembly)+'\n')
|
||||
bad_return = ProgramCompiler().compile({0x5000:body([0xd282469e,0xd65f03c0])},0x5000)
|
||||
(OUT/'badreturn.wat').write_text(bad_return)
|
||||
run(ROOT/'build/wabt/wat2wasm',OUT/'badreturn.wat','-o',OUT/'badreturn.wasm')
|
||||
run(ROOT/'build/wabt/wasm2c',OUT/'badreturn.wasm','-o',OUT/'badreturn.c','--module-name=badreturn')
|
||||
for functions,entry in [({0x1000:body([bl(0x1000,0x1100),0xd65f03c0])},0x1000),
|
||||
({0x1000:body([0xd65f03c0]),0x1100:body([0xd65f03c0])},0x1000),
|
||||
({0x1000:body([0x14000000])},0x1000),
|
||||
({0x1000:body([0x54000040,0xd2800000,0xd65f03c0])},0x1000)]:
|
||||
try: ProgramCompiler().compile(functions,entry)
|
||||
except UnsupportedInstruction: pass
|
||||
else: raise AssertionError('Invalid guest program accepted')
|
||||
|
||||
includes = '\n'.join(f'#include "{name}.h"\nextern uint64_t native_{name}(uint64_t);' for name in fixtures)
|
||||
checks=[]
|
||||
for name in fixtures:
|
||||
argument = 'i%51' if name=='recursive' else 'next(&seed)'
|
||||
prepare = 'memcpy(memory+2048,&expected,8); registers[0]=2048;' if name=='memory' else ''
|
||||
callarg = '(uint64_t)&expected' if name=='memory' else 'input'
|
||||
memory_check = 'assert(memcmp(memory+2048,&expected,8)==0);' if name=='memory' else ''
|
||||
checks.append(f'''
|
||||
{{
|
||||
w2c_{name} module; wasm2c_{name}_instantiate(&module);
|
||||
uint8_t *memory=w2c_{name}_memory(&module)->data;
|
||||
for(unsigned i=0;i<20000;++i) {{
|
||||
uint64_t input={argument}, expected=input, registers[32], after[32];
|
||||
for(unsigned r=0;r<32;++r) registers[r]=next(&seed);
|
||||
registers[0]=input; registers[30]=0; registers[31]=65280;
|
||||
memset(memory,0xa5,65536);
|
||||
{prepare}
|
||||
memcpy(memory,registers,sizeof(registers));
|
||||
uint64_t native=native_{name}({callarg});
|
||||
assert(w2c_{name}_run(&module,0,1000)==0);
|
||||
memcpy(after,memory,sizeof(after));
|
||||
assert(after[0]==native);
|
||||
for(unsigned r=19;r<32;++r) assert(after[r]==registers[r]);
|
||||
{memory_check}
|
||||
for(unsigned a=256;a<65536;++a) {{
|
||||
if(a>=64464 && a<65280) continue;
|
||||
if(a>=2048 && a<2056) continue;
|
||||
assert(memory[a]==0xa5);
|
||||
}}
|
||||
}}
|
||||
uint64_t start[32]={{0}}; start[0]=20; start[31]=65280;
|
||||
memcpy(memory,start,sizeof(start));
|
||||
assert(w2c_{name}_run(&module,0,0)==3);
|
||||
memcpy(memory,start,sizeof(start));
|
||||
assert(w2c_{name}_run(&module,0,1)==3);
|
||||
memcpy(memory,start,sizeof(start));
|
||||
start[30]={fixtures[name][0]}; memcpy(memory,start,sizeof(start));
|
||||
assert(w2c_{name}_run(&module,0,1000)==2);
|
||||
wasm2c_{name}_free(&module);
|
||||
}}
|
||||
''')
|
||||
(OUT/'verify.c').write_text(f'''
|
||||
#include <assert.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
{includes}
|
||||
#include "badreturn.h"
|
||||
static uint64_t next(uint64_t *s) {{ *s^=*s<<13; *s^=*s>>7; *s^=*s<<17; return *s; }}
|
||||
int main(void) {{
|
||||
wasm_rt_init(); uint64_t seed=0x123456789abcdef;
|
||||
{''.join(checks)}
|
||||
w2c_nested bounded; wasm2c_nested_instantiate(&bounded);
|
||||
uint64_t registers[32]={{0}}; registers[0]=3; registers[31]=65280;
|
||||
memcpy(w2c_nested_memory(&bounded)->data,registers,sizeof(registers));
|
||||
assert(w2c_nested_run(&bounded,0,15)==3);
|
||||
memcpy(w2c_nested_memory(&bounded)->data,registers,sizeof(registers));
|
||||
assert(w2c_nested_run(&bounded,0,16)==0);
|
||||
memcpy(registers,w2c_nested_memory(&bounded)->data,sizeof(registers));
|
||||
assert(registers[0]==10);
|
||||
wasm2c_nested_free(&bounded);
|
||||
w2c_badreturn bad; wasm2c_badreturn_instantiate(&bad);
|
||||
assert(w2c_badreturn_run(&bad,0,10)==4);
|
||||
wasm2c_badreturn_free(&bad);
|
||||
wasm_rt_free();
|
||||
puts("PASS: 80000 native guest-program comparisons, nested calls, recursion, tails, shared memory, guards and failure statuses");
|
||||
return 0;
|
||||
}}
|
||||
''')
|
||||
runtime=ROOT/'third_party/wabt/wasm2c'
|
||||
run('xcrun','clang','-O2','-DWASM_RT_USE_MMAP=0','-DWASM_RT_MEMCHECK_BOUNDS_CHECK=1',
|
||||
'-I'+str(OUT),'-I'+str(runtime),*(OUT/f'{name}.c' for name in [*fixtures,'badreturn']),
|
||||
OUT/'verify.c',OUT/'oracle.S',runtime/'wasm-rt-impl.c',runtime/'wasm-rt-mem-impl.c','-o',OUT/'verify')
|
||||
run(OUT/'verify')
|
||||
(OUT/'verification.json').write_text(json.dumps({'status':'pass','native_comparisons':80000,
|
||||
'source':'hand-authored instruction fixtures, not recovered game code','fixtures':list(fixtures),
|
||||
'scope':'bounded direct guest calls, known returns, shared registers/memory'},indent=2)+'\n')
|
||||
Reference in new issue
Block a user