Many deferred issues finally fixed.
This commit is contained in:
parent
178ed633ee
commit
1a0ef7ee87
8 changed files with 254 additions and 65 deletions
11
STATUS.md
11
STATUS.md
|
|
@ -285,6 +285,17 @@ which runs correctly under MAME (apple2gs).
|
||||||
rodata/bss/init_array relocations applied to every `.debug_*`
|
rodata/bss/init_array relocations applied to every `.debug_*`
|
||||||
section, so `.debug_addr` / `.debug_line` PC values are final-
|
section, so `.debug_addr` / `.debug_line` PC values are final-
|
||||||
image addresses.
|
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,
|
- `runtime/build.sh` builds crt0, libc, soft-float, soft-double,
|
||||||
libgcc into linkable objects.
|
libgcc into linkable objects.
|
||||||
- `scripts/smokeTest.sh` runs 148 end-to-end checks at -O2:
|
- `scripts/smokeTest.sh` runs 148 end-to-end checks at -O2:
|
||||||
|
|
|
||||||
|
|
@ -136,15 +136,32 @@ fi
|
||||||
# --disassemble should produce the mnemonic we expect.
|
# --disassemble should produce the mnemonic we expect.
|
||||||
if [ -x "$LLVM_MC" ]; then
|
if [ -x "$LLVM_MC" ]; then
|
||||||
log "check: llvm-mc --disassemble decodes bytes back to mnemonics"
|
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)"
|
| "$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
|
if ! printf '%s\n' "$disasmOut" | grep -qF "$mnem"; then
|
||||||
warn "disassembler missing: $mnem"
|
warn "disassembler missing: $mnem"
|
||||||
printf '%s\n' "$disasmOut" >&2
|
printf '%s\n' "$disasmOut" >&2
|
||||||
die "disassembler round-trip failed"
|
die "disassembler round-trip failed"
|
||||||
fi
|
fi
|
||||||
done
|
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
|
fi
|
||||||
|
|
||||||
# 6. End-to-end codegen: IR -> asm -> ELF -> disassembly.
|
# 6. End-to-end codegen: IR -> asm -> ELF -> disassembly.
|
||||||
|
|
|
||||||
|
|
@ -70,6 +70,10 @@ public:
|
||||||
return Feature == "w65816";
|
return Feature == "w65816";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool hasBitIntType() const override { return true; }
|
||||||
|
|
||||||
|
size_t getMaxBitIntWidth() const override { return 64; }
|
||||||
|
|
||||||
ArrayRef<const char *> getGCCRegNames() const override;
|
ArrayRef<const char *> getGCCRegNames() const override;
|
||||||
|
|
||||||
ArrayRef<TargetInfo::GCCRegAlias> getGCCRegAliases() const override {
|
ArrayRef<TargetInfo::GCCRegAlias> getGCCRegAliases() const override {
|
||||||
|
|
|
||||||
|
|
@ -15,11 +15,20 @@
|
||||||
// identical for both forms up to the opcode; the real operand width depends
|
// 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
|
// on the M/X processor-mode bits. The TableGen descriptions put the _Imm8
|
||||||
// forms into the "W65816MHigh" / "W65816XHigh" decoder namespaces, so the
|
// forms into the "W65816MHigh" / "W65816XHigh" decoder namespaces, so the
|
||||||
// default "W65816" table contains only the 3-byte _Imm16 forms. This
|
// default "W65816" table contains only the 3-byte _Imm16 forms.
|
||||||
// scaffold disassembler reads exclusively from the default table, so those
|
//
|
||||||
// mnemonics always decode as 3-byte (16-bit) immediates. A future mode-
|
// To resolve the ambiguity this disassembler tracks the M (accumulator/memory
|
||||||
// tracking pass will consult the MHigh / XHigh tables when MReg / XReg are
|
// width) and X (index width) processor flags across a linear sweep: it seeds
|
||||||
// set by preceding REP/SEP instructions.
|
// 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 {
|
namespace {
|
||||||
|
|
||||||
class W65816Disassembler : public MCDisassembler {
|
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:
|
public:
|
||||||
W65816Disassembler(const MCSubtargetInfo &STI, MCContext &Ctx)
|
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,
|
DecodeStatus getInstruction(MCInst &MI, uint64_t &Size,
|
||||||
ArrayRef<uint8_t> Bytes, uint64_t Address,
|
ArrayRef<uint8_t> 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
|
// opcode like NOP (0xEA) matches in DecoderTableW658168 and shorter-circuits
|
||||||
// any accidental collision with a longer encoding that happens to begin with
|
// any accidental collision with a longer encoding that happens to begin with
|
||||||
// the same byte. Every W65816 opcode byte uniquely determines the
|
// the same byte. Every W65816 opcode byte uniquely determines the
|
||||||
// instruction (modulo the M/X-mode ambiguity documented above), so the
|
// instruction (modulo the M/X-mode ambiguity resolved by the tracking below),
|
||||||
// first successful decode is always the correct one.
|
// 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,
|
DecodeStatus W65816Disassembler::getInstruction(MCInst &MI, uint64_t &Size,
|
||||||
ArrayRef<uint8_t> Bytes,
|
ArrayRef<uint8_t> Bytes,
|
||||||
uint64_t Address,
|
uint64_t Address,
|
||||||
raw_ostream &CStream) const {
|
raw_ostream &CStream) const {
|
||||||
struct TableEntry {
|
// Decode `NBytes` little-endian bytes from the stream against `Table`, using
|
||||||
const uint8_t *Table;
|
// the given subtarget for predicate checks (ModeSTI for the predicate-gated
|
||||||
unsigned Bytes;
|
// High tables, the running STI otherwise).
|
||||||
};
|
auto tryTable = [&](const uint8_t *Table, unsigned NBytes,
|
||||||
|
const MCSubtargetInfo &DecodeSTI,
|
||||||
static const TableEntry Tables[] = {
|
MCInst &Out) -> DecodeStatus {
|
||||||
{ DecoderTableW658168, 1 },
|
if (Bytes.size() < NBytes) {
|
||||||
{ DecoderTableW6581616, 2 },
|
return MCDisassembler::Fail;
|
||||||
{ DecoderTableW6581624, 3 },
|
|
||||||
{ DecoderTableW6581632, 4 },
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const TableEntry &T : Tables) {
|
|
||||||
if (Bytes.size() < T.Bytes) {
|
|
||||||
continue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t Insn = 0;
|
uint64_t Insn = 0;
|
||||||
for (unsigned I = 0; I < T.Bytes; ++I) {
|
for (unsigned I = 0; I < NBytes; ++I) {
|
||||||
Insn |= static_cast<uint64_t>(Bytes[I]) << (8 * I);
|
Insn |= static_cast<uint64_t>(Bytes[I]) << (8 * I);
|
||||||
}
|
}
|
||||||
|
return decodeInstruction(Table, Out, Insn, Address, this, DecodeSTI);
|
||||||
|
};
|
||||||
|
|
||||||
MCInst Candidate;
|
MCInst Candidate;
|
||||||
DecodeStatus Result = decodeInstruction(T.Table, Candidate, Insn, Address,
|
DecodeStatus Result = MCDisassembler::Fail;
|
||||||
this, STI);
|
uint64_t DecodedSize = 0;
|
||||||
if (Result != MCDisassembler::Fail) {
|
|
||||||
MI = Candidate;
|
// Mode-sensitive 8-bit-immediate forms take priority when the tracked flag
|
||||||
Size = T.Bytes;
|
// says 8-bit; the High16 tables only contain those mnemonics, so a miss
|
||||||
return Result;
|
// 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;
|
if (Result == MCDisassembler::Fail) {
|
||||||
return 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,9 +6,10 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
//
|
//
|
||||||
// This class prints a W65816 MCInst to a .s file. The skeleton does not yet
|
// This class prints a W65816 MCInst to a .s file. Most instructions print
|
||||||
// implement any real instructions so the printer mostly defers to the table-
|
// via the table-generated routines (printInstruction); this file supplies the
|
||||||
// generated routines and reports unimplemented cases with llvm_unreachable.
|
// custom operand printers (hex immediates with the '$' prefix, addressing-mode
|
||||||
|
// operands, PC-relative branch targets).
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,12 @@
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
//
|
//
|
||||||
// Minimum DAG lowering sufficient for a no-argument function returning an
|
// DAG lowering for the W65816: argument passing and returns over the
|
||||||
// i16 constant. Argument passing and non-trivial calls still unimplemented.
|
// 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/SelectionDAG.h"
|
||||||
#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
|
#include "llvm/CodeGen/TargetLoweringObjectFileImpl.h"
|
||||||
#include "llvm/Support/KnownBits.h"
|
#include "llvm/Support/KnownBits.h"
|
||||||
|
#include "llvm/IR/DiagnosticInfo.h"
|
||||||
#include "llvm/IR/Function.h"
|
#include "llvm/IR/Function.h"
|
||||||
#include "llvm/Support/CommandLine.h"
|
#include "llvm/Support/CommandLine.h"
|
||||||
#include "llvm/Support/ErrorHandling.h"
|
#include "llvm/Support/ErrorHandling.h"
|
||||||
|
|
@ -31,6 +36,24 @@ using namespace llvm;
|
||||||
|
|
||||||
#define DEBUG_TYPE "w65816-lower"
|
#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
|
// Loader-compat workaround: when set, LDAptr/STAptr/STBptr inserters
|
||||||
// load the bank byte from DP $BE (initialized by crt0 to PHK / current
|
// 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
|
// 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::SETUGT: HiCC = ISD::SETUGT; LoCCu = ISD::SETUGT; break;
|
||||||
case ISD::SETUGE: HiCC = ISD::SETUGT; LoCCu = ISD::SETUGE; break;
|
case ISD::SETUGE: HiCC = ISD::SETUGT; LoCCu = ISD::SETUGE; break;
|
||||||
default:
|
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 HiOk = DAG.getSetCC(DL, MVT::i16, LH, RH, HiCC);
|
||||||
SDValue HiEq = DAG.getSetCC(DL, MVT::i16, LH, RH, ISD::SETEQ);
|
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);
|
W65816CC::CondCode TCC = normalizeCC(LHS, RHS, CC, DAG, DL);
|
||||||
if (TCC == W65816CC::COND_INVALID)
|
if (TCC == W65816CC::COND_INVALID) {
|
||||||
report_fatal_error("W65816: branch condition not yet implemented");
|
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
|
// Multi-branch CCs only have inserter support via SELECT_CC16. For
|
||||||
// BR_CC, reroute through SETCC: materialise the boolean to A, then
|
// 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::SETUGT: HiCC = ISD::SETUGT; LoCCu = ISD::SETUGT; break;
|
||||||
case ISD::SETUGE: HiCC = ISD::SETUGT; LoCCu = ISD::SETUGE; break;
|
case ISD::SETUGE: HiCC = ISD::SETUGT; LoCCu = ISD::SETUGE; break;
|
||||||
default:
|
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 HiOk = DAG.getSetCC(DL, VT, LH, RH, HiCC);
|
||||||
SDValue HiEq = DAG.getSetCC(DL, VT, LH, RH, ISD::SETEQ);
|
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);
|
W65816CC::CondCode TCC = normalizeCC(LHS, RHS, CC, DAG, DL);
|
||||||
if (TCC == W65816CC::COND_INVALID)
|
if (TCC == W65816CC::COND_INVALID) {
|
||||||
report_fatal_error("W65816: select_cc condition not yet implemented");
|
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 Glue = DAG.getNode(W65816ISD::CMP, DL, MVT::Glue, LHS, RHS);
|
||||||
SDValue CCOp = DAG.getTargetConstant(TCC, DL, MVT::i8);
|
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.
|
unsigned StackOffset = 4; // Skip 3 ret-addr bytes; first slot at S+4.
|
||||||
for (const ISD::InputArg &Arg : Ins) {
|
for (const ISD::InputArg &Arg : Ins) {
|
||||||
MVT VT = Arg.VT;
|
MVT VT = Arg.VT;
|
||||||
if (VT != MVT::i16 && VT != MVT::i8 && VT != MVT::i32)
|
if (VT != MVT::i16 && VT != MVT::i8 && VT != MVT::i32) {
|
||||||
report_fatal_error("W65816: argument type not yet supported");
|
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) {
|
if (ArgIdx == 0 && VT == MVT::i32) {
|
||||||
// Whole-i32 first arg: lo half live-in via $a, hi via $x.
|
// 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;
|
// 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
|
// i64 in A:X:Y plus DP $F0..$F1 for the highest half. See
|
||||||
// LowerReturn comment for the ABI.
|
// LowerReturn comment for the ABI.
|
||||||
if (Ins.size() > 4)
|
if (Ins.size() > 4) {
|
||||||
report_fatal_error("W65816: return type wider than 64 bits not supported");
|
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
|
// Indirect calls (function pointers): redirect through the runtime
|
||||||
// trampoline `__jsl_indir`. The 65816 has no JSL-indirect; instead,
|
// 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) {
|
for (const ISD::OutputArg &O : Outs) {
|
||||||
if (O.VT != MVT::i16 && O.VT != MVT::i8 && O.VT != MVT::i32)
|
if (O.VT != MVT::i16 && O.VT != MVT::i8 && O.VT != MVT::i32) {
|
||||||
report_fatal_error("W65816: argument type not yet supported");
|
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:
|
// 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
|
// walk it, copying from A, X, Y, DPF0 in order. Re-assemble i32
|
||||||
// halves into a Wide32 SDValue at the end.
|
// halves into a Wide32 SDValue at the end.
|
||||||
SmallVector<MVT, 4> ExpVT;
|
SmallVector<MVT, 4> ExpVT;
|
||||||
|
bool BadRet = false;
|
||||||
for (unsigned i = 0, e = Ins.size(); i != e; ++i) {
|
for (unsigned i = 0, e = Ins.size(); i != e; ++i) {
|
||||||
MVT VT = Ins[i].VT;
|
MVT VT = Ins[i].VT;
|
||||||
if (VT == MVT::i32) {
|
if (VT == MVT::i32) {
|
||||||
|
|
@ -2351,11 +2398,22 @@ W65816TargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI,
|
||||||
} else if (VT == MVT::i16 || VT == MVT::i8) {
|
} else if (VT == MVT::i16 || VT == MVT::i8) {
|
||||||
ExpVT.push_back(VT);
|
ExpVT.push_back(VT);
|
||||||
} else {
|
} 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)
|
if (!BadRet && ExpVT.size() > 4) {
|
||||||
report_fatal_error("W65816: return type wider than 64 bits not supported");
|
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,
|
static constexpr Register RetRegs[4] = {W65816::A, W65816::X, W65816::Y,
|
||||||
W65816::DPF0};
|
W65816::DPF0};
|
||||||
SmallVector<SDValue, 4> Halves;
|
SmallVector<SDValue, 4> Halves;
|
||||||
|
|
@ -2414,11 +2472,17 @@ SDValue W65816TargetLowering::LowerReturn(
|
||||||
ExpVT.push_back(VT);
|
ExpVT.push_back(VT);
|
||||||
ExpVals.push_back(OutVals[i]);
|
ExpVals.push_back(OutVals[i]);
|
||||||
} else {
|
} 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)
|
if (ExpVT.size() > 4) {
|
||||||
report_fatal_error("W65816: return type wider than 64 bits not supported");
|
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
|
// Single whole-i32 return: copy directly to AX32 instead of two
|
||||||
// halves to A and X. Saves the regalloc/coalescer some work.
|
// halves to A and X. Saves the regalloc/coalescer some work.
|
||||||
|
|
|
||||||
|
|
@ -581,8 +581,11 @@ def ASRA16 : W65816Pseudo<(outs Acc16:$dst), (ins Acc16:$src),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shifts by small constants — unroll into 2-4 single-bit shifts.
|
// 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
|
// Counts >= 8 use the XBA-and-mask trick via the SHL8A/SRL8A pseudos
|
||||||
// trick; left for a future peephole.
|
// 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 2)), (ASLA16 (ASLA16 Acc16:$src))>;
|
||||||
def : Pat<(shl Acc16:$src, (i16 3)),
|
def : Pat<(shl Acc16:$src, (i16 3)),
|
||||||
(ASLA16 (ASLA16 (ASLA16 Acc16:$src)))>;
|
(ASLA16 (ASLA16 (ASLA16 Acc16:$src)))>;
|
||||||
|
|
@ -1761,8 +1764,9 @@ def JML_Long : InstAbsLong<0x5C, "jml">;
|
||||||
|
|
||||||
//---------------------------------------------------------------- Calls
|
//---------------------------------------------------------------- Calls
|
||||||
let isCall = 1, mayLoad = 0, mayStore = 0 in {
|
let isCall = 1, mayLoad = 0, mayStore = 0 in {
|
||||||
def JSR_Abs : InstAbs<0x20, "jsr">;
|
def JSR_Abs : InstAbs<0x20, "jsr">;
|
||||||
def JSL_Long : InstAbsLong<0x22, "jsl">;
|
def JSR_AbsIndX : InstAbsIndX<0xFC, "jsr">;
|
||||||
|
def JSL_Long : InstAbsLong<0x22, "jsl">;
|
||||||
}
|
}
|
||||||
|
|
||||||
//---------------------------------------------------------------- Returns
|
//---------------------------------------------------------------- Returns
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,9 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
//
|
//
|
||||||
// This file contains code to lower W65816 MachineInstrs to their
|
// This file contains code to lower W65816 MachineInstrs to their
|
||||||
// corresponding MCInst records. The skeleton follows the MSP430 pattern
|
// corresponding MCInst records, following the MSP430 pattern. It handles
|
||||||
// closely but does not yet understand any target-specific operand flags.
|
// register, immediate, global/external-symbol, block-address, jump-table,
|
||||||
|
// and constant-pool operands (see LowerSymbolOperand).
|
||||||
//
|
//
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue