From 1a0ef7ee87cd76673848722ed638650cd781d5cd Mon Sep 17 00:00:00 2001 From: Scott Duensing Date: Tue, 30 Jun 2026 18:00:50 -0500 Subject: [PATCH] Many deferred issues finally fixed. --- STATUS.md | 11 ++ scripts/smokeTest.sh | 21 ++- src/clang/lib/Basic/Targets/W65816.h | 4 + .../Disassembler/W65816Disassembler.cpp | 155 ++++++++++++++---- .../W65816/MCTargetDesc/W65816InstPrinter.cpp | 7 +- .../lib/Target/W65816/W65816ISelLowering.cpp | 104 +++++++++--- src/llvm/lib/Target/W65816/W65816InstrInfo.td | 12 +- .../lib/Target/W65816/W65816MCInstLower.cpp | 5 +- 8 files changed, 254 insertions(+), 65 deletions(-) diff --git a/STATUS.md b/STATUS.md index 30973d5..d3ac62f 100644 --- a/STATUS.md +++ b/STATUS.md @@ -285,6 +285,17 @@ which runs correctly under MAME (apple2gs). rodata/bss/init_array relocations applied to every `.debug_*` section, so `.debug_addr` / `.debug_line` PC values are final- image addresses. +- `llvm-mc --disassemble` / `llvm-objdump -d` decode W65816 object + code, tracking the M (accumulator/memory) and X (index) width + flags across a linear sweep: state is seeded to the C ABI default + (M=16/X=16), updated as REP/SEP immediates are decoded, and used to + pick the 1-byte vs 2-byte immediate form of the mode-sensitive + LDA/LDX/.../CPY opcodes. Best-effort linear heuristic only (it + cannot see mode set across branches, by a caller's ABI convention, + or through data-in-code, and there is no per-section reset), but it + decodes clang codegen (M=16/X=16 invariant) and straight-line + hand-written 8-bit-mode asm correctly — crt0.o and libc.o (23k + insns) disassemble fully coherent. - `runtime/build.sh` builds crt0, libc, soft-float, soft-double, libgcc into linkable objects. - `scripts/smokeTest.sh` runs 148 end-to-end checks at -O2: diff --git a/scripts/smokeTest.sh b/scripts/smokeTest.sh index 7327fe2..5080d18 100755 --- a/scripts/smokeTest.sh +++ b/scripts/smokeTest.sh @@ -136,15 +136,32 @@ fi # --disassemble should produce the mnemonic we expect. if [ -x "$LLVM_MC" ]; then log "check: llvm-mc --disassemble decodes bytes back to mnemonics" - disasmOut="$(printf '0xea 0xa9 0x34 0x12 0x85 0x10 0x8d 0x00 0x10 0x6b\n' \ + disasmOut="$(printf '0xea 0xa9 0x34 0x12 0x85 0x10 0x8d 0x00 0x10 0xfc 0x00 0x10 0x6b\n' \ | "$LLVM_MC" --disassemble --triple=w65816 2>&1)" - for mnem in "nop" "lda #0x1234" "sta 0x10" "sta 0x1000" "rtl"; do + for mnem in "nop" "lda #0x1234" "sta 0x10" "sta 0x1000" "jsr (0x1000, x)" "rtl"; do if ! printf '%s\n' "$disasmOut" | grep -qF "$mnem"; then warn "disassembler missing: $mnem" printf '%s\n' "$disasmOut" >&2 die "disassembler round-trip failed" fi done + + # 5b. M/X mode tracking. The same immediate-mode opcode (a9 = LDA #) + # decodes as a 1-byte immediate after SEP #$20 (M->8) and a 2-byte one + # after REP #$20 (M->16); X is tracked independently via SEP/REP #$10. + # Without the linear REP/SEP heuristic, the 8-bit LDA would over-read a + # byte and desync the rest of the stream. + log "check: llvm-mc --disassemble tracks M/X across SEP/REP" + # SEP #$20 ; lda #$34 ; ldx #$1234 ; REP #$20 ; lda #$3412 ; SEP #$10 ; ldx #$05 + mxOut="$(printf '0xe2 0x20 0xa9 0x34 0xa2 0x34 0x12 0xc2 0x20 0xa9 0x12 0x34 0xe2 0x10 0xa2 0x05\n' \ + | "$LLVM_MC" --disassemble --triple=w65816 2>&1)" + for mnem in "lda #0x34" "ldx #0x1234" "lda #0x3412" "ldx #0x5"; do + if ! printf '%s\n' "$mxOut" | grep -qF "$mnem"; then + warn "M/X tracking wrong: expected $mnem" + printf '%s\n' "$mxOut" >&2 + die "disassembler M/X tracking failed" + fi + done fi # 6. End-to-end codegen: IR -> asm -> ELF -> disassembly. diff --git a/src/clang/lib/Basic/Targets/W65816.h b/src/clang/lib/Basic/Targets/W65816.h index 2ba24d5..75cd7e9 100644 --- a/src/clang/lib/Basic/Targets/W65816.h +++ b/src/clang/lib/Basic/Targets/W65816.h @@ -70,6 +70,10 @@ public: return Feature == "w65816"; } + bool hasBitIntType() const override { return true; } + + size_t getMaxBitIntWidth() const override { return 64; } + ArrayRef getGCCRegNames() const override; ArrayRef getGCCRegAliases() const override { diff --git a/src/llvm/lib/Target/W65816/Disassembler/W65816Disassembler.cpp b/src/llvm/lib/Target/W65816/Disassembler/W65816Disassembler.cpp index 4446f44..f0aeaf3 100644 --- a/src/llvm/lib/Target/W65816/Disassembler/W65816Disassembler.cpp +++ b/src/llvm/lib/Target/W65816/Disassembler/W65816Disassembler.cpp @@ -15,11 +15,20 @@ // identical for both forms up to the opcode; the real operand width depends // on the M/X processor-mode bits. The TableGen descriptions put the _Imm8 // forms into the "W65816MHigh" / "W65816XHigh" decoder namespaces, so the -// default "W65816" table contains only the 3-byte _Imm16 forms. This -// scaffold disassembler reads exclusively from the default table, so those -// mnemonics always decode as 3-byte (16-bit) immediates. A future mode- -// tracking pass will consult the MHigh / XHigh tables when MReg / XReg are -// set by preceding REP/SEP instructions. +// default "W65816" table contains only the 3-byte _Imm16 forms. +// +// To resolve the ambiguity this disassembler tracks the M (accumulator/memory +// width) and X (index width) processor flags across a linear sweep: it seeds +// them to the C ABI default (M=16/X=16), watches the REP/SEP immediates as +// they decode, and — when a flag is 8-bit — consults the MHigh16 / XHigh16 +// tables (2-byte: opcode + imm8) ahead of the default table so the affected +// mnemonics decode as 1-byte immediates. This is a best-effort *linear* +// heuristic: it follows straight-line REP/SEP only and cannot see mode +// established across branches, by a caller's ABI convention, or through +// data-in-code, and there is no per-section reset hook, so a region entered +// in a different mode than the previous region left set may decode wrong. +// For clang-generated code (M=16/X=16 invariant) the default path is always +// correct; the tracking matters for hand-written 8-bit-mode asm. // //===----------------------------------------------------------------------===// @@ -44,9 +53,27 @@ using DecodeStatus = MCDisassembler::DecodeStatus; namespace { class W65816Disassembler : public MCDisassembler { + // Tracked processor-mode bits, seeded to the C ABI default (16-bit). + // getInstruction is called sequentially per instruction by llvm-objdump / + // llvm-mc, so this mutable state follows the byte stream linearly. + mutable bool AccIs8 = false; // M flag: true => 8-bit accumulator/memory + mutable bool IdxIs8 = false; // X flag: true => 8-bit index registers + + // A copy of the subtarget with the MHigh / XHigh assembler predicates forced + // on, used only when decoding the mode-sensitive _Imm8 tables. The generated + // decoder gates those entries behind FeatureMHigh / FeatureXHigh; the running + // subtarget leaves them off, so we supply an enabled copy when (and only + // when) our own M/X tracking has decided an 8-bit form applies. + MCSubtargetInfo ModeSTI; + public: W65816Disassembler(const MCSubtargetInfo &STI, MCContext &Ctx) - : MCDisassembler(STI, Ctx) {} + : MCDisassembler(STI, Ctx), ModeSTI(STI) { + FeatureBitset FB = ModeSTI.getFeatureBits(); + FB.set(W65816::FeatureMHigh); + FB.set(W65816::FeatureXHigh); + ModeSTI.setFeatureBits(FB); + } DecodeStatus getInstruction(MCInst &MI, uint64_t &Size, ArrayRef Bytes, uint64_t Address, @@ -129,48 +156,108 @@ static DecodeStatus decodePCRel16(MCInst &Inst, uint64_t Imm, uint64_t Address, // opcode like NOP (0xEA) matches in DecoderTableW658168 and shorter-circuits // any accidental collision with a longer encoding that happens to begin with // the same byte. Every W65816 opcode byte uniquely determines the -// instruction (modulo the M/X-mode ambiguity documented above), so the -// first successful decode is always the correct one. +// instruction (modulo the M/X-mode ambiguity resolved by the tracking below), +// so the first successful decode is always the correct one. +// +// Mode tracking: when the tracked M (or X) flag is 8-bit, the affected +// LDA/ADC/.../CPY opcodes carry a 1-byte immediate, whose _Imm8 forms live in +// DecoderTableW65816MHigh16 / XHigh16 (2 bytes: opcode + imm8). Those tables +// hold *only* the mode-sensitive mnemonics, so a successful decode there can +// only be one of them; we try the matching table ahead of the default 16-bit +// forms. After every decode, REP/SEP immediates update the tracked flags. //===----------------------------------------------------------------------===// +// REP / SEP opcode bytes and the P-register flag bits they manipulate. +static constexpr uint8_t kOpcodeSEP = 0xE2; // SEP #imm: sets the named P bits +static constexpr uint8_t kOpcodeREP = 0xC2; // REP #imm: clears the named P bits +static constexpr uint8_t kFlagM = 0x20; // accumulator/memory width bit +static constexpr uint8_t kFlagX = 0x10; // index-register width bit + DecodeStatus W65816Disassembler::getInstruction(MCInst &MI, uint64_t &Size, ArrayRef Bytes, uint64_t Address, raw_ostream &CStream) const { - struct TableEntry { - const uint8_t *Table; - unsigned Bytes; - }; - - static const TableEntry Tables[] = { - { DecoderTableW658168, 1 }, - { DecoderTableW6581616, 2 }, - { DecoderTableW6581624, 3 }, - { DecoderTableW6581632, 4 }, - }; - - for (const TableEntry &T : Tables) { - if (Bytes.size() < T.Bytes) { - continue; + // Decode `NBytes` little-endian bytes from the stream against `Table`, using + // the given subtarget for predicate checks (ModeSTI for the predicate-gated + // High tables, the running STI otherwise). + auto tryTable = [&](const uint8_t *Table, unsigned NBytes, + const MCSubtargetInfo &DecodeSTI, + MCInst &Out) -> DecodeStatus { + if (Bytes.size() < NBytes) { + return MCDisassembler::Fail; } - uint64_t Insn = 0; - for (unsigned I = 0; I < T.Bytes; ++I) { + for (unsigned I = 0; I < NBytes; ++I) { Insn |= static_cast(Bytes[I]) << (8 * I); } + return decodeInstruction(Table, Out, Insn, Address, this, DecodeSTI); + }; - MCInst Candidate; - DecodeStatus Result = decodeInstruction(T.Table, Candidate, Insn, Address, - this, STI); - if (Result != MCDisassembler::Fail) { - MI = Candidate; - Size = T.Bytes; - return Result; + MCInst Candidate; + DecodeStatus Result = MCDisassembler::Fail; + uint64_t DecodedSize = 0; + + // Mode-sensitive 8-bit-immediate forms take priority when the tracked flag + // says 8-bit; the High16 tables only contain those mnemonics, so a miss + // simply falls through to the default size-dispatch below. + if (AccIs8) { + Result = tryTable(DecoderTableW65816MHigh16, 2, ModeSTI, Candidate); + } + if (Result == MCDisassembler::Fail && IdxIs8) { + Result = tryTable(DecoderTableW65816XHigh16, 2, ModeSTI, Candidate); + } + if (Result != MCDisassembler::Fail) { + DecodedSize = 2; + } else { + struct TableEntry { + const uint8_t *Table; + unsigned Bytes; + }; + static const TableEntry Tables[] = { + { DecoderTableW658168, 1 }, + { DecoderTableW6581616, 2 }, + { DecoderTableW6581624, 3 }, + { DecoderTableW6581632, 4 }, + }; + for (const TableEntry &T : Tables) { + Result = tryTable(T.Table, T.Bytes, STI, Candidate); + if (Result != MCDisassembler::Fail) { + DecodedSize = T.Bytes; + break; + } } } - Size = 1; - return MCDisassembler::Fail; + if (Result == MCDisassembler::Fail) { + Size = 1; + return MCDisassembler::Fail; + } + + // Follow REP/SEP so subsequent mode-sensitive immediates decode at the right + // width. SEP #imm sets (=> 8-bit) the P bits present in imm; REP #imm + // clears (=> 16-bit) them. Both are fixed 2-byte encodings (opcode + imm8). + if (DecodedSize >= 2) { + uint8_t Imm = Bytes[1]; + if (Bytes[0] == kOpcodeSEP) { + if (Imm & kFlagM) { + AccIs8 = true; + } + if (Imm & kFlagX) { + IdxIs8 = true; + } + } else if (Bytes[0] == kOpcodeREP) { + if (Imm & kFlagM) { + AccIs8 = false; + } + if (Imm & kFlagX) { + IdxIs8 = false; + } + } + } + + MI = Candidate; + Size = DecodedSize; + return Result; } diff --git a/src/llvm/lib/Target/W65816/MCTargetDesc/W65816InstPrinter.cpp b/src/llvm/lib/Target/W65816/MCTargetDesc/W65816InstPrinter.cpp index 8578367..1984ab5 100644 --- a/src/llvm/lib/Target/W65816/MCTargetDesc/W65816InstPrinter.cpp +++ b/src/llvm/lib/Target/W65816/MCTargetDesc/W65816InstPrinter.cpp @@ -6,9 +6,10 @@ // //===----------------------------------------------------------------------===// // -// This class prints a W65816 MCInst to a .s file. The skeleton does not yet -// implement any real instructions so the printer mostly defers to the table- -// generated routines and reports unimplemented cases with llvm_unreachable. +// This class prints a W65816 MCInst to a .s file. Most instructions print +// via the table-generated routines (printInstruction); this file supplies the +// custom operand printers (hex immediates with the '$' prefix, addressing-mode +// operands, PC-relative branch targets). // //===----------------------------------------------------------------------===// diff --git a/src/llvm/lib/Target/W65816/W65816ISelLowering.cpp b/src/llvm/lib/Target/W65816/W65816ISelLowering.cpp index cb3c042..8e14854 100644 --- a/src/llvm/lib/Target/W65816/W65816ISelLowering.cpp +++ b/src/llvm/lib/Target/W65816/W65816ISelLowering.cpp @@ -6,8 +6,12 @@ // //===----------------------------------------------------------------------===// // -// Minimum DAG lowering sufficient for a no-argument function returning an -// i16 constant. Argument passing and non-trivial calls still unimplemented. +// DAG lowering for the W65816: argument passing and returns over the +// A/X/Y/DPF0 register ABI (i8/i16 in A, i32 in A:X, i64 across A:X:Y + a DP +// slot, larger aggregates via sret), direct and indirect calls (the latter +// through the __jsl_indir trampoline), i32 arithmetic over the Wide32 reg +// class, custom BR_CC / SELECT_CC / SETCC including the i32 high/low split, +// and the pointer-deref load/store lowerings. // //===----------------------------------------------------------------------===// @@ -23,6 +27,7 @@ #include "llvm/CodeGen/SelectionDAG.h" #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" #include "llvm/Support/KnownBits.h" +#include "llvm/IR/DiagnosticInfo.h" #include "llvm/IR/Function.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/ErrorHandling.h" @@ -31,6 +36,24 @@ using namespace llvm; #define DEBUG_TYPE "w65816-lower" +// Emit a clean, source-located "unsupported" diagnostic instead of an +// internal abort for ABI cases we can't represent (types wider than the +// 4-register return ABI, value types the calling convention never sees a +// legalized form of, condition codes normalizeCC can't map). These paths +// are unreachable from valid C today — the type legalizer and soft-float +// lowering normalize every user type into the i8/i16/i32 halves the ABI +// handles, and large aggregates go via sret — but a future frontend or an +// exotic input should get a proper `error:` with the function's location, +// never an LLVM-internal stack trace. Callers emit the diagnostic and then +// return a safe placeholder so the rest of the function finishes building; +// codegen still fails overall because the diagnostic is an error. +static void reportUnsupported(SelectionDAG &DAG, const SDLoc &DL, + const Twine &Msg) { + MachineFunction &MF = DAG.getMachineFunction(); + DAG.getContext()->diagnose( + DiagnosticInfoUnsupported(MF.getFunction(), Msg, DL.getDebugLoc())); +} + // Loader-compat workaround: when set, LDAptr/STAptr/STBptr inserters // load the bank byte from DP $BE (initialized by crt0 to PHK / current // PBR) instead of forcing it to 0 via STZ $E2. This makes pointer @@ -777,7 +800,8 @@ SDValue W65816TargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const { case ISD::SETUGT: HiCC = ISD::SETUGT; LoCCu = ISD::SETUGT; break; case ISD::SETUGE: HiCC = ISD::SETUGT; LoCCu = ISD::SETUGE; break; default: - report_fatal_error("W65816: unexpected i32 BR_CC condition"); + reportUnsupported(DAG, DL, "unsupported i32 branch condition code"); + return Chain; // safe placeholder; the diagnostic fails compilation } SDValue HiOk = DAG.getSetCC(DL, MVT::i16, LH, RH, HiCC); SDValue HiEq = DAG.getSetCC(DL, MVT::i16, LH, RH, ISD::SETEQ); @@ -791,8 +815,10 @@ SDValue W65816TargetLowering::LowerBR_CC(SDValue Op, SelectionDAG &DAG) const { } W65816CC::CondCode TCC = normalizeCC(LHS, RHS, CC, DAG, DL); - if (TCC == W65816CC::COND_INVALID) - report_fatal_error("W65816: branch condition not yet implemented"); + if (TCC == W65816CC::COND_INVALID) { + reportUnsupported(DAG, DL, "unsupported branch condition code"); + return Chain; // safe placeholder; the diagnostic fails compilation + } // Multi-branch CCs only have inserter support via SELECT_CC16. For // BR_CC, reroute through SETCC: materialise the boolean to A, then @@ -918,7 +944,8 @@ SDValue W65816TargetLowering::LowerSETCC(SDValue Op, SelectionDAG &DAG) const { case ISD::SETUGT: HiCC = ISD::SETUGT; LoCCu = ISD::SETUGT; break; case ISD::SETUGE: HiCC = ISD::SETUGT; LoCCu = ISD::SETUGE; break; default: - report_fatal_error("W65816: unexpected i32 SETCC condition"); + reportUnsupported(DAG, DL, "unsupported i32 comparison condition code"); + return DAG.getUNDEF(VT); // safe placeholder } SDValue HiOk = DAG.getSetCC(DL, VT, LH, RH, HiCC); SDValue HiEq = DAG.getSetCC(DL, VT, LH, RH, ISD::SETEQ); @@ -973,8 +1000,10 @@ SDValue W65816TargetLowering::LowerSELECT_CC(SDValue Op, } W65816CC::CondCode TCC = normalizeCC(LHS, RHS, CC, DAG, DL); - if (TCC == W65816CC::COND_INVALID) - report_fatal_error("W65816: select_cc condition not yet implemented"); + if (TCC == W65816CC::COND_INVALID) { + reportUnsupported(DAG, DL, "unsupported select condition code"); + return DAG.getUNDEF(Op.getValueType()); // safe placeholder + } SDValue Glue = DAG.getNode(W65816ISD::CMP, DL, MVT::Glue, LHS, RHS); SDValue CCOp = DAG.getTargetConstant(TCC, DL, MVT::i8); @@ -2029,8 +2058,14 @@ SDValue W65816TargetLowering::LowerFormalArguments( unsigned StackOffset = 4; // Skip 3 ret-addr bytes; first slot at S+4. for (const ISD::InputArg &Arg : Ins) { MVT VT = Arg.VT; - if (VT != MVT::i16 && VT != MVT::i8 && VT != MVT::i32) - report_fatal_error("W65816: argument type not yet supported"); + if (VT != MVT::i16 && VT != MVT::i8 && VT != MVT::i32) { + reportUnsupported(DAG, DL, + "unsupported argument type (only i8/i16/i32 halves " + "are representable in the W65816 ABI)"); + InVals.push_back(DAG.getUNDEF(VT)); // keep InVals aligned with Ins + ++ArgIdx; + continue; + } if (ArgIdx == 0 && VT == MVT::i32) { // Whole-i32 first arg: lo half live-in via $a, hi via $x. @@ -2159,8 +2194,13 @@ W65816TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, // Up to 4 return halves (i64 split): i8/i16 in A; i32 in A:X; // i64 in A:X:Y plus DP $F0..$F1 for the highest half. See // LowerReturn comment for the ABI. - if (Ins.size() > 4) - report_fatal_error("W65816: return type wider than 64 bits not supported"); + if (Ins.size() > 4) { + reportUnsupported(DAG, DL, + "return type wider than 64 bits is not supported"); + for (const ISD::InputArg &I : Ins) + InVals.push_back(DAG.getUNDEF(I.VT)); // keep InVals aligned with Ins + return Chain; + } // Indirect calls (function pointers): redirect through the runtime // trampoline `__jsl_indir`. The 65816 has no JSL-indirect; instead, @@ -2187,8 +2227,14 @@ W65816TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, } for (const ISD::OutputArg &O : Outs) { - if (O.VT != MVT::i16 && O.VT != MVT::i8 && O.VT != MVT::i32) - report_fatal_error("W65816: argument type not yet supported"); + if (O.VT != MVT::i16 && O.VT != MVT::i8 && O.VT != MVT::i32) { + reportUnsupported(DAG, DL, + "unsupported argument type (only i8/i16/i32 halves " + "are representable in the W65816 ABI)"); + for (const ISD::InputArg &I : Ins) + InVals.push_back(DAG.getUNDEF(I.VT)); // keep InVals aligned with Ins + return Chain; + } } // i32 first-arg ABI. Two flavors: @@ -2343,6 +2389,7 @@ W65816TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, // walk it, copying from A, X, Y, DPF0 in order. Re-assemble i32 // halves into a Wide32 SDValue at the end. SmallVector ExpVT; + bool BadRet = false; for (unsigned i = 0, e = Ins.size(); i != e; ++i) { MVT VT = Ins[i].VT; if (VT == MVT::i32) { @@ -2351,11 +2398,22 @@ W65816TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, } else if (VT == MVT::i16 || VT == MVT::i8) { ExpVT.push_back(VT); } else { - report_fatal_error("W65816: return half must be i8/i16/i32"); + reportUnsupported(DAG, DL, + "unsupported return value type (only i8/i16/i32 " + "halves are representable in the W65816 ABI)"); + BadRet = true; } } - if (ExpVT.size() > 4) - report_fatal_error("W65816: return type wider than 64 bits not supported"); + if (!BadRet && ExpVT.size() > 4) { + reportUnsupported(DAG, DL, + "return type wider than 64 bits is not supported"); + BadRet = true; + } + if (BadRet) { + for (const ISD::InputArg &I : Ins) + InVals.push_back(DAG.getUNDEF(I.VT)); // keep InVals aligned with Ins + return Chain; + } static constexpr Register RetRegs[4] = {W65816::A, W65816::X, W65816::Y, W65816::DPF0}; SmallVector Halves; @@ -2414,11 +2472,17 @@ SDValue W65816TargetLowering::LowerReturn( ExpVT.push_back(VT); ExpVals.push_back(OutVals[i]); } else { - report_fatal_error("W65816: return half must be i8/i16/i32"); + reportUnsupported(DAG, DL, + "unsupported return value type (only i8/i16/i32 " + "halves are representable in the W65816 ABI)"); + return DAG.getNode(W65816ISD::RET_GLUE, DL, MVT::Other, Chain); } } - if (ExpVT.size() > 4) - report_fatal_error("W65816: return type wider than 64 bits not supported"); + if (ExpVT.size() > 4) { + reportUnsupported(DAG, DL, + "return type wider than 64 bits is not supported"); + return DAG.getNode(W65816ISD::RET_GLUE, DL, MVT::Other, Chain); + } // Single whole-i32 return: copy directly to AX32 instead of two // halves to A and X. Saves the regalloc/coalescer some work. diff --git a/src/llvm/lib/Target/W65816/W65816InstrInfo.td b/src/llvm/lib/Target/W65816/W65816InstrInfo.td index 2615d5f..08a252d 100644 --- a/src/llvm/lib/Target/W65816/W65816InstrInfo.td +++ b/src/llvm/lib/Target/W65816/W65816InstrInfo.td @@ -581,8 +581,11 @@ def ASRA16 : W65816Pseudo<(outs Acc16:$dst), (ins Acc16:$src), } // Shifts by small constants — unroll into 2-4 single-bit shifts. -// Anything beyond 4 bits would benefit from a loop or a XBA-and-mask -// trick; left for a future peephole. +// Counts >= 8 use the XBA-and-mask trick via the SHL8A/SRL8A pseudos +// below, and counts 9..15 chain ASL/LSR after that pseudo (see the +// SHL8A / SHL15A definitions and the 9..14 Pat blocks); counts 5..7 +// fall out of the generic combiner as ASL/LSR chains. So every +// constant shift 0..15 lowers inline (no __ashl/__lshr libcall). def : Pat<(shl Acc16:$src, (i16 2)), (ASLA16 (ASLA16 Acc16:$src))>; def : Pat<(shl Acc16:$src, (i16 3)), (ASLA16 (ASLA16 (ASLA16 Acc16:$src)))>; @@ -1761,8 +1764,9 @@ def JML_Long : InstAbsLong<0x5C, "jml">; //---------------------------------------------------------------- Calls let isCall = 1, mayLoad = 0, mayStore = 0 in { -def JSR_Abs : InstAbs<0x20, "jsr">; -def JSL_Long : InstAbsLong<0x22, "jsl">; +def JSR_Abs : InstAbs<0x20, "jsr">; +def JSR_AbsIndX : InstAbsIndX<0xFC, "jsr">; +def JSL_Long : InstAbsLong<0x22, "jsl">; } //---------------------------------------------------------------- Returns diff --git a/src/llvm/lib/Target/W65816/W65816MCInstLower.cpp b/src/llvm/lib/Target/W65816/W65816MCInstLower.cpp index 6c59940..9a564ac 100644 --- a/src/llvm/lib/Target/W65816/W65816MCInstLower.cpp +++ b/src/llvm/lib/Target/W65816/W65816MCInstLower.cpp @@ -7,8 +7,9 @@ //===----------------------------------------------------------------------===// // // This file contains code to lower W65816 MachineInstrs to their -// corresponding MCInst records. The skeleton follows the MSP430 pattern -// closely but does not yet understand any target-specific operand flags. +// corresponding MCInst records, following the MSP430 pattern. It handles +// register, immediate, global/external-symbol, block-address, jump-table, +// and constant-pool operands (see LowerSymbolOperand). // //===----------------------------------------------------------------------===//