Far pointer code is now more efficient. More code gen errors fixed.
This commit is contained in:
parent
9ca7f1d0de
commit
deb1c96f73
25 changed files with 2273 additions and 133 deletions
|
|
@ -4,16 +4,18 @@
|
||||||
// the program must run to the end AND read the array back as all-zero.
|
// the program must run to the end AND read the array back as all-zero.
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
// ~5.75 KB. With the current GNO libc the runtime BSS base already sits at
|
// 4 KB. The GNO runtime's code already pushes the BSS base to ~0xD000 (well
|
||||||
// ~0xD000 (well above the old ~0xB100 heartbeat threshold), so this array's job
|
// above the old ~0xB100 heartbeat threshold), so this array only has to keep
|
||||||
// is to keep __bss_end large (near the bank-0 top) while still fitting bank 0.
|
// __bss_end large while the whole BSS still fits under the bank-0 top: the
|
||||||
static volatile uint8_t bigBss[0x1700];
|
// runtime's own BSS grows, and at 0x1700 the link overflowed by 236 bytes.
|
||||||
|
#define BIG_BSS_BYTES 0x1000u
|
||||||
|
static volatile uint8_t bigBss[BIG_BSS_BYTES];
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
*(volatile uint16_t *)0x025010UL = 0xBEEF;
|
*(volatile uint16_t *)0x025010UL = 0xBEEF;
|
||||||
uint16_t zeroed = 1;
|
uint16_t zeroed = 1;
|
||||||
unsigned i;
|
unsigned i;
|
||||||
for (i = 0; i < 0x1700; i++) {
|
for (i = 0; i < BIG_BSS_BYTES; i++) {
|
||||||
if (bigBss[i] != 0) {
|
if (bigBss[i] != 0) {
|
||||||
zeroed = 0;
|
zeroed = 0;
|
||||||
break;
|
break;
|
||||||
|
|
@ -21,7 +23,7 @@ int main(void) {
|
||||||
}
|
}
|
||||||
*(volatile uint16_t *)0x025012UL = zeroed ? 0x0001 : 0xDEAD;
|
*(volatile uint16_t *)0x025012UL = zeroed ? 0x0001 : 0xDEAD;
|
||||||
bigBss[0] = 1; // retain
|
bigBss[0] = 1; // retain
|
||||||
bigBss[0x16FF] = 2;
|
bigBss[BIG_BSS_BYTES - 1u] = 2;
|
||||||
*(volatile uint16_t *)0x025000UL = 0xC0DE;
|
*(volatile uint16_t *)0x025000UL = 0xC0DE;
|
||||||
for (volatile unsigned long j = 0; j < 600000UL; j++) {
|
for (volatile unsigned long j = 0; j < 600000UL; j++) {
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
|
@ -165,15 +165,10 @@ __start:
|
||||||
; 3. returns whatever main returned.
|
; 3. returns whatever main returned.
|
||||||
jsl __gnoStartup
|
jsl __gnoStartup
|
||||||
|
|
||||||
; Run global dtors registered via __cxa_atexit (LIFO order) before
|
; ---- Terminate via exit -> _exit (libcGno.c) ----
|
||||||
; QUIT. A holds main()'s return value (exit status), which _exit
|
; __gnoStartup returned with A = main()'s exit status. exit() (libc.c)
|
||||||
; consumes — preserve it across the call.
|
; runs the atexit handlers, then the C++ dtors registered through
|
||||||
pha
|
; __cxa_atexit, then _exit, which issues
|
||||||
jsl __run_cxa_atexit
|
|
||||||
pla
|
|
||||||
|
|
||||||
; ---- Terminate via _exit (libcGno.c) ----
|
|
||||||
; __gnoStartup returned with A = main()'s exit status. _exit issues
|
|
||||||
; GS/OS QUIT through GNO's INLINE dispatch (__gnoGsosCall), which is
|
; GS/OS QUIT through GNO's INLINE dispatch (__gnoGsosCall), which is
|
||||||
; REQUIRED under GNO: the old stack-based QUIT here (ldx #$2029 ;
|
; REQUIRED under GNO: the old stack-based QUIT here (ldx #$2029 ;
|
||||||
; jsl $e100a8 with the pblock pushed) was misread by GNO's OurGSOS
|
; jsl $e100a8 with the pblock pushed) was misread by GNO's OurGSOS
|
||||||
|
|
@ -181,9 +176,9 @@ __start:
|
||||||
; bytes after the jsl, not the stack — so the QUIT was mis-dispatched,
|
; bytes after the jsl, not the stack — so the QUIT was mis-dispatched,
|
||||||
; the process was left restartable, and GNO RE-RAN main() a second
|
; the process was left restartable, and GNO RE-RAN main() a second
|
||||||
; time (silently draining redirected stdin on the first pass).
|
; time (silently draining redirected stdin on the first pass).
|
||||||
jsl _exit
|
jsl exit
|
||||||
|
|
||||||
; _exit does not return. If it ever does, spin rather than fall
|
; exit does not return. If it ever does, spin rather than fall
|
||||||
; into a BRK (headless MAME mis-vectors BRK to $0000, which looks
|
; into a BRK (headless MAME mis-vectors BRK to $0000, which looks
|
||||||
; like a wild crash) or off the end of the segment.
|
; like a wild crash) or off the end of the segment.
|
||||||
.Lhang:
|
.Lhang:
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,10 @@
|
||||||
; - No language-card RAM enable (GS/OS configures memory).
|
; - No language-card RAM enable (GS/OS configures memory).
|
||||||
; - No stack base reset (GS/OS allocated and set our SP).
|
; - No stack base reset (GS/OS allocated and set our SP).
|
||||||
; - Honors GS/OS's DBR=our-bank, DP=allocated-page setup.
|
; - Honors GS/OS's DBR=our-bank, DP=allocated-page setup.
|
||||||
; - On main() return, calls GS/OS QUIT(pcount=0) to return control to
|
; - On main() return, calls exit(), which runs the atexit handlers and
|
||||||
; the launching program (Finder / shell / boot chain) — the standard
|
; C++ dtors and then _exit (below): GS/OS QUIT(pcount=0), returning
|
||||||
; GS/OS application exit.
|
; control to the launching program (Finder / shell / boot chain) --
|
||||||
|
; the standard GS/OS application exit.
|
||||||
;
|
;
|
||||||
; Entry from the System Loader (per Apple IIgs Toolbox Reference):
|
; Entry from the System Loader (per Apple IIgs Toolbox Reference):
|
||||||
; E=0 (native), M=0 (16-bit accumulator), X=0 (16-bit index)
|
; E=0 (native), M=0 (16-bit accumulator), X=0 (16-bit index)
|
||||||
|
|
@ -154,35 +155,51 @@ __start:
|
||||||
rep #0x30
|
rep #0x30
|
||||||
jsl main
|
jsl main
|
||||||
|
|
||||||
; Run global dtors registered via __cxa_atexit (LIFO order) before
|
; Returning from main is exit(main's value) (C99 5.1.2.2.3): exit runs
|
||||||
; QUIT. A holds main()'s return value; QUIT itself doesn't consume
|
; the atexit handlers, then the C++ static dtors registered through
|
||||||
; it (we set up the parm block immediately below), but keep the
|
; __cxa_atexit, then _exit below. A holds main()'s return value, which
|
||||||
; save/restore for parity with crt0Gno.
|
; is exit's argument.
|
||||||
pha
|
jsl exit
|
||||||
jsl __run_cxa_atexit
|
|
||||||
pla
|
|
||||||
|
|
||||||
; ---- QUIT (pcount=0): return to the launching program ----------
|
; exit does not return. Spin rather than fall into what follows.
|
||||||
; The standard GS/OS application exit. A pcount=0 parm block (just
|
|
||||||
; a zero pcount word) tells QUIT to return control to whoever
|
|
||||||
; launched us (Finder, a shell, the boot chain) with default flags
|
|
||||||
; (non-restartable) — no hardcoded chain path needed. A program
|
|
||||||
; that wants to chain elsewhere can provide its own _exit/crt0.
|
|
||||||
rep #0x30
|
|
||||||
stz 0x80 ; pcount = 0 (parm block at DP $0080, bank 0)
|
|
||||||
tdc
|
|
||||||
clc
|
|
||||||
adc #0x80
|
|
||||||
pha ; parm-block ptr: low 16 (offset)
|
|
||||||
pea 0 ; parm-block ptr: high 16 (bank 0)
|
|
||||||
ldx #0x2029 ; QUIT class-1 call number
|
|
||||||
jsl 0xe100a8 ; GS/OS dispatcher
|
|
||||||
|
|
||||||
; QUIT only returns on failure. Clean up the pushed pointer and
|
|
||||||
; spin (don't fall into a BRK, which headless MAME mis-vectors).
|
|
||||||
pla
|
|
||||||
pla
|
|
||||||
.Lhang:
|
.Lhang:
|
||||||
bra .Lhang
|
bra .Lhang
|
||||||
|
|
||||||
.size __start, . - __start
|
.size __start, . - __start
|
||||||
|
|
||||||
|
; ---------------------------------------------------------------------
|
||||||
|
; void _exit(int status) -- GS/OS QUIT (pcount=0): return control to
|
||||||
|
; whoever launched us (Finder, a shell, the boot chain) with default
|
||||||
|
; flags (non-restartable). The status is not passed on.
|
||||||
|
;
|
||||||
|
; GS/OS takes class-1 calls in the INLINE form (see iigsGsos.s): the call
|
||||||
|
; number and the parm-block pointer follow the jsl, and the dispatcher
|
||||||
|
; returns past them. This used to push the pointer and load the call
|
||||||
|
; number into X for the same $E100A8 entry, so GS/OS read the code after
|
||||||
|
; the jsl as the call, failed, and returned into the middle of the next
|
||||||
|
; instructions -- every program that returned from main crashed into the
|
||||||
|
; monitor (JoeyLib's LLVM816-ASKS #6). The parm block is a zero pcount
|
||||||
|
; word at DP $80 (bank 0); its address is patched into the inline slot,
|
||||||
|
; like gsosDispatch does.
|
||||||
|
; ---------------------------------------------------------------------
|
||||||
|
.globl _exit
|
||||||
|
_exit:
|
||||||
|
rep #0x30
|
||||||
|
phk
|
||||||
|
plb ; DBR := PBR: the stores below patch this code
|
||||||
|
stz 0x80 ; pcount = 0
|
||||||
|
tdc
|
||||||
|
clc
|
||||||
|
adc #0x80
|
||||||
|
sta .Lquit_pb ; parm-block pointer, low 16
|
||||||
|
stz .Lquit_pb+2 ; bank 0, pad 0
|
||||||
|
jsl 0xe100a8
|
||||||
|
.word 0x2029 ; QUIT
|
||||||
|
.Lquit_pb:
|
||||||
|
.long 0
|
||||||
|
; QUIT only returns on failure. Spin (a BRK is mis-vectored by
|
||||||
|
; headless MAME and reads as a wild crash).
|
||||||
|
.Lquit_failed:
|
||||||
|
bra .Lquit_failed
|
||||||
|
|
||||||
|
.size _exit, . - _exit
|
||||||
|
|
|
||||||
|
|
@ -806,33 +806,33 @@ void *realloc(void *ptr, size_t n) {
|
||||||
|
|
||||||
// ---- atexit / exit ----
|
// ---- atexit / exit ----
|
||||||
//
|
//
|
||||||
// Standard exit() halts via BRK after running any registered atexit
|
// exit() runs the atexit handlers (last registered first), then the C++
|
||||||
// handler. Programs running under the IIgs runtime typically would
|
// static destructors registered through __cxa_atexit, then _exit.
|
||||||
// call back into GS/OS Quit; here we just wedge the CPU. Single-slot
|
// Returning from main is the same exit: the GS/OS and GNO crt0s call exit
|
||||||
// atexit (the storage and registration function are below).
|
// with main's value. _exit is the platform's way out -- crt0Gsos.s issues
|
||||||
|
// GS/OS QUIT, libcGno.c GNO's QUIT, and the weak fallback in libgcc.s
|
||||||
|
// halts bare metal (BRK so MAME's debugger catches it, then spin).
|
||||||
|
|
||||||
typedef void (*AtexitFn)(void);
|
typedef void (*AtexitFn)(void);
|
||||||
static AtexitFn __atexitFn = (AtexitFn)0;
|
|
||||||
|
|
||||||
// BRK $00 then spin -- halts a 65816 in BRK so MAME's debugger catches
|
// C99 7.20.4.2 promises at least 32 registrations.
|
||||||
// it; the spin loop guards against the (rare) case where BRK returns.
|
#define ATEXIT_MAX 32
|
||||||
static void __halt(void) __attribute__((noreturn));
|
static AtexitFn __atexitFns[ATEXIT_MAX];
|
||||||
static void __halt(void) {
|
static unsigned int __atexitCount = 0;
|
||||||
__asm__ volatile (".byte 0x00, 0x00");
|
|
||||||
while (1) {}
|
void _exit(int code) __attribute__((noreturn));
|
||||||
}
|
void __run_cxa_atexit(void);
|
||||||
|
|
||||||
|
|
||||||
void exit(int code) {
|
void exit(int code) {
|
||||||
(void)code;
|
// Pop before calling, so a handler that registers another one has it
|
||||||
// C99 7.20.4.3: exit() must invoke registered atexit handlers in
|
// run next and none runs twice.
|
||||||
// reverse-registration order before terminating.
|
while (__atexitCount > 0) {
|
||||||
if (__atexitFn) {
|
__atexitCount--;
|
||||||
AtexitFn fn = __atexitFn;
|
__atexitFns[__atexitCount]();
|
||||||
__atexitFn = (AtexitFn)0; // prevent re-entry if fn calls exit
|
|
||||||
fn();
|
|
||||||
}
|
}
|
||||||
__halt();
|
__run_cxa_atexit();
|
||||||
|
_exit(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- errno ----
|
// ---- errno ----
|
||||||
|
|
@ -1447,10 +1447,11 @@ void abort(void) {
|
||||||
exit(127);
|
exit(127);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- atexit (single slot; storage + exit() invocation above) ----
|
// ---- atexit (storage + exit() above) ----
|
||||||
int atexit(AtexitFn fn) {
|
int atexit(AtexitFn fn) {
|
||||||
if (__atexitFn) return -1;
|
if (__atexitCount >= ATEXIT_MAX) return -1;
|
||||||
__atexitFn = fn;
|
__atexitFns[__atexitCount] = fn;
|
||||||
|
__atexitCount++;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1458,24 +1459,22 @@ int atexit(AtexitFn fn) {
|
||||||
//
|
//
|
||||||
// _Exit terminates without invoking atexit handlers (unlike exit).
|
// _Exit terminates without invoking atexit handlers (unlike exit).
|
||||||
// quick_exit terminates after invoking at_quick_exit handlers (a
|
// quick_exit terminates after invoking at_quick_exit handlers (a
|
||||||
// separate chain from atexit). We share the single-slot pattern
|
// separate chain from atexit). at_quick_exit keeps a single slot;
|
||||||
// with atexit — single-shot handler, second registration fails.
|
// a second registration fails.
|
||||||
|
|
||||||
static AtexitFn __quickFn = (AtexitFn)0;
|
static AtexitFn __quickFn = (AtexitFn)0;
|
||||||
|
|
||||||
void _Exit(int code) {
|
void _Exit(int code) {
|
||||||
(void)code;
|
_exit(code);
|
||||||
__halt();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void quick_exit(int code) {
|
void quick_exit(int code) {
|
||||||
(void)code;
|
|
||||||
if (__quickFn) {
|
if (__quickFn) {
|
||||||
AtexitFn fn = __quickFn;
|
AtexitFn fn = __quickFn;
|
||||||
__quickFn = (AtexitFn)0;
|
__quickFn = (AtexitFn)0;
|
||||||
fn();
|
fn();
|
||||||
}
|
}
|
||||||
__halt();
|
_exit(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
int at_quick_exit(AtexitFn fn) {
|
int at_quick_exit(AtexitFn fn) {
|
||||||
|
|
|
||||||
|
|
@ -100,6 +100,29 @@ __jsl_indir:
|
||||||
__run_cxa_atexit:
|
__run_cxa_atexit:
|
||||||
rtl
|
rtl
|
||||||
|
|
||||||
|
; --------------------------------------------------------------------
|
||||||
|
; exit / _exit -- weak fallbacks.
|
||||||
|
;
|
||||||
|
; crt0Gsos.s and crt0Gno.s end a program by calling exit() with main()'s
|
||||||
|
; value in A. libc.c has the real exit (atexit handlers, then C++ dtors,
|
||||||
|
; then _exit); this weak one serves programs that link no libc: dtors,
|
||||||
|
; then _exit. _exit is the platform's way out: crt0Gsos.s (GS/OS QUIT)
|
||||||
|
; and libcGno.c (GNO QUIT) override the weak halt below, which is bare
|
||||||
|
; metal's -- BRK so MAME's debugger catches it, then spin.
|
||||||
|
; --------------------------------------------------------------------
|
||||||
|
.weak exit
|
||||||
|
exit:
|
||||||
|
pha ; status
|
||||||
|
jsl __run_cxa_atexit
|
||||||
|
pla
|
||||||
|
jsl _exit ; does not return
|
||||||
|
|
||||||
|
.weak _exit
|
||||||
|
_exit:
|
||||||
|
.byte 0x00, 0x00 ; BRK
|
||||||
|
.L_exitSpin:
|
||||||
|
bra .L_exitSpin
|
||||||
|
|
||||||
; --------------------------------------------------------------------
|
; --------------------------------------------------------------------
|
||||||
; __heapLock / __heapUnlock -- weak no-op heap-serialization hooks.
|
; __heapLock / __heapUnlock -- weak no-op heap-serialization hooks.
|
||||||
;
|
;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# runFileCheckTests.sh - run W65816 backend regression tests.
|
# runFileCheckTests.sh - run W65816 backend regression tests.
|
||||||
#
|
#
|
||||||
# Walks src/llvm/test/CodeGen/W65816/*.ll and for each:
|
# Walks src/llvm/test/CodeGen/W65816/*.ll and *.mir and for each:
|
||||||
# - reads RUN: lines from the test header (lit-compatible syntax)
|
# - reads RUN: lines from the test header (lit-compatible syntax; `;`
|
||||||
|
# comments in .ll files, `#` comments in .mir files)
|
||||||
# - executes them with %s -> the test path
|
# - executes them with %s -> the test path
|
||||||
# - any non-zero exit fails the run.
|
# - any non-zero exit fails the run.
|
||||||
#
|
#
|
||||||
|
|
@ -32,7 +33,7 @@ if [ $# -gt 0 ]; then
|
||||||
files+=("$TEST_DIR/$f")
|
files+=("$TEST_DIR/$f")
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
mapfile -t files < <(find "$TEST_DIR" -maxdepth 1 -name '*.ll' | sort)
|
mapfile -t files < <(find "$TEST_DIR" -maxdepth 1 \( -name '*.ll' -o -name '*.mir' \) | sort)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
pass=0
|
pass=0
|
||||||
|
|
@ -42,7 +43,7 @@ for f in "${files[@]}"; do
|
||||||
[ -f "$f" ] || { echo "skip missing: $f"; continue; }
|
[ -f "$f" ] || { echo "skip missing: $f"; continue; }
|
||||||
name="$(basename "$f")"
|
name="$(basename "$f")"
|
||||||
|
|
||||||
runs=$(grep -E '^[[:space:]]*;[[:space:]]*RUN:' "$f" | sed -E 's/^[[:space:]]*;[[:space:]]*RUN:[[:space:]]*//')
|
runs=$(grep -E '^[[:space:]]*[;#][[:space:]]*RUN:' "$f" | sed -E 's/^[[:space:]]*[;#][[:space:]]*RUN:[[:space:]]*//')
|
||||||
if [ -z "$runs" ]; then
|
if [ -z "$runs" ]; then
|
||||||
echo "SKIP $name (no RUN: line)"
|
echo "SKIP $name (no RUN: line)"
|
||||||
continue
|
continue
|
||||||
|
|
|
||||||
|
|
@ -6713,6 +6713,68 @@ EOF
|
||||||
"$omfGsf" "$testFileGsf"
|
"$omfGsf" "$testFileGsf"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# GS/OS program exit: returning from main is exit(main's value), which must
|
||||||
|
# run the atexit handlers (last registered first), then the C++ dtors, then
|
||||||
|
# QUIT back to the Finder. crt0Gsos used to issue QUIT with the pointer
|
||||||
|
# pushed onto the stack at the INLINE entry $E100A8, so GS/OS misread the
|
||||||
|
# call and returned into the middle of the following code: every program
|
||||||
|
# that returned from main crashed into the monitor (text mode), and the
|
||||||
|
# atexit handlers never ran at all. Three handlers fold their ids into $70
|
||||||
|
# (LIFO gives 0x39); main marks $71; NEWVIDEO ($C029) reads 0xC1 only if
|
||||||
|
# the Finder's Super Hi-Res desktop is back (the monitor leaves 0x01).
|
||||||
|
# Skips under SMOKE_SKIP_GSOSEXIT=1.
|
||||||
|
if [ "${SMOKE_SKIP_GSOSEXIT:-0}" != "1" ] \
|
||||||
|
&& [ -x "$CLANG" ] && [ -x "$CADIUS" ] && [ -f "$SYSDISK" ] \
|
||||||
|
&& command -v mame >/dev/null 2>&1; then
|
||||||
|
log "check: GS/OS exit runs atexit handlers LIFO and QUITs to the Finder"
|
||||||
|
cGsx="$(mktemp --suffix=.c)"
|
||||||
|
oGsx="$(mktemp --suffix=.o)"
|
||||||
|
binGsx="$(mktemp --suffix=.bin)"
|
||||||
|
mapGsx="$(mktemp --suffix=.map)"
|
||||||
|
relGsx="$(mktemp --suffix=.reloc)"
|
||||||
|
omfGsx="$(mktemp --suffix=.omf)"
|
||||||
|
cat > "$cGsx" <<'EOF'
|
||||||
|
#include <stdlib.h>
|
||||||
|
#define MB ((volatile unsigned char *)0x70)
|
||||||
|
static void a(void) { MB[0] = (unsigned char)((MB[0] << 2) | 1u); }
|
||||||
|
static void b(void) { MB[0] = (unsigned char)((MB[0] << 2) | 2u); }
|
||||||
|
static void c(void) { MB[0] = (unsigned char)((MB[0] << 2) | 3u); }
|
||||||
|
int main(void) {
|
||||||
|
MB[0] = 0u;
|
||||||
|
atexit(a);
|
||||||
|
atexit(b);
|
||||||
|
atexit(c);
|
||||||
|
MB[1] = 0xA5u;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
"$CLANG" --target=w65816 -I"$PROJECT_ROOT/runtime/include" -O2 -ffunction-sections -c \
|
||||||
|
"$cGsx" -o "$oGsx"
|
||||||
|
"$PROJECT_ROOT/tools/link816" -o "$binGsx" --text-base 0x1000 \
|
||||||
|
--map "$mapGsx" --reloc-out "$relGsx" \
|
||||||
|
"$PROJECT_ROOT/runtime/crt0Gsos.o" "$oGsx" \
|
||||||
|
"$PROJECT_ROOT/runtime/libc.o" \
|
||||||
|
"$PROJECT_ROOT/runtime/snprintf.o" \
|
||||||
|
"$PROJECT_ROOT/runtime/extras.o" \
|
||||||
|
"$PROJECT_ROOT/runtime/softFloat.o" \
|
||||||
|
"$PROJECT_ROOT/runtime/softDouble.o" \
|
||||||
|
"$PROJECT_ROOT/runtime/iigsGsos.o" \
|
||||||
|
"$PROJECT_ROOT/runtime/iigsToolbox.o" "$PROJECT_ROOT/runtime/iigsToolboxExtra.o" \
|
||||||
|
"$PROJECT_ROOT/runtime/libgcc.o" >/dev/null \
|
||||||
|
|| die "GS/OS exit smoke: link failed"
|
||||||
|
"$PROJECT_ROOT/tools/omfEmit" --input "$binGsx" --map "$mapGsx" \
|
||||||
|
--base 0x1000 --entry __start --output "$omfGsx" \
|
||||||
|
--name HELLO --expressload --relocs "$relGsx" >/dev/null 2>&1
|
||||||
|
[ -s "$omfGsx" ] || die "GS/OS exit smoke: omfEmit produced empty/missing OMF"
|
||||||
|
if ! bash "$PROJECT_ROOT/scripts/runViaFinder.sh" "$omfGsx" \
|
||||||
|
--check 0x70=0x39 --check 0x71=0xa5 --check 0xc029=0xc1 >/dev/null 2>&1; then
|
||||||
|
bash "$PROJECT_ROOT/scripts/runViaFinder.sh" "$omfGsx" \
|
||||||
|
--check 0x70=0x39 --check 0x71=0xa5 --check 0xc029=0xc1 2>&1 | tail -5 >&2
|
||||||
|
die "GS/OS exit smoke FAILED (atexit order / QUIT to the Finder)"
|
||||||
|
fi
|
||||||
|
rm -f "$cGsx" "$oGsx" "$binGsx" "$mapGsx" "$relGsx" "$omfGsx"
|
||||||
|
fi
|
||||||
|
|
||||||
# Phase C.2: cINTERSEG cross-segment JSL under real GS/OS Loader.
|
# Phase C.2: cINTERSEG cross-segment JSL under real GS/OS Loader.
|
||||||
# Links a 2-user-seg program (main in seg 1, pinnedFn in seg 2 via
|
# Links a 2-user-seg program (main in seg 1, pinnedFn in seg 2 via
|
||||||
# __attribute__((section(".text.seg2")))), uses --multi-seg-expressload
|
# __attribute__((section(".text.seg2")))), uses --multi-seg-expressload
|
||||||
|
|
|
||||||
|
|
@ -764,22 +764,7 @@ static void applyTextReloc(std::vector<uint8_t> &buf, uint32_t off,
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!gMultiSegRelocMode) {
|
|
||||||
applyReloc(buf, off, patchAddr, target, rtype, symName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (patchSeg == 0 || patchSeg > segments.size()) {
|
|
||||||
applyReloc(buf, off, patchAddr, target, rtype, symName);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const TextSeg &pseg = segments[patchSeg - 1];
|
|
||||||
uint32_t targetSeg = findSegContaining(target, segments);
|
uint32_t targetSeg = findSegContaining(target, segments);
|
||||||
gDbgTextCalls++;
|
|
||||||
// TEMP TRACE (RetroNet hunt): decisions for the mirror-pack refs.
|
|
||||||
if (symName.find("gRnInPack") != std::string::npos) {
|
|
||||||
std::fprintf(stderr, "TRACE %s rtype=%u patchSeg=%u targetSeg=%u target=0x%X rec=%d\n",
|
|
||||||
symName.c_str(), rtype, patchSeg, targetSeg, target, (int)gRecordSites);
|
|
||||||
}
|
|
||||||
// Cross-segment FUNCTION POINTER (IMM16 &func to a function in seg2+):
|
// Cross-segment FUNCTION POINTER (IMM16 &func to a function in seg2+):
|
||||||
// clang forms the pointer as func's 16-bit offset + the $00BE seg1 bank,
|
// clang forms the pointer as func's 16-bit offset + the $00BE seg1 bank,
|
||||||
// which is wrong for a function in another bank. Redirect the IMM16 to
|
// which is wrong for a function in another bank. Redirect the IMM16 to
|
||||||
|
|
@ -790,6 +775,11 @@ static void applyTextReloc(std::vector<uint8_t> &buf, uint32_t off,
|
||||||
// non-seg1 bank) is the real key. No cRELOC: the baked value is a seg1
|
// non-seg1 bank) is the real key. No cRELOC: the baked value is a seg1
|
||||||
// offset and seg1 (KIND=0x1000) always bank-aligns, so the low16 is
|
// offset and seg1 (KIND=0x1000) always bank-aligns, so the low16 is
|
||||||
// already the runtime offset (matches the --text-base 0 IMM16 contract).
|
// already the runtime offset (matches the --text-base 0 IMM16 contract).
|
||||||
|
// Any multi-segment layout needs this, not just the GS/OS ExpressLoad
|
||||||
|
// one: a bare-metal --segment-cap link (fixed bank bases, no Loader)
|
||||||
|
// forms &func the same way, and without the trampoline a pointer to a
|
||||||
|
// seg2+ function is called in bank 0 (Lua's allocator, the first thing
|
||||||
|
// lua_newstate calls, ran into vfprintf and the machine went wild).
|
||||||
if (rtype == R_W65816_IMM16 && targetSeg > 1) {
|
if (rtype == R_W65816_IMM16 && targetSeg > 1) {
|
||||||
uint32_t targetOff = target - segments[targetSeg - 1].base;
|
uint32_t targetOff = target - segments[targetSeg - 1].base;
|
||||||
auto tIt = gTrampMap.find(TrampKey{targetSeg, targetOff});
|
auto tIt = gTrampMap.find(TrampKey{targetSeg, targetOff});
|
||||||
|
|
@ -800,6 +790,16 @@ static void applyTextReloc(std::vector<uint8_t> &buf, uint32_t off,
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!gMultiSegRelocMode) {
|
||||||
|
applyReloc(buf, off, patchAddr, target, rtype, symName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (patchSeg == 0 || patchSeg > segments.size()) {
|
||||||
|
applyReloc(buf, off, patchAddr, target, rtype, symName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const TextSeg &pseg = segments[patchSeg - 1];
|
||||||
|
gDbgTextCalls++;
|
||||||
// Non-text target (rodata / bss / init_array / weak-zero) — use
|
// Non-text target (rodata / bss / init_array / weak-zero) — use
|
||||||
// the legacy applyReloc path (which records via recordCRelocSite
|
// the legacy applyReloc path (which records via recordCRelocSite
|
||||||
// into gImm24Sites for back-compat AND also pushes into the new
|
// into gImm24Sites for back-compat AND also pushes into the new
|
||||||
|
|
@ -821,11 +821,16 @@ static void applyTextReloc(std::vector<uint8_t> &buf, uint32_t off,
|
||||||
uint8_t cnt = (rtype == R_W65816_IMM24 || rtype == R_W65816_DATA32) ? 3 : 2;
|
uint8_t cnt = (rtype == R_W65816_IMM24 || rtype == R_W65816_DATA32) ? 3 : 2;
|
||||||
for (uint8_t i = 0; i < cnt; ++i)
|
for (uint8_t i = 0; i < cnt; ++i)
|
||||||
buf[off + i] = 0;
|
buf[off + i] = 0;
|
||||||
|
// The Loader adds seg 1's load address to an OFFSET within seg 1,
|
||||||
|
// so subtract seg 1's link base. (Passing the link address only
|
||||||
|
// worked at --text-base 0; at 0x1000 every such data reference
|
||||||
|
// landed 0x1000 past its variable -- smoke Phase C.2 read an
|
||||||
|
// initialised global as 0.)
|
||||||
InterImm24Site s;
|
InterImm24Site s;
|
||||||
s.patchSeg = static_cast<uint16_t>(patchSeg);
|
s.patchSeg = static_cast<uint16_t>(patchSeg);
|
||||||
s.patchOff = static_cast<uint16_t>(patchAddr - pseg.base);
|
s.patchOff = static_cast<uint16_t>(patchAddr - pseg.base);
|
||||||
s.targetSeg = 1;
|
s.targetSeg = 1;
|
||||||
s.targetOff = static_cast<uint16_t>(target);
|
s.targetOff = static_cast<uint16_t>(target - segments[0].base);
|
||||||
s.byteCnt = cnt;
|
s.byteCnt = cnt;
|
||||||
s.bitShift = (rtype == R_W65816_BANK16) ? 16 : 0;
|
s.bitShift = (rtype == R_W65816_BANK16) ? 16 : 0;
|
||||||
gInterImm24Sites.push_back(s);
|
gInterImm24Sites.push_back(s);
|
||||||
|
|
@ -1370,7 +1375,7 @@ struct Linker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ---- TRAMPOLINE pre-pass (multi-seg only). Find every IMM16
|
// ---- TRAMPOLINE pre-pass (multi-segment text only). Find every IMM16
|
||||||
// address-take of a function the layout placed in a text seg N>1 (a
|
// address-take of a function the layout placed in a text seg N>1 (a
|
||||||
// different bank than seg1, whose bank is what $00BE/&func will use)
|
// different bank than seg1, whose bank is what $00BE/&func will use)
|
||||||
// and reserve one `JML func` trampoline in seg1 per distinct target.
|
// and reserve one `JML func` trampoline in seg1 per distinct target.
|
||||||
|
|
@ -1382,7 +1387,7 @@ struct Linker {
|
||||||
gTrampMap.clear();
|
gTrampMap.clear();
|
||||||
gTrampList.clear();
|
gTrampList.clear();
|
||||||
gTrampBase = 0;
|
gTrampBase = 0;
|
||||||
if (gMultiSegRelocMode) {
|
if (segSizes.size() > 1) {
|
||||||
std::map<std::string, TrampKey> symSeg; // name -> (seg, off)
|
std::map<std::string, TrampKey> symSeg; // name -> (seg, off)
|
||||||
std::map<std::string, bool> symStrong;
|
std::map<std::string, bool> symStrong;
|
||||||
for (size_t fi = 0; fi < objs.size(); ++fi) {
|
for (size_t fi = 0; fi < objs.size(); ++fi) {
|
||||||
|
|
@ -1902,16 +1907,24 @@ struct Linker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// ---- Emit the cross-seg trampoline table at the end of seg 1's text.
|
// ---- Emit the cross-seg trampoline table at the end of seg 1's text.
|
||||||
// Each entry is `JML func` (0x5C + 24-bit target, zeroed); its cINTERSEG
|
// Each entry is `JML func` (0x5C + 24-bit target). Under ExpressLoad
|
||||||
// site makes the Loader patch the target to func's runtime bank:offset.
|
// the target is zeroed and its cINTERSEG site makes the Loader patch
|
||||||
|
// func's runtime bank:offset; a bare-metal link's segment bases are
|
||||||
|
// final, so the absolute target is baked in.
|
||||||
// segTextBufs[0] holds exactly gTrampBase bytes here (all seg-1 text),
|
// segTextBufs[0] holds exactly gTrampBase bytes here (all seg-1 text),
|
||||||
// so entry i lands at offset gTrampBase + 4*i, and applyTextReloc
|
// so entry i lands at offset gTrampBase + 4*i, and applyTextReloc
|
||||||
// redirects each cross-seg IMM16 &func to that offset.
|
// redirects each cross-seg IMM16 &func to that offset.
|
||||||
if (!gTrampList.empty()) {
|
if (!gTrampList.empty()) {
|
||||||
auto &b0 = segTextBufs[0];
|
auto &b0 = segTextBufs[0];
|
||||||
for (size_t i = 0; i < gTrampList.size(); ++i) {
|
for (size_t i = 0; i < gTrampList.size(); ++i) {
|
||||||
|
uint32_t tgt = gMultiSegRelocMode
|
||||||
|
? 0u
|
||||||
|
: L.segments[gTrampList[i].seg - 1].base
|
||||||
|
+ gTrampList[i].off;
|
||||||
b0.push_back(0x5C); // JML abs-long opcode
|
b0.push_back(0x5C); // JML abs-long opcode
|
||||||
b0.push_back(0); b0.push_back(0); b0.push_back(0); // 24-bit tgt
|
b0.push_back(static_cast<uint8_t>(tgt & 0xFF));
|
||||||
|
b0.push_back(static_cast<uint8_t>((tgt >> 8) & 0xFF));
|
||||||
|
b0.push_back(static_cast<uint8_t>((tgt >> 16) & 0xFF));
|
||||||
if (gRecordSites) {
|
if (gRecordSites) {
|
||||||
InterImm24Site s;
|
InterImm24Site s;
|
||||||
s.patchSeg = 1;
|
s.patchSeg = 1;
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ add_llvm_target(W65816CodeGen
|
||||||
W65816Subtarget.cpp
|
W65816Subtarget.cpp
|
||||||
W65816StackSlotCleanup.cpp
|
W65816StackSlotCleanup.cpp
|
||||||
W65816SepRepCleanup.cpp
|
W65816SepRepCleanup.cpp
|
||||||
|
W65816FarPtrStageReuse.cpp
|
||||||
W65816BranchExpand.cpp
|
W65816BranchExpand.cpp
|
||||||
W65816TiedDefSpill.cpp
|
W65816TiedDefSpill.cpp
|
||||||
W65816ABridgeViaX.cpp
|
W65816ABridgeViaX.cpp
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,12 @@ FunctionPass *createW65816StackSlotCleanup();
|
||||||
// that cancel. See W65816SepRepCleanup.cpp.
|
// that cancel. See W65816SepRepCleanup.cpp.
|
||||||
FunctionPass *createW65816SepRepCleanup();
|
FunctionPass *createW65816SepRepCleanup();
|
||||||
|
|
||||||
|
// Pre-emit pass (last): deletes far-pointer re-staging into DP $E0..$E3
|
||||||
|
// when the scratch already holds the same pointer, plus the loads,
|
||||||
|
// transfers and stack temporaries that only fed it. See
|
||||||
|
// W65816FarPtrStageReuse.cpp.
|
||||||
|
FunctionPass *createW65816FarPtrStageReuse();
|
||||||
|
|
||||||
// Pre-emit pass: expands long conditional branches into the
|
// Pre-emit pass: expands long conditional branches into the
|
||||||
// `INVERTED_Bxx skip ; BRA target ; skip:` pattern when the byte
|
// `INVERTED_Bxx skip ; BRA target ; skip:` pattern when the byte
|
||||||
// distance to the target exceeds the +/-128 reach of an 8-bit-PCREL
|
// distance to the target exceeds the +/-128 reach of an 8-bit-PCREL
|
||||||
|
|
@ -195,6 +201,7 @@ void initializeW65816DAGToDAGISelLegacyPass(PassRegistry &);
|
||||||
void initializeW65816StackSlotCleanupPass(PassRegistry &);
|
void initializeW65816StackSlotCleanupPass(PassRegistry &);
|
||||||
void initializeW65816I32IncFoldPass(PassRegistry &);
|
void initializeW65816I32IncFoldPass(PassRegistry &);
|
||||||
void initializeW65816SepRepCleanupPass(PassRegistry &);
|
void initializeW65816SepRepCleanupPass(PassRegistry &);
|
||||||
|
void initializeW65816FarPtrStageReusePass(PassRegistry &);
|
||||||
void initializeW65816BranchExpandPass(PassRegistry &);
|
void initializeW65816BranchExpandPass(PassRegistry &);
|
||||||
void initializeW65816TiedDefSpillPass(PassRegistry &);
|
void initializeW65816TiedDefSpillPass(PassRegistry &);
|
||||||
void initializeW65816ABridgeViaXPass(PassRegistry &);
|
void initializeW65816ABridgeViaXPass(PassRegistry &);
|
||||||
|
|
|
||||||
1237
src/llvm/lib/Target/W65816/W65816FarPtrStageReuse.cpp
Normal file
1237
src/llvm/lib/Target/W65816/W65816FarPtrStageReuse.cpp
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -3018,6 +3018,22 @@ emitRROp(MachineInstr &MI, MachineBasicBlock *BB, unsigned StoreOp,
|
||||||
return BB;
|
return BB;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// The `[dp],Y` access a pointer-load pseudo expands to. A volatile byte
|
||||||
|
// load gets the atomic 8-bit LDB_DPIndLongY: the usual 16-bit load + mask
|
||||||
|
// also reads address+1, a phantom read of the next register when the
|
||||||
|
// pointer is I/O. Everything else keeps the cheaper 16-bit LDA.
|
||||||
|
static bool isVolatileByteLoad(const MachineInstr &MI) {
|
||||||
|
for (const MachineMemOperand *MMO : MI.memoperands())
|
||||||
|
if (MMO->isLoad() && MMO->isVolatile() &&
|
||||||
|
MMO->getSize() == LocationSize::precise(1))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
static unsigned ptrLoadOpcode(const MachineInstr &MI) {
|
||||||
|
return isVolatileByteLoad(MI) ? W65816::LDB_DPIndLongY
|
||||||
|
: W65816::LDA_DPIndLongY;
|
||||||
|
}
|
||||||
|
|
||||||
MachineBasicBlock *
|
MachineBasicBlock *
|
||||||
W65816TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
|
W65816TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
|
||||||
MachineBasicBlock *BB) const {
|
MachineBasicBlock *BB) const {
|
||||||
|
|
@ -3106,7 +3122,7 @@ W65816TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(W65816::LDY_Imm16)).addImm(0);
|
TII.get(W65816::LDY_Imm16)).addImm(0);
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(W65816::LDA_DPIndLongY)).addImm(0xE0);
|
TII.get(ptrLoadOpcode(MI))).addImm(0xE0);
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(TargetOpcode::COPY), Dst).addReg(W65816::A);
|
TII.get(TargetOpcode::COPY), Dst).addReg(W65816::A);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -3361,7 +3377,8 @@ W65816TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
|
||||||
// a STAfi of sub_lo and an LDAfi_indY/STAfi_indY deref via the
|
// a STAfi of sub_lo and an LDAfi_indY/STAfi_indY deref via the
|
||||||
// 16-bit stack-rel-indirect-Y opcode (0xB3 / 0x93). ~4 instr per
|
// 16-bit stack-rel-indirect-Y opcode (0xB3 / 0x93). ~4 instr per
|
||||||
// deref saved vs the heavy [dp],Y indirect-long path.
|
// deref saved vs the heavy [dp],Y indirect-long path.
|
||||||
if (DbrSafePtrs) {
|
// (Not for a volatile byte load: `lda (sr,s),y` is 16-bit too.)
|
||||||
|
if (DbrSafePtrs && !(IsLoad && isVolatileByteLoad(MI))) {
|
||||||
Register PtrLo = MRI.createVirtualRegister(&W65816::Wide16RegClass);
|
Register PtrLo = MRI.createVirtualRegister(&W65816::Wide16RegClass);
|
||||||
if (PeelOff) {
|
if (PeelOff) {
|
||||||
BuildMI(*BB, MI.getIterator(), DL, TII.get(TargetOpcode::COPY), PtrLo)
|
BuildMI(*BB, MI.getIterator(), DL, TII.get(TargetOpcode::COPY), PtrLo)
|
||||||
|
|
@ -3602,7 +3619,7 @@ W65816TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(W65816::LDY_Imm16)).addImm(PeelOff);
|
TII.get(W65816::LDY_Imm16)).addImm(PeelOff);
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(W65816::LDA_DPIndLongY)).addImm(0xE0);
|
TII.get(ptrLoadOpcode(MI))).addImm(0xE0);
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(TargetOpcode::COPY), Dst).addReg(W65816::A);
|
TII.get(TargetOpcode::COPY), Dst).addReg(W65816::A);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -3694,7 +3711,7 @@ W65816TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(W65816::LDY_Imm16)).addImm(Off);
|
TII.get(W65816::LDY_Imm16)).addImm(Off);
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(W65816::LDA_DPIndLongY)).addImm(0xE0);
|
TII.get(ptrLoadOpcode(MI))).addImm(0xE0);
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(TargetOpcode::COPY), Dst).addReg(W65816::A);
|
TII.get(TargetOpcode::COPY), Dst).addReg(W65816::A);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -3770,7 +3787,7 @@ W65816TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(W65816::LDY_Imm16)).addImm(0);
|
TII.get(W65816::LDY_Imm16)).addImm(0);
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(W65816::LDA_DPIndLongY)).addImm(0xE0);
|
TII.get(ptrLoadOpcode(MI))).addImm(0xE0);
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(TargetOpcode::COPY), Dst).addReg(W65816::A);
|
TII.get(TargetOpcode::COPY), Dst).addReg(W65816::A);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -3945,7 +3962,7 @@ W65816TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(W65816::LDY_Imm16)).addImm(0);
|
TII.get(W65816::LDY_Imm16)).addImm(0);
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(W65816::LDA_DPIndLongY)).addImm(0xE0);
|
TII.get(ptrLoadOpcode(MI))).addImm(0xE0);
|
||||||
BuildMI(*BB, MI.getIterator(), DL,
|
BuildMI(*BB, MI.getIterator(), DL,
|
||||||
TII.get(TargetOpcode::COPY), Dst).addReg(W65816::A);
|
TII.get(TargetOpcode::COPY), Dst).addReg(W65816::A);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -25,9 +25,10 @@ using namespace llvm;
|
||||||
|
|
||||||
void W65816InstrInfo::anchor() {}
|
void W65816InstrInfo::anchor() {}
|
||||||
|
|
||||||
// Expand STB_DPIndLongY -> SEP #$20 ; sta [dp],y ; REP #$20. Runs in the
|
// Expand STB_DPIndLongY -> SEP #$20 ; sta [dp],y ; REP #$20 and
|
||||||
|
// LDB_DPIndLongY -> SEP #$20 ; lda [dp],y ; REP #$20. Runs in the
|
||||||
// post-RA pseudo-expansion pass (after the fast/greedy allocator, before
|
// post-RA pseudo-expansion pass (after the fast/greedy allocator, before
|
||||||
// SepRepCleanup and the AsmPrinter). Keeping the byte store as a single
|
// SepRepCleanup and the AsmPrinter). Keeping the byte access as a single
|
||||||
// pseudo through register allocation is what fixes the -O0 miscompile: the
|
// pseudo through register allocation is what fixes the -O0 miscompile: the
|
||||||
// fast allocator used to wedge a 16-bit reload between a hand-emitted SEP
|
// fast allocator used to wedge a 16-bit reload between a hand-emitted SEP
|
||||||
// and REP, running it in M=8 and truncating a live value. Expanding here
|
// and REP, running it in M=8 and truncating a live value. Expanding here
|
||||||
|
|
@ -35,13 +36,22 @@ void W65816InstrInfo::anchor() {}
|
||||||
// REP coalescer and the AsmPrinter's LDAi8imm mode-tracking behave exactly
|
// REP coalescer and the AsmPrinter's LDAi8imm mode-tracking behave exactly
|
||||||
// as they did for the old three-instruction form.
|
// as they did for the old three-instruction form.
|
||||||
bool W65816InstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
|
bool W65816InstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
|
||||||
if (MI.getOpcode() != W65816::STB_DPIndLongY)
|
unsigned Access;
|
||||||
|
switch (MI.getOpcode()) {
|
||||||
|
case W65816::STB_DPIndLongY:
|
||||||
|
Access = W65816::STA_DPIndLongY;
|
||||||
|
break;
|
||||||
|
case W65816::LDB_DPIndLongY:
|
||||||
|
Access = W65816::LDA_DPIndLongY;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
MachineBasicBlock &MBB = *MI.getParent();
|
MachineBasicBlock &MBB = *MI.getParent();
|
||||||
DebugLoc DL = MI.getDebugLoc();
|
DebugLoc DL = MI.getDebugLoc();
|
||||||
int64_t Addr = MI.getOperand(0).getImm();
|
int64_t Addr = MI.getOperand(0).getImm();
|
||||||
BuildMI(MBB, MI, DL, get(W65816::SEP)).addImm(0x20);
|
BuildMI(MBB, MI, DL, get(W65816::SEP)).addImm(0x20);
|
||||||
BuildMI(MBB, MI, DL, get(W65816::STA_DPIndLongY)).addImm(Addr);
|
BuildMI(MBB, MI, DL, get(Access)).addImm(Addr);
|
||||||
BuildMI(MBB, MI, DL, get(W65816::REP)).addImm(0x20);
|
BuildMI(MBB, MI, DL, get(W65816::REP)).addImm(0x20);
|
||||||
MI.eraseFromParent();
|
MI.eraseFromParent();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -1200,6 +1200,18 @@ def STB_DPIndLongY : W65816Pseudo<(outs), (ins addrDP:$addr),
|
||||||
"# STB_DPIndLongY [${addr}], y", []>;
|
"# STB_DPIndLongY [${addr}], y", []>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Atomic i8-load-through-[dp],Y for VOLATILE byte loads, expanded post-RA
|
||||||
|
// to `SEP #$20 ; lda [dp],y ; REP #$20` for the same reason as the store
|
||||||
|
// above. A byte load is otherwise a 16-bit `lda [dp],y` + mask, which also
|
||||||
|
// reads address+1: harmless for memory, but for I/O it is a phantom read of
|
||||||
|
// the neighbouring register (an IIgs SCC data read at $C03A popped channel
|
||||||
|
// A's receive FIFO at $C03B). A's high byte is left as it was: zextload
|
||||||
|
// still masks, anyext does not care.
|
||||||
|
let mayLoad = 1, hasSideEffects = 1, Uses = [Y], Defs = [A, P] in {
|
||||||
|
def LDB_DPIndLongY : W65816Pseudo<(outs), (ins addrDP:$addr),
|
||||||
|
"# LDB_DPIndLongY [${addr}], y", []>;
|
||||||
|
}
|
||||||
|
|
||||||
// Pointer access with constant offset. `(load (add ptr, $off))` and
|
// Pointer access with constant offset. `(load (add ptr, $off))` and
|
||||||
// `(store val, (add ptr, $off))` come up for struct field access and
|
// `(store val, (add ptr, $off))` come up for struct field access and
|
||||||
// array indexing with small constant offsets. Without these patterns,
|
// array indexing with small constant offsets. Without these patterns,
|
||||||
|
|
|
||||||
|
|
@ -988,7 +988,35 @@ bool W65816StackSlotCleanup::runOnMachineFunction(MachineFunction &MF) {
|
||||||
// (t != 0) instead of (i != 0) and runs forever.
|
// (t != 0) instead of (i != 0) and runs forever.
|
||||||
Opc == W65816::TAX || Opc == W65816::TXA ||
|
Opc == W65816::TAX || Opc == W65816::TXA ||
|
||||||
Opc == W65816::TAY || Opc == W65816::TYA ||
|
Opc == W65816::TAY || Opc == W65816::TYA ||
|
||||||
Opc == W65816::TXY || Opc == W65816::TYX;
|
Opc == W65816::TXY || Opc == W65816::TYX ||
|
||||||
|
Opc == W65816::TSC || Opc == W65816::TDC ||
|
||||||
|
Opc == W65816::TSX ||
|
||||||
|
// The byte-move and negate pseudos, for the same reason as the
|
||||||
|
// i8 logic ops above: all set N/Z, none models Defs=[P].
|
||||||
|
// SRL8A / SHL8A expand to XBA ; AND #imm. Live symptom: a
|
||||||
|
// loop's `if (started && ...) break` lowered to `cmp #1`, then
|
||||||
|
// five inlined `(uint8_t)(v >> 8)` mailbox byte splits, then
|
||||||
|
// `bne`. SRL8A was taken as THE test, nothing was wrapped, and
|
||||||
|
// the BNE read the last AND's Z, so the break never fired
|
||||||
|
// (JoeyLib's IIgs serial-loss probe, 2026-09-28).
|
||||||
|
Opc == W65816::SRL8A || Opc == W65816::SHL8A ||
|
||||||
|
Opc == W65816::XBA || Opc == W65816::XBA16 ||
|
||||||
|
Opc == W65816::NEGA16 || Opc == W65816::NEGA8 ||
|
||||||
|
// Every other load, pseudo or already lowered: a load is never
|
||||||
|
// the test here, any more than LDA_DP above. Custom inserters
|
||||||
|
// emit real addressing modes before this pass runs (the far-
|
||||||
|
// pointer load is LDA_DPIndLongY, or the atomic 8-bit
|
||||||
|
// LDB_DPIndLongY for a volatile byte).
|
||||||
|
Opc == W65816::LDB_DPIndLongY ||
|
||||||
|
Opc == W65816::LDAabs || Opc == W65816::LDA8abs ||
|
||||||
|
Opc == W65816::LDA8absX || Opc == W65816::LDA8long ||
|
||||||
|
Opc == W65816::LDA_AbsX || Opc == W65816::LDA_AbsY ||
|
||||||
|
Opc == W65816::LDA_DPX || Opc == W65816::LDA_DPInd ||
|
||||||
|
Opc == W65816::LDA_DPIndX || Opc == W65816::LDA_DPIndY ||
|
||||||
|
Opc == W65816::LDA_DPIndLong || Opc == W65816::LDA_DPIndLongY ||
|
||||||
|
Opc == W65816::LDA_LongX || Opc == W65816::LDA_Imm16_Bank ||
|
||||||
|
Opc == W65816::LDX_AbsY || Opc == W65816::LDX_DPY ||
|
||||||
|
Opc == W65816::LDY_AbsX || Opc == W65816::LDY_DPX;
|
||||||
};
|
};
|
||||||
auto isStackRel = [](unsigned Opc) {
|
auto isStackRel = [](unsigned Opc) {
|
||||||
// Stack-relative ops read/write at S+disp. PHP decrements S by 1,
|
// Stack-relative ops read/write at S+disp. PHP decrements S by 1,
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,7 @@ LLVMInitializeW65816Target() {
|
||||||
initializeW65816DAGToDAGISelLegacyPass(PR);
|
initializeW65816DAGToDAGISelLegacyPass(PR);
|
||||||
initializeW65816StackSlotCleanupPass(PR);
|
initializeW65816StackSlotCleanupPass(PR);
|
||||||
initializeW65816SepRepCleanupPass(PR);
|
initializeW65816SepRepCleanupPass(PR);
|
||||||
|
initializeW65816FarPtrStageReusePass(PR);
|
||||||
initializeW65816BranchExpandPass(PR);
|
initializeW65816BranchExpandPass(PR);
|
||||||
initializeW65816TiedDefSpillPass(PR);
|
initializeW65816TiedDefSpillPass(PR);
|
||||||
initializeW65816ABridgeViaXPass(PR);
|
initializeW65816ABridgeViaXPass(PR);
|
||||||
|
|
@ -344,6 +345,10 @@ void W65816PassConfig::addPreEmitPass() {
|
||||||
if (!getenv("W65816_DISABLE_STACKOPT")) {
|
if (!getenv("W65816_DISABLE_STACKOPT")) {
|
||||||
addPass(createW65816StackRelToImg());
|
addPass(createW65816StackRelToImg());
|
||||||
}
|
}
|
||||||
|
// Last: only now is every instruction concrete (far frames decided,
|
||||||
|
// stack slots promoted), so the far-pointer staging into $E0..$E3 can be
|
||||||
|
// tracked across the whole function. Branch distances only shrink.
|
||||||
|
addPass(createW65816FarPtrStageReuse());
|
||||||
}
|
}
|
||||||
|
|
||||||
MachineFunctionInfo *W65816TargetMachine::createMachineFunctionInfo(
|
MachineFunctionInfo *W65816TargetMachine::createMachineFunctionInfo(
|
||||||
|
|
|
||||||
465
src/llvm/test/CodeGen/W65816/far-ptr-stage-reuse.mir
Normal file
465
src/llvm/test/CodeGen/W65816/far-ptr-stage-reuse.mir
Normal file
|
|
@ -0,0 +1,465 @@
|
||||||
|
# Pin W65816FarPtrStageReuse (pre-emit): the far-pointer staging into DP
|
||||||
|
# $E0 (224, low word) / $E2 (226, bank word) is deleted only when both words
|
||||||
|
# provably already hold the value being stored, and the loads, transfers and
|
||||||
|
# stack temporaries that fed a deleted staging go with it.
|
||||||
|
#
|
||||||
|
# The staging shape is the one the LDAptr32 inserter leaves after RA:
|
||||||
|
# LDA_DP lo ; TAX ; LDA_DP bank ; STA_StackRel t ; TXA ; STA_DP 224 ;
|
||||||
|
# LDA_StackRel t ; STA_DP 226
|
||||||
|
#
|
||||||
|
# reuse_diamond: staged in the entry block, re-staged in both arms and
|
||||||
|
# at the join -> only the entry staging survives.
|
||||||
|
# kept_after_call: a call forgets everything -> re-staging kept.
|
||||||
|
# kept_other_ptr: a different source word -> kept.
|
||||||
|
# kept_src_rewritten: the source word is stored in between -> kept.
|
||||||
|
# kept_dpx_store: a DP-indexed store may hit any DP word -> kept.
|
||||||
|
# loop_invariant: pointer not written in the loop -> the in-loop staging
|
||||||
|
# goes (the preheader's still holds on the back edge).
|
||||||
|
# loop_variant: low word bumped every iteration -> its staging kept,
|
||||||
|
# the bank word's goes.
|
||||||
|
# nz_live: a load of the value A already holds stays when a
|
||||||
|
# branch reads its N/Z.
|
||||||
|
# stale_liveness: a redundant load at the top of the successor goes, and
|
||||||
|
# the definition in the predecessor that now reaches its
|
||||||
|
# user must stay (deleting both used to be one step).
|
||||||
|
# dse_dead_slot: a stack temporary nothing reads is deleted; a read one
|
||||||
|
# is kept.
|
||||||
|
# dse_escape: TSX takes a stack address -> no stack store is touched.
|
||||||
|
# ret_void: RTL reads only its implicit uses: a staging leftover
|
||||||
|
# (LDA ; TAX) before a void return is dead.
|
||||||
|
# ret_i32: RTL implicit $a, implicit $x: X is the high word, kept.
|
||||||
|
#
|
||||||
|
# RUN: llc -mtriple=w65816 -run-pass=w65816-farptr-reuse -o - %s | FileCheck %s
|
||||||
|
--- |
|
||||||
|
target triple = "w65816"
|
||||||
|
declare void @g()
|
||||||
|
define void @reuse_diamond() { ret void }
|
||||||
|
define void @kept_after_call() { ret void }
|
||||||
|
define void @kept_other_ptr() { ret void }
|
||||||
|
define void @kept_src_rewritten() { ret void }
|
||||||
|
define void @kept_dpx_store() { ret void }
|
||||||
|
define void @loop_invariant() { ret void }
|
||||||
|
define void @loop_variant() { ret void }
|
||||||
|
define void @nz_live() { ret void }
|
||||||
|
define void @stale_liveness() { ret void }
|
||||||
|
define void @dse_dead_slot() { ret void }
|
||||||
|
define void @dse_escape() { ret void }
|
||||||
|
define void @ret_void() { ret void }
|
||||||
|
define void @ret_i32() { ret void }
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# CHECK-LABEL: name: reuse_diamond
|
||||||
|
# CHECK: bb.0:
|
||||||
|
# CHECK: STA_DP 224
|
||||||
|
# CHECK: STA_DP 226
|
||||||
|
# CHECK: bb.1:
|
||||||
|
# CHECK-NEXT: successors
|
||||||
|
# CHECK-NEXT: {{^ *$}}
|
||||||
|
# CHECK-NEXT: LDY_Imm16 2
|
||||||
|
# CHECK-NEXT: LDA_DPIndLongY 224
|
||||||
|
# CHECK: bb.2:
|
||||||
|
# CHECK-NEXT: successors
|
||||||
|
# CHECK-NEXT: {{^ *$}}
|
||||||
|
# CHECK-NEXT: LDY_Imm16 4
|
||||||
|
# CHECK-NEXT: LDA_DPIndLongY 224
|
||||||
|
# CHECK: bb.3:
|
||||||
|
# CHECK-NEXT: STA_DP 214
|
||||||
|
# CHECK-NEXT: LDY_Imm16 6
|
||||||
|
# CHECK-NEXT: LDA_DPIndLongY 224
|
||||||
|
name: reuse_diamond
|
||||||
|
tracksRegLiveness: false
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
successors: %bb.2, %bb.1
|
||||||
|
REP 48
|
||||||
|
TSC
|
||||||
|
SEC
|
||||||
|
SBC_Imm16 16
|
||||||
|
TCS
|
||||||
|
LDA_DP 210
|
||||||
|
TAX implicit-def $x, implicit $a
|
||||||
|
LDA_DP 208
|
||||||
|
STA_StackRel 17
|
||||||
|
TXA implicit-def $a, implicit $x
|
||||||
|
STA_DP 224
|
||||||
|
LDA_StackRel 17
|
||||||
|
STA_DP 226
|
||||||
|
LDY_Imm16 0, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
STA_DP 212
|
||||||
|
LDA_DP 200
|
||||||
|
BEQ %bb.2, implicit $p
|
||||||
|
|
||||||
|
bb.1:
|
||||||
|
successors: %bb.3
|
||||||
|
LDA_DP 210
|
||||||
|
TAX implicit-def $x, implicit $a
|
||||||
|
LDA_DP 208
|
||||||
|
STA_StackRel 13
|
||||||
|
TXA implicit-def $a, implicit $x
|
||||||
|
STA_DP 224
|
||||||
|
LDA_StackRel 13
|
||||||
|
STA_DP 226
|
||||||
|
LDY_Imm16 2, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
CLC
|
||||||
|
ADC_DP 212
|
||||||
|
BRA %bb.3
|
||||||
|
|
||||||
|
bb.2:
|
||||||
|
successors: %bb.3
|
||||||
|
LDA_DP 210
|
||||||
|
TAX implicit-def $x, implicit $a
|
||||||
|
LDA_DP 208
|
||||||
|
STA_StackRel 11
|
||||||
|
TXA implicit-def $a, implicit $x
|
||||||
|
STA_DP 224
|
||||||
|
LDA_StackRel 11
|
||||||
|
STA_DP 226
|
||||||
|
LDY_Imm16 4, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
SEC
|
||||||
|
SBC_DP 212
|
||||||
|
|
||||||
|
bb.3:
|
||||||
|
STA_DP 214
|
||||||
|
LDA_DP 210
|
||||||
|
TAX implicit-def $x, implicit $a
|
||||||
|
LDA_DP 208
|
||||||
|
STA_StackRel 7
|
||||||
|
TXA implicit-def $a, implicit $x
|
||||||
|
STA_DP 224
|
||||||
|
LDA_StackRel 7
|
||||||
|
STA_DP 226
|
||||||
|
LDY_Imm16 6, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
CLC
|
||||||
|
ADC_DP 214
|
||||||
|
TAY implicit-def $y, implicit $a
|
||||||
|
TSC
|
||||||
|
CLC
|
||||||
|
ADC_Imm16 16
|
||||||
|
TCS
|
||||||
|
TYA implicit-def $a, implicit $y
|
||||||
|
RTL
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# CHECK-LABEL: name: kept_after_call
|
||||||
|
# CHECK: JSLpseudo32
|
||||||
|
# CHECK-NEXT: REP 48
|
||||||
|
# CHECK-NEXT: LDA_DP 210
|
||||||
|
# CHECK-NEXT: STA_DP 224
|
||||||
|
# CHECK-NEXT: LDA_DP 208
|
||||||
|
# CHECK-NEXT: STA_DP 226
|
||||||
|
name: kept_after_call
|
||||||
|
tracksRegLiveness: false
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
REP 48
|
||||||
|
LDA_DP 210
|
||||||
|
STA_DP 224
|
||||||
|
LDA_DP 208
|
||||||
|
STA_DP 226
|
||||||
|
LDY_Imm16 0, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
STA_DP 212
|
||||||
|
JSLpseudo32 @g
|
||||||
|
REP 48
|
||||||
|
LDA_DP 210
|
||||||
|
STA_DP 224
|
||||||
|
LDA_DP 208
|
||||||
|
STA_DP 226
|
||||||
|
LDY_Imm16 2, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
RTL
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# The low word changes and is re-staged; the bank word does not.
|
||||||
|
# CHECK-LABEL: name: kept_other_ptr
|
||||||
|
# CHECK: LDY_Imm16 0
|
||||||
|
# CHECK: LDA_DP 216
|
||||||
|
# CHECK-NEXT: STA_DP 224
|
||||||
|
# CHECK-NEXT: LDY_Imm16 2
|
||||||
|
name: kept_other_ptr
|
||||||
|
tracksRegLiveness: false
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
REP 48
|
||||||
|
LDA_DP 210
|
||||||
|
STA_DP 224
|
||||||
|
LDA_DP 208
|
||||||
|
STA_DP 226
|
||||||
|
LDY_Imm16 0, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
STA_DP 212
|
||||||
|
LDA_DP 216
|
||||||
|
STA_DP 224
|
||||||
|
LDA_DP 208
|
||||||
|
STA_DP 226
|
||||||
|
LDY_Imm16 2, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
RTL
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# A already holds what was just stored to 210, so the reload goes too.
|
||||||
|
# CHECK-LABEL: name: kept_src_rewritten
|
||||||
|
# CHECK: STA_DP 210
|
||||||
|
# CHECK-NEXT: STA_DP 224
|
||||||
|
# CHECK-NEXT: LDY_Imm16 2
|
||||||
|
name: kept_src_rewritten
|
||||||
|
tracksRegLiveness: false
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
REP 48
|
||||||
|
LDA_DP 210
|
||||||
|
STA_DP 224
|
||||||
|
LDA_DP 208
|
||||||
|
STA_DP 226
|
||||||
|
LDY_Imm16 0, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
STA_DP 210
|
||||||
|
LDA_DP 210
|
||||||
|
STA_DP 224
|
||||||
|
LDA_DP 208
|
||||||
|
STA_DP 226
|
||||||
|
LDY_Imm16 2, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
RTL
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# CHECK-LABEL: name: kept_dpx_store
|
||||||
|
# CHECK: STA_DPX 0
|
||||||
|
# CHECK-NEXT: LDA_DP 210
|
||||||
|
# CHECK-NEXT: STA_DP 224
|
||||||
|
# CHECK-NEXT: LDA_DP 208
|
||||||
|
# CHECK-NEXT: STA_DP 226
|
||||||
|
name: kept_dpx_store
|
||||||
|
tracksRegLiveness: false
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
REP 48
|
||||||
|
LDA_DP 210
|
||||||
|
STA_DP 224
|
||||||
|
LDA_DP 208
|
||||||
|
STA_DP 226
|
||||||
|
LDY_Imm16 0, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
LDX_DP 218
|
||||||
|
STA_DPX 0, implicit $a, implicit $x
|
||||||
|
LDA_DP 210
|
||||||
|
STA_DP 224
|
||||||
|
LDA_DP 208
|
||||||
|
STA_DP 226
|
||||||
|
LDY_Imm16 2, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
RTL
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# CHECK-LABEL: name: loop_invariant
|
||||||
|
# CHECK: bb.1:
|
||||||
|
# CHECK-NEXT: successors
|
||||||
|
# CHECK-NEXT: {{^ *$}}
|
||||||
|
# CHECK-NEXT: LDY_Imm16 0
|
||||||
|
# CHECK-NEXT: LDA_DPIndLongY 224
|
||||||
|
name: loop_invariant
|
||||||
|
tracksRegLiveness: false
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
successors: %bb.1
|
||||||
|
REP 48
|
||||||
|
LDA_DP 210
|
||||||
|
STA_DP 224
|
||||||
|
LDA_DP 208
|
||||||
|
STA_DP 226
|
||||||
|
|
||||||
|
bb.1:
|
||||||
|
successors: %bb.1, %bb.2
|
||||||
|
LDA_DP 210
|
||||||
|
STA_DP 224
|
||||||
|
LDA_DP 208
|
||||||
|
STA_DP 226
|
||||||
|
LDY_Imm16 0, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
STA_DP 212
|
||||||
|
DEC_DP 214
|
||||||
|
BNE %bb.1, implicit $p
|
||||||
|
|
||||||
|
bb.2:
|
||||||
|
RTL
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# The low word is bumped with CLC ; ADC #2 (ADC_Imm16 carries TableGen's
|
||||||
|
# default mayStore, which must not end the bank word's reuse).
|
||||||
|
# CHECK-LABEL: name: loop_variant
|
||||||
|
# CHECK: bb.1:
|
||||||
|
# CHECK-NEXT: successors
|
||||||
|
# CHECK-NEXT: {{^ *$}}
|
||||||
|
# CHECK-NEXT: LDA_DP 210
|
||||||
|
# CHECK-NEXT: STA_DP 224
|
||||||
|
# CHECK-NEXT: LDY_Imm16 0
|
||||||
|
name: loop_variant
|
||||||
|
tracksRegLiveness: false
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
successors: %bb.1
|
||||||
|
REP 48
|
||||||
|
LDA_DP 210
|
||||||
|
STA_DP 224
|
||||||
|
LDA_DP 208
|
||||||
|
STA_DP 226
|
||||||
|
|
||||||
|
bb.1:
|
||||||
|
successors: %bb.1, %bb.2
|
||||||
|
LDA_DP 210
|
||||||
|
STA_DP 224
|
||||||
|
LDA_DP 208
|
||||||
|
STA_DP 226
|
||||||
|
LDY_Imm16 0, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
STA_DP 212
|
||||||
|
LDA_DP 210
|
||||||
|
CLC
|
||||||
|
ADC_Imm16 2
|
||||||
|
STA_DP 210
|
||||||
|
DEC_DP 214
|
||||||
|
BNE %bb.1, implicit $p
|
||||||
|
|
||||||
|
bb.2:
|
||||||
|
RTL
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# CHECK-LABEL: name: nz_live
|
||||||
|
# CHECK: LDA_DP 210
|
||||||
|
# CHECK-NEXT: STA_DP 212
|
||||||
|
# CHECK-NEXT: LDA_DP 210
|
||||||
|
# CHECK-NEXT: BEQ
|
||||||
|
name: nz_live
|
||||||
|
tracksRegLiveness: false
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
successors: %bb.1, %bb.2
|
||||||
|
REP 48
|
||||||
|
LDA_DP 210
|
||||||
|
STA_DP 212
|
||||||
|
LDA_DP 210
|
||||||
|
BEQ %bb.2, implicit $p
|
||||||
|
|
||||||
|
bb.1:
|
||||||
|
successors: %bb.2
|
||||||
|
STA_DP 214
|
||||||
|
|
||||||
|
bb.2:
|
||||||
|
LDA_Imm16 0, implicit-def $a
|
||||||
|
RTL
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# CHECK-LABEL: name: stale_liveness
|
||||||
|
# CHECK: bb.0:
|
||||||
|
# CHECK: LDA_DP 210
|
||||||
|
# CHECK-NEXT: BRA %bb.1
|
||||||
|
# CHECK: bb.1:
|
||||||
|
# CHECK-NEXT: STA_DP 212
|
||||||
|
name: stale_liveness
|
||||||
|
tracksRegLiveness: false
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
successors: %bb.1
|
||||||
|
REP 48
|
||||||
|
LDA_DP 210
|
||||||
|
BRA %bb.1
|
||||||
|
|
||||||
|
bb.1:
|
||||||
|
LDA_DP 210
|
||||||
|
STA_DP 212
|
||||||
|
LDA_DP 216
|
||||||
|
RTL
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# CHECK-LABEL: name: dse_dead_slot
|
||||||
|
# CHECK: LDA_DP 210
|
||||||
|
# CHECK-NEXT: STA_StackRel 3
|
||||||
|
# CHECK-NEXT: LDA_DP 216
|
||||||
|
# CHECK-NEXT: STA_DP 214
|
||||||
|
# CHECK-NEXT: LDA_StackRel 3
|
||||||
|
name: dse_dead_slot
|
||||||
|
tracksRegLiveness: false
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
REP 48
|
||||||
|
TSC
|
||||||
|
SEC
|
||||||
|
SBC_Imm16 4
|
||||||
|
TCS
|
||||||
|
LDA_DP 210
|
||||||
|
STA_StackRel 1
|
||||||
|
STA_StackRel 3
|
||||||
|
LDA_DP 216
|
||||||
|
STA_DP 214
|
||||||
|
LDA_StackRel 3
|
||||||
|
STA_DP 212
|
||||||
|
TSC
|
||||||
|
CLC
|
||||||
|
ADC_Imm16 4
|
||||||
|
TCS
|
||||||
|
RTL
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# CHECK-LABEL: name: dse_escape
|
||||||
|
# CHECK: LDA_DP 210
|
||||||
|
# CHECK-NEXT: STA_StackRel 1
|
||||||
|
# CHECK-NEXT: STA_StackRel 3
|
||||||
|
name: dse_escape
|
||||||
|
tracksRegLiveness: false
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
REP 48
|
||||||
|
TSC
|
||||||
|
SEC
|
||||||
|
SBC_Imm16 4
|
||||||
|
TCS
|
||||||
|
LDA_DP 210
|
||||||
|
STA_StackRel 1
|
||||||
|
STA_StackRel 3
|
||||||
|
LDA_DP 216
|
||||||
|
STA_DP 214
|
||||||
|
LDA_StackRel 3
|
||||||
|
STA_DP 212
|
||||||
|
TSX implicit-def $x, implicit $sp
|
||||||
|
STX_DP 218
|
||||||
|
TSC
|
||||||
|
CLC
|
||||||
|
ADC_Imm16 4
|
||||||
|
TCS
|
||||||
|
RTL
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# CHECK-LABEL: name: ret_void
|
||||||
|
# CHECK: REP 48
|
||||||
|
# CHECK-NEXT: STA_DP 212
|
||||||
|
# CHECK-NEXT: RTL
|
||||||
|
name: ret_void
|
||||||
|
tracksRegLiveness: false
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
REP 48
|
||||||
|
STA_DP 212
|
||||||
|
LDA_DP 210
|
||||||
|
TAX implicit-def $x, implicit $a
|
||||||
|
RTL
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# CHECK-LABEL: name: ret_i32
|
||||||
|
# CHECK: LDA_DP 210
|
||||||
|
# CHECK-NEXT: TAX
|
||||||
|
# CHECK-NEXT: LDA_DP 208
|
||||||
|
# CHECK-NEXT: RTL implicit $a, implicit $x
|
||||||
|
name: ret_i32
|
||||||
|
tracksRegLiveness: false
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
REP 48
|
||||||
|
LDA_DP 210
|
||||||
|
TAX implicit-def $x, implicit $a
|
||||||
|
LDA_DP 208
|
||||||
|
RTL implicit $a, implicit $x
|
||||||
|
...
|
||||||
112
src/llvm/test/CodeGen/W65816/srl8a-flag-corrupt-wrap.mir
Normal file
112
src/llvm/test/CodeGen/W65816/srl8a-flag-corrupt-wrap.mir
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
# Pin: the byte-move pseudos (SRL8A / SHL8A, which expand to XBA ; AND #imm)
|
||||||
|
# and every load form count as flag CORRUPTION, not as the flag-defining
|
||||||
|
# test, in the PHP/PLP wrap (W65816StackSlotCleanup Pass -2.5).
|
||||||
|
#
|
||||||
|
# None of them models Defs=[P], so a branch can never take its modelled $p
|
||||||
|
# from one; while they were missing from the corrupter list, isFlagDefining's
|
||||||
|
# default claimed them as THE test, the backward walk stopped there and the
|
||||||
|
# pass declined to wrap. Same family as andi8-flag-corrupt-wrap.mir.
|
||||||
|
#
|
||||||
|
# Live symptom: a loop's `if (started && ...) break` lowered to
|
||||||
|
# cmp #1 ; lda n,s ; xba ; and #$ff ; sta m,s ; (x5) ; bne
|
||||||
|
# -- five inlined `(uint8_t)(v >> 8)` mailbox byte splits scheduled between
|
||||||
|
# the compare and its branch. The BNE read the last AND's Z instead of the
|
||||||
|
# CMP's, so the break never fired (JoeyLib's IIgs serial-loss probe).
|
||||||
|
#
|
||||||
|
# srl8a: the reported shape: reload + SRL8A + spill between CMP and BNE.
|
||||||
|
# The spill after the last corrupter stays outside the wrap.
|
||||||
|
# shl8a: same for SHL8A.
|
||||||
|
# far_load: the far-pointer load the LDAptr32 inserter emits (a real
|
||||||
|
# LDA_DPIndLongY, not a pseudo) between CMP and BEQ; the wrap
|
||||||
|
# starts at the index load in front of it.
|
||||||
|
#
|
||||||
|
# RUN: llc -mtriple=w65816 -run-pass=w65816-stack-slot-cleanup -o - %s | FileCheck %s
|
||||||
|
--- |
|
||||||
|
target triple = "w65816"
|
||||||
|
define void @srl8a() { ret void }
|
||||||
|
define void @shl8a() { ret void }
|
||||||
|
define void @far_load() { ret void }
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# CHECK-LABEL: name: srl8a
|
||||||
|
# CHECK: CMPi16imm killed $a, 3, implicit-def $p
|
||||||
|
# CHECK-NEXT: PHP
|
||||||
|
# CHECK-NEXT: $a = LDAfi %stack.1, 1
|
||||||
|
# CHECK-NEXT: $a = SRL8A killed $a
|
||||||
|
# CHECK-NEXT: PLP
|
||||||
|
# CHECK-NEXT: STAfi killed $a, %stack.2, 0
|
||||||
|
# CHECK-NEXT: BNE %bb.1, implicit $p
|
||||||
|
name: srl8a
|
||||||
|
tracksRegLiveness: false
|
||||||
|
stack:
|
||||||
|
- { id: 0, size: 2, alignment: 1 }
|
||||||
|
- { id: 1, size: 2, alignment: 1 }
|
||||||
|
- { id: 2, size: 2, alignment: 1 }
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
successors: %bb.1, %bb.2
|
||||||
|
$a = LDAfi %stack.0, 0
|
||||||
|
CMPi16imm killed $a, 3, implicit-def $p
|
||||||
|
$a = LDAfi %stack.1, 0
|
||||||
|
$a = SRL8A killed $a
|
||||||
|
STAfi killed $a, %stack.2, 0, implicit-def $a
|
||||||
|
BNE %bb.1, implicit $p
|
||||||
|
BRA %bb.2
|
||||||
|
bb.1:
|
||||||
|
RTL
|
||||||
|
bb.2:
|
||||||
|
RTL
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# CHECK-LABEL: name: shl8a
|
||||||
|
# CHECK: CMPi16imm killed $a, 5, implicit-def $p
|
||||||
|
# CHECK-NEXT: PHP
|
||||||
|
# CHECK-NEXT: $a = LDAfi %stack.1, 1
|
||||||
|
# CHECK-NEXT: $a = SHL8A killed $a
|
||||||
|
# CHECK-NEXT: PLP
|
||||||
|
# CHECK-NEXT: BEQ %bb.1, implicit $p
|
||||||
|
name: shl8a
|
||||||
|
tracksRegLiveness: false
|
||||||
|
stack:
|
||||||
|
- { id: 0, size: 2, alignment: 1 }
|
||||||
|
- { id: 1, size: 2, alignment: 1 }
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
successors: %bb.1, %bb.2
|
||||||
|
$a = LDAfi %stack.0, 0
|
||||||
|
CMPi16imm killed $a, 5, implicit-def $p
|
||||||
|
$a = LDAfi %stack.1, 0
|
||||||
|
$a = SHL8A killed $a
|
||||||
|
BEQ %bb.1, implicit $p
|
||||||
|
BRA %bb.2
|
||||||
|
bb.1:
|
||||||
|
RTL
|
||||||
|
bb.2:
|
||||||
|
RTL
|
||||||
|
...
|
||||||
|
---
|
||||||
|
# CHECK-LABEL: name: far_load
|
||||||
|
# CHECK: CMPi16imm killed $a, 7, implicit-def $p
|
||||||
|
# CHECK-NEXT: PHP
|
||||||
|
# CHECK-NEXT: LDY_Imm16 2
|
||||||
|
# CHECK-NEXT: LDA_DPIndLongY 224
|
||||||
|
# CHECK-NEXT: PLP
|
||||||
|
# CHECK-NEXT: BEQ %bb.1, implicit $p
|
||||||
|
name: far_load
|
||||||
|
tracksRegLiveness: false
|
||||||
|
stack:
|
||||||
|
- { id: 0, size: 2, alignment: 1 }
|
||||||
|
body: |
|
||||||
|
bb.0:
|
||||||
|
successors: %bb.1, %bb.2
|
||||||
|
$a = LDAfi %stack.0, 0
|
||||||
|
CMPi16imm killed $a, 7, implicit-def $p
|
||||||
|
LDY_Imm16 2, implicit-def $y
|
||||||
|
LDA_DPIndLongY 224, implicit-def $a, implicit $y
|
||||||
|
BEQ %bb.1, implicit $p
|
||||||
|
BRA %bb.2
|
||||||
|
bb.1:
|
||||||
|
RTL
|
||||||
|
bb.2:
|
||||||
|
RTL
|
||||||
|
...
|
||||||
76
src/llvm/test/CodeGen/W65816/volatile-byte-load-8bit.ll
Normal file
76
src/llvm/test/CodeGen/W65816/volatile-byte-load-8bit.ll
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
; RUN: llc -mtriple=w65816 -O2 < %s | FileCheck %s
|
||||||
|
;
|
||||||
|
; Pin: a VOLATILE i8 load through a far pointer is a true 8-bit bus read,
|
||||||
|
; `sep #$20 ; lda [$e0],y ; rep #$20` (the atomic LDB_DPIndLongY pseudo).
|
||||||
|
;
|
||||||
|
; A byte load is otherwise a 16-bit `lda [$e0],y` followed by a mask, which
|
||||||
|
; also reads address+1. For memory that is harmless and cheaper, so plain
|
||||||
|
; byte loads keep it; for I/O it is a phantom read of the neighbouring
|
||||||
|
; register with that register's read side effects. On the IIgs SCC (four
|
||||||
|
; adjacent registers at $C038-$C03B) a channel-B data read at $C03A popped
|
||||||
|
; channel A's receive FIFO at $C03B (claudeDocs/LLVM816-ASKS.md #7 in
|
||||||
|
; JoeyLib). Constant-address byte loads and every byte store were already
|
||||||
|
; 8-bit.
|
||||||
|
;
|
||||||
|
; vol_byte: plain pointer, LDAptr32.
|
||||||
|
; vol_byte_off: pointer + constant, LDAptr32Off (offset goes in Y).
|
||||||
|
; vol_sbyte: sign-extending volatile byte load, same 8-bit read.
|
||||||
|
; plain_byte: non-volatile byte load keeps the 16-bit read + mask.
|
||||||
|
; vol_word: volatile i16 load is a 16-bit read, no mode switch.
|
||||||
|
|
||||||
|
target datalayout = "e-m:e-p:32:16-i16:16-i32:16-i64:16-f32:16-f64:16-a:8-n8:16-S8"
|
||||||
|
target triple = "w65816"
|
||||||
|
|
||||||
|
; CHECK-LABEL: vol_byte:
|
||||||
|
; CHECK: sep #0x20
|
||||||
|
; CHECK-NEXT: lda [0xe0], y
|
||||||
|
; CHECK-NEXT: rep #0x20
|
||||||
|
; CHECK-NEXT: and #0xff
|
||||||
|
define zeroext i8 @vol_byte(ptr %p) {
|
||||||
|
entry:
|
||||||
|
%v = load volatile i8, ptr %p, align 1
|
||||||
|
ret i8 %v
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: vol_byte_off:
|
||||||
|
; CHECK: ldy #0x2
|
||||||
|
; CHECK-NEXT: sep #0x20
|
||||||
|
; CHECK-NEXT: lda [0xe0], y
|
||||||
|
; CHECK-NEXT: rep #0x20
|
||||||
|
define zeroext i8 @vol_byte_off(ptr %p) {
|
||||||
|
entry:
|
||||||
|
%a = getelementptr inbounds i8, ptr %p, i32 2
|
||||||
|
%v = load volatile i8, ptr %a, align 1
|
||||||
|
ret i8 %v
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: vol_sbyte:
|
||||||
|
; CHECK: sep #0x20
|
||||||
|
; CHECK-NEXT: lda [0xe0], y
|
||||||
|
; CHECK-NEXT: rep #0x20
|
||||||
|
define signext i8 @vol_sbyte(ptr %p) {
|
||||||
|
entry:
|
||||||
|
%v = load volatile i8, ptr %p, align 1
|
||||||
|
ret i8 %v
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: plain_byte:
|
||||||
|
; CHECK-NOT: sep
|
||||||
|
; CHECK: lda [0xe0], y
|
||||||
|
; CHECK-NEXT: and #0xff
|
||||||
|
define zeroext i8 @plain_byte(ptr %p) {
|
||||||
|
entry:
|
||||||
|
%v = load i8, ptr %p, align 1
|
||||||
|
ret i8 %v
|
||||||
|
}
|
||||||
|
|
||||||
|
; CHECK-LABEL: vol_word:
|
||||||
|
; CHECK-NOT: sep
|
||||||
|
; CHECK: lda [0xe0], y
|
||||||
|
; CHECK-NOT: sep
|
||||||
|
; CHECK: rtl
|
||||||
|
define zeroext i16 @vol_word(ptr %p) {
|
||||||
|
entry:
|
||||||
|
%v = load volatile i16, ptr %p, align 2
|
||||||
|
ret i16 %v
|
||||||
|
}
|
||||||
|
|
@ -30,10 +30,13 @@ sanity check on our backend's code-quality.
|
||||||
bash tests/coremark/build.sh --layer2
|
bash tests/coremark/build.sh --layer2
|
||||||
```
|
```
|
||||||
|
|
||||||
`--layer2` enables `-mllvm -w65816-dbr-safe-ptrs`. This is **required**
|
`--layer2` enables `-mllvm -w65816-dbr-safe-ptrs` (smaller, faster
|
||||||
to fit the binary in a single bank; without it, text crosses the IO
|
pointer code; correct here because CoreMark only touches malloc/static-
|
||||||
window at `0xC000`. CoreMark only touches malloc/static-array memory,
|
array memory). It is optional: CoreMark plus the runtime it pulls in
|
||||||
so the dbr-safe-ptrs assumption is correct.
|
(printf, soft-float) no longer fits the ~44 KB below the IO window at
|
||||||
|
`0xC000` either way, so `runCoreMark.sh` links it multi-segment like the
|
||||||
|
Lua test (`--segment-cap`, extra segments at bank 4+) and runs it through
|
||||||
|
`scripts/runMultiSeg.sh`.
|
||||||
|
|
||||||
Default iteration count is 1 (smallest valid run). Override for
|
Default iteration count is 1 (smallest valid run). Override for
|
||||||
publishable scores:
|
publishable scores:
|
||||||
|
|
|
||||||
27
tests/coremark/coreMark.manifest.json
Normal file
27
tests/coremark/coreMark.manifest.json
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
{
|
||||||
|
"version": 1,
|
||||||
|
"main": "coreMark.bin",
|
||||||
|
"entry": "__start",
|
||||||
|
"segments": [
|
||||||
|
{
|
||||||
|
"num": 1,
|
||||||
|
"name": "SEG1",
|
||||||
|
"base": "0x001000",
|
||||||
|
"size": 44929,
|
||||||
|
"bss_start": "0x00d800",
|
||||||
|
"bss_size": 6268,
|
||||||
|
"image": "coreMark.bin",
|
||||||
|
"entry_offset": "0x0000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"num": 2,
|
||||||
|
"name": "SEG2",
|
||||||
|
"base": "0x040000",
|
||||||
|
"size": 16546,
|
||||||
|
"bss_start": "0x000000",
|
||||||
|
"bss_size": 0,
|
||||||
|
"image": "coreMark.seg2.bin",
|
||||||
|
"entry_offset": "0x0000"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -16,20 +16,26 @@ cd "$SCRIPT_DIR"
|
||||||
rm -rf "$CM_OBJ"
|
rm -rf "$CM_OBJ"
|
||||||
bash "$SCRIPT_DIR/build.sh" "$@"
|
bash "$SCRIPT_DIR/build.sh" "$@"
|
||||||
|
|
||||||
# Link. CoreMark is ~50KB compiled; should fit single bank.
|
# Link. CoreMark plus the runtime it pulls in (printf, soft-float) is more
|
||||||
|
# than the ~44 KB bank 0 has below the IO window, so it links multi-segment
|
||||||
|
# like the Lua test: text packs into 48 KB chunks from bank 0, the rest at
|
||||||
|
# bank 4+ (runMultiSeg.sh places each segment from the manifest).
|
||||||
"$PROJECT_ROOT/tools/link816" -o coreMark.bin --text-base 0x1000 \
|
"$PROJECT_ROOT/tools/link816" -o coreMark.bin --text-base 0x1000 \
|
||||||
|
--segment-cap 0xB000 \
|
||||||
|
--segment-bank-base 0x040000 \
|
||||||
|
--manifest coreMark.manifest.json \
|
||||||
"$RT/crt0.o" \
|
"$RT/crt0.o" \
|
||||||
"$RT/libc.o" "$RT/libgcc.o" "$RT/softFloat.o" "$RT/softDouble.o" \
|
"$RT/libc.o" "$RT/libgcc.o" "$RT/softFloat.o" "$RT/softDouble.o" \
|
||||||
"$RT/math.o" "$RT/strtol.o" "$RT/snprintf.o" \
|
"$RT/math.o" "$RT/strtol.o" "$RT/snprintf.o" \
|
||||||
"$RT/qsort.o" "$RT/timeExt.o" "$RT/extras.o" \
|
"$RT/qsort.o" "$RT/timeExt.o" "$RT/extras.o" \
|
||||||
$CM_OBJ/*.o
|
$CM_OBJ/*.o
|
||||||
|
|
||||||
ls -la coreMark.bin
|
ls -la coreMark*.bin
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# 0xC0DE sentinel = success; otherwise 0xBEEF = mid-run hang.
|
# 0xC0DE sentinel = success; otherwise 0xBEEF = mid-run hang.
|
||||||
# Elapsed ticks at 0x025002..0x025005 (low/hi).
|
# Elapsed ticks at 0x025002..0x025005 (low/hi).
|
||||||
MAME_CHECK_FRAME=900 MAME_SECS=30 MAME_TIMEOUT=300 \
|
MAME_CHECK_FRAME=900 MAME_SECS=30 MAME_TIMEOUT=300 MAME_RAMSIZE=4M \
|
||||||
bash "$PROJECT_ROOT/scripts/runInMame.sh" \
|
bash "$PROJECT_ROOT/scripts/runMultiSeg.sh" \
|
||||||
"$SCRIPT_DIR/coreMark.bin" \
|
"$SCRIPT_DIR/coreMark.manifest.json" \
|
||||||
--check 0x025000=c0de
|
--check 0x025000=c0de
|
||||||
|
|
|
||||||
|
|
@ -38,14 +38,20 @@ discussion in [`../../LLVM_65816_DESIGN.md`](../../LLVM_65816_DESIGN.md).
|
||||||
|
|
||||||
## Runtime execution status
|
## Runtime execution status
|
||||||
|
|
||||||
The compiled binary loads under MAME and reaches its entry point
|
`runLuaTest.sh` passes: the API smoke test (create a state, push three
|
||||||
(verified via direct `mame` invocation showing `MAME-READY pc=0x001000`).
|
integers, sum them, check a string, pop, close) writes the `0xC0DE`
|
||||||
Wrapping the run inside `scripts/runMultiSeg.sh` from the agent shell
|
sentinel under MAME.
|
||||||
crashes with SIGSEGV before producing output — this is sandbox-related,
|
|
||||||
not a code defect. The Lua API smoke test (push 3 ints, sum, check
|
It used to crash, and not because of any sandbox. The link is
|
||||||
strings, close state) compiles and links to 5 segments (~200 KB) so a
|
multi-segment (`--segment-cap`, five segments), and clang forms a
|
||||||
full Lua interpreter session is feasible if the harness is run in a
|
function pointer as a 16-bit offset plus the bank in DP `$BE` -- bank 0.
|
||||||
plain shell.
|
link816 only redirected pointers to seg2+ functions through bank-0 `JML`
|
||||||
|
trampolines for GS/OS ExpressLoad links, so here Lua's allocator (the
|
||||||
|
first thing `lua_newstate` calls, through a pointer) was called in bank 0,
|
||||||
|
landed in `vfprintf`, and the machine ran wild; MAME itself then died of
|
||||||
|
the I/O the runaway stack wrote. link816 now builds the trampolines for
|
||||||
|
every multi-segment link (fixed bank bases: the absolute target is baked
|
||||||
|
in).
|
||||||
|
|
||||||
## Files
|
## Files
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,9 @@
|
||||||
"num": 1,
|
"num": 1,
|
||||||
"name": "SEG1",
|
"name": "SEG1",
|
||||||
"base": "0x001000",
|
"base": "0x001000",
|
||||||
"size": 42092,
|
"size": 25189,
|
||||||
|
"bss_start": "0x008400",
|
||||||
|
"bss_size": 4248,
|
||||||
"image": "luaTest.bin",
|
"image": "luaTest.bin",
|
||||||
"entry_offset": "0x0000"
|
"entry_offset": "0x0000"
|
||||||
},
|
},
|
||||||
|
|
@ -15,7 +17,9 @@
|
||||||
"num": 2,
|
"num": 2,
|
||||||
"name": "SEG2",
|
"name": "SEG2",
|
||||||
"base": "0x040000",
|
"base": "0x040000",
|
||||||
"size": 45028,
|
"size": 41006,
|
||||||
|
"bss_start": "0x000000",
|
||||||
|
"bss_size": 0,
|
||||||
"image": "luaTest.seg2.bin",
|
"image": "luaTest.seg2.bin",
|
||||||
"entry_offset": "0x0000"
|
"entry_offset": "0x0000"
|
||||||
},
|
},
|
||||||
|
|
@ -23,9 +27,21 @@
|
||||||
"num": 3,
|
"num": 3,
|
||||||
"name": "SEG3",
|
"name": "SEG3",
|
||||||
"base": "0x050000",
|
"base": "0x050000",
|
||||||
"size": 36298,
|
"size": 43429,
|
||||||
|
"bss_start": "0x000000",
|
||||||
|
"bss_size": 0,
|
||||||
"image": "luaTest.seg3.bin",
|
"image": "luaTest.seg3.bin",
|
||||||
"entry_offset": "0x0000"
|
"entry_offset": "0x0000"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"num": 4,
|
||||||
|
"name": "SEG4",
|
||||||
|
"base": "0x060000",
|
||||||
|
"size": 26491,
|
||||||
|
"bss_start": "0x000000",
|
||||||
|
"bss_size": 0,
|
||||||
|
"image": "luaTest.seg4.bin",
|
||||||
|
"entry_offset": "0x0000"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue