Skip to content

Commit 8556b08

Browse files
committed
Make the compiler aware that add\sub\and\or\xor can be used on memory operands
Closes #46
1 parent abc979a commit 8556b08

3 files changed

Lines changed: 202 additions & 21 deletions

File tree

llvm/lib/Target/Z80/Z80InstrInfo.cpp

Lines changed: 156 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1854,32 +1854,133 @@ bool Z80InstrInfo::expandPostRAPseudo(MachineInstr &MI) const {
18541854
// Expand 8-bit ALU Pseudos into physical accumulator sequence:
18551855
// COPY src1 → A, ALU op, COPY A → dst
18561856
case Z80::ADD8_gisel:
1857+
case Z80::ADD8_gisel_p:
1858+
case Z80::ADD8_gisel_o:
18571859
case Z80::SUB8_gisel:
1860+
case Z80::SUB8_gisel_p:
1861+
case Z80::SUB8_gisel_o:
18581862
case Z80::AND8_gisel:
1863+
case Z80::AND8_gisel_p:
1864+
case Z80::AND8_gisel_o:
18591865
case Z80::OR8_gisel:
1860-
case Z80::XOR8_gisel: {
1866+
case Z80::OR8_gisel_p:
1867+
case Z80::OR8_gisel_o:
1868+
case Z80::XOR8_gisel:
1869+
case Z80::XOR8_gisel_p:
1870+
case Z80::XOR8_gisel_o: {
18611871
unsigned RealOpc;
18621872
switch (Opc) {
18631873
default: llvm_unreachable("Unknown gisel opcode");
1864-
case Z80::ADD8_gisel: RealOpc = Z80::ADD8ar; break;
1865-
case Z80::SUB8_gisel: RealOpc = Z80::SUB8ar; break;
1866-
case Z80::AND8_gisel: RealOpc = Z80::AND8ar; break;
1867-
case Z80::OR8_gisel: RealOpc = Z80::OR8ar; break;
1868-
case Z80::XOR8_gisel: RealOpc = Z80::XOR8ar; break;
1874+
case Z80::ADD8_gisel: RealOpc = Z80::ADD8ar; break;
1875+
case Z80::ADD8_gisel_p: RealOpc = Z80::ADD8ap; break;
1876+
case Z80::ADD8_gisel_o: RealOpc = Z80::ADD8ao; break;
1877+
case Z80::SUB8_gisel: RealOpc = Z80::SUB8ar; break;
1878+
case Z80::SUB8_gisel_p: RealOpc = Z80::SUB8ap; break;
1879+
case Z80::SUB8_gisel_o: RealOpc = Z80::SUB8ao; break;
1880+
case Z80::AND8_gisel: RealOpc = Z80::AND8ar; break;
1881+
case Z80::AND8_gisel_p: RealOpc = Z80::AND8ap; break;
1882+
case Z80::AND8_gisel_o: RealOpc = Z80::AND8ao; break;
1883+
case Z80::OR8_gisel: RealOpc = Z80::OR8ar; break;
1884+
case Z80::OR8_gisel_p: RealOpc = Z80::OR8ap; break;
1885+
case Z80::OR8_gisel_o: RealOpc = Z80::OR8ao; break;
1886+
case Z80::XOR8_gisel: RealOpc = Z80::XOR8ar; break;
1887+
case Z80::XOR8_gisel_p: RealOpc = Z80::XOR8ap; break;
1888+
case Z80::XOR8_gisel_o: RealOpc = Z80::XOR8ao; break;
18691889
}
18701890
Register DstReg = MI.getOperand(0).getReg();
18711891
Register Src1 = MI.getOperand(1).getReg();
1872-
Register Src2 = MI.getOperand(2).getReg();
1892+
1893+
auto hasOtherUseOfReg = [&](MachineInstr &UseMI, Register Reg,
1894+
unsigned OperandNo) {
1895+
for (unsigned I = 0, E = UseMI.getNumOperands(); I != E; ++I) {
1896+
if (I == OperandNo)
1897+
continue;
1898+
MachineOperand &MO = UseMI.getOperand(I);
1899+
if (MO.isReg() && MO.isUse() && MO.getReg() &&
1900+
TRI.regsOverlap(MO.getReg(), Reg))
1901+
return true;
1902+
}
1903+
return false;
1904+
};
1905+
1906+
if (Src1 != Z80::A && MI.getOperand(1).isKill() &&
1907+
!hasOtherUseOfReg(MI, Src1, 1) &&
1908+
MachineBasicBlock::iterator(MI) != MBB.begin()) {
1909+
auto PrevMI = prev_nodbg(MachineBasicBlock::iterator(MI), MBB.begin());
1910+
if (!PrevMI->isDebugInstr() &&
1911+
(PrevMI->getOpcode() == Z80::LD8gp ||
1912+
PrevMI->getOpcode() == Z80::LD8go) &&
1913+
PrevMI->getOperand(0).getReg() == Src1) {
1914+
PrevMI->getOperand(0).setReg(Z80::A);
1915+
MI.getOperand(1).setReg(Z80::A);
1916+
Src1 = Z80::A;
1917+
}
1918+
}
18731919

18741920
// 1. Copy Src1 to Accumulator (if not already there)
18751921
if (Src1 != Z80::A)
18761922
copyPhysReg(MBB, MI, DL, Z80::A, Src1, true);
18771923

18781924
// 2. Execute ALU Instruction (implicitly uses A, defines A and F)
1879-
BuildMI(MBB, MI, DL, get(RealOpc)).addReg(Src2);
1925+
MachineInstrBuilder Alu = BuildMI(MBB, MI, DL, get(RealOpc));
1926+
switch (Opc) {
1927+
default:
1928+
Alu.add(MI.getOperand(2));
1929+
break;
1930+
case Z80::ADD8_gisel_o:
1931+
case Z80::SUB8_gisel_o:
1932+
case Z80::AND8_gisel_o:
1933+
case Z80::OR8_gisel_o:
1934+
case Z80::XOR8_gisel_o:
1935+
Alu.add(MI.getOperand(2)).add(MI.getOperand(3));
1936+
break;
1937+
}
1938+
Alu.cloneMemRefs(MI);
18801939

18811940
// 3. Copy Accumulator to Dst (if Dst is not A)
1882-
if (DstReg != Z80::A)
1941+
auto forwardAResultToOperand = [&](MachineInstr &UseMI, unsigned OperandNo) {
1942+
MachineOperand &UseMO = UseMI.getOperand(OperandNo);
1943+
if (UseMO.getReg() != DstReg || !UseMO.isKill() ||
1944+
hasOtherUseOfReg(UseMI, DstReg, OperandNo))
1945+
return false;
1946+
UseMO.setReg(Z80::A);
1947+
return true;
1948+
};
1949+
1950+
auto canForwardAResult = [&]() {
1951+
if (MI.getOperand(0).isDead())
1952+
return true;
1953+
1954+
auto NextMI = skipDebugInstructionsForward(Next, MBB.end());
1955+
if (NextMI == MBB.end())
1956+
return false;
1957+
1958+
switch (NextMI->getOpcode()) {
1959+
default:
1960+
return false;
1961+
case Z80::ADD8_gisel:
1962+
case Z80::ADD8_gisel_p:
1963+
case Z80::ADD8_gisel_o:
1964+
case Z80::SUB8_gisel:
1965+
case Z80::SUB8_gisel_p:
1966+
case Z80::SUB8_gisel_o:
1967+
case Z80::AND8_gisel:
1968+
case Z80::AND8_gisel_p:
1969+
case Z80::AND8_gisel_o:
1970+
case Z80::OR8_gisel:
1971+
case Z80::OR8_gisel_p:
1972+
case Z80::OR8_gisel_o:
1973+
case Z80::XOR8_gisel:
1974+
case Z80::XOR8_gisel_p:
1975+
case Z80::XOR8_gisel_o:
1976+
case Z80::LD8pg:
1977+
return forwardAResultToOperand(*NextMI, 1);
1978+
case Z80::LD8og:
1979+
return forwardAResultToOperand(*NextMI, 2);
1980+
}
1981+
};
1982+
1983+
if (DstReg != Z80::A && !canForwardAResult())
18831984
copyPhysReg(MBB, MI, DL, DstReg, Z80::A, true);
18841985

18851986
MI.eraseFromParent();
@@ -2657,6 +2758,42 @@ MachineInstr *Z80InstrInfo::foldMemoryOperandImpl(
26572758

26582759
unsigned Opc;
26592760
unsigned OpSize = 1;
2761+
if (OpNum == 1) {
2762+
unsigned CommuteOpc = 0;
2763+
switch (MI.getOpcode()) {
2764+
default:
2765+
break;
2766+
case Z80::ADD8_gisel:
2767+
CommuteOpc = IsOff ? Z80::ADD8_gisel_o : Z80::ADD8_gisel_p;
2768+
break;
2769+
case Z80::AND8_gisel:
2770+
CommuteOpc = IsOff ? Z80::AND8_gisel_o : Z80::AND8_gisel_p;
2771+
break;
2772+
case Z80::XOR8_gisel:
2773+
CommuteOpc = IsOff ? Z80::XOR8_gisel_o : Z80::XOR8_gisel_p;
2774+
break;
2775+
case Z80::OR8_gisel:
2776+
CommuteOpc = IsOff ? Z80::OR8_gisel_o : Z80::OR8_gisel_p;
2777+
break;
2778+
}
2779+
2780+
if (CommuteOpc) {
2781+
if (Size && Size != OpSize)
2782+
return nullptr;
2783+
2784+
MachineInstrBuilder MIB(
2785+
MF, MF.CreateMachineInstr(get(CommuteOpc), MI.getDebugLoc(), true));
2786+
MIB.add(MI.getOperand(0)).add(MI.getOperand(2));
2787+
for (auto &AddrMO : MOs)
2788+
MIB.add(AddrMO);
2789+
for (auto &MO : MI.implicit_operands())
2790+
MIB.add(MO);
2791+
updateOperandRegConstraints(MF, *MIB);
2792+
InsertPt->getParent()->insert(InsertPt, MIB);
2793+
return MIB;
2794+
}
2795+
}
2796+
26602797
switch (OpNum) {
26612798
default: return nullptr;
26622799
case 0:
@@ -2692,6 +2829,16 @@ MachineInstr *Z80InstrInfo::foldMemoryOperandImpl(
26922829
break;
26932830
}
26942831
break;
2832+
case 2:
2833+
switch (MI.getOpcode()) {
2834+
default: return nullptr;
2835+
case Z80::ADD8_gisel: Opc = IsOff ? Z80::ADD8_gisel_o : Z80::ADD8_gisel_p; break;
2836+
case Z80::SUB8_gisel: Opc = IsOff ? Z80::SUB8_gisel_o : Z80::SUB8_gisel_p; break;
2837+
case Z80::AND8_gisel: Opc = IsOff ? Z80::AND8_gisel_o : Z80::AND8_gisel_p; break;
2838+
case Z80::XOR8_gisel: Opc = IsOff ? Z80::XOR8_gisel_o : Z80::XOR8_gisel_p; break;
2839+
case Z80::OR8_gisel: Opc = IsOff ? Z80::OR8_gisel_o : Z80::OR8_gisel_p; break;
2840+
}
2841+
break;
26952842
}
26962843

26972844
if (Size && Size != OpSize)

llvm/lib/Target/Z80/Z80InstrInfo.td

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -950,11 +950,23 @@ class GiselBinOp8Pseudo<SDNode opnode>
950950
let Defs = [A, F];
951951
}
952952

953-
def ADD8_gisel : GiselBinOp8Pseudo<add>;
954-
def SUB8_gisel : GiselBinOp8Pseudo<sub>;
955-
def AND8_gisel : GiselBinOp8Pseudo<and>;
956-
def OR8_gisel : GiselBinOp8Pseudo<or>;
957-
def XOR8_gisel : GiselBinOp8Pseudo<xor>;
953+
let mayLoad = true in
954+
class GiselBinOp8MemPseudo<Operand memop>
955+
: P<(outs R8:$dst), (ins R8:$src1, memop:$src2)> {
956+
let Defs = [A, F];
957+
}
958+
959+
multiclass GiselBinOp8Pseudos<SDNode opnode> {
960+
def "" : GiselBinOp8Pseudo<opnode>;
961+
def _p : GiselBinOp8MemPseudo<aptr>;
962+
def _o : GiselBinOp8MemPseudo<off>;
963+
}
964+
965+
defm ADD8_gisel : GiselBinOp8Pseudos<add>;
966+
defm SUB8_gisel : GiselBinOp8Pseudos<sub>;
967+
defm AND8_gisel : GiselBinOp8Pseudos<and>;
968+
defm OR8_gisel : GiselBinOp8Pseudos<or>;
969+
defm XOR8_gisel : GiselBinOp8Pseudos<xor>;
958970

959971
defm ADD : BinOp8RF < 0, "add">;
960972
defm ADC : BinOp8RFF< 1, "adc", adde>;

llvm/lib/Target/Z80/Z80MachineLateOptimization.cpp

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1558,10 +1558,20 @@ bool Z80MachineLateOptimization::runOnMachineFunction(MachineFunction &MF) {
15581558
// and implicit def A (because Z80 ALU ops always write to A)
15591559
// after these instructions, A and the dest register hold the same value
15601560
case Z80::ADD8_gisel:
1561+
case Z80::ADD8_gisel_p:
1562+
case Z80::ADD8_gisel_o:
15611563
case Z80::SUB8_gisel:
1564+
case Z80::SUB8_gisel_p:
1565+
case Z80::SUB8_gisel_o:
15621566
case Z80::AND8_gisel:
1567+
case Z80::AND8_gisel_p:
1568+
case Z80::AND8_gisel_o:
15631569
case Z80::OR8_gisel:
1564-
case Z80::XOR8_gisel: {
1570+
case Z80::OR8_gisel_p:
1571+
case Z80::OR8_gisel_o:
1572+
case Z80::XOR8_gisel:
1573+
case Z80::XOR8_gisel_p:
1574+
case Z80::XOR8_gisel_o: {
15651575
MCRegister ExplicitDst = MIB->getOperand(0).getReg();
15661576
// after ALU pseudo, explicit dest and A have the same value if dest is not A itself, record that dest mirrors A
15671577
if (ExplicitDst != Z80::A) {
@@ -1695,16 +1705,28 @@ bool Z80MachineLateOptimization::runOnMachineFunction(MachineFunction &MF) {
16951705
if (ClobberedA) {
16961706
bool isCopyFromA = Opc == TargetOpcode::COPY &&
16971707
MIB->getOperand(1).getReg() == Z80::A;
1698-
bool isALUPseudo = (Opc == Z80::ADD8_gisel || Opc == Z80::SUB8_gisel ||
1699-
Opc == Z80::AND8_gisel || Opc == Z80::OR8_gisel ||
1700-
Opc == Z80::XOR8_gisel);
1708+
bool isALUPseudo =
1709+
(Opc == Z80::ADD8_gisel || Opc == Z80::ADD8_gisel_p ||
1710+
Opc == Z80::ADD8_gisel_o || Opc == Z80::SUB8_gisel ||
1711+
Opc == Z80::SUB8_gisel_p || Opc == Z80::SUB8_gisel_o ||
1712+
Opc == Z80::AND8_gisel || Opc == Z80::AND8_gisel_p ||
1713+
Opc == Z80::AND8_gisel_o || Opc == Z80::OR8_gisel ||
1714+
Opc == Z80::OR8_gisel_p || Opc == Z80::OR8_gisel_o ||
1715+
Opc == Z80::XOR8_gisel || Opc == Z80::XOR8_gisel_p ||
1716+
Opc == Z80::XOR8_gisel_o);
17011717
if (!isCopyFromA && !isALUPseudo)
17021718
AMirroredInReg = Z80::NoRegister;
17031719
}
17041720
if (AMirroredInReg != Z80::NoRegister) {
1705-
bool isALUPseudoOrCopy = (Opc == Z80::ADD8_gisel || Opc == Z80::SUB8_gisel ||
1706-
Opc == Z80::AND8_gisel || Opc == Z80::OR8_gisel ||
1707-
Opc == Z80::XOR8_gisel || Opc == TargetOpcode::COPY);
1721+
bool isALUPseudoOrCopy =
1722+
(Opc == Z80::ADD8_gisel || Opc == Z80::ADD8_gisel_p ||
1723+
Opc == Z80::ADD8_gisel_o || Opc == Z80::SUB8_gisel ||
1724+
Opc == Z80::SUB8_gisel_p || Opc == Z80::SUB8_gisel_o ||
1725+
Opc == Z80::AND8_gisel || Opc == Z80::AND8_gisel_p ||
1726+
Opc == Z80::AND8_gisel_o || Opc == Z80::OR8_gisel ||
1727+
Opc == Z80::OR8_gisel_p || Opc == Z80::OR8_gisel_o ||
1728+
Opc == Z80::XOR8_gisel || Opc == Z80::XOR8_gisel_p ||
1729+
Opc == Z80::XOR8_gisel_o || Opc == TargetOpcode::COPY);
17081730
if (!isALUPseudoOrCopy) {
17091731
for (MachineOperand &MO : MIB->operands()) {
17101732
if (MO.isReg() && MO.isDef()) {

0 commit comments

Comments
 (0)