Several more codegen fixes.
This commit is contained in:
parent
5aaa0b240c
commit
4def5417a3
8 changed files with 276 additions and 53 deletions
|
|
@ -590,13 +590,32 @@ void __heapInitMM(unsigned short userID) {
|
|||
if (bumpPtr) {
|
||||
return;
|
||||
}
|
||||
// Heap size: keep the link-time window when it is in a sane range,
|
||||
// else a 32 KB default. The window is bank-0-bounded, so it never
|
||||
// spans more than a single bank.
|
||||
// Heap size to ask the Memory Manager for.
|
||||
//
|
||||
// The link-time window is the space left in the ENTRY BANK above bss.
|
||||
// That is the right answer for the fallback path below (where malloc
|
||||
// really does run in that window) but it is NOT a statement of how
|
||||
// much heap the program wants: this handle carries no attrBank, so
|
||||
// the MM may place it in any bank and its size is not bounded by what
|
||||
// the entry bank had left. So the window is a floor to beat, not the
|
||||
// request itself -- take whichever of it and the default is larger.
|
||||
//
|
||||
// Using the window directly used to be masked by it being tiny: it
|
||||
// was capped at $BF00 to dodge a bank-0 IO window the program does
|
||||
// not live in, so most programs' windows fell under the old 0x2000
|
||||
// floor and silently got the 32 KB default. Widening the window to
|
||||
// the real top of the bank (link816's __heap_start comment) put those
|
||||
// windows IN range, which SHRANK the request -- AGI went from 32 KB
|
||||
// to 20 KB and stopped drawing. Clamping up instead of down means no
|
||||
// program can ever get a smaller heap than it did before.
|
||||
#define HEAP_MM_DEFAULT 0x8000UL // 32 KB
|
||||
#define HEAP_MM_MAX 0xC000UL // 48 KB; must still fit one bank
|
||||
unsigned long want = (unsigned long)(void *)__heap_end
|
||||
- (unsigned long)(void *)__heap_start;
|
||||
if (want < 0x2000UL || want > 0xC000UL) {
|
||||
want = 0x8000UL; // 32 KB
|
||||
if (want < HEAP_MM_DEFAULT) {
|
||||
want = HEAP_MM_DEFAULT;
|
||||
} else if (want > HEAP_MM_MAX) {
|
||||
want = HEAP_MM_MAX;
|
||||
}
|
||||
// attr 0x8014 = attrLocked | attrNoCross | attrPage. Every bit here is
|
||||
// set in the proven-good desktop.c DP-block value 0xC015, minus
|
||||
|
|
|
|||
|
|
@ -919,6 +919,30 @@ struct Linker {
|
|||
// bank's tiny budget to them wastes ~50K per bank. 0 = same as
|
||||
// segmentCap (the historical behaviour).
|
||||
uint32_t overflowCap = 0;
|
||||
|
||||
// Set by --full-bank: this image owns a whole 64K bank, so there is
|
||||
// no $C000-$CFFF IO window and no $D000+ language card to dodge, and
|
||||
// text / rodata / .init_array / BSS may use the bank right up to the
|
||||
// ceiling.
|
||||
//
|
||||
// That is true of a GS/OS OMF image: the Loader asks the Memory
|
||||
// Manager for each bank-aligned segment and gets whatever is free
|
||||
// (measured under MAME: joeylib's examples run in banks $08-$0B, and
|
||||
// bank $00 appears only in ROM and GS/OS glue). Those banks are plain
|
||||
// RAM throughout.
|
||||
//
|
||||
// It is NOT true of an image raw-loaded into bank 0, which is what
|
||||
// scripts/runInMame.sh does (loads at $00:1000 and jumps) for the
|
||||
// smoke, bench and probe scripts. There the hazards are real and
|
||||
// measured: smoke #86 had string literals read back as IO register
|
||||
// values, and smoke #128 had BSS clearing hit soft switches and crash
|
||||
// MAME.
|
||||
//
|
||||
// DEFAULT OFF, i.e. assume bank-0 hazards. The conservative layout is
|
||||
// merely smaller, while guessing "full bank" for an image that really
|
||||
// does land in bank 0 corrupts it -- so every raw-load consumer keeps
|
||||
// working untouched and only the OMF driver opts in.
|
||||
bool fullBank = false;
|
||||
uint32_t segmentBankBase = 0x040000;
|
||||
std::string manifestPath;
|
||||
// Phase C.2 — opt-in for ExpressLoad-multi-seg cINTERSEG emission.
|
||||
|
|
@ -1479,9 +1503,15 @@ struct Linker {
|
|||
// below the window). The small trampoline table may use that
|
||||
// margin; an actual IO-window crossing is still caught here
|
||||
// (and again by the textSize check below).
|
||||
if (segSizes[0] > kIoWindowStart)
|
||||
die("seg 1 text + cross-segment trampoline table crosses "
|
||||
"the IIgs IO window 0xC000");
|
||||
uint32_t textCeiling = fullBank ? kBank0Ceiling
|
||||
: kIoWindowStart;
|
||||
if (segSizes[0] > textCeiling) {
|
||||
char msg[160];
|
||||
std::snprintf(msg, sizeof(msg),
|
||||
"seg 1 text + cross-segment trampoline table (%u "
|
||||
"bytes) overruns 0x%X", segSizes[0], textCeiling);
|
||||
die(msg);
|
||||
}
|
||||
std::fprintf(stderr,
|
||||
"link816: %zu cross-seg function trampolines (%u bytes) "
|
||||
"in seg 1\n", gTrampList.size(),
|
||||
|
|
@ -1527,7 +1557,7 @@ struct Linker {
|
|||
// would fetch instructions from hardware registers. Programs
|
||||
// that grow this big need to split into bank 1 (not currently
|
||||
// supported by this linker).
|
||||
if (overlapsIoWindow(L.textBase, L.textSize) &&
|
||||
if (!fullBank && overlapsIoWindow(L.textBase, L.textSize) &&
|
||||
L.textBase < kIoWindowStart) {
|
||||
char msg[160];
|
||||
std::snprintf(msg, sizeof(msg),
|
||||
|
|
@ -1546,7 +1576,8 @@ struct Linker {
|
|||
// returned garbage because the keys read back as IO register
|
||||
// values). Catches both "starts before IO, crosses in" and
|
||||
// "starts inside IO" cases.
|
||||
if (!rodataBase && overlapsIoWindow(L.rodataBase, L.rodataSize)) {
|
||||
if (!fullBank && !rodataBase &&
|
||||
overlapsIoWindow(L.rodataBase, L.rodataSize)) {
|
||||
// Page-align upward past the IO window.
|
||||
L.rodataBase = kIoWindowEnd;
|
||||
// Pad the image so the gap between text-end and rodata-
|
||||
|
|
@ -1559,7 +1590,7 @@ struct Linker {
|
|||
L.initSize = curInit;
|
||||
// Init_array can also land in IO if rodata ends just before
|
||||
// or starts inside.
|
||||
if (overlapsIoWindow(L.initBase, L.initSize)) {
|
||||
if (!fullBank && overlapsIoWindow(L.initBase, L.initSize)) {
|
||||
L.initBase = kIoWindowEnd;
|
||||
}
|
||||
// After all skips, sanity-check we haven't gone past the LC
|
||||
|
|
@ -1609,7 +1640,8 @@ struct Linker {
|
|||
if (L.bssBase < loadEnd) {
|
||||
// Page-align upward for nicer addresses in the map.
|
||||
L.bssBase = (loadEnd + 0xFF) & ~0xFFu;
|
||||
if (L.bssBase >= kIoWindowStart && L.bssBase < kIoWindowEnd) {
|
||||
if (!fullBank && L.bssBase >= kIoWindowStart &&
|
||||
L.bssBase < kIoWindowEnd) {
|
||||
L.bssBase = kIoWindowEnd;
|
||||
}
|
||||
}
|
||||
|
|
@ -1618,7 +1650,7 @@ struct Linker {
|
|||
// BSS writes to the IO range hit soft switches — caught
|
||||
// by smoke #128 hex dumper, where ~954-byte BSS pushed
|
||||
// past kIoWindowStart and BSS-clear writes crashed MAME.
|
||||
if (overlapsIoWindow(L.bssBase, L.bssSize)) {
|
||||
if (!fullBank && overlapsIoWindow(L.bssBase, L.bssSize)) {
|
||||
L.bssBase = kIoWindowEnd;
|
||||
}
|
||||
if (L.bssBase + L.bssSize > kBank0Ceiling) {
|
||||
|
|
@ -1723,48 +1755,46 @@ struct Linker {
|
|||
globalSyms[sizeNames[i]] = 0;
|
||||
}
|
||||
}
|
||||
// __heap_start / __heap_end: pick the largest contiguous safe
|
||||
// range above bss_end. Without this, the previous hardcoded
|
||||
// heap_end=$BF00 gave heap_end < heap_start whenever BSS
|
||||
// spilled into LC1 — malloc immediately returned NULL.
|
||||
// If bank-0 heap would be tiny (<512B) push to LC1 (just past
|
||||
// the IO window).
|
||||
// __heap_start / __heap_end: everything from bss_end to the top
|
||||
// of the entry segment's BANK.
|
||||
//
|
||||
// Bank-0 heap top sits one page below the IO window so heap
|
||||
// alloc bumps never touch soft switches. kIoWindowStart - 0x100
|
||||
// = $BF00; encoded here for clarity rather than as a raw
|
||||
// constant.
|
||||
constexpr uint32_t kBank0HeapTop = kIoWindowStart - 0x100; // $BF00
|
||||
constexpr uint32_t MIN_HEAP = 512;
|
||||
// Prefer a bank-0 heap below the IO window. Pushing the heap into the
|
||||
// bank-0 LC ($D000-$FFFF) is a LAST RESORT and unreliable: GS/OS uses
|
||||
// that RAM, corrupting heap-resident state (a surface struct landing
|
||||
// there had its palette read back as garbage -- both the $D000-$DFFF
|
||||
// bank-switched half AND the $E000-$FFFF common half are unsafe). The
|
||||
// driver (clang-build.sh) avoids it by lowering the multi-seg
|
||||
// --segment-cap until rodata + BSS + heap fit below the IO window; this
|
||||
// LC fallback only fires for a program too large for even that.
|
||||
// There is no IO window and no language card to dodge here. Those
|
||||
// exist only in banks $00/$01/$E0/$E1; GS/OS places bank-aligned
|
||||
// OMF segments in free banks (measured under MAME: joeylib's
|
||||
// examples run in $08-$0B, and bank $00 shows up only in ROM and
|
||||
// GS/OS glue), and crt0Gsos points DBR at the entry segment's
|
||||
// bank, so these offsets address plain RAM across the whole 64K.
|
||||
//
|
||||
// The old model capped the heap at $BF00 ("kBank0HeapTop") and
|
||||
// fell back to a bank-0 language-card range, which cost ~16K of
|
||||
// the entry bank and drove clang-build.sh to shrink --segment-cap
|
||||
// to fit rodata + BSS + heap under a window that is not there. It
|
||||
// also justified itself with "crt0's $C083 read-twice enables
|
||||
// read+write", but crt0Gsos.s does no language-card enable at all
|
||||
// ("GS/OS configures memory").
|
||||
//
|
||||
// What this window actually does in a program that links libc:
|
||||
// it is a SIZE REQUEST. libc's __heapInitMM asks the Memory
|
||||
// Manager for (heap_end - heap_start) bytes and malloc runs
|
||||
// inside that handle, wherever the MM places it. The window is
|
||||
// only addressed directly if that allocation fails, and on that
|
||||
// path it is still in the entry bank, so it is still plain RAM.
|
||||
//
|
||||
// 0xFFFE rather than 0x10000: relocation patching at the use site
|
||||
// is 16-bit, so 0x10000 truncates to 0 and malloc would compute
|
||||
// heap_end < heap_start and return NULL for everything.
|
||||
constexpr uint32_t kEntryBankHeapTop = 0xFFFE;
|
||||
uint32_t heapStart = L.bssBase + L.bssSize;
|
||||
if (heapStart >= kBank0HeapTop && heapStart < kIoWindowEnd) {
|
||||
heapStart = kIoWindowEnd; // skip IO window + tiny tail
|
||||
} else if (heapStart < kBank0HeapTop &&
|
||||
(kBank0HeapTop - heapStart) < MIN_HEAP) {
|
||||
heapStart = kIoWindowEnd; // bank-0 sliver too small; use LC
|
||||
uint32_t heapEnd = kEntryBankHeapTop;
|
||||
if (heapStart >= heapEnd) {
|
||||
// BSS filled the bank (it may span several). No link-time
|
||||
// window is possible; libc's __heapInitMM sees a zero-size
|
||||
// request and applies its own 32K default, which is the best
|
||||
// available answer.
|
||||
heapEnd = heapStart;
|
||||
}
|
||||
globalSyms["__heap_start"] = heapStart;
|
||||
if (heapStart < kIoWindowStart) {
|
||||
globalSyms["__heap_end"] = kBank0HeapTop;
|
||||
} else if (heapStart < kBank0Ceiling) {
|
||||
// Heap in LC area ($D000-$FFFF). crt0's $C083 read-twice
|
||||
// enables read+write for the whole range. Cap at 0xFFFE
|
||||
// (not kBank0Ceiling) — relocation patching at the use
|
||||
// site is 16-bit and 0x10000 truncates to 0; malloc would
|
||||
// then think heap_end < heap_start and return NULL.
|
||||
globalSyms["__heap_end"] = 0xFFFE;
|
||||
} else {
|
||||
// Unreachable — bssBase + bssSize > kBank0Ceiling check above.
|
||||
globalSyms["__heap_end"] = heapStart;
|
||||
}
|
||||
globalSyms["__heap_end"] = heapEnd;
|
||||
|
||||
// 2. Build global symbol map. Honor weak vs strong binding:
|
||||
// - strong def overrides any prior weak def
|
||||
|
|
@ -2446,6 +2476,9 @@ int main(int argc, char **argv) {
|
|||
} else if (a == "--segment-cap") {
|
||||
if (++i >= argc) usage(argv[0]);
|
||||
linker.segmentCap = parseInt(argv[i++]);
|
||||
} else if (a == "--full-bank") {
|
||||
linker.fullBank = true;
|
||||
i++;
|
||||
} else if (a == "--overflow-cap") {
|
||||
if (++i >= argc) usage(argv[0]);
|
||||
linker.overflowCap = parseInt(argv[i++]);
|
||||
|
|
|
|||
|
|
@ -509,6 +509,16 @@ void W65816AsmPrinter::emitInstruction(const MachineInstr *MI) {
|
|||
// non-zero so the null test passes and the direct call uses the
|
||||
// symbol's own reloc, not this bank byte). Defined/normal globals
|
||||
// and compiler external symbols (libgcc helpers) keep `lda $BE`.
|
||||
//
|
||||
// CODE symbols in a multi-segment OMF app are NOT an exception, even
|
||||
// though every overflow segment lives in its own bank: link816
|
||||
// redirects the 16-bit half of `&fn` for a function outside the
|
||||
// entry segment to a `JML fn` trampoline it appends to the ENTRY
|
||||
// segment (see the cross-segment function-pointer trampoline table
|
||||
// in link816.cpp), so the entry bank from $BE is exactly right for
|
||||
// that offset. A per-function bank slot read from .data was tried
|
||||
// on 2026-09-22 and broke every such pointer (bank of the callee +
|
||||
// offset of the trampoline). Do not resurrect it.
|
||||
const MachineOperand &Sym = MI->getOperand(1);
|
||||
bool WeakUndef = Sym.isGlobal() &&
|
||||
Sym.getGlobal()->hasExternalWeakLinkage();
|
||||
|
|
|
|||
|
|
@ -4082,3 +4082,14 @@ W65816TargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI,
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void W65816TargetLowering::computeKnownBitsForFrameIndex(int FIOp,
|
||||
KnownBits &Known,
|
||||
const MachineFunction &MF) const {
|
||||
// See the header: stack objects carry no realised alignment here, so
|
||||
// nothing about the address is known.
|
||||
(void)FIOp;
|
||||
(void)MF;
|
||||
(void)Known;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,16 @@ public:
|
|||
SDValue LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
||||
SmallVectorImpl<SDValue> &InVals) const override;
|
||||
|
||||
// A frame index promises NO known-zero low bits: the 65816 stack pointer
|
||||
// is whatever it is (JSL pushes 3 bytes, PHP 1), so a stack object's
|
||||
// alignment is never realised in its address. The default derives known
|
||||
// bits from MachineFrameInfo's object alignment, which let DAGCombine
|
||||
// fold `slot + 1` into `slot | 1` -- a volatile union's byte reads then
|
||||
// hit the wrong byte whenever the frame base was odd (Space Taxi's
|
||||
// collision masks, 2026-09-21).
|
||||
void computeKnownBitsForFrameIndex(int FIOp, KnownBits &Known,
|
||||
const MachineFunction &MF) const override;
|
||||
|
||||
SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg,
|
||||
const SmallVectorImpl<ISD::OutputArg> &Outs,
|
||||
const SmallVectorImpl<SDValue> &OutVals, const SDLoc &DL,
|
||||
|
|
|
|||
|
|
@ -941,9 +941,29 @@ bool W65816StackSlotCleanup::runOnMachineFunction(MachineFunction &MF) {
|
|||
// real CMP. NOTE: the *fi variants (ANDfi/ORAfi/EORfi) and
|
||||
// ADC/SBC DO model Defs=[P] and can legitimately be the branch's
|
||||
// flag source, so they are handled by $p liveness and are NOT
|
||||
// listed here; same for INA/DEA and the shifts, which the DEC/
|
||||
// INC and shift-carry idioms may read directly.
|
||||
// listed here; same for the shifts, which the shift-carry idiom
|
||||
// reads directly.
|
||||
Opc == W65816::EORi16imm || Opc == W65816::EORabs ||
|
||||
// INC/DEC in every form. None of them models Defs=[P] either,
|
||||
// so at this pass's point no branch can take its flags from
|
||||
// one: the `dec a / bne` idiom only exists AFTER SepRepCleanup
|
||||
// folds ADC #±1 (Defs=[P]) into INA/DEA and drops the `cmp #0`,
|
||||
// both of which run later. Left out, isFlagDefining's default
|
||||
// made a loop's own `r++` the "test": `for (r = 0; r < 21; r++)`
|
||||
// lowered to `cmp #end ; inc a ; bne loop`, the INA trampled the
|
||||
// CMP's Z, and the loop only stopped when the counter wrapped
|
||||
// through zero -- Space Taxi's cab-vs-background test walked 51
|
||||
// rows of a 21-row sprite into the next cel's data and crashed
|
||||
// the IIgs attract demo on a landing (2026-09-21).
|
||||
Opc == W65816::INA_PSEUDO || Opc == W65816::DEA_PSEUDO ||
|
||||
Opc == W65816::INA_PSEUDO8 || Opc == W65816::DEA_PSEUDO8 ||
|
||||
Opc == W65816::INA || Opc == W65816::DEA ||
|
||||
Opc == W65816::INX || Opc == W65816::DEX ||
|
||||
Opc == W65816::INY || Opc == W65816::DEY ||
|
||||
Opc == W65816::INC_DP || Opc == W65816::INC_Abs ||
|
||||
Opc == W65816::INC_DPX || Opc == W65816::INC_AbsX ||
|
||||
Opc == W65816::DEC_DP || Opc == W65816::DEC_Abs ||
|
||||
Opc == W65816::DEC_DPX || Opc == W65816::DEC_AbsX ||
|
||||
Opc == W65816::ANDi16imm || Opc == W65816::ANDabs ||
|
||||
Opc == W65816::ORAi16imm || Opc == W65816::ORAabs ||
|
||||
// ...and the i8 siblings, for exactly the same reason (cf.
|
||||
|
|
|
|||
20
src/llvm/test/CodeGen/W65816/frameindex-no-known-bits.ll
Normal file
20
src/llvm/test/CodeGen/W65816/frameindex-no-known-bits.ll
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
; Pin: a frame index has no known-zero low bits (W65816TargetLowering::
|
||||
; computeKnownBitsForFrameIndex). The 65816 stack pointer carries no
|
||||
; alignment (JSL pushes 3 bytes, PHP 1), so the default known bits from the
|
||||
; object's alignment let DAGCombine fold `slot + 1` into `slot | 1`: a
|
||||
; volatile union's second byte was read from the FIRST byte whenever the
|
||||
; frame base was odd. JoeyLib's Space Taxi lost its collision masks that
|
||||
; way (2026-09-21).
|
||||
;
|
||||
; RUN: llc -mtriple=w65816 -O2 < %s | FileCheck %s
|
||||
; CHECK-LABEL: byte1:
|
||||
; CHECK-NOT: ora #0x1
|
||||
; CHECK: rtl
|
||||
define i8 @byte1(i32 %v) {
|
||||
entry:
|
||||
%u = alloca i32, align 2
|
||||
store volatile i32 %v, ptr %u, align 2
|
||||
%p1 = getelementptr inbounds i8, ptr %u, i16 1
|
||||
%b = load volatile i8, ptr %p1, align 1
|
||||
ret i8 %b
|
||||
}
|
||||
100
src/llvm/test/CodeGen/W65816/ina-flag-corrupt-wrap.mir
Normal file
100
src/llvm/test/CodeGen/W65816/ina-flag-corrupt-wrap.mir
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
# Pin: INC/DEC in every form (INA_PSEUDO / DEA_PSEUDO, their i8 twins, the
|
||||
# raw INA/DEA/INX/DEX/INY/DEY and the memory INC/DEC) 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 at this pass's point no branch can
|
||||
# legitimately take its flags from one -- the `dec a / bne` idiom only
|
||||
# exists after SepRepCleanup folds ADC #+-1 into INA/DEA and drops the
|
||||
# `cmp #0`, both of which run later. While they were missing,
|
||||
# isFlagDefining's default claimed a loop's own increment as THE test:
|
||||
# the backward walk from the Bxx stopped at the INA and declined to wrap.
|
||||
#
|
||||
# Live symptom: `for (r = 0; r < 21; r++) { ... }` lowered to
|
||||
# cmp #end ; inc a ; bne loop
|
||||
# so the branch read the INC's Z, not the CMP's, and the loop only ended
|
||||
# when the counter wrapped through zero. JoeyLib's Space Taxi walked 51
|
||||
# rows of a 21-row sprite into the next cel's data and crashed the IIgs
|
||||
# attract demo on a landing.
|
||||
#
|
||||
# ina_i16: the reported shape -- INA_PSEUDO between CMP and BNE.
|
||||
# dea_i8: DEA_PSEUDO8 between CMP and BEQ.
|
||||
# no_test: an INA_PSEUDO with no test before it gets no wrap (nothing to
|
||||
# preserve, and the wrap costs 8 cycles).
|
||||
#
|
||||
# RUN: llc -mtriple=w65816 -run-pass=w65816-stack-slot-cleanup -o - %s | FileCheck %s
|
||||
--- |
|
||||
target triple = "w65816"
|
||||
define void @ina_i16() { ret void }
|
||||
define void @dea_i8() { ret void }
|
||||
define void @no_test() { ret void }
|
||||
...
|
||||
---
|
||||
# CHECK-LABEL: name: ina_i16
|
||||
# CHECK: CMPi16imm killed $a, 65506, implicit-def $p
|
||||
# CHECK-NEXT: PHP
|
||||
# CHECK-NEXT: $a = INA_PSEUDO $a
|
||||
# CHECK-NEXT: PLP
|
||||
# CHECK-NEXT: BNE %bb.1, implicit $p
|
||||
name: ina_i16
|
||||
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, 65506, implicit-def $p
|
||||
$a = INA_PSEUDO $a
|
||||
BNE %bb.1, implicit $p
|
||||
BRA %bb.2
|
||||
bb.1:
|
||||
RTL
|
||||
bb.2:
|
||||
RTL
|
||||
...
|
||||
---
|
||||
# CHECK-LABEL: name: dea_i8
|
||||
# CHECK: CMPi16imm killed $a, 7, implicit-def $p
|
||||
# CHECK-NEXT: PHP
|
||||
# CHECK-NEXT: $a = DEA_PSEUDO8 $a
|
||||
# CHECK-NEXT: PLP
|
||||
# CHECK-NEXT: BEQ %bb.1, implicit $p
|
||||
name: dea_i8
|
||||
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
|
||||
$a = DEA_PSEUDO8 $a
|
||||
BEQ %bb.1, implicit $p
|
||||
BRA %bb.2
|
||||
bb.1:
|
||||
RTL
|
||||
bb.2:
|
||||
RTL
|
||||
...
|
||||
---
|
||||
# CHECK-LABEL: name: no_test
|
||||
# CHECK-NOT: PHP
|
||||
# CHECK: $a = INA_PSEUDO $a
|
||||
# CHECK-NEXT: BNE %bb.1, implicit $p
|
||||
name: no_test
|
||||
tracksRegLiveness: false
|
||||
stack:
|
||||
- { id: 0, size: 2, alignment: 1 }
|
||||
body: |
|
||||
bb.0:
|
||||
successors: %bb.1, %bb.2
|
||||
$a = LDAfi %stack.0, 0
|
||||
$a = INA_PSEUDO $a
|
||||
BNE %bb.1, implicit $p
|
||||
BRA %bb.2
|
||||
bb.1:
|
||||
RTL
|
||||
bb.2:
|
||||
RTL
|
||||
...
|
||||
Loading…
Add table
Reference in a new issue